1use std::{cell::RefCell, net::SocketAddr, rc::Rc};
11
12use mio::{Token, net::TcpStream};
13use rusty_ulid::Ulid;
14use sozu_command::{
15 config::MAX_LOOP_ITERATIONS,
16 logging::{EndpointRecord, LogContext, ansi_palette},
17};
18
19use crate::metrics::names;
20use crate::{
21 L7Proxy, ListenerHandler, Protocol, Readiness, SessionMetrics, SessionResult, StateResult,
22 backends::Backend,
23 pool::Checkout,
24 protocol::{SessionState, http::parser::Method},
25 socket::{SocketHandler, SocketResult, TransportProtocol, stats::socket_rtt},
26 sozu_command::ready::Ready,
27 timer::TimeoutContainer,
28};
29
30#[cfg(all(target_os = "linux", feature = "splice"))]
31use crate::splice::{self, SplicePipe};
32
33macro_rules! log_context {
40 ($self:expr) => {{
41 let (open, reset, grey, gray, white) = ansi_palette();
42 format!(
43 "{gray}{ctx}{reset}\t{open}PIPE{reset}\t{grey}Session{reset}({gray}address{reset}={white}{address}{reset}, {gray}frontend{reset}={white}{frontend}{reset}, {gray}frontend_readiness{reset}={white}{frontend_readiness}{reset}, {gray}frontend_status{reset}={white}{frontend_status:?}{reset}, {gray}backend{reset}={white}{backend}{reset}, {gray}backend_status{reset}={white}{backend_status:?}{reset}, {gray}backend_readiness{reset}={white}{backend_readiness}{reset})\t >>>",
44 open = open,
45 reset = reset,
46 grey = grey,
47 gray = gray,
48 white = white,
49 ctx = $self.log_context(),
50 address = $self.session_address.map(|addr| addr.to_string()).unwrap_or_else(|| "<none>".to_string()),
51 frontend = $self.frontend_token.0,
52 frontend_readiness = $self.frontend_readiness,
53 frontend_status = $self.frontend_status,
54 backend = $self.backend_token.map(|token| token.0.to_string()).unwrap_or_else(|| "<none>".to_string()),
55 backend_status = $self.backend_status,
56 backend_readiness = $self.backend_readiness,
57 )
58 }};
59}
60
61#[derive(PartialEq, Eq)]
62pub enum SessionStatus {
63 Normal,
64 DefaultAnswer,
65}
66
67#[derive(Copy, Clone, Debug)]
68enum ConnectionStatus {
69 Normal,
70 ReadOpen,
71 WriteOpen,
72 Closed,
73}
74
75pub enum WebSocketContext {
77 Http {
78 method: Option<Method>,
79 authority: Option<String>,
80 path: Option<String>,
81 status: Option<u16>,
82 reason: Option<String>,
83 },
84 Tcp,
85}
86
87pub struct Pipe<Front: SocketHandler, L: ListenerHandler> {
88 backend_buffer: Checkout,
89 backend_id: Option<String>,
90 pub backend_readiness: Readiness,
91 backend_socket: Option<TcpStream>,
92 backend_status: ConnectionStatus,
93 backend_token: Option<Token>,
94 pub backend: Option<Rc<RefCell<Backend>>>,
95 cluster_id: Option<String>,
96 pub container_backend_timeout: Option<TimeoutContainer>,
97 pub container_frontend_timeout: Option<TimeoutContainer>,
98 frontend_buffer: Checkout,
99 pub frontend_readiness: Readiness,
100 frontend_status: ConnectionStatus,
101 frontend_token: Token,
102 frontend: Front,
103 listener: Rc<RefCell<L>>,
104 protocol: Protocol,
105 session_id: Ulid,
108 request_id: Ulid,
109 session_address: Option<SocketAddr>,
110 websocket_context: WebSocketContext,
111 tls_version: Option<&'static str>,
116 tls_cipher: Option<&'static str>,
117 tls_sni: Option<String>,
120 tls_alpn: Option<&'static str>,
121 #[cfg(all(target_os = "linux", feature = "splice"))]
126 splice_pipe: Option<SplicePipe>,
127}
128
129impl<Front: SocketHandler, L: ListenerHandler> Pipe<Front, L> {
130 #[allow(clippy::too_many_arguments)]
139 pub fn new(
140 backend_buffer: Checkout,
141 backend_id: Option<String>,
142 backend_socket: Option<TcpStream>,
143 backend: Option<Rc<RefCell<Backend>>>,
144 container_backend_timeout: Option<TimeoutContainer>,
145 container_frontend_timeout: Option<TimeoutContainer>,
146 cluster_id: Option<String>,
147 frontend_buffer: Checkout,
148 frontend_token: Token,
149 frontend: Front,
150 listener: Rc<RefCell<L>>,
151 protocol: Protocol,
152 session_id: Ulid,
153 request_id: Ulid,
154 session_address: Option<SocketAddr>,
155 websocket_context: WebSocketContext,
156 ) -> Pipe<Front, L> {
157 let frontend_status = ConnectionStatus::Normal;
158 let backend_status = if backend_socket.is_none() {
159 ConnectionStatus::Closed
160 } else {
161 ConnectionStatus::Normal
162 };
163
164 let mut session = Pipe {
165 backend_buffer,
166 backend_id,
167 backend_readiness: Readiness {
168 interest: Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
169 event: Ready::EMPTY,
170 },
171 backend_socket,
172 backend_status,
173 backend_token: None,
174 backend,
175 cluster_id,
176 container_backend_timeout,
177 container_frontend_timeout,
178 frontend_buffer,
179 frontend_readiness: Readiness {
180 interest: Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
181 event: Ready::EMPTY,
182 },
183 frontend_status,
184 frontend_token,
185 frontend,
186 listener,
187 protocol,
188 session_id,
189 request_id,
190 session_address,
191 websocket_context,
192 tls_version: None,
193 tls_cipher: None,
194 tls_sni: None,
195 tls_alpn: None,
196 #[cfg(all(target_os = "linux", feature = "splice"))]
197 splice_pipe: if protocol == Protocol::TCP {
198 SplicePipe::new()
199 } else {
200 None
201 },
202 };
203
204 session.arm_inherited_buffer_writes();
205
206 trace!("{} created pipe", log_context!(session));
207 session
208 }
209
210 fn arm_inherited_buffer_writes(&mut self) {
211 if self.backend_buffer.available_data() > 0 {
212 self.frontend_readiness.arm_writable();
213 }
214 if self.frontend_buffer.available_data() > 0 && self.backend_socket.is_some() {
215 self.backend_readiness.arm_writable();
216 }
217 }
218
219 pub fn restore_readiness_events(&mut self, frontend_event: Ready, backend_event: Ready) {
220 self.frontend_readiness.event = frontend_event;
221 self.backend_readiness.event = backend_event;
222 self.arm_inherited_buffer_writes();
223 }
224
225 pub fn set_tls_metadata(
232 &mut self,
233 version: Option<&'static str>,
234 cipher: Option<&'static str>,
235 sni: Option<String>,
236 alpn: Option<&'static str>,
237 ) {
238 self.tls_version = version;
239 self.tls_cipher = cipher;
240 self.tls_sni = sni;
241 self.tls_alpn = alpn;
242 }
243
244 pub fn front_socket(&self) -> &TcpStream {
245 self.frontend.socket_ref()
246 }
247
248 pub fn front_socket_mut(&mut self) -> &mut TcpStream {
249 self.frontend.socket_mut()
250 }
251
252 pub fn back_socket(&self) -> Option<&TcpStream> {
253 self.backend_socket.as_ref()
254 }
255
256 pub fn back_socket_mut(&mut self) -> Option<&mut TcpStream> {
257 self.backend_socket.as_mut()
258 }
259
260 pub fn set_back_socket(&mut self, socket: TcpStream) {
261 self.backend_socket = Some(socket);
262 self.backend_status = ConnectionStatus::Normal;
263 }
264
265 pub fn back_token(&self) -> Vec<Token> {
266 self.backend_token.iter().cloned().collect()
267 }
268
269 fn reset_timeouts(&mut self) {
270 if let Some(t) = self.container_frontend_timeout.as_mut()
271 && !t.reset()
272 {
273 error!(
274 "{} Could not reset front timeout (pipe)",
275 log_context!(self)
276 );
277 }
278
279 if let Some(t) = self.container_backend_timeout.as_mut()
280 && !t.reset()
281 {
282 error!("{} Could not reset back timeout (pipe)", log_context!(self));
283 }
284 }
285
286 pub fn set_cluster_id(&mut self, cluster_id: Option<String>) {
287 self.cluster_id = cluster_id;
288 }
289
290 pub fn set_backend_id(&mut self, backend_id: Option<String>) {
291 self.backend_id = backend_id;
292 }
293
294 pub fn set_back_token(&mut self, token: Token) {
295 self.backend_token = Some(token);
296 }
297
298 pub fn get_session_address(&self) -> Option<SocketAddr> {
299 self.session_address
300 .or_else(|| self.frontend.socket_ref().peer_addr().ok())
301 }
302
303 pub fn get_backend_address(&self) -> Option<SocketAddr> {
304 self.backend_socket
305 .as_ref()
306 .and_then(|backend| backend.peer_addr().ok())
307 }
308
309 fn protocol_string(&self) -> &'static str {
310 match self.protocol {
311 Protocol::TCP => "TCP",
312 Protocol::HTTP => "WS",
313 Protocol::HTTPS => match self.frontend.protocol() {
314 TransportProtocol::Ssl2 => "WSS-SSL2",
315 TransportProtocol::Ssl3 => "WSS-SSL3",
316 TransportProtocol::Tls1_0 => "WSS-TLS1.0",
317 TransportProtocol::Tls1_1 => "WSS-TLS1.1",
318 TransportProtocol::Tls1_2 => "WSS-TLS1.2",
319 TransportProtocol::Tls1_3 => "WSS-TLS1.3",
320 _ => unreachable!(),
321 },
322 _ => unreachable!(),
323 }
324 }
325
326 pub fn log_request(&self, metrics: &SessionMetrics, error: bool, message: Option<&str>) {
327 let listener = self.listener.borrow();
328 let context = self.log_context();
329 let endpoint = self.log_endpoint();
330 metrics.register_end_of_session(&context);
331 log_access!(
332 error,
333 on_failure: { incr!(names::access_logs::UNSENT) },
334 message,
335 context,
336 session_address: self.get_session_address(),
337 backend_address: self.get_backend_address(),
338 protocol: self.protocol_string(),
339 endpoint,
340 tags: listener.get_tags(&listener.get_addr().to_string()),
341 client_rtt: socket_rtt(self.front_socket()),
342 server_rtt: self.backend_socket.as_ref().and_then(socket_rtt),
343 service_time: metrics.service_time(),
344 response_time: metrics.backend_response_time(),
345 request_time: metrics.request_time(),
346 start_time_ns: metrics.start_wall_ns(),
347 bytes_in: metrics.bin,
348 bytes_out: metrics.bout,
349 user_agent: None,
350 x_request_id: None,
351 tls_version: self.tls_version,
356 tls_cipher: self.tls_cipher,
357 tls_sni: self.tls_sni.as_deref(),
358 tls_alpn: self.tls_alpn,
359 xff_chain: None,
360 otel: None,
361 );
362 }
363
364 pub fn log_request_success(&self, metrics: &SessionMetrics) {
365 self.log_request(metrics, false, None);
366 }
367
368 pub fn log_request_error(&self, metrics: &SessionMetrics, message: &str) {
369 incr!(names::pipe::ERRORS);
370 error!(
371 "{} Could not process request properly got: {}",
372 log_context!(self),
373 message
374 );
375 self.print_state(self.protocol_string());
376 self.log_request(metrics, true, Some(message));
377 }
378
379 pub fn log_request_timeout(&self, metrics: &SessionMetrics, message: &str) {
386 debug!("{} pipe timeout: {}", log_context!(self), message);
387 self.log_request(metrics, true, Some(message));
388 }
389
390 #[cfg(all(target_os = "linux", feature = "splice"))]
395 fn splice_in_pending(&self) -> usize {
396 self.splice_pipe
397 .as_ref()
398 .map(|p| p.in_pipe_pending)
399 .unwrap_or(0)
400 }
401 #[cfg(not(all(target_os = "linux", feature = "splice")))]
402 fn splice_in_pending(&self) -> usize {
403 0
404 }
405
406 #[cfg(all(target_os = "linux", feature = "splice"))]
410 fn splice_out_pending(&self) -> usize {
411 self.splice_pipe
412 .as_ref()
413 .map(|p| p.out_pipe_pending)
414 .unwrap_or(0)
415 }
416 #[cfg(not(all(target_os = "linux", feature = "splice")))]
417 fn splice_out_pending(&self) -> usize {
418 0
419 }
420
421 #[cfg(all(target_os = "linux", feature = "splice"))]
425 fn splice_capacity(&self) -> usize {
426 self.splice_pipe.as_ref().map(|p| p.capacity).unwrap_or(0)
427 }
428
429 fn reset_readiness_for_close(&mut self) {
439 self.frontend_readiness.reset();
440 self.backend_readiness.reset();
441 debug_assert!(
442 self.frontend_readiness.interest.is_empty() && self.frontend_readiness.event.is_empty(),
443 "frontend readiness must be fully cleared on close (write-only-shutdown discipline)"
444 );
445 debug_assert!(
446 self.backend_readiness.interest.is_empty() && self.backend_readiness.event.is_empty(),
447 "backend readiness must be fully cleared on close (write-only-shutdown discipline)"
448 );
449 }
450
451 pub fn check_connections(&self) -> bool {
454 debug_assert!(
462 self.frontend_buffer.available_data() <= self.frontend_buffer.capacity(),
463 "frontend buffered data exceeds its capacity"
464 );
465 debug_assert!(
466 self.backend_buffer.available_data() <= self.backend_buffer.capacity(),
467 "backend buffered data exceeds its capacity"
468 );
469
470 let request_is_inflight = self.frontend_buffer.available_data() > 0
471 || self.frontend_readiness.event.is_readable()
472 || self.splice_in_pending() > 0;
473 let response_is_inflight = self.backend_buffer.available_data() > 0
474 || self.backend_readiness.event.is_readable()
475 || self.splice_out_pending() > 0;
476 match (self.frontend_status, self.backend_status) {
477 (ConnectionStatus::Normal, ConnectionStatus::Normal) => true,
478 (ConnectionStatus::Normal, ConnectionStatus::ReadOpen) => true,
479 (ConnectionStatus::Normal, ConnectionStatus::WriteOpen) => {
480 request_is_inflight || response_is_inflight
485 }
486 (ConnectionStatus::Normal, ConnectionStatus::Closed) => response_is_inflight,
487
488 (ConnectionStatus::WriteOpen, ConnectionStatus::Normal) => {
489 request_is_inflight || response_is_inflight
492 }
493 (ConnectionStatus::WriteOpen, ConnectionStatus::ReadOpen) => true,
494 (ConnectionStatus::WriteOpen, ConnectionStatus::WriteOpen) => {
495 request_is_inflight || response_is_inflight
496 }
497 (ConnectionStatus::WriteOpen, ConnectionStatus::Closed) => response_is_inflight,
498
499 (ConnectionStatus::ReadOpen, ConnectionStatus::Normal) => true,
500 (ConnectionStatus::ReadOpen, ConnectionStatus::ReadOpen) => false,
501 (ConnectionStatus::ReadOpen, ConnectionStatus::WriteOpen) => true,
502 (ConnectionStatus::ReadOpen, ConnectionStatus::Closed) => false,
503
504 (ConnectionStatus::Closed, ConnectionStatus::Normal) => request_is_inflight,
505 (ConnectionStatus::Closed, ConnectionStatus::ReadOpen) => false,
506 (ConnectionStatus::Closed, ConnectionStatus::WriteOpen) => request_is_inflight,
507 (ConnectionStatus::Closed, ConnectionStatus::Closed) => false,
508 }
509 }
510
511 pub fn frontend_hup(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
512 self.log_request_success(metrics);
513 self.frontend_status = ConnectionStatus::Closed;
514 SessionResult::Close
515 }
516
517 pub fn backend_hup(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
518 self.backend_status = ConnectionStatus::Closed;
519 debug_assert!(
522 matches!(self.backend_status, ConnectionStatus::Closed),
523 "backend_hup must mark the backend Closed"
524 );
525 let pipe_has_data = self.splice_out_pending() > 0;
526 if self.backend_buffer.available_data() == 0 && !pipe_has_data {
527 debug_assert_eq!(
530 self.backend_buffer.available_data(),
531 0,
532 "no-data branch entered with response bytes still buffered"
533 );
534 if self.backend_readiness.event.is_readable() {
535 self.backend_readiness.interest.insert(Ready::READABLE);
536 debug!(
537 "{} Pipe::backend_hup: backend connection closed, keeping alive due to inflight data in kernel.",
538 log_context!(self)
539 );
540 SessionResult::Continue
541 } else {
542 self.log_request_success(metrics);
543 SessionResult::Close
544 }
545 } else {
546 debug!(
547 "{} Pipe::backend_hup: backend connection closed, keeping alive due to inflight data in buffers.",
548 log_context!(self)
549 );
550 self.frontend_readiness.arm_writable();
551 if self.backend_readiness.event.is_readable() {
552 self.backend_readiness.interest.insert(Ready::READABLE);
553 }
554 SessionResult::Continue
555 }
556 }
557
558 pub fn readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
560 #[cfg(all(target_os = "linux", feature = "splice"))]
561 if self.protocol == Protocol::TCP && self.splice_pipe.is_some() {
562 return self.splice_readable(metrics);
563 }
564
565 self.reset_timeouts();
566
567 trace!("{} pipe readable", log_context!(self));
568 if self.frontend_buffer.available_space() == 0 {
569 self.frontend_readiness.interest.remove(Ready::READABLE);
570 self.backend_readiness.arm_writable();
571 return SessionResult::Continue;
572 }
573
574 let space_before = self.frontend_buffer.available_space();
575 let data_before = self.frontend_buffer.available_data();
576 let bin_before = metrics.bin;
577 let (sz, res) = self.frontend.socket_read(self.frontend_buffer.space());
578 debug_assert!(
581 sz <= space_before,
582 "frontend socket_read reported more bytes ({sz}) than the buffer space offered ({space_before})"
583 );
584 debug!("{} Read {} bytes", log_context!(self), sz);
585
586 if sz > 0 {
587 self.frontend_buffer.fill(sz);
589 debug_assert_eq!(
592 self.frontend_buffer.available_data(),
593 data_before + sz,
594 "fill must grow readable data by exactly the bytes read"
595 );
596
597 count!(names::backend::BYTES_IN, sz as i64);
598 metrics.bin += sz;
599 debug_assert_eq!(
601 metrics.bin,
602 bin_before + sz,
603 "metrics.bin must advance by exactly the bytes read"
604 );
605
606 if self.frontend_buffer.available_space() == 0 {
607 self.frontend_readiness.interest.remove(Ready::READABLE);
608 }
609 self.backend_readiness.arm_writable();
610 } else {
611 self.frontend_readiness.event.remove(Ready::READABLE);
612
613 if res == SocketResult::Continue {
614 self.frontend_status = match self.frontend_status {
615 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
616 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
617 s => s,
618 };
619 }
620 }
621
622 if !self.check_connections() {
623 self.reset_readiness_for_close();
624 self.log_request_success(metrics);
625 return SessionResult::Close;
626 }
627
628 match res {
629 SocketResult::Error => {
630 self.reset_readiness_for_close();
631 self.log_request_error(metrics, "front socket read error");
632 return SessionResult::Close;
633 }
634 SocketResult::Closed => {
635 self.reset_readiness_for_close();
636 self.log_request_success(metrics);
637 return SessionResult::Close;
638 }
639 SocketResult::WouldBlock => {
640 self.frontend_readiness.event.remove(Ready::READABLE);
641 }
642 SocketResult::Continue => {}
643 };
644
645 self.backend_readiness.arm_writable();
646 SessionResult::Continue
647 }
648
649 pub fn writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
651 #[cfg(all(target_os = "linux", feature = "splice"))]
652 if self.protocol == Protocol::TCP && self.splice_pipe.is_some() {
653 return self.splice_writable(metrics);
654 }
655
656 trace!("{} Pipe writable", log_context!(self));
657 if self.backend_buffer.available_data() == 0 {
658 self.backend_readiness.interest.insert(Ready::READABLE);
659 self.frontend_readiness.interest.remove(Ready::WRITABLE);
660 return SessionResult::Continue;
661 }
662
663 let queued_total = self.backend_buffer.available_data();
664 let mut sz = 0usize;
665 let mut res = SocketResult::Continue;
666 while res == SocketResult::Continue {
667 if self.backend_buffer.available_data() == 0 {
669 count!(names::backend::BYTES_OUT, sz as i64);
670 metrics.bout += sz;
671 self.backend_readiness.interest.insert(Ready::READABLE);
672 self.frontend_readiness.interest.remove(Ready::WRITABLE);
673 return SessionResult::Continue;
674 }
675 let queued = self.backend_buffer.available_data();
676 let (current_sz, current_res) = self.frontend.socket_write(self.backend_buffer.data());
677 debug_assert!(
680 current_sz <= queued,
681 "frontend socket_write reported {current_sz} bytes but only {queued} were queued"
682 );
683 res = current_res;
684 let consumed = self.backend_buffer.consume(current_sz);
685 debug_assert_eq!(
688 consumed, current_sz,
689 "consume must drop exactly the bytes written to the frontend"
690 );
691 sz += current_sz;
692 debug_assert!(
694 sz <= queued_total,
695 "cumulative frontend write ({sz}) exceeded the queued backend data ({queued_total})"
696 );
697
698 if current_sz == 0 && res == SocketResult::Continue {
699 self.frontend_status = match self.frontend_status {
700 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
701 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
702 s => s,
703 };
704 }
705
706 if !self.check_connections() {
707 metrics.bout += sz;
708 count!(names::backend::BYTES_OUT, sz as i64);
709 self.reset_readiness_for_close();
710 self.log_request_success(metrics);
711 return SessionResult::Close;
712 }
713 }
714
715 if sz > 0 {
716 count!(names::backend::BYTES_OUT, sz as i64);
717 self.backend_readiness.interest.insert(Ready::READABLE);
718 metrics.bout += sz;
719 }
720
721 debug!(
722 "{} Wrote {} bytes of {}",
723 log_context!(self),
724 sz,
725 self.backend_buffer.available_data()
726 );
727
728 match res {
729 SocketResult::Error => {
730 self.reset_readiness_for_close();
731 self.log_request_error(metrics, "front socket write error");
732 return SessionResult::Close;
733 }
734 SocketResult::Closed => {
735 self.reset_readiness_for_close();
736 self.log_request_success(metrics);
737 return SessionResult::Close;
738 }
739 SocketResult::WouldBlock => {
740 self.frontend_readiness.event.remove(Ready::WRITABLE);
741 }
742 SocketResult::Continue => {}
743 }
744
745 SessionResult::Continue
746 }
747
748 pub fn backend_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
750 #[cfg(all(target_os = "linux", feature = "splice"))]
751 if self.protocol == Protocol::TCP && self.splice_pipe.is_some() {
752 return self.splice_backend_writable(metrics);
753 }
754
755 trace!("{} pipe back_writable", log_context!(self));
756
757 if self.frontend_buffer.available_data() == 0 {
758 self.frontend_readiness.interest.insert(Ready::READABLE);
759 self.backend_readiness.interest.remove(Ready::WRITABLE);
760 return SessionResult::Continue;
761 }
762
763 let output_size = self.frontend_buffer.available_data();
764
765 let mut sz = 0usize;
766 let mut socket_res = SocketResult::Continue;
767
768 if let Some(ref mut backend) = self.backend_socket {
769 while socket_res == SocketResult::Continue {
770 if self.frontend_buffer.available_data() == 0 {
772 self.frontend_readiness.interest.insert(Ready::READABLE);
773 self.backend_readiness.interest.remove(Ready::WRITABLE);
774 count!(names::backend::BACK_BYTES_OUT, sz as i64);
775 metrics.backend_bout += sz;
776 return SessionResult::Continue;
777 }
778
779 let queued = self.frontend_buffer.available_data();
780 let (current_sz, current_res) = backend.socket_write(self.frontend_buffer.data());
781 debug_assert!(
783 current_sz <= queued,
784 "backend socket_write reported {current_sz} bytes but only {queued} were queued"
785 );
786 socket_res = current_res;
787 let consumed = self.frontend_buffer.consume(current_sz);
788 debug_assert_eq!(
789 consumed, current_sz,
790 "consume must drop exactly the bytes written to the backend"
791 );
792 sz += current_sz;
793 debug_assert!(
795 sz <= output_size,
796 "cumulative backend write ({sz}) exceeded the queued frontend data ({output_size})"
797 );
798
799 if current_sz == 0 && current_res == SocketResult::Continue {
800 self.backend_status = match self.backend_status {
801 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
802 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
803 s => s,
804 };
805 }
806 }
807 }
808
809 let backend_bout_before = metrics.backend_bout;
810 count!(names::backend::BACK_BYTES_OUT, sz as i64);
811 metrics.backend_bout += sz;
812 debug_assert_eq!(
814 metrics.backend_bout,
815 backend_bout_before + sz,
816 "metrics.backend_bout must advance by exactly the bytes written"
817 );
818
819 if !self.check_connections() {
820 self.reset_readiness_for_close();
821 self.log_request_success(metrics);
822 return SessionResult::Close;
823 }
824
825 debug!(
826 "{} Wrote {} bytes of {}",
827 log_context!(self),
828 sz,
829 output_size
830 );
831
832 match socket_res {
833 SocketResult::Error => {
834 self.reset_readiness_for_close();
835 self.log_request_error(metrics, "back socket write error");
836 return SessionResult::Close;
837 }
838 SocketResult::Closed => {
839 self.reset_readiness_for_close();
840 self.log_request_success(metrics);
841 return SessionResult::Close;
842 }
843 SocketResult::WouldBlock => {
844 self.backend_readiness.event.remove(Ready::WRITABLE);
845 }
846 SocketResult::Continue => {}
847 }
848 SessionResult::Continue
849 }
850
851 pub fn backend_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
853 #[cfg(all(target_os = "linux", feature = "splice"))]
854 if self.protocol == Protocol::TCP && self.splice_pipe.is_some() {
855 return self.splice_backend_readable(metrics);
856 }
857
858 self.reset_timeouts();
859
860 trace!("{} Pipe backend_readable", log_context!(self));
861 if self.backend_buffer.available_space() == 0 {
862 self.backend_readiness.interest.remove(Ready::READABLE);
863 return SessionResult::Continue;
864 }
865
866 let space_before = self.backend_buffer.available_space();
867 let data_before = self.backend_buffer.available_data();
868 let backend_bin_before = metrics.backend_bin;
869 if let Some(ref mut backend) = self.backend_socket {
870 let (size, remaining) = backend.socket_read(self.backend_buffer.space());
871 debug_assert!(
873 size <= space_before,
874 "backend socket_read reported more bytes ({size}) than the buffer space offered ({space_before})"
875 );
876 self.backend_buffer.fill(size);
877 debug_assert_eq!(
880 self.backend_buffer.available_data(),
881 data_before + size,
882 "fill must grow readable data by exactly the bytes read"
883 );
884
885 debug!("{} Read {} bytes", log_context!(self), size);
886
887 if remaining != SocketResult::Continue || size == 0 {
888 self.backend_readiness.event.remove(Ready::READABLE);
889 }
890 if size > 0 {
891 self.frontend_readiness.arm_writable();
892 count!(names::backend::BACK_BYTES_IN, size as i64);
893 metrics.backend_bin += size;
894 debug_assert_eq!(
896 metrics.backend_bin,
897 backend_bin_before + size,
898 "metrics.backend_bin must advance by exactly the bytes read"
899 );
900 }
901
902 if size == 0 && remaining == SocketResult::Closed {
903 self.backend_status = match self.backend_status {
904 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
905 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
906 s => s,
907 };
908
909 if !self.check_connections() {
910 self.reset_readiness_for_close();
911 self.log_request_success(metrics);
912 return SessionResult::Close;
913 }
914 }
915
916 match remaining {
917 SocketResult::Error => {
918 self.reset_readiness_for_close();
919 self.log_request_error(metrics, "back socket read error");
920 return SessionResult::Close;
921 }
922 SocketResult::Closed => {
923 if !self.check_connections() {
924 self.reset_readiness_for_close();
925 self.log_request_success(metrics);
926 return SessionResult::Close;
927 }
928 }
929 SocketResult::WouldBlock => {
930 self.backend_readiness.event.remove(Ready::READABLE);
931 }
932 SocketResult::Continue => {}
933 }
934 }
935
936 SessionResult::Continue
937 }
938
939 #[cfg(all(target_os = "linux", feature = "splice"))]
947 fn splice_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
948 self.reset_timeouts();
949
950 trace!("{} pipe splice_readable", log_context!(self));
951 let capacity = self.splice_capacity();
952 if self.splice_in_pending() >= capacity {
953 self.frontend_readiness.interest.remove(Ready::READABLE);
955 self.backend_readiness.arm_writable();
956 return SessionResult::Continue;
957 }
958
959 let pending_before = self.splice_in_pending();
960 let bin_before = metrics.bin;
961 let pipe_write_end = self.splice_pipe.as_ref().unwrap().in_pipe[1];
962 let (sz, res) = splice::splice_in(self.frontend.socket_ref(), pipe_write_end, capacity);
963 debug_assert!(
972 sz <= capacity,
973 "splice_in reported {sz} bytes but was capped at len {capacity}"
974 );
975 debug!("{} Spliced {} bytes from frontend", log_context!(self), sz);
976
977 if sz > 0 {
978 self.splice_pipe.as_mut().unwrap().in_pipe_pending += sz;
979 debug_assert_eq!(
982 self.splice_in_pending(),
983 pending_before + sz,
984 "in_pipe_pending must grow by exactly the spliced bytes"
985 );
986 count!(names::backend::BYTES_IN, sz as i64);
987 metrics.bin += sz;
988 debug_assert_eq!(
989 metrics.bin,
990 bin_before + sz,
991 "metrics.bin must advance by exactly the spliced bytes"
992 );
993 self.backend_readiness.arm_writable();
994 } else {
995 self.frontend_readiness.event.remove(Ready::READABLE);
996
997 if res == SocketResult::Continue {
998 self.frontend_status = match self.frontend_status {
999 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
1000 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
1001 s => s,
1002 };
1003 }
1004 }
1005
1006 if !self.check_connections() {
1007 self.reset_readiness_for_close();
1008 self.log_request_success(metrics);
1009 return SessionResult::Close;
1010 }
1011
1012 match res {
1013 SocketResult::Error => {
1014 self.reset_readiness_for_close();
1015 self.log_request_error(metrics, "splice front socket read error");
1016 return SessionResult::Close;
1017 }
1018 SocketResult::Closed => {
1019 self.reset_readiness_for_close();
1020 self.log_request_success(metrics);
1021 return SessionResult::Close;
1022 }
1023 SocketResult::WouldBlock => {
1024 self.frontend_readiness.event.remove(Ready::READABLE);
1025 }
1026 SocketResult::Continue => {}
1027 }
1028
1029 self.backend_readiness.arm_writable();
1030 SessionResult::Continue
1031 }
1032
1033 #[cfg(all(target_os = "linux", feature = "splice"))]
1038 fn splice_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1039 trace!("{} Pipe splice_writable", log_context!(self));
1040 if self.splice_out_pending() == 0 {
1041 self.backend_readiness.interest.insert(Ready::READABLE);
1042 self.frontend_readiness.interest.remove(Ready::WRITABLE);
1043 return SessionResult::Continue;
1044 }
1045
1046 let mut sz = 0usize;
1047 let mut res = SocketResult::Continue;
1048 while res == SocketResult::Continue {
1049 let pending = self.splice_out_pending();
1050 if pending == 0 {
1052 count!(names::backend::BYTES_OUT, sz as i64);
1053 metrics.bout += sz;
1054 self.backend_readiness.interest.insert(Ready::READABLE);
1055 self.frontend_readiness.interest.remove(Ready::WRITABLE);
1056 return SessionResult::Continue;
1057 }
1058
1059 let pipe_read_end = self.splice_pipe.as_ref().unwrap().out_pipe[0];
1060 let (current_sz, current_res) =
1061 splice::splice_out(pipe_read_end, self.frontend.socket_ref(), pending);
1062 debug_assert!(
1066 current_sz <= pending,
1067 "splice_out drained {current_sz} bytes but only {pending} were pending (would underflow)"
1068 );
1069 res = current_res;
1070 if current_sz > 0 {
1071 self.splice_pipe.as_mut().unwrap().out_pipe_pending -= current_sz;
1072 debug_assert_eq!(
1073 self.splice_out_pending(),
1074 pending - current_sz,
1075 "out_pipe_pending must shrink by exactly the drained bytes"
1076 );
1077 }
1078 sz += current_sz;
1079
1080 if current_sz == 0 && res == SocketResult::Continue {
1081 self.frontend_status = match self.frontend_status {
1082 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
1083 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
1084 s => s,
1085 };
1086 }
1087
1088 if !self.check_connections() {
1089 metrics.bout += sz;
1090 count!(names::backend::BYTES_OUT, sz as i64);
1091 self.reset_readiness_for_close();
1092 self.log_request_success(metrics);
1093 return SessionResult::Close;
1094 }
1095 }
1096
1097 if sz > 0 {
1098 count!(names::backend::BYTES_OUT, sz as i64);
1099 self.backend_readiness.interest.insert(Ready::READABLE);
1100 metrics.bout += sz;
1101 }
1102
1103 debug!(
1104 "{} Spliced {} bytes (out_pipe_pending={})",
1105 log_context!(self),
1106 sz,
1107 self.splice_out_pending()
1108 );
1109
1110 match res {
1111 SocketResult::Error => {
1112 self.reset_readiness_for_close();
1113 self.log_request_error(metrics, "splice front socket write error");
1114 return SessionResult::Close;
1115 }
1116 SocketResult::Closed => {
1117 self.reset_readiness_for_close();
1118 self.log_request_success(metrics);
1119 return SessionResult::Close;
1120 }
1121 SocketResult::WouldBlock => {
1122 self.frontend_readiness.event.remove(Ready::WRITABLE);
1123 }
1124 SocketResult::Continue => {}
1125 }
1126
1127 SessionResult::Continue
1128 }
1129
1130 #[cfg(all(target_os = "linux", feature = "splice"))]
1135 fn splice_backend_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1136 trace!("{} pipe splice_backend_writable", log_context!(self));
1137
1138 if self.splice_in_pending() == 0 {
1139 self.frontend_readiness.interest.insert(Ready::READABLE);
1140 self.backend_readiness.interest.remove(Ready::WRITABLE);
1141 return SessionResult::Continue;
1142 }
1143
1144 let output_size = self.splice_in_pending();
1145 let mut sz = 0usize;
1146 let mut socket_res = SocketResult::Continue;
1147
1148 while socket_res == SocketResult::Continue {
1149 let pending = self.splice_in_pending();
1150 if pending == 0 {
1152 self.frontend_readiness.interest.insert(Ready::READABLE);
1153 self.backend_readiness.interest.remove(Ready::WRITABLE);
1154 count!(names::backend::BACK_BYTES_OUT, sz as i64);
1155 metrics.backend_bout += sz;
1156 return SessionResult::Continue;
1157 }
1158
1159 let pipe_read_end = self.splice_pipe.as_ref().unwrap().in_pipe[0];
1160 let (current_sz, current_res) = match self.backend_socket.as_ref() {
1161 Some(b) => splice::splice_out(pipe_read_end, b, pending),
1162 None => break,
1163 };
1164 debug_assert!(
1166 current_sz <= pending,
1167 "splice_out drained {current_sz} bytes but only {pending} were pending (would underflow)"
1168 );
1169 socket_res = current_res;
1170 if current_sz > 0 {
1171 self.splice_pipe.as_mut().unwrap().in_pipe_pending -= current_sz;
1172 debug_assert_eq!(
1173 self.splice_in_pending(),
1174 pending - current_sz,
1175 "in_pipe_pending must shrink by exactly the drained bytes"
1176 );
1177 }
1178 sz += current_sz;
1179 debug_assert!(
1181 sz <= output_size,
1182 "cumulative splice drain ({sz}) exceeded the bytes pending at entry ({output_size})"
1183 );
1184
1185 if current_sz == 0 && current_res == SocketResult::Continue {
1186 self.backend_status = match self.backend_status {
1187 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
1188 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
1189 s => s,
1190 };
1191 }
1192 }
1193
1194 count!(names::backend::BACK_BYTES_OUT, sz as i64);
1195 metrics.backend_bout += sz;
1196
1197 if !self.check_connections() {
1198 self.reset_readiness_for_close();
1199 self.log_request_success(metrics);
1200 return SessionResult::Close;
1201 }
1202
1203 debug!(
1204 "{} Spliced {} bytes of {}",
1205 log_context!(self),
1206 sz,
1207 output_size
1208 );
1209
1210 match socket_res {
1211 SocketResult::Error => {
1212 self.reset_readiness_for_close();
1213 self.log_request_error(metrics, "splice back socket write error");
1214 return SessionResult::Close;
1215 }
1216 SocketResult::Closed => {
1217 self.reset_readiness_for_close();
1218 self.log_request_success(metrics);
1219 return SessionResult::Close;
1220 }
1221 SocketResult::WouldBlock => {
1222 self.backend_readiness.event.remove(Ready::WRITABLE);
1223 }
1224 SocketResult::Continue => {}
1225 }
1226 SessionResult::Continue
1227 }
1228
1229 #[cfg(all(target_os = "linux", feature = "splice"))]
1235 fn splice_backend_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1236 self.reset_timeouts();
1237
1238 trace!("{} Pipe splice_backend_readable", log_context!(self));
1239 let capacity = self.splice_capacity();
1240 if self.splice_out_pending() >= capacity {
1241 self.backend_readiness.interest.remove(Ready::READABLE);
1243 self.frontend_readiness.arm_writable();
1244 return SessionResult::Continue;
1245 }
1246
1247 let pending_before = self.splice_out_pending();
1248 let backend_bin_before = metrics.backend_bin;
1249 let pipe_write_end = self.splice_pipe.as_ref().unwrap().out_pipe[1];
1250 let (size, remaining) = match self.backend_socket.as_ref() {
1251 Some(b) => splice::splice_in(b, pipe_write_end, capacity),
1252 None => return SessionResult::Continue,
1253 };
1254 debug_assert!(
1261 size <= capacity,
1262 "splice_in reported {size} bytes but was capped at len {capacity}"
1263 );
1264
1265 debug!("{} Spliced {} bytes from backend", log_context!(self), size);
1266
1267 if remaining != SocketResult::Continue || size == 0 {
1268 self.backend_readiness.event.remove(Ready::READABLE);
1269 }
1270 if size > 0 {
1271 self.splice_pipe.as_mut().unwrap().out_pipe_pending += size;
1272 debug_assert_eq!(
1273 self.splice_out_pending(),
1274 pending_before + size,
1275 "out_pipe_pending must grow by exactly the spliced bytes"
1276 );
1277 self.frontend_readiness.arm_writable();
1278 count!(names::backend::BACK_BYTES_IN, size as i64);
1279 metrics.backend_bin += size;
1280 debug_assert_eq!(
1281 metrics.backend_bin,
1282 backend_bin_before + size,
1283 "metrics.backend_bin must advance by exactly the spliced bytes"
1284 );
1285 }
1286
1287 if size == 0 && remaining == SocketResult::Closed {
1288 self.backend_status = match self.backend_status {
1289 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
1290 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
1291 s => s,
1292 };
1293
1294 if !self.check_connections() {
1295 self.reset_readiness_for_close();
1296 self.log_request_success(metrics);
1297 return SessionResult::Close;
1298 }
1299 }
1300
1301 match remaining {
1302 SocketResult::Error => {
1303 self.reset_readiness_for_close();
1304 self.log_request_error(metrics, "splice back socket read error");
1305 return SessionResult::Close;
1306 }
1307 SocketResult::Closed => {
1308 if !self.check_connections() {
1309 self.reset_readiness_for_close();
1310 self.log_request_success(metrics);
1311 return SessionResult::Close;
1312 }
1313 }
1314 SocketResult::WouldBlock => {
1315 self.backend_readiness.event.remove(Ready::READABLE);
1316 }
1317 SocketResult::Continue => {}
1318 }
1319
1320 SessionResult::Continue
1321 }
1322
1323 pub fn log_context(&self) -> LogContext<'_> {
1324 LogContext {
1325 session_id: self.session_id,
1326 request_id: Some(self.request_id),
1327 cluster_id: self.cluster_id.as_deref(),
1328 backend_id: self.backend_id.as_deref(),
1329 }
1330 }
1331
1332 fn log_endpoint(&self) -> EndpointRecord<'_> {
1333 match &self.websocket_context {
1334 WebSocketContext::Http {
1335 method,
1336 authority,
1337 path,
1338 status,
1339 reason,
1340 } => EndpointRecord::Http {
1341 method: method.as_deref(),
1342 authority: authority.as_deref(),
1343 path: path.as_deref(),
1344 status: status.to_owned(),
1345 reason: reason.as_deref(),
1346 },
1347 WebSocketContext::Tcp => EndpointRecord::Tcp,
1348 }
1349 }
1350}
1351
1352impl<Front: SocketHandler, L: ListenerHandler> SessionState for Pipe<Front, L> {
1353 fn ready(
1354 &mut self,
1355 _session: Rc<RefCell<dyn crate::ProxySession>>,
1356 _proxy: Rc<RefCell<dyn crate::L7Proxy>>,
1357 metrics: &mut SessionMetrics,
1358 ) -> SessionResult {
1359 let mut counter = 0;
1360
1361 if self.frontend_readiness.event.is_hup() {
1362 return SessionResult::Close;
1363 }
1364
1365 while counter < MAX_LOOP_ITERATIONS {
1366 let frontend_interest = self.frontend_readiness.filter_interest();
1367 let backend_interest = self.backend_readiness.filter_interest();
1368
1369 trace!(
1370 "{} Frontend interest({:?}), backend interest({:?})",
1371 log_context!(self),
1372 frontend_interest,
1373 backend_interest
1374 );
1375 if frontend_interest.is_empty() && backend_interest.is_empty() {
1376 break;
1377 }
1378
1379 if self.backend_readiness.event.is_hup()
1380 && self.frontend_readiness.interest.is_writable()
1381 && !self.frontend_readiness.event.is_writable()
1382 {
1383 break;
1384 }
1385
1386 if frontend_interest.is_readable() && self.readable(metrics) == SessionResult::Close {
1387 return SessionResult::Close;
1388 }
1389
1390 if backend_interest.is_writable()
1391 && self.backend_writable(metrics) == SessionResult::Close
1392 {
1393 return SessionResult::Close;
1394 }
1395
1396 if backend_interest.is_readable()
1397 && self.backend_readable(metrics) == SessionResult::Close
1398 {
1399 return SessionResult::Close;
1400 }
1401
1402 if frontend_interest.is_writable() && self.writable(metrics) == SessionResult::Close {
1403 return SessionResult::Close;
1404 }
1405
1406 if backend_interest.is_hup() && self.backend_hup(metrics) == SessionResult::Close {
1407 return SessionResult::Close;
1408 }
1409
1410 if frontend_interest.is_error() {
1411 error!(
1412 "{} Frontend socket error, disconnecting",
1413 log_context!(self)
1414 );
1415
1416 self.frontend_readiness.interest = Ready::EMPTY;
1417 self.backend_readiness.interest = Ready::EMPTY;
1418
1419 return SessionResult::Close;
1420 }
1421
1422 if backend_interest.is_error() && self.backend_hup(metrics) == SessionResult::Close {
1423 self.frontend_readiness.interest = Ready::EMPTY;
1424 self.backend_readiness.interest = Ready::EMPTY;
1425
1426 error!("{} Backend socket error, disconnecting", log_context!(self));
1427 return SessionResult::Close;
1428 }
1429
1430 counter += 1;
1431 }
1432
1433 if counter >= MAX_LOOP_ITERATIONS {
1434 error!(
1435 "{}\tHandling session went through {} iterations, there's a probable infinite loop bug, closing the connection",
1436 log_context!(self),
1437 MAX_LOOP_ITERATIONS
1438 );
1439
1440 incr!(names::http::INFINITE_LOOP_ERROR);
1441 self.print_state(self.protocol_string());
1442
1443 return SessionResult::Close;
1444 }
1445
1446 SessionResult::Continue
1447 }
1448
1449 fn update_readiness(&mut self, token: Token, events: Ready) {
1450 if self.frontend_token == token {
1451 self.frontend_readiness.event |= events;
1452 } else if self.backend_token == Some(token) {
1453 self.backend_readiness.event |= events;
1454 }
1455 }
1456
1457 fn timeout(&mut self, token: Token, metrics: &mut SessionMetrics) -> StateResult {
1458 if self.frontend_token == token {
1460 self.log_request_timeout(metrics, "frontend socket timeout");
1461 if let Some(timeout) = self.container_frontend_timeout.as_mut() {
1462 timeout.triggered()
1463 }
1464 return StateResult::CloseSession;
1465 }
1466
1467 if self.backend_token == Some(token) {
1468 if let Some(timeout) = self.container_backend_timeout.as_mut() {
1470 timeout.triggered()
1471 }
1472
1473 self.log_request_timeout(metrics, "backend socket timeout");
1474 return StateResult::CloseSession;
1475 }
1476
1477 error!("{} Got timeout for an invalid token", log_context!(self));
1478 self.log_request_error(metrics, "invalid token timeout");
1479 StateResult::CloseSession
1480 }
1481
1482 fn cancel_timeouts(&mut self) {
1483 self.container_frontend_timeout.as_mut().map(|t| t.cancel());
1484 self.container_backend_timeout.as_mut().map(|t| t.cancel());
1485 }
1486
1487 fn close(&mut self, _proxy: Rc<RefCell<dyn L7Proxy>>, _metrics: &mut SessionMetrics) {
1488 if let Some(backend) = self.backend.as_mut() {
1489 let mut backend = backend.borrow_mut();
1490 backend.active_requests = backend.active_requests.saturating_sub(1);
1491 }
1492 }
1493
1494 fn print_state(&self, context: &str) {
1495 error!(
1496 "\
1497{} {} Session(Pipe)
1498\tFrontend:
1499\t\ttoken: {:?}\treadiness: {:?}
1500\tBackend:
1501\t\ttoken: {:?}\treadiness: {:?}",
1502 log_context!(self),
1503 context,
1504 self.frontend_token,
1505 self.frontend_readiness,
1506 self.backend_token,
1507 self.backend_readiness
1508 );
1509 }
1510}
1511
1512#[cfg(test)]
1513mod tests {
1514 use std::{
1515 collections::BTreeMap,
1516 io::Write,
1517 net::{TcpListener as StdTcpListener, TcpStream as StdTcpStream},
1518 time::Duration,
1519 };
1520
1521 use super::*;
1522 use crate::pool::Pool;
1523
1524 struct TestListener {
1525 address: SocketAddr,
1526 }
1527
1528 impl ListenerHandler for TestListener {
1529 fn get_addr(&self) -> &SocketAddr {
1530 &self.address
1531 }
1532
1533 fn get_tags(&self, _key: &str) -> Option<&sozu_command::logging::CachedTags> {
1534 None
1535 }
1536
1537 fn set_tags(&mut self, _key: String, _tags: Option<BTreeMap<String, String>>) {}
1538
1539 fn protocol(&self) -> Protocol {
1540 Protocol::HTTP
1541 }
1542
1543 fn public_address(&self) -> SocketAddr {
1544 self.address
1545 }
1546 }
1547
1548 fn connected_pair() -> (StdTcpStream, StdTcpStream) {
1549 let listener = StdTcpListener::bind("127.0.0.1:0").expect("bind test listener");
1550 let address = listener.local_addr().expect("listener local addr");
1551 let client = StdTcpStream::connect(address).expect("connect test client");
1552 let (server, _) = listener.accept().expect("accept test server");
1553 client.set_nonblocking(true).expect("client nonblocking");
1554 server.set_nonblocking(true).expect("server nonblocking");
1555 (client, server)
1556 }
1557
1558 #[test]
1559 fn backend_readable_arms_frontend_writable_event_when_buffering_response() {
1560 let (frontend_peer, frontend_socket) = connected_pair();
1561 let (mut backend_peer, backend_socket) = connected_pair();
1562
1563 let mut pool = Pool::with_capacity(2, 2, 4096);
1564 let backend_buffer = pool.checkout().expect("backend buffer");
1565 let frontend_buffer = pool.checkout().expect("frontend buffer");
1566 let address = "127.0.0.1:0".parse().expect("test address");
1567 let listener = Rc::new(RefCell::new(TestListener { address }));
1568
1569 let mut pipe = Pipe::new(
1570 backend_buffer,
1571 None,
1572 Some(TcpStream::from_std(backend_socket)),
1573 None,
1574 None,
1575 None,
1576 None,
1577 frontend_buffer,
1578 Token(0),
1579 TcpStream::from_std(frontend_socket),
1580 listener,
1581 Protocol::HTTP,
1582 Ulid::generate(),
1583 Ulid::generate(),
1584 None,
1585 WebSocketContext::Tcp,
1586 );
1587 pipe.set_back_token(Token(1));
1588 pipe.frontend_readiness.event = Ready::EMPTY;
1589 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1590 pipe.backend_readiness.event = Ready::READABLE;
1591 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1592
1593 backend_peer
1594 .write_all(b"server-speaks-first")
1595 .expect("write backend payload");
1596
1597 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
1598 assert_eq!(pipe.backend_readable(&mut metrics), SessionResult::Continue);
1599
1600 assert!(
1601 pipe.backend_buffer.available_data() > 0,
1602 "backend_readable must buffer backend bytes"
1603 );
1604 assert!(
1605 pipe.frontend_readiness.interest.is_writable(),
1606 "buffered backend bytes must arm frontend WRITABLE interest"
1607 );
1608 assert!(
1609 pipe.frontend_readiness.event.is_writable(),
1610 "buffered backend bytes must queue a frontend WRITABLE event"
1611 );
1612
1613 drop(frontend_peer);
1614 }
1615
1616 #[test]
1617 fn frontend_readable_arms_backend_writable_event_when_buffering_request() {
1618 let (mut frontend_peer, frontend_socket) = connected_pair();
1619 let (backend_peer, backend_socket) = connected_pair();
1620
1621 let mut pool = Pool::with_capacity(2, 2, 4096);
1622 let backend_buffer = pool.checkout().expect("backend buffer");
1623 let frontend_buffer = pool.checkout().expect("frontend buffer");
1624 let address = "127.0.0.1:0".parse().expect("test address");
1625 let listener = Rc::new(RefCell::new(TestListener { address }));
1626
1627 let mut pipe = Pipe::new(
1628 backend_buffer,
1629 None,
1630 Some(TcpStream::from_std(backend_socket)),
1631 None,
1632 None,
1633 None,
1634 None,
1635 frontend_buffer,
1636 Token(0),
1637 TcpStream::from_std(frontend_socket),
1638 listener,
1639 Protocol::HTTP,
1640 Ulid::generate(),
1641 Ulid::generate(),
1642 None,
1643 WebSocketContext::Tcp,
1644 );
1645 pipe.set_back_token(Token(1));
1646 pipe.frontend_readiness.event = Ready::READABLE;
1647 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1648 pipe.backend_readiness.event = Ready::EMPTY;
1649 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1650
1651 frontend_peer
1652 .write_all(b"client-speaks-after-upgrade")
1653 .expect("write frontend payload");
1654
1655 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
1656 assert_eq!(pipe.readable(&mut metrics), SessionResult::Continue);
1657
1658 assert!(
1659 pipe.frontend_buffer.available_data() > 0,
1660 "readable must buffer frontend bytes"
1661 );
1662 assert!(
1663 pipe.backend_readiness.interest.is_writable(),
1664 "buffered frontend bytes must arm backend WRITABLE interest"
1665 );
1666 assert!(
1667 pipe.backend_readiness.event.is_writable(),
1668 "buffered frontend bytes must queue a backend WRITABLE event"
1669 );
1670
1671 drop(backend_peer);
1672 }
1673
1674 #[test]
1675 fn restore_readiness_events_rearms_inherited_buffered_writes() {
1676 let (frontend_peer, frontend_socket) = connected_pair();
1677 let (backend_peer, backend_socket) = connected_pair();
1678
1679 let mut pool = Pool::with_capacity(2, 2, 4096);
1680 let mut backend_buffer = pool.checkout().expect("backend buffer");
1681 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
1682 backend_buffer
1683 .write_all(b"backend bytes inherited from 101 read")
1684 .expect("write backend buffer");
1685 frontend_buffer
1686 .write_all(b"frontend bytes inherited from upgrade read")
1687 .expect("write frontend buffer");
1688 let address = "127.0.0.1:0".parse().expect("test address");
1689 let listener = Rc::new(RefCell::new(TestListener { address }));
1690
1691 let mut pipe = Pipe::new(
1692 backend_buffer,
1693 None,
1694 Some(TcpStream::from_std(backend_socket)),
1695 None,
1696 None,
1697 None,
1698 None,
1699 frontend_buffer,
1700 Token(0),
1701 TcpStream::from_std(frontend_socket),
1702 listener,
1703 Protocol::HTTP,
1704 Ulid::generate(),
1705 Ulid::generate(),
1706 None,
1707 WebSocketContext::Tcp,
1708 );
1709
1710 pipe.restore_readiness_events(Ready::EMPTY, Ready::EMPTY);
1711
1712 assert!(
1713 pipe.frontend_readiness.event.is_writable(),
1714 "restoring inherited frontend events must not park backend-buffered bytes"
1715 );
1716 assert!(
1717 pipe.backend_readiness.event.is_writable(),
1718 "restoring inherited backend events must not park frontend-buffered bytes"
1719 );
1720
1721 drop(frontend_peer);
1722 drop(backend_peer);
1723 }
1724}