1use std::{collections::BTreeMap, io, sync::Arc};
4
5use bytes::{Buf, Bytes, BytesMut};
6use rustls::{
7 ClientConfig, ServerConfig,
8 pki_types::{CertificateDer, ServerName},
9};
10use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
11use tokio_util::codec::{Decoder, Encoder};
12
13use crate::{
14 Conn,
15 auth::TlsServerEndPoint,
16 codec::{Backend, BackendMessage, Direction, Frame, Frontend, FrontendMessage, PgCodec},
17 demux::{
18 CancelKey, Demux, Notification, OrderedAsyncEvent, ParameterStatus, SessionItem,
19 TaggedNotice,
20 },
21 pre_startup::{
22 AwaitingSslReply, DEFAULT_MAX_PRE_STARTUP_PACKET_LEN, EncryptionReply, Negotiation,
23 PreStartup, PreStartupMessage, ServerSslDecision, SslMode, SslModeNegotiation,
24 TlsHandshake, decode_pre_startup_with_limit, gssenc_request_packet, ssl_request_packet,
25 },
26 tls::{ClientTls, ServerTls},
27};
28
29#[derive(Debug)]
31pub struct Buffered<S, D = Backend> {
32 io: S,
33 outbound: BytesMut,
34 inbound: BytesMut,
35 inbound_codec: PgCodec<D>,
36 max_pre_startup_packet_len: usize,
37 demux: Demux,
38}
39
40impl<S> Buffered<S, Backend> {
41 pub fn new(io: S) -> Self {
43 Self {
44 io,
45 outbound: BytesMut::new(),
46 inbound: BytesMut::new(),
47 inbound_codec: PgCodec::default(),
48 max_pre_startup_packet_len: DEFAULT_MAX_PRE_STARTUP_PACKET_LEN,
49 demux: Demux::default(),
50 }
51 }
52
53 pub fn with_max_frame_len(io: S, max_frame_len: usize) -> io::Result<Self> {
59 Ok(Self {
60 io,
61 outbound: BytesMut::new(),
62 inbound: BytesMut::new(),
63 inbound_codec: PgCodec::with_max_frame_len(max_frame_len)?,
64 max_pre_startup_packet_len: DEFAULT_MAX_PRE_STARTUP_PACKET_LEN,
65 demux: Demux::default(),
66 })
67 }
68}
69
70impl<S> Buffered<S, Frontend> {
71 pub fn new_frontend(io: S) -> Self {
73 Self {
74 io,
75 outbound: BytesMut::new(),
76 inbound: BytesMut::new(),
77 inbound_codec: PgCodec::default(),
78 max_pre_startup_packet_len: DEFAULT_MAX_PRE_STARTUP_PACKET_LEN,
79 demux: Demux::default(),
80 }
81 }
82
83 pub fn with_max_frame_len_frontend(io: S, max_frame_len: usize) -> io::Result<Self> {
89 Self::with_limits_frontend(io, max_frame_len, DEFAULT_MAX_PRE_STARTUP_PACKET_LEN)
90 }
91
92 pub fn with_limits_frontend(
98 io: S,
99 max_frame_len: usize,
100 max_pre_startup_packet_len: usize,
101 ) -> io::Result<Self> {
102 if !(8..=i32::MAX as usize).contains(&max_pre_startup_packet_len) {
103 return Err(io::Error::new(
104 io::ErrorKind::InvalidInput,
105 "pre-startup packet limit must be between 8 and i32::MAX bytes",
106 ));
107 }
108 Ok(Self {
109 io,
110 outbound: BytesMut::new(),
111 inbound: BytesMut::new(),
112 inbound_codec: PgCodec::with_max_frame_len(max_frame_len)?,
113 max_pre_startup_packet_len,
114 demux: Demux::default(),
115 })
116 }
117}
118
119impl<S, D> Buffered<S, D> {
120 pub fn push(&mut self, frame: Frame) -> io::Result<()> {
126 self.inbound_codec.encode(frame, &mut self.outbound)
127 }
128
129 #[must_use]
130 pub fn pending(&self) -> &[u8] {
132 &self.outbound
133 }
134
135 pub fn into_inner(self) -> S {
137 self.io
138 }
139
140 fn push_raw(&mut self, bytes: &[u8]) {
141 self.outbound.extend_from_slice(bytes);
142 }
143
144 #[must_use]
145 pub const fn demux(&self) -> &Demux {
147 &self.demux
148 }
149
150 pub const fn demux_mut(&mut self) -> &mut Demux {
152 &mut self.demux
153 }
154}
155
156impl<S, D> Buffered<S, D>
157where
158 S: AsyncRead + AsyncWrite + Unpin,
159{
160 async fn connect_tls(
161 self,
162 server_name: ServerName<'static>,
163 config: Arc<ClientConfig>,
164 ) -> io::Result<Buffered<ClientTls<S>, D>> {
165 if !self.outbound.is_empty() || !self.inbound.is_empty() {
166 return Err(io::Error::new(
167 io::ErrorKind::InvalidInput,
168 "TLS upgrade requires empty plaintext buffers",
169 ));
170 }
171 Ok(Buffered {
172 io: crate::tls::connect(self.io, server_name, config).await?,
173 outbound: self.outbound,
174 inbound: self.inbound,
175 inbound_codec: self.inbound_codec,
176 max_pre_startup_packet_len: self.max_pre_startup_packet_len,
177 demux: self.demux,
178 })
179 }
180
181 async fn accept_tls(
182 self,
183 config: Arc<ServerConfig>,
184 leaf_certificate: CertificateDer<'static>,
185 ) -> io::Result<Buffered<ServerTls<S>, D>> {
186 if !self.outbound.is_empty() || !self.inbound.is_empty() {
187 return Err(io::Error::new(
188 io::ErrorKind::InvalidInput,
189 "TLS upgrade requires empty plaintext buffers",
190 ));
191 }
192 Ok(Buffered {
193 io: crate::tls::accept(self.io, config, &leaf_certificate).await?,
194 outbound: self.outbound,
195 inbound: self.inbound,
196 inbound_codec: self.inbound_codec,
197 max_pre_startup_packet_len: self.max_pre_startup_packet_len,
198 demux: self.demux,
199 })
200 }
201}
202
203impl<S: TlsServerEndPoint, D> TlsServerEndPoint for Buffered<S, D> {
204 fn tls_server_end_point(&self) -> &[u8] {
205 self.io.tls_server_end_point()
206 }
207}
208
209impl<S: AsyncWrite + Unpin, D> Buffered<S, D> {
210 pub async fn flush(&mut self) -> io::Result<()> {
220 while !self.outbound.is_empty() {
221 let written = self.io.write(&self.outbound).await?;
222 if written == 0 {
223 return Err(io::Error::new(
224 io::ErrorKind::WriteZero,
225 "transport wrote zero buffered bytes",
226 ));
227 }
228 self.outbound.advance(written);
229 }
230 self.io.flush().await
231 }
232}
233
234impl<S: AsyncRead + Unpin, D: Direction> Buffered<S, D> {
235 pub async fn receive_wire(&mut self) -> io::Result<D::Message> {
241 loop {
242 if let Some(message) = self.inbound_codec.decode(&mut self.inbound)? {
243 return Ok(message);
244 }
245 if self.io.read_buf(&mut self.inbound).await? == 0 {
246 return Err(io::Error::new(
247 io::ErrorKind::UnexpectedEof,
248 "peer closed with no complete message",
249 ));
250 }
251 }
252 }
253}
254
255impl<S: AsyncRead + Unpin> Buffered<S, Backend> {
256 async fn receive_encryption_reply(&mut self) -> io::Result<EncryptionReply> {
257 let byte = self.io.read_u8().await?;
258 EncryptionReply::try_from(byte)
259 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid encryption reply"))
260 }
261}
262
263impl<S: AsyncRead + Unpin> Buffered<S, Frontend> {
264 pub async fn receive_pre_startup(&mut self) -> io::Result<PreStartupMessage> {
270 loop {
271 if let Some(message) =
272 decode_pre_startup_with_limit(&mut self.inbound, self.max_pre_startup_packet_len)?
273 {
274 return Ok(message);
275 }
276 if self.io.read_buf(&mut self.inbound).await? == 0 {
277 return Err(io::Error::new(
278 io::ErrorKind::UnexpectedEof,
279 "client closed with no complete pre-startup packet",
280 ));
281 }
282 }
283 }
284}
285
286impl<S: AsyncRead + Unpin> Buffered<S, Backend> {
287 pub async fn receive_backend(&mut self) -> io::Result<BackendMessage> {
293 self.receive_wire().await
294 }
295
296 pub async fn receive_session(&mut self) -> io::Result<SessionItem> {
302 loop {
303 let message = self.receive_backend().await?;
304 if let Some(item) = self.project_backend(message) {
305 return Ok(item);
306 }
307 }
308 }
309
310 pub fn project_backend(&mut self, message: BackendMessage) -> Option<SessionItem> {
312 self.demux.route(message)
313 }
314}
315
316impl<S, D, Phase, Cleanliness> Conn<Buffered<S, D>, Phase, Cleanliness> {
317 pub fn push_frame(&mut self, frame: Frame) -> io::Result<()> {
323 self.transport_mut().push(frame)
324 }
325
326 #[must_use]
327 pub fn pending_output(&self) -> &[u8] {
329 self.transport().pending()
330 }
331}
332
333impl<S, Cleanliness> Conn<Buffered<S, Backend>, PreStartup, Cleanliness> {
334 pub fn request_ssl(mut self) -> Conn<Buffered<S, Backend>, AwaitingSslReply, Cleanliness> {
336 self.transport_mut().push_raw(&ssl_request_packet());
337 self.transition()
338 }
339
340 pub fn request_gss(
342 mut self,
343 ) -> Conn<Buffered<S, Backend>, crate::pre_startup::AwaitingGssReply, Cleanliness> {
344 self.transport_mut().push_raw(&gssenc_request_packet());
345 self.transition()
346 }
347}
348
349impl<S, Cleanliness> Conn<Buffered<S, Frontend>, ServerSslDecision, Cleanliness> {
350 pub fn approve_ssl(mut self) -> Conn<Buffered<S, Frontend>, TlsHandshake, Cleanliness> {
352 self.transport_mut().push_raw(b"S");
353 self.transition()
354 }
355
356 pub fn decline_ssl(mut self) -> Conn<Buffered<S, Frontend>, PreStartup, Cleanliness> {
358 self.transport_mut().push_raw(b"N");
359 self.transition()
360 }
361
362 pub fn reject_ssl_with_legacy_error(
364 mut self,
365 ) -> Conn<Buffered<S, Frontend>, crate::pre_startup::Terminated, Cleanliness> {
366 self.transport_mut().push_raw(b"E");
367 self.transition()
368 }
369}
370
371impl<S, Cleanliness>
372 Conn<Buffered<S, Frontend>, crate::pre_startup::ServerGssDecision, Cleanliness>
373{
374 pub fn approve_gss(
376 mut self,
377 ) -> Conn<Buffered<S, Frontend>, crate::pre_startup::GssHandshake, Cleanliness> {
378 self.transport_mut().push_raw(b"S");
379 self.transition()
380 }
381
382 pub fn decline_gss(mut self) -> Conn<Buffered<S, Frontend>, PreStartup, Cleanliness> {
384 self.transport_mut().push_raw(b"N");
385 self.transition()
386 }
387
388 pub fn reject_gss_with_legacy_error(
390 mut self,
391 ) -> Conn<Buffered<S, Frontend>, crate::pre_startup::Terminated, Cleanliness> {
392 self.transport_mut().push_raw(b"E");
393 self.transition()
394 }
395}
396
397impl<S: AsyncRead + Unpin, Cleanliness> Conn<Buffered<S, Backend>, AwaitingSslReply, Cleanliness> {
398 pub async fn receive_ssl_reply(
404 mut self,
405 ) -> io::Result<Negotiation<Buffered<S, Backend>, TlsHandshake, Cleanliness>> {
406 let reply = self.transport_mut().receive_encryption_reply().await?;
407 Ok(match reply {
408 EncryptionReply::Accepted => Negotiation::Accepted(self.transition()),
409 EncryptionReply::Rejected => Negotiation::Rejected(self.transition()),
410 EncryptionReply::LegacyError => Negotiation::LegacyError(self.transition()),
411 })
412 }
413
414 pub async fn receive_ssl_reply_for_mode(
420 mut self,
421 mode: SslMode,
422 ) -> io::Result<SslModeNegotiation<Buffered<S, Backend>, Cleanliness>> {
423 let reply = self.transport_mut().receive_encryption_reply().await?;
424 Ok(self.apply_ssl_reply(reply, mode))
425 }
426}
427
428impl<S: AsyncRead + Unpin, Cleanliness>
429 Conn<Buffered<S, Backend>, crate::pre_startup::AwaitingGssReply, Cleanliness>
430{
431 pub async fn receive_gss_reply(
437 mut self,
438 ) -> io::Result<Negotiation<Buffered<S, Backend>, crate::pre_startup::GssHandshake, Cleanliness>>
439 {
440 let reply = self.transport_mut().receive_encryption_reply().await?;
441 Ok(match reply {
442 EncryptionReply::Accepted => Negotiation::Accepted(self.transition()),
443 EncryptionReply::Rejected => Negotiation::Rejected(self.transition()),
444 EncryptionReply::LegacyError => Negotiation::LegacyError(self.transition()),
445 })
446 }
447}
448
449impl<S, Cleanliness> Conn<Buffered<S, Backend>, TlsHandshake, Cleanliness>
450where
451 S: AsyncRead + AsyncWrite + Unpin,
452{
453 pub async fn connect_tls(
459 self,
460 server_name: ServerName<'static>,
461 config: Arc<ClientConfig>,
462 ) -> io::Result<Conn<Buffered<ClientTls<S>, Backend>, PreStartup, Cleanliness>> {
463 let transport = self.into_transport();
464 Ok(Conn::new(transport.connect_tls(server_name, config).await?)
465 .transition::<PreStartup, Cleanliness>())
466 }
467}
468
469impl<S, Cleanliness> Conn<Buffered<S, Frontend>, TlsHandshake, Cleanliness>
470where
471 S: AsyncRead + AsyncWrite + Unpin,
472{
473 pub async fn accept_tls(
479 self,
480 config: Arc<ServerConfig>,
481 leaf_certificate: CertificateDer<'static>,
482 ) -> io::Result<Conn<Buffered<ServerTls<S>, Frontend>, PreStartup, Cleanliness>> {
483 let transport = self.into_transport();
484 Ok(
485 Conn::new(transport.accept_tls(config, leaf_certificate).await?)
486 .transition::<PreStartup, Cleanliness>(),
487 )
488 }
489}
490
491impl<S, D, Cleanliness> Conn<Buffered<S, D>, crate::pre_startup::Startup, Cleanliness> {
492 pub fn push_startup_packet(&mut self, packet: &[u8]) {
494 self.transport_mut().outbound.extend_from_slice(packet);
495 }
496}
497
498impl<S: AsyncWrite + Unpin, D, Phase, Cleanliness> Conn<Buffered<S, D>, Phase, Cleanliness> {
499 pub async fn flush(&mut self) -> io::Result<()> {
505 self.transport_mut().flush().await
506 }
507}
508
509impl<S: AsyncRead + Unpin, Phase, Cleanliness> Conn<Buffered<S, Backend>, Phase, Cleanliness> {
510 pub async fn receive_backend_wire(&mut self) -> io::Result<BackendMessage> {
517 self.transport_mut().receive_backend().await
518 }
519
520 pub fn project_backend(&mut self, message: BackendMessage) -> Option<SessionItem> {
522 self.transport_mut().project_backend(message)
523 }
524
525 pub async fn receive(&mut self) -> io::Result<SessionItem> {
531 self.transport_mut().receive_session().await
532 }
533
534 #[must_use]
535 pub fn cancel_key(&self) -> Option<&CancelKey> {
537 self.transport().demux().cancel_key()
538 }
539
540 #[must_use]
542 pub fn parameters(&self) -> &BTreeMap<Bytes, Bytes> {
543 self.transport().demux().parameters()
544 }
545
546 #[must_use]
548 pub fn parameters_changed(&self) -> bool {
549 self.transport().demux().parameters_changed()
550 }
551
552 #[must_use]
554 pub fn transaction_status(&self) -> Option<crate::codec::TransactionStatus> {
555 self.transport().demux().transaction_status()
556 }
557
558 pub fn pop_notification(&mut self) -> Option<Notification> {
560 self.transport_mut().demux_mut().pop_notification()
561 }
562
563 pub fn pop_notice(&mut self) -> Option<TaggedNotice> {
565 self.transport_mut().demux_mut().pop_notice()
566 }
567
568 pub fn pop_parameter_status(&mut self) -> Option<ParameterStatus> {
570 self.transport_mut().demux_mut().pop_parameter_status()
571 }
572
573 pub fn pop_async_event(&mut self) -> Option<OrderedAsyncEvent> {
575 self.transport_mut().demux_mut().pop_async_event()
576 }
577}
578
579impl<S: AsyncRead + Unpin, Phase, Cleanliness> Conn<Buffered<S, Frontend>, Phase, Cleanliness> {
580 pub async fn receive_frontend_wire(&mut self) -> io::Result<FrontendMessage> {
586 self.transport_mut().receive_wire().await
587 }
588}
589
590impl<S: AsyncRead + Unpin, Cleanliness> Conn<Buffered<S, Frontend>, PreStartup, Cleanliness> {
591 pub async fn receive_pre_startup_wire(&mut self) -> io::Result<PreStartupMessage> {
597 self.transport_mut().receive_pre_startup().await
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use std::{
604 future::Future,
605 pin::Pin,
606 task::{Context, Poll},
607 };
608
609 use bytes::Bytes;
610 use tokio::io::AsyncWrite;
611
612 use super::*;
613
614 #[derive(Debug, Default)]
615 struct ShortWriter {
616 output: Vec<u8>,
617 }
618
619 impl AsyncWrite for ShortWriter {
620 fn poll_write(
621 mut self: Pin<&mut Self>,
622 _cx: &mut Context<'_>,
623 buffer: &[u8],
624 ) -> Poll<io::Result<usize>> {
625 let written = buffer.len().min(2);
626 self.output.extend_from_slice(&buffer[..written]);
627 Poll::Ready(Ok(written))
628 }
629
630 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
631 Poll::Ready(Ok(()))
632 }
633
634 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
635 Poll::Ready(Ok(()))
636 }
637 }
638
639 #[tokio::test]
640 async fn flush_handles_partial_writes_without_losing_bytes() {
641 let frame = Frame {
642 tag: b'S',
643 body: Bytes::new(),
644 };
645 let mut transport = Buffered::new(ShortWriter::default());
646 transport.push(frame).expect("encodable frame");
647 assert_eq!(transport.pending(), &[b'S', 0, 0, 0, 4]);
648 transport.flush().await.expect("writable transport");
649 assert!(transport.pending().is_empty());
650 assert_eq!(transport.into_inner().output, [b'S', 0, 0, 0, 4]);
651 }
652
653 #[test]
654 fn buffered_transport_enforces_its_frame_limit_on_output() {
655 let mut transport = Buffered::<_, Backend>::with_max_frame_len((), 9).unwrap();
656 let error = transport
657 .push(Frame {
658 tag: b'Q',
659 body: Bytes::from_static(b"12345"),
660 })
661 .unwrap_err();
662 assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
663 assert!(transport.pending().is_empty());
664 }
665
666 #[test]
667 fn cancelling_flush_retains_unwritten_bytes() {
668 #[derive(Debug, Default)]
669 struct PausingWriter {
670 output: Vec<u8>,
671 blocked: bool,
672 }
673
674 impl AsyncWrite for PausingWriter {
675 fn poll_write(
676 mut self: Pin<&mut Self>,
677 _cx: &mut Context<'_>,
678 buffer: &[u8],
679 ) -> Poll<io::Result<usize>> {
680 if self.blocked {
681 return Poll::Pending;
682 }
683 let written = buffer.len().min(2);
684 self.output.extend_from_slice(&buffer[..written]);
685 self.blocked = true;
686 Poll::Ready(Ok(written))
687 }
688
689 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
690 Poll::Ready(Ok(()))
691 }
692
693 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
694 Poll::Ready(Ok(()))
695 }
696 }
697
698 let mut transport = Buffered::new(PausingWriter::default());
699 transport
700 .push(Frame {
701 tag: b'S',
702 body: Bytes::new(),
703 })
704 .expect("encodable frame");
705
706 let mut flush = Box::pin(transport.flush());
707 let waker = std::task::Waker::noop();
708 let mut context = Context::from_waker(waker);
709 assert!(flush.as_mut().poll(&mut context).is_pending());
710 drop(flush);
711
712 assert_eq!(transport.pending(), &[0, 0, 4]);
713 assert_eq!(transport.io.output, [b'S', 0]);
714 }
715
716 #[tokio::test]
717 async fn receive_filters_parameter_status_before_session_message() {
718 let (client, mut server) = tokio::io::duplex(256);
719 let mut wire = BytesMut::new();
720 let mut encoder = PgCodec::<Backend>::default();
721 encoder
722 .encode(
723 Frame {
724 tag: b'S',
725 body: Bytes::from_static(b"client_encoding\0UTF8\0"),
726 },
727 &mut wire,
728 )
729 .expect("encodable ParameterStatus");
730 encoder
731 .encode(
732 Frame {
733 tag: b'Z',
734 body: Bytes::from_static(b"I"),
735 },
736 &mut wire,
737 )
738 .expect("encodable ReadyForQuery");
739 server.write_all(&wire).await.expect("writable test peer");
740
741 let mut transport = Buffered::new(client);
742 assert_eq!(
743 transport.receive_session().await.expect("valid messages"),
744 SessionItem::ReadyForQuery {
745 status: crate::codec::TransactionStatus::Idle,
746 parameters_changed: false,
747 }
748 );
749 assert_eq!(
750 transport
751 .demux()
752 .parameters()
753 .get(&Bytes::from_static(b"client_encoding")),
754 Some(&Bytes::from_static(b"UTF8"))
755 );
756 let conn: Conn<_, crate::auth::Ready> = Conn::new(transport).transition();
757 assert_eq!(
758 conn.parameters().get(b"client_encoding".as_slice()),
759 Some(&Bytes::from_static(b"UTF8"))
760 );
761 assert!(!conn.parameters_changed());
762 assert_eq!(
763 conn.transaction_status(),
764 Some(crate::codec::TransactionStatus::Idle)
765 );
766 conn.into_transport();
767 }
768
769 #[tokio::test]
770 async fn wire_message_can_be_modified_before_projection() {
771 let (client, mut server) = tokio::io::duplex(128);
772 let original = BackendMessage::ParameterStatus {
773 name: Bytes::from_static(b"application_name"),
774 value: Bytes::from_static(b"upstream"),
775 };
776 let mut bytes = BytesMut::new();
777 PgCodec::<Backend>::default()
778 .encode(
779 original.to_frame().expect("reconstructable message"),
780 &mut bytes,
781 )
782 .expect("encodable message");
783 server.write_all(&bytes).await.expect("writable test peer");
784
785 let mut transport = Buffered::new(client);
786 let mut message = transport
787 .receive_backend()
788 .await
789 .expect("decodable message");
790 let BackendMessage::ParameterStatus { value, .. } = &mut message else {
791 panic!("unexpected message")
792 };
793 *value = Bytes::from_static(b"proxy");
794 assert!(transport.project_backend(message).is_none());
795 assert_eq!(
796 transport
797 .demux()
798 .parameters()
799 .get(&Bytes::from_static(b"application_name")),
800 Some(&Bytes::from_static(b"proxy"))
801 );
802 }
803
804 #[tokio::test]
805 async fn client_facing_transport_intercepts_typed_frontend_messages() {
806 let (proxy, mut client) = tokio::io::duplex(128);
807 let message = FrontendMessage::Query(Bytes::from_static(b"select plaintext"));
808 let mut bytes = BytesMut::new();
809 PgCodec::<Frontend>::default()
810 .encode(
811 message.to_frame().expect("reconstructable Query"),
812 &mut bytes,
813 )
814 .expect("encodable Query");
815 client.write_all(&bytes).await.expect("writable client");
816
817 let mut transport = Buffered::<_, Frontend>::new_frontend(proxy);
818 let mut intercepted = transport.receive_wire().await.expect("decodable Query");
819 let FrontendMessage::Query(query) = &mut intercepted else {
820 panic!("unexpected frontend message")
821 };
822 *query = Bytes::from_static(b"select encrypted");
823 assert_eq!(
824 intercepted,
825 FrontendMessage::Query(Bytes::from_static(b"select encrypted"))
826 );
827 }
828
829 #[tokio::test]
830 async fn client_facing_transport_projects_repeated_pre_startup_choice() {
831 let (proxy, mut client) = tokio::io::duplex(256);
832 let ssl = PreStartupMessage::SslRequest
833 .to_packet()
834 .expect("encodable SSLRequest");
835 let startup = PreStartupMessage::Startup(crate::startup::StartupMessage {
836 version: crate::startup::ProtocolVersion::V3_2,
837 parameters: std::collections::BTreeMap::from([(
838 Bytes::from_static(b"user"),
839 Bytes::from_static(b"postgres"),
840 )]),
841 });
842 let startup_packet = startup.to_packet().expect("encodable StartupMessage");
843 client.write_all(&ssl).await.expect("writable client");
844 client
845 .write_all(&startup_packet)
846 .await
847 .expect("writable client");
848
849 let mut conn = Conn::new(Buffered::<_, Frontend>::new_frontend(proxy));
850 let ssl = conn
851 .receive_pre_startup_wire()
852 .await
853 .expect("decodable SSLRequest");
854 let crate::pre_startup::PreStartupOffer::Ssl(decision) = conn.offer_pre_startup(ssl) else {
855 panic!("unexpected pre-startup branch")
856 };
857 let (mut conn, reply) = decision.reject_ssl();
858 assert_eq!(reply, b'N');
859 let message = conn
860 .receive_pre_startup_wire()
861 .await
862 .expect("decodable StartupMessage");
863 assert_eq!(message, startup);
864 let crate::pre_startup::PreStartupOffer::Startup { conn, .. } =
865 conn.offer_pre_startup(message)
866 else {
867 panic!("unexpected pre-startup branch")
868 };
869 let _transport = conn.into_transport();
870 }
871
872 #[tokio::test]
873 async fn client_facing_transport_applies_its_pre_startup_limit() {
874 let (proxy, mut client) = tokio::io::duplex(32);
875 client
876 .write_all(&17_u32.to_be_bytes())
877 .await
878 .expect("writable client");
879
880 let mut transport =
881 Buffered::<_, Frontend>::with_limits_frontend(proxy, 64, 16).expect("valid limits");
882 let error = transport
883 .receive_pre_startup()
884 .await
885 .expect_err("declared packet exceeds the configured limit");
886
887 assert_eq!(error.kind(), io::ErrorKind::InvalidData);
888 }
889
890 #[tokio::test]
891 async fn upstream_transport_negotiates_raw_gssenc_reply() {
892 let (proxy, mut server) = tokio::io::duplex(32);
893 let mut pending = Conn::new(Buffered::new(proxy)).request_gss();
894 pending.flush().await.expect("GSSENCRequest is writable");
895
896 let mut request = [0_u8; 8];
897 server
898 .read_exact(&mut request)
899 .await
900 .expect("server receives GSSENCRequest");
901 assert_eq!(request, gssenc_request_packet());
902 server
903 .write_all(b"N")
904 .await
905 .expect("server writes decision");
906
907 let Negotiation::Rejected(plaintext) = pending
908 .receive_gss_reply()
909 .await
910 .expect("valid GSSENC decision")
911 else {
912 panic!("expected plaintext fallback")
913 };
914 plaintext.into_transport();
915 }
916
917 #[test]
918 fn client_facing_transport_buffers_raw_gssenc_decision() {
919 let conn = Conn::new(Buffered::<_, Frontend>::new_frontend(()));
920 let crate::pre_startup::PreStartupOffer::Gss(decision) =
921 conn.offer_pre_startup(PreStartupMessage::GssEncRequest)
922 else {
923 panic!("expected GSSENC decision")
924 };
925
926 let handshake = decision.approve_gss();
927 assert_eq!(handshake.pending_output(), b"S");
928 handshake.into_transport();
929
930 let conn = Conn::new(Buffered::<_, Frontend>::new_frontend(()));
931 let crate::pre_startup::PreStartupOffer::Gss(decision) =
932 conn.offer_pre_startup(PreStartupMessage::GssEncRequest)
933 else {
934 panic!("expected GSSENC decision")
935 };
936 let terminated = decision.reject_gss_with_legacy_error();
937 assert_eq!(terminated.pending_output(), b"E");
938 terminated.into_transport();
939 }
940
941 #[test]
942 fn client_facing_transport_buffers_legacy_ssl_error() {
943 let conn = Conn::new(Buffered::<_, Frontend>::new_frontend(()));
944 let crate::pre_startup::PreStartupOffer::Ssl(decision) =
945 conn.offer_pre_startup(PreStartupMessage::SslRequest)
946 else {
947 panic!("expected SSL decision")
948 };
949
950 let terminated = decision.reject_ssl_with_legacy_error();
951 assert_eq!(terminated.pending_output(), b"E");
952 terminated.into_transport();
953 }
954}