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 tags_key: Option<String>,
127 #[cfg(all(target_os = "linux", feature = "splice"))]
132 splice_pipe: Option<SplicePipe>,
133}
134
135impl<Front: SocketHandler, L: ListenerHandler> Pipe<Front, L> {
136 #[allow(clippy::too_many_arguments)]
145 pub fn new(
146 backend_buffer: Checkout,
147 backend_id: Option<String>,
148 backend_socket: Option<TcpStream>,
149 backend: Option<Rc<RefCell<Backend>>>,
150 container_backend_timeout: Option<TimeoutContainer>,
151 container_frontend_timeout: Option<TimeoutContainer>,
152 cluster_id: Option<String>,
153 frontend_buffer: Checkout,
154 frontend_token: Token,
155 frontend: Front,
156 listener: Rc<RefCell<L>>,
157 protocol: Protocol,
158 session_id: Ulid,
159 request_id: Ulid,
160 session_address: Option<SocketAddr>,
161 websocket_context: WebSocketContext,
162 ) -> Pipe<Front, L> {
163 let frontend_status = ConnectionStatus::Normal;
164 let backend_status = if backend_socket.is_none() {
165 ConnectionStatus::Closed
166 } else {
167 ConnectionStatus::Normal
168 };
169
170 let mut session = Pipe {
171 backend_buffer,
172 backend_id,
173 backend_readiness: Readiness {
174 interest: Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
175 event: Ready::EMPTY,
176 },
177 backend_socket,
178 backend_status,
179 backend_token: None,
180 backend,
181 cluster_id,
182 container_backend_timeout,
183 container_frontend_timeout,
184 frontend_buffer,
185 frontend_readiness: Readiness {
186 interest: Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
187 event: Ready::EMPTY,
188 },
189 frontend_status,
190 frontend_token,
191 frontend,
192 listener,
193 protocol,
194 session_id,
195 request_id,
196 session_address,
197 websocket_context,
198 tls_version: None,
199 tls_cipher: None,
200 tls_sni: None,
201 tls_alpn: None,
202 tags_key: None,
203 #[cfg(all(target_os = "linux", feature = "splice"))]
204 splice_pipe: if protocol == Protocol::TCP {
205 SplicePipe::new()
206 } else {
207 None
208 },
209 };
210
211 session.arm_inherited_buffer_writes();
212
213 trace!("{} created pipe", log_context!(session));
214 session
215 }
216
217 fn arm_inherited_buffer_writes(&mut self) {
218 if self.backend_buffer.available_data() > 0 {
219 self.frontend_readiness.arm_writable();
220 }
221 if self.frontend_buffer.available_data() > 0 && self.backend_socket.is_some() {
222 self.backend_readiness.arm_writable();
223 }
224 }
225
226 pub fn restore_readiness_events(&mut self, frontend_event: Ready, backend_event: Ready) {
227 self.frontend_readiness.event = frontend_event;
228 self.backend_readiness.event = backend_event;
229 self.arm_inherited_buffer_writes();
230 }
231
232 pub fn set_tls_metadata(
249 &mut self,
250 version: Option<&'static str>,
251 cipher: Option<&'static str>,
252 sni: Option<String>,
253 alpn: Option<&'static str>,
254 ) {
255 self.tls_version = version;
256 self.tls_cipher = cipher;
257 self.tls_sni = sni;
258 self.tls_alpn = alpn;
259 }
260
261 pub fn front_socket(&self) -> &TcpStream {
262 self.frontend.socket_ref()
263 }
264
265 pub fn front_socket_mut(&mut self) -> &mut TcpStream {
266 self.frontend.socket_mut()
267 }
268
269 pub fn back_socket(&self) -> Option<&TcpStream> {
270 self.backend_socket.as_ref()
271 }
272
273 pub fn back_socket_mut(&mut self) -> Option<&mut TcpStream> {
274 self.backend_socket.as_mut()
275 }
276
277 pub fn set_back_socket(&mut self, socket: TcpStream) {
278 self.backend_socket = Some(socket);
279 self.backend_status = ConnectionStatus::Normal;
280 }
281
282 pub fn back_token(&self) -> Vec<Token> {
283 self.backend_token.iter().cloned().collect()
284 }
285
286 fn reset_timeouts(&mut self) {
287 if let Some(t) = self.container_frontend_timeout.as_mut()
288 && !t.reset()
289 {
290 error!(
291 "{} Could not reset front timeout (pipe)",
292 log_context!(self)
293 );
294 }
295
296 if let Some(t) = self.container_backend_timeout.as_mut()
297 && !t.reset()
298 {
299 error!("{} Could not reset back timeout (pipe)", log_context!(self));
300 }
301 }
302
303 pub fn set_cluster_id(&mut self, cluster_id: Option<String>) {
304 self.cluster_id = cluster_id;
305 }
306
307 pub fn set_tags_key(&mut self, tags_key: Option<String>) {
312 self.tags_key = tags_key;
313 }
314
315 pub fn set_backend_id(&mut self, backend_id: Option<String>) {
316 self.backend_id = backend_id;
317 }
318
319 pub fn set_back_token(&mut self, token: Token) {
320 self.backend_token = Some(token);
321 }
322
323 pub fn get_session_address(&self) -> Option<SocketAddr> {
324 self.session_address
325 .or_else(|| self.frontend.socket_ref().peer_addr().ok())
326 }
327
328 pub fn get_backend_address(&self) -> Option<SocketAddr> {
329 self.backend_socket
330 .as_ref()
331 .and_then(|backend| backend.peer_addr().ok())
332 }
333
334 fn protocol_string(&self) -> &'static str {
335 match self.protocol {
336 Protocol::TCP => "TCP",
337 Protocol::HTTP => "WS",
338 Protocol::HTTPS => match self.frontend.protocol() {
339 TransportProtocol::Ssl2 => "WSS-SSL2",
340 TransportProtocol::Ssl3 => "WSS-SSL3",
341 TransportProtocol::Tls1_0 => "WSS-TLS1.0",
342 TransportProtocol::Tls1_1 => "WSS-TLS1.1",
343 TransportProtocol::Tls1_2 => "WSS-TLS1.2",
344 TransportProtocol::Tls1_3 => "WSS-TLS1.3",
345 _ => unreachable!(),
346 },
347 _ => unreachable!(),
348 }
349 }
350
351 pub fn log_request(&self, metrics: &SessionMetrics, error: bool, message: Option<&str>) {
352 let listener = self.listener.borrow();
353 let context = self.log_context();
354 let endpoint = self.log_endpoint();
355 metrics.register_end_of_session(&context);
356 let address_key = listener.get_addr().to_string();
360 let tags_key = self.tags_key.as_deref().unwrap_or(&address_key);
361 log_access!(
362 error,
363 on_failure: { incr!(names::access_logs::UNSENT) },
364 message,
365 context,
366 session_address: self.get_session_address(),
367 backend_address: self.get_backend_address(),
368 protocol: self.protocol_string(),
369 endpoint,
370 tags: listener.get_tags(tags_key),
371 client_rtt: socket_rtt(self.front_socket()),
372 server_rtt: self.backend_socket.as_ref().and_then(socket_rtt),
373 service_time: metrics.service_time(),
374 response_time: metrics.backend_response_time(),
375 request_time: metrics.request_time(),
376 start_time_ns: metrics.start_wall_ns(),
377 bytes_in: metrics.bin,
378 bytes_out: metrics.bout,
379 user_agent: None,
380 x_request_id: None,
381 tls_version: self.tls_version,
386 tls_cipher: self.tls_cipher,
387 tls_sni: self.tls_sni.as_deref(),
388 tls_alpn: self.tls_alpn,
389 xff_chain: None,
390 otel: None,
391 );
392 }
393
394 pub fn log_request_success(&self, metrics: &SessionMetrics) {
395 self.log_request(metrics, false, None);
396 }
397
398 pub fn log_request_error(&self, metrics: &SessionMetrics, message: &str) {
399 incr!(names::pipe::ERRORS);
400 error!(
401 "{} Could not process request properly got: {}",
402 log_context!(self),
403 message
404 );
405 self.print_state(self.protocol_string());
406 self.log_request(metrics, true, Some(message));
407 }
408
409 pub fn log_request_timeout(&self, metrics: &SessionMetrics, message: &str) {
416 debug!("{} pipe timeout: {}", log_context!(self), message);
417 self.log_request(metrics, true, Some(message));
418 }
419
420 #[cfg(all(target_os = "linux", feature = "splice"))]
425 fn splice_in_pending(&self) -> usize {
426 self.splice_pipe
427 .as_ref()
428 .map(|p| p.in_pipe_pending)
429 .unwrap_or(0)
430 }
431 #[cfg(not(all(target_os = "linux", feature = "splice")))]
432 fn splice_in_pending(&self) -> usize {
433 0
434 }
435
436 #[cfg(all(target_os = "linux", feature = "splice"))]
440 fn splice_out_pending(&self) -> usize {
441 self.splice_pipe
442 .as_ref()
443 .map(|p| p.out_pipe_pending)
444 .unwrap_or(0)
445 }
446 #[cfg(not(all(target_os = "linux", feature = "splice")))]
447 fn splice_out_pending(&self) -> usize {
448 0
449 }
450
451 #[cfg(all(target_os = "linux", feature = "splice"))]
455 fn splice_capacity(&self) -> usize {
456 self.splice_pipe.as_ref().map(|p| p.capacity).unwrap_or(0)
457 }
458
459 fn reset_readiness_for_close(&mut self) {
469 self.frontend_readiness.reset();
470 self.backend_readiness.reset();
471 debug_assert!(
472 self.frontend_readiness.interest.is_empty() && self.frontend_readiness.event.is_empty(),
473 "frontend readiness must be fully cleared on close (write-only-shutdown discipline)"
474 );
475 debug_assert!(
476 self.backend_readiness.interest.is_empty() && self.backend_readiness.event.is_empty(),
477 "backend readiness must be fully cleared on close (write-only-shutdown discipline)"
478 );
479 }
480
481 pub fn check_connections(&self) -> bool {
484 debug_assert!(
492 self.frontend_buffer.available_data() <= self.frontend_buffer.capacity(),
493 "frontend buffered data exceeds its capacity"
494 );
495 debug_assert!(
496 self.backend_buffer.available_data() <= self.backend_buffer.capacity(),
497 "backend buffered data exceeds its capacity"
498 );
499
500 let request_is_inflight = self.frontend_buffer.available_data() > 0
501 || self.frontend_readiness.event.is_readable()
502 || self.splice_in_pending() > 0;
503 let response_is_inflight = self.backend_buffer.available_data() > 0
504 || self.backend_readiness.event.is_readable()
505 || self.splice_out_pending() > 0;
506 match (self.frontend_status, self.backend_status) {
507 (ConnectionStatus::Normal, ConnectionStatus::Normal) => true,
508 (ConnectionStatus::Normal, ConnectionStatus::ReadOpen) => true,
509 (ConnectionStatus::Normal, ConnectionStatus::WriteOpen) => {
510 request_is_inflight || response_is_inflight
515 }
516 (ConnectionStatus::Normal, ConnectionStatus::Closed) => response_is_inflight,
517
518 (ConnectionStatus::WriteOpen, ConnectionStatus::Normal) => {
519 request_is_inflight || response_is_inflight
522 }
523 (ConnectionStatus::WriteOpen, ConnectionStatus::ReadOpen) => true,
524 (ConnectionStatus::WriteOpen, ConnectionStatus::WriteOpen) => {
525 request_is_inflight || response_is_inflight
526 }
527 (ConnectionStatus::WriteOpen, ConnectionStatus::Closed) => response_is_inflight,
528
529 (ConnectionStatus::ReadOpen, ConnectionStatus::Normal) => true,
530 (ConnectionStatus::ReadOpen, ConnectionStatus::ReadOpen) => false,
531 (ConnectionStatus::ReadOpen, ConnectionStatus::WriteOpen) => true,
532 (ConnectionStatus::ReadOpen, ConnectionStatus::Closed) => false,
533
534 (ConnectionStatus::Closed, ConnectionStatus::Normal) => request_is_inflight,
535 (ConnectionStatus::Closed, ConnectionStatus::ReadOpen) => false,
536 (ConnectionStatus::Closed, ConnectionStatus::WriteOpen) => request_is_inflight,
537 (ConnectionStatus::Closed, ConnectionStatus::Closed) => false,
538 }
539 }
540
541 pub fn frontend_hup(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
542 self.frontend_status = ConnectionStatus::Closed;
543 debug_assert!(
546 matches!(self.frontend_status, ConnectionStatus::Closed),
547 "frontend_hup must mark the frontend Closed"
548 );
549 let request_is_inflight = self.frontend_buffer.available_data() > 0
559 || self.frontend_readiness.event.is_readable()
560 || self.splice_in_pending() > 0;
561 if request_is_inflight && self.backend_socket.is_some() {
562 debug_assert!(
565 self.frontend_buffer.available_data() > 0
566 || self.frontend_readiness.event.is_readable()
567 || self.splice_in_pending() > 0,
568 "drain branch entered without any observable in-flight request bytes"
569 );
570 if self.frontend_readiness.event.is_readable() {
571 self.frontend_readiness.interest.insert(Ready::READABLE);
575 }
576 self.backend_readiness.arm_writable();
577 debug!(
578 "{} Pipe::frontend_hup: frontend connection closed, keeping alive due to inflight request data.",
579 log_context!(self)
580 );
581 SessionResult::Continue
582 } else {
583 debug_assert!(
586 self.backend_socket.is_none() || self.frontend_buffer.available_data() == 0,
587 "close branch entered with backend present but request bytes still queued"
588 );
589 self.log_request_success(metrics);
590 SessionResult::Close
591 }
592 }
593
594 pub fn backend_hup(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
595 self.backend_status = ConnectionStatus::Closed;
596 debug_assert!(
599 matches!(self.backend_status, ConnectionStatus::Closed),
600 "backend_hup must mark the backend Closed"
601 );
602 let pipe_has_data = self.splice_out_pending() > 0;
603 if self.backend_buffer.available_data() == 0 && !pipe_has_data {
604 debug_assert_eq!(
607 self.backend_buffer.available_data(),
608 0,
609 "no-data branch entered with response bytes still buffered"
610 );
611 if self.backend_readiness.event.is_readable() {
612 self.backend_readiness.interest.insert(Ready::READABLE);
613 debug!(
614 "{} Pipe::backend_hup: backend connection closed, keeping alive due to inflight data in kernel.",
615 log_context!(self)
616 );
617 SessionResult::Continue
618 } else {
619 self.log_request_success(metrics);
620 SessionResult::Close
621 }
622 } else {
623 debug!(
624 "{} Pipe::backend_hup: backend connection closed, keeping alive due to inflight data in buffers.",
625 log_context!(self)
626 );
627 self.frontend_readiness.arm_writable();
628 if self.backend_readiness.event.is_readable() {
629 self.backend_readiness.interest.insert(Ready::READABLE);
630 }
631 SessionResult::Continue
632 }
633 }
634
635 fn close_frontend_read_side(
645 &mut self,
646 metrics: &mut SessionMetrics,
647 has_pending: bool,
648 ) -> SessionResult {
649 self.frontend_status = match self.frontend_status {
650 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
651 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
652 s => s,
653 };
654 self.frontend_readiness.event.remove(Ready::READABLE);
655 self.frontend_readiness.interest.remove(Ready::READABLE);
656 if has_pending && self.backend_socket.is_some() {
657 self.backend_readiness.arm_writable();
658 }
659 if !self.check_connections() {
660 self.reset_readiness_for_close();
661 self.log_request_success(metrics);
662 return SessionResult::Close;
663 }
664 SessionResult::Continue
665 }
666
667 pub fn readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
669 #[cfg(all(target_os = "linux", feature = "splice"))]
673 if self.protocol == Protocol::TCP
674 && self.splice_pipe.is_some()
675 && self.frontend_buffer.available_data() == 0
676 {
677 return self.splice_readable(metrics);
678 }
679
680 self.reset_timeouts();
681
682 trace!("{} pipe readable", log_context!(self));
683 if self.frontend_buffer.available_space() == 0 {
684 self.frontend_readiness.interest.remove(Ready::READABLE);
685 self.backend_readiness.arm_writable();
686 return SessionResult::Continue;
687 }
688
689 let space_before = self.frontend_buffer.available_space();
690 let data_before = self.frontend_buffer.available_data();
691 let bin_before = metrics.bin;
692 let (sz, res) = self.frontend.socket_read(self.frontend_buffer.space());
693 debug_assert!(
696 sz <= space_before,
697 "frontend socket_read reported more bytes ({sz}) than the buffer space offered ({space_before})"
698 );
699 debug!("{} Read {} bytes", log_context!(self), sz);
700
701 if sz > 0 {
702 self.frontend_buffer.fill(sz);
704 debug_assert_eq!(
707 self.frontend_buffer.available_data(),
708 data_before + sz,
709 "fill must grow readable data by exactly the bytes read"
710 );
711
712 count!(names::backend::BYTES_IN, sz as i64);
713 metrics.bin += sz;
714 debug_assert_eq!(
716 metrics.bin,
717 bin_before + sz,
718 "metrics.bin must advance by exactly the bytes read"
719 );
720
721 if self.frontend_buffer.available_space() == 0 {
722 self.frontend_readiness.interest.remove(Ready::READABLE);
723 }
724 self.backend_readiness.arm_writable();
725 } else {
726 self.frontend_readiness.event.remove(Ready::READABLE);
727
728 if res == SocketResult::Continue {
729 self.frontend_status = match self.frontend_status {
730 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
731 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
732 s => s,
733 };
734 }
735 }
736
737 if !self.check_connections() {
738 self.reset_readiness_for_close();
739 self.log_request_success(metrics);
740 return SessionResult::Close;
741 }
742
743 match res {
744 SocketResult::Error => {
745 self.reset_readiness_for_close();
746 self.log_request_error(metrics, "front socket read error");
747 return SessionResult::Close;
748 }
749 SocketResult::Closed => {
750 let has_pending = self.frontend_buffer.available_data() > 0;
768 return self.close_frontend_read_side(metrics, has_pending);
769 }
770 SocketResult::WouldBlock => {
771 self.frontend_readiness.event.remove(Ready::READABLE);
772 }
773 SocketResult::Continue => {}
774 };
775
776 self.backend_readiness.arm_writable();
777 SessionResult::Continue
778 }
779
780 pub fn writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
782 #[cfg(all(target_os = "linux", feature = "splice"))]
786 if self.protocol == Protocol::TCP
787 && self.splice_pipe.is_some()
788 && self.backend_buffer.available_data() == 0
789 {
790 return self.splice_writable(metrics);
791 }
792
793 trace!("{} Pipe writable", log_context!(self));
794 if self.backend_buffer.available_data() == 0 {
795 self.backend_readiness.interest.insert(Ready::READABLE);
796 self.frontend_readiness.interest.remove(Ready::WRITABLE);
797 return SessionResult::Continue;
798 }
799
800 let queued_total = self.backend_buffer.available_data();
801 let mut sz = 0usize;
802 let mut res = SocketResult::Continue;
803 while res == SocketResult::Continue {
804 if self.backend_buffer.available_data() == 0 {
806 count!(names::backend::BYTES_OUT, sz as i64);
807 metrics.bout += sz;
808 self.backend_readiness.interest.insert(Ready::READABLE);
809 self.frontend_readiness.interest.remove(Ready::WRITABLE);
810 return SessionResult::Continue;
811 }
812 let queued = self.backend_buffer.available_data();
813 let (current_sz, current_res) = self.frontend.socket_write(self.backend_buffer.data());
814 debug_assert!(
817 current_sz <= queued,
818 "frontend socket_write reported {current_sz} bytes but only {queued} were queued"
819 );
820 res = current_res;
821 let consumed = self.backend_buffer.consume(current_sz);
822 debug_assert_eq!(
825 consumed, current_sz,
826 "consume must drop exactly the bytes written to the frontend"
827 );
828 sz += current_sz;
829 debug_assert!(
831 sz <= queued_total,
832 "cumulative frontend write ({sz}) exceeded the queued backend data ({queued_total})"
833 );
834
835 if current_sz == 0 && res == SocketResult::Continue {
836 self.frontend_status = match self.frontend_status {
837 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
838 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
839 s => s,
840 };
841 }
842
843 if !self.check_connections() {
844 metrics.bout += sz;
845 count!(names::backend::BYTES_OUT, sz as i64);
846 self.reset_readiness_for_close();
847 self.log_request_success(metrics);
848 return SessionResult::Close;
849 }
850 }
851
852 if sz > 0 {
853 count!(names::backend::BYTES_OUT, sz as i64);
854 self.backend_readiness.interest.insert(Ready::READABLE);
855 metrics.bout += sz;
856 }
857
858 debug!(
859 "{} Wrote {} bytes of {}",
860 log_context!(self),
861 sz,
862 self.backend_buffer.available_data()
863 );
864
865 match res {
866 SocketResult::Error => {
867 self.reset_readiness_for_close();
868 self.log_request_error(metrics, "front socket write error");
869 return SessionResult::Close;
870 }
871 SocketResult::Closed => {
872 self.reset_readiness_for_close();
873 self.log_request_success(metrics);
874 return SessionResult::Close;
875 }
876 SocketResult::WouldBlock => {
877 self.frontend_readiness.event.remove(Ready::WRITABLE);
878 }
879 SocketResult::Continue => {}
880 }
881
882 SessionResult::Continue
883 }
884
885 pub fn backend_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
887 #[cfg(all(target_os = "linux", feature = "splice"))]
891 if self.protocol == Protocol::TCP
892 && self.splice_pipe.is_some()
893 && self.frontend_buffer.available_data() == 0
894 {
895 return self.splice_backend_writable(metrics);
896 }
897
898 trace!("{} pipe back_writable", log_context!(self));
899
900 if self.frontend_buffer.available_data() == 0 {
901 self.frontend_readiness.interest.insert(Ready::READABLE);
902 self.backend_readiness.interest.remove(Ready::WRITABLE);
903 return SessionResult::Continue;
904 }
905
906 let output_size = self.frontend_buffer.available_data();
907
908 let mut sz = 0usize;
909 let mut socket_res = SocketResult::Continue;
910 let mut drained = false;
917
918 if let Some(ref mut backend) = self.backend_socket {
919 while socket_res == SocketResult::Continue {
920 if self.frontend_buffer.available_data() == 0 {
922 self.frontend_readiness.interest.insert(Ready::READABLE);
923 self.backend_readiness.interest.remove(Ready::WRITABLE);
924 drained = true;
925 break;
926 }
927
928 let queued = self.frontend_buffer.available_data();
929 let (current_sz, current_res) = backend.socket_write(self.frontend_buffer.data());
930 debug_assert!(
932 current_sz <= queued,
933 "backend socket_write reported {current_sz} bytes but only {queued} were queued"
934 );
935 socket_res = current_res;
936 let consumed = self.frontend_buffer.consume(current_sz);
937 debug_assert_eq!(
938 consumed, current_sz,
939 "consume must drop exactly the bytes written to the backend"
940 );
941 sz += current_sz;
942 debug_assert!(
944 sz <= output_size,
945 "cumulative backend write ({sz}) exceeded the queued frontend data ({output_size})"
946 );
947
948 if current_sz == 0 && current_res == SocketResult::Continue {
949 self.backend_status = match self.backend_status {
950 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
951 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
952 s => s,
953 };
954 }
955 }
956 }
957
958 if drained {
959 count!(names::backend::BACK_BYTES_OUT, sz as i64);
960 metrics.backend_bout += sz;
961 if matches!(
977 self.frontend_status,
978 ConnectionStatus::WriteOpen | ConnectionStatus::Closed
979 ) && !self.check_connections()
980 {
981 self.reset_readiness_for_close();
982 self.log_request_success(metrics);
983 return SessionResult::Close;
984 }
985 return SessionResult::Continue;
986 }
987
988 let backend_bout_before = metrics.backend_bout;
989 count!(names::backend::BACK_BYTES_OUT, sz as i64);
990 metrics.backend_bout += sz;
991 debug_assert_eq!(
993 metrics.backend_bout,
994 backend_bout_before + sz,
995 "metrics.backend_bout must advance by exactly the bytes written"
996 );
997
998 if !self.check_connections() {
999 self.reset_readiness_for_close();
1000 self.log_request_success(metrics);
1001 return SessionResult::Close;
1002 }
1003
1004 debug!(
1005 "{} Wrote {} bytes of {}",
1006 log_context!(self),
1007 sz,
1008 output_size
1009 );
1010
1011 match socket_res {
1012 SocketResult::Error => {
1013 self.reset_readiness_for_close();
1014 self.log_request_error(metrics, "back socket write error");
1015 return SessionResult::Close;
1016 }
1017 SocketResult::Closed => {
1018 self.reset_readiness_for_close();
1019 self.log_request_success(metrics);
1020 return SessionResult::Close;
1021 }
1022 SocketResult::WouldBlock => {
1023 self.backend_readiness.event.remove(Ready::WRITABLE);
1024 }
1025 SocketResult::Continue => {}
1026 }
1027 SessionResult::Continue
1028 }
1029
1030 pub fn backend_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1032 #[cfg(all(target_os = "linux", feature = "splice"))]
1036 if self.protocol == Protocol::TCP
1037 && self.splice_pipe.is_some()
1038 && self.backend_buffer.available_data() == 0
1039 {
1040 return self.splice_backend_readable(metrics);
1041 }
1042
1043 self.reset_timeouts();
1044
1045 trace!("{} Pipe backend_readable", log_context!(self));
1046 if self.backend_buffer.available_space() == 0 {
1047 self.backend_readiness.interest.remove(Ready::READABLE);
1048 return SessionResult::Continue;
1049 }
1050
1051 let space_before = self.backend_buffer.available_space();
1052 let data_before = self.backend_buffer.available_data();
1053 let backend_bin_before = metrics.backend_bin;
1054 if let Some(ref mut backend) = self.backend_socket {
1055 let (size, remaining) = backend.socket_read(self.backend_buffer.space());
1056 debug_assert!(
1058 size <= space_before,
1059 "backend socket_read reported more bytes ({size}) than the buffer space offered ({space_before})"
1060 );
1061 self.backend_buffer.fill(size);
1062 debug_assert_eq!(
1065 self.backend_buffer.available_data(),
1066 data_before + size,
1067 "fill must grow readable data by exactly the bytes read"
1068 );
1069
1070 debug!("{} Read {} bytes", log_context!(self), size);
1071
1072 if remaining != SocketResult::Continue || size == 0 {
1073 self.backend_readiness.event.remove(Ready::READABLE);
1074 }
1075 if size > 0 {
1076 self.frontend_readiness.arm_writable();
1077 count!(names::backend::BACK_BYTES_IN, size as i64);
1078 metrics.backend_bin += size;
1079 debug_assert_eq!(
1081 metrics.backend_bin,
1082 backend_bin_before + size,
1083 "metrics.backend_bin must advance by exactly the bytes read"
1084 );
1085 }
1086
1087 if size == 0 && remaining == SocketResult::Closed {
1088 self.backend_status = match self.backend_status {
1089 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
1090 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
1091 s => s,
1092 };
1093
1094 if !self.check_connections() {
1095 self.reset_readiness_for_close();
1096 self.log_request_success(metrics);
1097 return SessionResult::Close;
1098 }
1099 }
1100
1101 match remaining {
1102 SocketResult::Error => {
1103 self.reset_readiness_for_close();
1104 self.log_request_error(metrics, "back socket read error");
1105 return SessionResult::Close;
1106 }
1107 SocketResult::Closed => {
1108 if !self.check_connections() {
1109 self.reset_readiness_for_close();
1110 self.log_request_success(metrics);
1111 return SessionResult::Close;
1112 }
1113 }
1114 SocketResult::WouldBlock => {
1115 self.backend_readiness.event.remove(Ready::READABLE);
1116 }
1117 SocketResult::Continue => {}
1118 }
1119 }
1120
1121 SessionResult::Continue
1122 }
1123
1124 #[cfg(all(target_os = "linux", feature = "splice"))]
1132 fn splice_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1133 self.reset_timeouts();
1134
1135 trace!("{} pipe splice_readable", log_context!(self));
1136 let capacity = self.splice_capacity();
1137 if self.splice_in_pending() >= capacity {
1138 self.frontend_readiness.interest.remove(Ready::READABLE);
1140 self.backend_readiness.arm_writable();
1141 return SessionResult::Continue;
1142 }
1143
1144 let pending_before = self.splice_in_pending();
1145 let bin_before = metrics.bin;
1146 let pipe_write_end = self.splice_pipe.as_ref().unwrap().in_pipe[1];
1147 let (sz, res) = splice::splice_in(self.frontend.socket_ref(), pipe_write_end, capacity);
1148 debug_assert!(
1157 sz <= capacity,
1158 "splice_in reported {sz} bytes but was capped at len {capacity}"
1159 );
1160 debug!("{} Spliced {} bytes from frontend", log_context!(self), sz);
1161
1162 if sz > 0 {
1163 self.splice_pipe.as_mut().unwrap().in_pipe_pending += sz;
1164 debug_assert_eq!(
1167 self.splice_in_pending(),
1168 pending_before + sz,
1169 "in_pipe_pending must grow by exactly the spliced bytes"
1170 );
1171 count!(names::backend::BYTES_IN, sz as i64);
1172 metrics.bin += sz;
1173 debug_assert_eq!(
1174 metrics.bin,
1175 bin_before + sz,
1176 "metrics.bin must advance by exactly the spliced bytes"
1177 );
1178 self.backend_readiness.arm_writable();
1179 } else {
1180 self.frontend_readiness.event.remove(Ready::READABLE);
1181
1182 if res == SocketResult::Continue {
1183 self.frontend_status = match self.frontend_status {
1184 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
1185 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
1186 s => s,
1187 };
1188 }
1189 }
1190
1191 if !self.check_connections() {
1192 self.reset_readiness_for_close();
1193 self.log_request_success(metrics);
1194 return SessionResult::Close;
1195 }
1196
1197 match res {
1198 SocketResult::Error => {
1199 self.reset_readiness_for_close();
1200 self.log_request_error(metrics, "splice front socket read error");
1201 return SessionResult::Close;
1202 }
1203 SocketResult::Closed => {
1204 let has_pending = self.splice_in_pending() > 0;
1219 return self.close_frontend_read_side(metrics, has_pending);
1220 }
1221 SocketResult::WouldBlock => {
1222 self.frontend_readiness.event.remove(Ready::READABLE);
1223 }
1224 SocketResult::Continue => {}
1225 }
1226
1227 self.backend_readiness.arm_writable();
1228 SessionResult::Continue
1229 }
1230
1231 #[cfg(all(target_os = "linux", feature = "splice"))]
1236 fn splice_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1237 trace!("{} Pipe splice_writable", log_context!(self));
1238 if self.splice_out_pending() == 0 {
1239 self.backend_readiness.interest.insert(Ready::READABLE);
1240 self.frontend_readiness.interest.remove(Ready::WRITABLE);
1241 return SessionResult::Continue;
1242 }
1243
1244 let mut sz = 0usize;
1245 let mut res = SocketResult::Continue;
1246 while res == SocketResult::Continue {
1247 let pending = self.splice_out_pending();
1248 if pending == 0 {
1250 count!(names::backend::BYTES_OUT, sz as i64);
1251 metrics.bout += sz;
1252 self.backend_readiness.interest.insert(Ready::READABLE);
1253 self.frontend_readiness.interest.remove(Ready::WRITABLE);
1254 return SessionResult::Continue;
1255 }
1256
1257 let pipe_read_end = self.splice_pipe.as_ref().unwrap().out_pipe[0];
1258 let (current_sz, current_res) =
1259 splice::splice_out(pipe_read_end, self.frontend.socket_ref(), pending);
1260 debug_assert!(
1264 current_sz <= pending,
1265 "splice_out drained {current_sz} bytes but only {pending} were pending (would underflow)"
1266 );
1267 res = current_res;
1268 if current_sz > 0 {
1269 self.splice_pipe.as_mut().unwrap().out_pipe_pending -= current_sz;
1270 debug_assert_eq!(
1271 self.splice_out_pending(),
1272 pending - current_sz,
1273 "out_pipe_pending must shrink by exactly the drained bytes"
1274 );
1275 }
1276 sz += current_sz;
1277
1278 if current_sz == 0 && res == SocketResult::Continue {
1279 self.frontend_status = match self.frontend_status {
1280 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
1281 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
1282 s => s,
1283 };
1284 }
1285
1286 if !self.check_connections() {
1287 metrics.bout += sz;
1288 count!(names::backend::BYTES_OUT, sz as i64);
1289 self.reset_readiness_for_close();
1290 self.log_request_success(metrics);
1291 return SessionResult::Close;
1292 }
1293 }
1294
1295 if sz > 0 {
1296 count!(names::backend::BYTES_OUT, sz as i64);
1297 self.backend_readiness.interest.insert(Ready::READABLE);
1298 metrics.bout += sz;
1299 }
1300
1301 debug!(
1302 "{} Spliced {} bytes (out_pipe_pending={})",
1303 log_context!(self),
1304 sz,
1305 self.splice_out_pending()
1306 );
1307
1308 match res {
1309 SocketResult::Error => {
1310 self.reset_readiness_for_close();
1311 self.log_request_error(metrics, "splice front socket write error");
1312 return SessionResult::Close;
1313 }
1314 SocketResult::Closed => {
1315 self.reset_readiness_for_close();
1316 self.log_request_success(metrics);
1317 return SessionResult::Close;
1318 }
1319 SocketResult::WouldBlock => {
1320 self.frontend_readiness.event.remove(Ready::WRITABLE);
1321 }
1322 SocketResult::Continue => {}
1323 }
1324
1325 SessionResult::Continue
1326 }
1327
1328 #[cfg(all(target_os = "linux", feature = "splice"))]
1333 fn splice_backend_writable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1334 trace!("{} pipe splice_backend_writable", log_context!(self));
1335
1336 if self.splice_in_pending() == 0 {
1337 self.frontend_readiness.interest.insert(Ready::READABLE);
1338 self.backend_readiness.interest.remove(Ready::WRITABLE);
1339 return SessionResult::Continue;
1340 }
1341
1342 let output_size = self.splice_in_pending();
1343 let mut sz = 0usize;
1344 let mut socket_res = SocketResult::Continue;
1345
1346 while socket_res == SocketResult::Continue {
1347 let pending = self.splice_in_pending();
1348 if pending == 0 {
1350 self.frontend_readiness.interest.insert(Ready::READABLE);
1351 self.backend_readiness.interest.remove(Ready::WRITABLE);
1352 count!(names::backend::BACK_BYTES_OUT, sz as i64);
1353 metrics.backend_bout += sz;
1354 if matches!(
1371 self.frontend_status,
1372 ConnectionStatus::WriteOpen | ConnectionStatus::Closed
1373 ) && !self.check_connections()
1374 {
1375 self.reset_readiness_for_close();
1376 self.log_request_success(metrics);
1377 return SessionResult::Close;
1378 }
1379 return SessionResult::Continue;
1380 }
1381
1382 let pipe_read_end = self.splice_pipe.as_ref().unwrap().in_pipe[0];
1383 let (current_sz, current_res) = match self.backend_socket.as_ref() {
1384 Some(b) => splice::splice_out(pipe_read_end, b, pending),
1385 None => break,
1386 };
1387 debug_assert!(
1389 current_sz <= pending,
1390 "splice_out drained {current_sz} bytes but only {pending} were pending (would underflow)"
1391 );
1392 socket_res = current_res;
1393 if current_sz > 0 {
1394 self.splice_pipe.as_mut().unwrap().in_pipe_pending -= current_sz;
1395 debug_assert_eq!(
1396 self.splice_in_pending(),
1397 pending - current_sz,
1398 "in_pipe_pending must shrink by exactly the drained bytes"
1399 );
1400 }
1401 sz += current_sz;
1402 debug_assert!(
1404 sz <= output_size,
1405 "cumulative splice drain ({sz}) exceeded the bytes pending at entry ({output_size})"
1406 );
1407
1408 if current_sz == 0 && current_res == SocketResult::Continue {
1409 self.backend_status = match self.backend_status {
1410 ConnectionStatus::Normal => ConnectionStatus::ReadOpen,
1411 ConnectionStatus::WriteOpen => ConnectionStatus::Closed,
1412 s => s,
1413 };
1414 }
1415 }
1416
1417 count!(names::backend::BACK_BYTES_OUT, sz as i64);
1418 metrics.backend_bout += sz;
1419
1420 if !self.check_connections() {
1421 self.reset_readiness_for_close();
1422 self.log_request_success(metrics);
1423 return SessionResult::Close;
1424 }
1425
1426 debug!(
1427 "{} Spliced {} bytes of {}",
1428 log_context!(self),
1429 sz,
1430 output_size
1431 );
1432
1433 match socket_res {
1434 SocketResult::Error => {
1435 self.reset_readiness_for_close();
1436 self.log_request_error(metrics, "splice back socket write error");
1437 return SessionResult::Close;
1438 }
1439 SocketResult::Closed => {
1440 self.reset_readiness_for_close();
1441 self.log_request_success(metrics);
1442 return SessionResult::Close;
1443 }
1444 SocketResult::WouldBlock => {
1445 self.backend_readiness.event.remove(Ready::WRITABLE);
1446 }
1447 SocketResult::Continue => {}
1448 }
1449 SessionResult::Continue
1450 }
1451
1452 #[cfg(all(target_os = "linux", feature = "splice"))]
1458 fn splice_backend_readable(&mut self, metrics: &mut SessionMetrics) -> SessionResult {
1459 self.reset_timeouts();
1460
1461 trace!("{} Pipe splice_backend_readable", log_context!(self));
1462 let capacity = self.splice_capacity();
1463 if self.splice_out_pending() >= capacity {
1464 self.backend_readiness.interest.remove(Ready::READABLE);
1466 self.frontend_readiness.arm_writable();
1467 return SessionResult::Continue;
1468 }
1469
1470 let pending_before = self.splice_out_pending();
1471 let backend_bin_before = metrics.backend_bin;
1472 let pipe_write_end = self.splice_pipe.as_ref().unwrap().out_pipe[1];
1473 let (size, remaining) = match self.backend_socket.as_ref() {
1474 Some(b) => splice::splice_in(b, pipe_write_end, capacity),
1475 None => return SessionResult::Continue,
1476 };
1477 debug_assert!(
1484 size <= capacity,
1485 "splice_in reported {size} bytes but was capped at len {capacity}"
1486 );
1487
1488 debug!("{} Spliced {} bytes from backend", log_context!(self), size);
1489
1490 if remaining != SocketResult::Continue || size == 0 {
1491 self.backend_readiness.event.remove(Ready::READABLE);
1492 }
1493 if size > 0 {
1494 self.splice_pipe.as_mut().unwrap().out_pipe_pending += size;
1495 debug_assert_eq!(
1496 self.splice_out_pending(),
1497 pending_before + size,
1498 "out_pipe_pending must grow by exactly the spliced bytes"
1499 );
1500 self.frontend_readiness.arm_writable();
1501 count!(names::backend::BACK_BYTES_IN, size as i64);
1502 metrics.backend_bin += size;
1503 debug_assert_eq!(
1504 metrics.backend_bin,
1505 backend_bin_before + size,
1506 "metrics.backend_bin must advance by exactly the spliced bytes"
1507 );
1508 }
1509
1510 if size == 0 && remaining == SocketResult::Closed {
1511 self.backend_status = match self.backend_status {
1512 ConnectionStatus::Normal => ConnectionStatus::WriteOpen,
1513 ConnectionStatus::ReadOpen => ConnectionStatus::Closed,
1514 s => s,
1515 };
1516
1517 if !self.check_connections() {
1518 self.reset_readiness_for_close();
1519 self.log_request_success(metrics);
1520 return SessionResult::Close;
1521 }
1522 }
1523
1524 match remaining {
1525 SocketResult::Error => {
1526 self.reset_readiness_for_close();
1527 self.log_request_error(metrics, "splice back socket read error");
1528 return SessionResult::Close;
1529 }
1530 SocketResult::Closed => {
1531 if !self.check_connections() {
1532 self.reset_readiness_for_close();
1533 self.log_request_success(metrics);
1534 return SessionResult::Close;
1535 }
1536 }
1537 SocketResult::WouldBlock => {
1538 self.backend_readiness.event.remove(Ready::READABLE);
1539 }
1540 SocketResult::Continue => {}
1541 }
1542
1543 SessionResult::Continue
1544 }
1545
1546 pub fn log_context(&self) -> LogContext<'_> {
1547 LogContext {
1548 session_id: self.session_id,
1549 request_id: Some(self.request_id),
1550 cluster_id: self.cluster_id.as_deref(),
1551 backend_id: self.backend_id.as_deref(),
1552 }
1553 }
1554
1555 fn log_endpoint(&self) -> EndpointRecord<'_> {
1556 match &self.websocket_context {
1557 WebSocketContext::Http {
1558 method,
1559 authority,
1560 path,
1561 status,
1562 reason,
1563 } => EndpointRecord::Http {
1564 method: method.as_deref(),
1565 authority: authority.as_deref(),
1566 path: path.as_deref(),
1567 status: status.to_owned(),
1568 reason: reason.as_deref(),
1569 },
1570 WebSocketContext::Tcp => EndpointRecord::Tcp,
1571 }
1572 }
1573}
1574
1575impl<Front: SocketHandler, L: ListenerHandler> SessionState for Pipe<Front, L> {
1576 fn ready(
1577 &mut self,
1578 _session: Rc<RefCell<dyn crate::ProxySession>>,
1579 _proxy: Rc<RefCell<dyn crate::L7Proxy>>,
1580 metrics: &mut SessionMetrics,
1581 ) -> SessionResult {
1582 let mut counter = 0;
1583
1584 if self.frontend_readiness.event.is_hup() {
1585 return SessionResult::Close;
1586 }
1587
1588 while counter < MAX_LOOP_ITERATIONS {
1589 let frontend_interest = self.frontend_readiness.filter_interest();
1590 let backend_interest = self.backend_readiness.filter_interest();
1591
1592 trace!(
1593 "{} Frontend interest({:?}), backend interest({:?})",
1594 log_context!(self),
1595 frontend_interest,
1596 backend_interest
1597 );
1598 if frontend_interest.is_empty() && backend_interest.is_empty() {
1599 break;
1600 }
1601
1602 if self.backend_readiness.event.is_hup()
1603 && self.frontend_readiness.interest.is_writable()
1604 && !self.frontend_readiness.event.is_writable()
1605 {
1606 break;
1607 }
1608
1609 if frontend_interest.is_readable() && self.readable(metrics) == SessionResult::Close {
1610 return SessionResult::Close;
1611 }
1612
1613 if backend_interest.is_writable()
1614 && self.backend_writable(metrics) == SessionResult::Close
1615 {
1616 return SessionResult::Close;
1617 }
1618
1619 if backend_interest.is_readable()
1620 && self.backend_readable(metrics) == SessionResult::Close
1621 {
1622 return SessionResult::Close;
1623 }
1624
1625 if frontend_interest.is_writable() && self.writable(metrics) == SessionResult::Close {
1626 return SessionResult::Close;
1627 }
1628
1629 if backend_interest.is_hup() && self.backend_hup(metrics) == SessionResult::Close {
1630 return SessionResult::Close;
1631 }
1632
1633 if frontend_interest.is_error() {
1634 error!(
1635 "{} Frontend socket error, disconnecting",
1636 log_context!(self)
1637 );
1638
1639 self.frontend_readiness.interest = Ready::EMPTY;
1640 self.backend_readiness.interest = Ready::EMPTY;
1641
1642 return SessionResult::Close;
1643 }
1644
1645 if backend_interest.is_error() && self.backend_hup(metrics) == SessionResult::Close {
1646 self.frontend_readiness.interest = Ready::EMPTY;
1647 self.backend_readiness.interest = Ready::EMPTY;
1648
1649 error!("{} Backend socket error, disconnecting", log_context!(self));
1650 return SessionResult::Close;
1651 }
1652
1653 counter += 1;
1654 }
1655
1656 if counter >= MAX_LOOP_ITERATIONS {
1657 error!(
1658 "{}\tHandling session went through {} iterations, there's a probable infinite loop bug, closing the connection",
1659 log_context!(self),
1660 MAX_LOOP_ITERATIONS
1661 );
1662
1663 incr!(names::http::INFINITE_LOOP_ERROR);
1664 self.print_state(self.protocol_string());
1665
1666 return SessionResult::Close;
1667 }
1668
1669 SessionResult::Continue
1670 }
1671
1672 fn update_readiness(&mut self, token: Token, events: Ready) {
1673 if self.frontend_token == token {
1674 self.frontend_readiness.event |= events;
1675 } else if self.backend_token == Some(token) {
1676 self.backend_readiness.event |= events;
1677 }
1678 }
1679
1680 fn timeout(&mut self, token: Token, metrics: &mut SessionMetrics) -> StateResult {
1681 if self.frontend_token == token {
1683 self.log_request_timeout(metrics, "frontend socket timeout");
1684 if let Some(timeout) = self.container_frontend_timeout.as_mut() {
1685 timeout.triggered()
1686 }
1687 return StateResult::CloseSession;
1688 }
1689
1690 if self.backend_token == Some(token) {
1691 if let Some(timeout) = self.container_backend_timeout.as_mut() {
1693 timeout.triggered()
1694 }
1695
1696 self.log_request_timeout(metrics, "backend socket timeout");
1697 return StateResult::CloseSession;
1698 }
1699
1700 error!("{} Got timeout for an invalid token", log_context!(self));
1701 self.log_request_error(metrics, "invalid token timeout");
1702 StateResult::CloseSession
1703 }
1704
1705 fn cancel_timeouts(&mut self) {
1706 self.container_frontend_timeout.as_mut().map(|t| t.cancel());
1707 self.container_backend_timeout.as_mut().map(|t| t.cancel());
1708 }
1709
1710 fn close(&mut self, _proxy: Rc<RefCell<dyn L7Proxy>>, _metrics: &mut SessionMetrics) {
1711 if let Some(backend) = self.backend.as_mut() {
1712 let mut backend = backend.borrow_mut();
1713 backend.active_requests = backend.active_requests.saturating_sub(1);
1714 }
1715 }
1716
1717 fn print_state(&self, context: &str) {
1718 error!(
1719 "\
1720{} {} Session(Pipe)
1721\tFrontend:
1722\t\ttoken: {:?}\treadiness: {:?}
1723\tBackend:
1724\t\ttoken: {:?}\treadiness: {:?}",
1725 log_context!(self),
1726 context,
1727 self.frontend_token,
1728 self.frontend_readiness,
1729 self.backend_token,
1730 self.backend_readiness
1731 );
1732 }
1733}
1734
1735#[cfg(test)]
1736mod tests {
1737 use std::{
1738 collections::BTreeMap,
1739 io::Write,
1740 net::{TcpListener as StdTcpListener, TcpStream as StdTcpStream},
1741 time::Duration,
1742 };
1743
1744 use super::*;
1745 use crate::pool::Pool;
1746
1747 struct TestListener {
1748 address: SocketAddr,
1749 }
1750
1751 impl ListenerHandler for TestListener {
1752 fn get_addr(&self) -> &SocketAddr {
1753 &self.address
1754 }
1755
1756 fn get_tags(&self, _key: &str) -> Option<&sozu_command::logging::CachedTags> {
1757 None
1758 }
1759
1760 fn set_tags(&mut self, _key: String, _tags: Option<BTreeMap<String, String>>) {}
1761
1762 fn protocol(&self) -> Protocol {
1763 Protocol::HTTP
1764 }
1765
1766 fn public_address(&self) -> SocketAddr {
1767 self.address
1768 }
1769 }
1770
1771 fn connected_pair() -> (StdTcpStream, StdTcpStream) {
1772 let listener = StdTcpListener::bind("127.0.0.1:0").expect("bind test listener");
1773 let address = listener.local_addr().expect("listener local addr");
1774 let client = StdTcpStream::connect(address).expect("connect test client");
1775 let (server, _) = listener.accept().expect("accept test server");
1776 client.set_nonblocking(true).expect("client nonblocking");
1777 server.set_nonblocking(true).expect("server nonblocking");
1778 (client, server)
1779 }
1780
1781 #[test]
1782 fn backend_readable_arms_frontend_writable_event_when_buffering_response() {
1783 let (frontend_peer, frontend_socket) = connected_pair();
1784 let (mut backend_peer, backend_socket) = connected_pair();
1785
1786 let mut pool = Pool::with_capacity(2, 2, 4096);
1787 let backend_buffer = pool.checkout().expect("backend buffer");
1788 let frontend_buffer = pool.checkout().expect("frontend buffer");
1789 let address = "127.0.0.1:0".parse().expect("test address");
1790 let listener = Rc::new(RefCell::new(TestListener { address }));
1791
1792 let mut pipe = Pipe::new(
1793 backend_buffer,
1794 None,
1795 Some(TcpStream::from_std(backend_socket)),
1796 None,
1797 None,
1798 None,
1799 None,
1800 frontend_buffer,
1801 Token(0),
1802 TcpStream::from_std(frontend_socket),
1803 listener,
1804 Protocol::HTTP,
1805 Ulid::generate(),
1806 Ulid::generate(),
1807 None,
1808 WebSocketContext::Tcp,
1809 );
1810 pipe.set_back_token(Token(1));
1811 pipe.frontend_readiness.event = Ready::EMPTY;
1812 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1813 pipe.backend_readiness.event = Ready::READABLE;
1814 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1815
1816 backend_peer
1817 .write_all(b"server-speaks-first")
1818 .expect("write backend payload");
1819
1820 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
1821 assert_eq!(pipe.backend_readable(&mut metrics), SessionResult::Continue);
1822
1823 assert!(
1824 pipe.backend_buffer.available_data() > 0,
1825 "backend_readable must buffer backend bytes"
1826 );
1827 assert!(
1828 pipe.frontend_readiness.interest.is_writable(),
1829 "buffered backend bytes must arm frontend WRITABLE interest"
1830 );
1831 assert!(
1832 pipe.frontend_readiness.event.is_writable(),
1833 "buffered backend bytes must queue a frontend WRITABLE event"
1834 );
1835
1836 drop(frontend_peer);
1837 }
1838
1839 #[test]
1840 fn frontend_readable_arms_backend_writable_event_when_buffering_request() {
1841 let (mut frontend_peer, frontend_socket) = connected_pair();
1842 let (backend_peer, backend_socket) = connected_pair();
1843
1844 let mut pool = Pool::with_capacity(2, 2, 4096);
1845 let backend_buffer = pool.checkout().expect("backend buffer");
1846 let frontend_buffer = pool.checkout().expect("frontend buffer");
1847 let address = "127.0.0.1:0".parse().expect("test address");
1848 let listener = Rc::new(RefCell::new(TestListener { address }));
1849
1850 let mut pipe = Pipe::new(
1851 backend_buffer,
1852 None,
1853 Some(TcpStream::from_std(backend_socket)),
1854 None,
1855 None,
1856 None,
1857 None,
1858 frontend_buffer,
1859 Token(0),
1860 TcpStream::from_std(frontend_socket),
1861 listener,
1862 Protocol::HTTP,
1863 Ulid::generate(),
1864 Ulid::generate(),
1865 None,
1866 WebSocketContext::Tcp,
1867 );
1868 pipe.set_back_token(Token(1));
1869 pipe.frontend_readiness.event = Ready::READABLE;
1870 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1871 pipe.backend_readiness.event = Ready::EMPTY;
1872 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
1873
1874 frontend_peer
1875 .write_all(b"client-speaks-after-upgrade")
1876 .expect("write frontend payload");
1877
1878 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
1879 assert_eq!(pipe.readable(&mut metrics), SessionResult::Continue);
1880
1881 assert!(
1882 pipe.frontend_buffer.available_data() > 0,
1883 "readable must buffer frontend bytes"
1884 );
1885 assert!(
1886 pipe.backend_readiness.interest.is_writable(),
1887 "buffered frontend bytes must arm backend WRITABLE interest"
1888 );
1889 assert!(
1890 pipe.backend_readiness.event.is_writable(),
1891 "buffered frontend bytes must queue a backend WRITABLE event"
1892 );
1893
1894 drop(backend_peer);
1895 }
1896
1897 #[test]
1898 fn restore_readiness_events_rearms_inherited_buffered_writes() {
1899 let (frontend_peer, frontend_socket) = connected_pair();
1900 let (backend_peer, backend_socket) = connected_pair();
1901
1902 let mut pool = Pool::with_capacity(2, 2, 4096);
1903 let mut backend_buffer = pool.checkout().expect("backend buffer");
1904 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
1905 backend_buffer
1906 .write_all(b"backend bytes inherited from 101 read")
1907 .expect("write backend buffer");
1908 frontend_buffer
1909 .write_all(b"frontend bytes inherited from upgrade read")
1910 .expect("write frontend buffer");
1911 let address = "127.0.0.1:0".parse().expect("test address");
1912 let listener = Rc::new(RefCell::new(TestListener { address }));
1913
1914 let mut pipe = Pipe::new(
1915 backend_buffer,
1916 None,
1917 Some(TcpStream::from_std(backend_socket)),
1918 None,
1919 None,
1920 None,
1921 None,
1922 frontend_buffer,
1923 Token(0),
1924 TcpStream::from_std(frontend_socket),
1925 listener,
1926 Protocol::HTTP,
1927 Ulid::generate(),
1928 Ulid::generate(),
1929 None,
1930 WebSocketContext::Tcp,
1931 );
1932
1933 pipe.restore_readiness_events(Ready::EMPTY, Ready::EMPTY);
1934
1935 assert!(
1936 pipe.frontend_readiness.event.is_writable(),
1937 "restoring inherited frontend events must not park backend-buffered bytes"
1938 );
1939 assert!(
1940 pipe.backend_readiness.event.is_writable(),
1941 "restoring inherited backend events must not park frontend-buffered bytes"
1942 );
1943
1944 drop(frontend_peer);
1945 drop(backend_peer);
1946 }
1947
1948 #[cfg(all(target_os = "linux", feature = "splice"))]
1959 #[test]
1960 fn backend_writable_drains_inherited_frontend_buffer_before_splice_engages() {
1961 use std::io::Read;
1962
1963 let (frontend_peer, frontend_socket) = connected_pair();
1964 let (mut backend_peer, backend_socket) = connected_pair();
1965
1966 let mut pool = Pool::with_capacity(2, 2, 4096);
1967 let backend_buffer = pool.checkout().expect("backend buffer");
1968 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
1969 frontend_buffer
1970 .write_all(b"inherited-preread-client-hello")
1971 .expect("write inherited frontend buffer");
1972 let address = "127.0.0.1:0".parse().expect("test address");
1973 let listener = Rc::new(RefCell::new(TestListener { address }));
1974
1975 let mut pipe = Pipe::new(
1976 backend_buffer,
1977 None,
1978 Some(TcpStream::from_std(backend_socket)),
1979 None,
1980 None,
1981 None,
1982 None,
1983 frontend_buffer,
1984 Token(0),
1985 TcpStream::from_std(frontend_socket),
1986 listener,
1987 Protocol::TCP,
1988 Ulid::generate(),
1989 Ulid::generate(),
1990 None,
1991 WebSocketContext::Tcp,
1992 );
1993 pipe.set_back_token(Token(1));
1994
1995 assert!(
1996 pipe.splice_pipe.is_some(),
1997 "Protocol::TCP must allocate the splice kernel pipe for this test to be meaningful"
1998 );
1999 assert!(
2000 pipe.frontend_buffer.available_data() > 0,
2001 "test setup must inherit a non-empty frontend buffer"
2002 );
2003
2004 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2005 assert_eq!(pipe.backend_writable(&mut metrics), SessionResult::Continue);
2006
2007 assert_eq!(
2008 pipe.frontend_buffer.available_data(),
2009 0,
2010 "inherited preread bytes must drain through the buffered path before splice engages"
2011 );
2012
2013 let mut received = [0u8; 64];
2014 let n = backend_peer
2015 .read(&mut received)
2016 .expect("backend socket must have received the drained preread bytes");
2017 assert_eq!(&received[..n], b"inherited-preread-client-hello");
2018
2019 drop(frontend_peer);
2020 }
2021
2022 #[test]
2031 fn frontend_eof_flushes_queued_backend_bytes_before_closing() {
2032 use std::io::{Read, Write};
2033
2034 let (frontend_peer, frontend_socket) = connected_pair();
2035 let (mut backend_peer, backend_socket) = connected_pair();
2036
2037 let mut pool = Pool::with_capacity(2, 2, 4096);
2038 let backend_buffer = pool.checkout().expect("backend buffer");
2039 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
2040 frontend_buffer
2042 .write_all(b"front-to-back-tail-still-queued")
2043 .expect("seed queued frontend bytes");
2044 let address = "127.0.0.1:0".parse().expect("test address");
2045 let listener = Rc::new(RefCell::new(TestListener { address }));
2046
2047 let mut pipe = Pipe::new(
2048 backend_buffer,
2049 None,
2050 Some(TcpStream::from_std(backend_socket)),
2051 None,
2052 None,
2053 None,
2054 None,
2055 frontend_buffer,
2056 Token(0),
2057 TcpStream::from_std(frontend_socket),
2058 listener,
2059 Protocol::TCP,
2060 Ulid::generate(),
2061 Ulid::generate(),
2062 None,
2063 WebSocketContext::Tcp,
2064 );
2065 pipe.set_back_token(Token(1));
2066 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2067 pipe.frontend_readiness.event = Ready::READABLE;
2068
2069 drop(frontend_peer);
2072
2073 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2078 let mut saw_eof = false;
2079 for _ in 0..100_000 {
2080 let result = pipe.readable(&mut metrics);
2081 assert_ne!(
2082 result,
2083 SessionResult::Close,
2084 "readable must not close while {} queued backend bytes remain",
2085 pipe.frontend_buffer.available_data()
2086 );
2087 if matches!(pipe.frontend_status, ConnectionStatus::WriteOpen) {
2088 saw_eof = true;
2089 break;
2090 }
2091 }
2092 assert!(
2093 saw_eof,
2094 "frontend EOF was never observed within the retry budget"
2095 );
2096 assert!(
2097 pipe.frontend_buffer.available_data() > 0,
2098 "the queued backend bytes must survive the frontend EOF, not be dropped"
2099 );
2100
2101 assert_eq!(pipe.backend_writable(&mut metrics), SessionResult::Close);
2108 assert_eq!(
2109 pipe.frontend_buffer.available_data(),
2110 0,
2111 "the queued bytes must all drain to the backend after EOF"
2112 );
2113
2114 let mut received = Vec::new();
2115 for _ in 0..100_000 {
2117 let mut buf = [0u8; 64];
2118 match backend_peer.read(&mut buf) {
2119 Ok(0) => break,
2120 Ok(n) => {
2121 received.extend_from_slice(&buf[..n]);
2122 if received.len() >= b"front-to-back-tail-still-queued".len() {
2123 break;
2124 }
2125 }
2126 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
2127 Err(_) => break,
2128 }
2129 }
2130 assert_eq!(
2131 received, b"front-to-back-tail-still-queued",
2132 "the backend must receive the queued tail byte-for-byte, not a truncation"
2133 );
2134 }
2135
2136 #[test]
2148 fn frontend_eof_with_queued_bytes_closes_on_the_draining_backend_writable_call() {
2149 use std::io::Read;
2150
2151 let (mut frontend_peer, frontend_socket) = connected_pair();
2152 let (mut backend_peer, backend_socket) = connected_pair();
2153
2154 let mut pool = Pool::with_capacity(2, 2, 4096);
2155 let backend_buffer = pool.checkout().expect("backend buffer");
2156 let frontend_buffer = pool.checkout().expect("frontend buffer");
2157 let address = "127.0.0.1:0".parse().expect("test address");
2158 let listener = Rc::new(RefCell::new(TestListener { address }));
2159
2160 let mut pipe = Pipe::new(
2161 backend_buffer,
2162 None,
2163 Some(TcpStream::from_std(backend_socket)),
2164 None,
2165 None,
2166 None,
2167 None,
2168 frontend_buffer,
2169 Token(0),
2170 TcpStream::from_std(frontend_socket),
2171 listener,
2172 Protocol::HTTP,
2173 Ulid::generate(),
2174 Ulid::generate(),
2175 None,
2176 WebSocketContext::Tcp,
2177 );
2178 pipe.set_back_token(Token(1));
2179 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2180 pipe.frontend_readiness.event = Ready::READABLE;
2181
2182 frontend_peer
2186 .write_all(b"queued-request-tail")
2187 .expect("write frontend payload");
2188 drop(frontend_peer);
2189
2190 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2194 let mut saw_eof = false;
2195 for _ in 0..100_000 {
2196 let result = pipe.readable(&mut metrics);
2197 assert_ne!(
2198 result,
2199 SessionResult::Close,
2200 "readable must not close while queued backend bytes remain"
2201 );
2202 if matches!(pipe.frontend_status, ConnectionStatus::WriteOpen) {
2203 saw_eof = true;
2204 break;
2205 }
2206 }
2207 assert!(
2208 saw_eof,
2209 "frontend EOF was never observed within the retry budget"
2210 );
2211 assert!(
2212 pipe.frontend_buffer.available_data() > 0,
2213 "the queued bytes must survive the frontend EOF"
2214 );
2215 assert!(
2216 pipe.backend_readiness.interest.is_writable()
2217 && pipe.backend_readiness.event.is_writable(),
2218 "observing EOF with queued bytes must arm backend WRITABLE to drain them"
2219 );
2220
2221 let result = pipe.backend_writable(&mut metrics);
2226
2227 assert_eq!(
2228 result,
2229 SessionResult::Close,
2230 "the single draining backend_writable call must close the session by itself -- \
2231 edge-triggered epoll already consumed the EOF edge and offers no other event to \
2232 hang the teardown on"
2233 );
2234 assert_eq!(
2235 pipe.frontend_buffer.available_data(),
2236 0,
2237 "the queued bytes must have fully drained before the session closed"
2238 );
2239
2240 let mut received = Vec::new();
2241 for _ in 0..100_000 {
2242 let mut buf = [0u8; 64];
2243 match backend_peer.read(&mut buf) {
2244 Ok(0) => break,
2245 Ok(n) => {
2246 received.extend_from_slice(&buf[..n]);
2247 if received.len() >= b"queued-request-tail".len() {
2248 break;
2249 }
2250 }
2251 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => continue,
2252 Err(_) => break,
2253 }
2254 }
2255 assert_eq!(
2256 received, b"queued-request-tail",
2257 "the backend must still receive the queued bytes byte-for-byte before the close"
2258 );
2259 }
2260
2261 #[test]
2272 fn frontend_hup_drains_inflight_request_bytes_before_closing() {
2273 let (frontend_peer, frontend_socket) = connected_pair();
2274 let (_backend_peer, backend_socket) = connected_pair();
2275
2276 let mut pool = Pool::with_capacity(2, 2, 4096);
2277 let backend_buffer = pool.checkout().expect("backend buffer");
2278 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
2279 frontend_buffer
2280 .write_all(b"front-to-back-tail-still-queued")
2281 .expect("seed queued frontend bytes");
2282 let address = "127.0.0.1:0".parse().expect("test address");
2283 let listener = Rc::new(RefCell::new(TestListener { address }));
2284
2285 let mut pipe = Pipe::new(
2286 backend_buffer,
2287 None,
2288 Some(TcpStream::from_std(backend_socket)),
2289 None,
2290 None,
2291 None,
2292 None,
2293 frontend_buffer,
2294 Token(0),
2295 TcpStream::from_std(frontend_socket),
2296 listener,
2297 Protocol::TCP,
2298 Ulid::generate(),
2299 Ulid::generate(),
2300 None,
2301 WebSocketContext::Tcp,
2302 );
2303 pipe.set_back_token(Token(1));
2304 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2307 pipe.frontend_readiness.event = Ready::READABLE | Ready::HUP;
2308 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2309 pipe.backend_readiness.event = Ready::EMPTY;
2310
2311 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2312 let result = pipe.frontend_hup(&mut metrics);
2313
2314 assert_eq!(
2315 result,
2316 SessionResult::Continue,
2317 "frontend_hup must keep the session alive while request bytes are still in flight"
2318 );
2319 assert!(
2320 matches!(pipe.frontend_status, ConnectionStatus::Closed),
2321 "frontend_hup must still mark the frontend Closed even on the drain branch"
2322 );
2323 assert!(
2324 pipe.backend_readiness.interest.is_writable()
2325 && pipe.backend_readiness.event.is_writable(),
2326 "the drain branch must arm backend WRITABLE to flush the queued frontend bytes"
2327 );
2328 assert!(
2329 pipe.frontend_readiness.interest.is_readable(),
2330 "the drain branch must retain frontend READABLE interest to read the kernel tail to EOF"
2331 );
2332
2333 drop(frontend_peer);
2334 }
2335
2336 #[test]
2340 fn frontend_hup_closes_immediately_when_nothing_is_inflight() {
2341 let (frontend_peer, frontend_socket) = connected_pair();
2342 let (_backend_peer, backend_socket) = connected_pair();
2343
2344 let mut pool = Pool::with_capacity(2, 2, 4096);
2345 let backend_buffer = pool.checkout().expect("backend buffer");
2346 let frontend_buffer = pool.checkout().expect("frontend buffer");
2347 let address = "127.0.0.1:0".parse().expect("test address");
2348 let listener = Rc::new(RefCell::new(TestListener { address }));
2349
2350 let mut pipe = Pipe::new(
2351 backend_buffer,
2352 None,
2353 Some(TcpStream::from_std(backend_socket)),
2354 None,
2355 None,
2356 None,
2357 None,
2358 frontend_buffer,
2359 Token(0),
2360 TcpStream::from_std(frontend_socket),
2361 listener,
2362 Protocol::TCP,
2363 Ulid::generate(),
2364 Ulid::generate(),
2365 None,
2366 WebSocketContext::Tcp,
2367 );
2368 pipe.set_back_token(Token(1));
2369 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2371 pipe.frontend_readiness.event = Ready::HUP;
2372 pipe.backend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2373 pipe.backend_readiness.event = Ready::EMPTY;
2374
2375 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2376 let result = pipe.frontend_hup(&mut metrics);
2377
2378 assert_eq!(
2379 result,
2380 SessionResult::Close,
2381 "frontend_hup must close immediately when nothing is in flight (unchanged legacy behavior)"
2382 );
2383 assert!(
2384 matches!(pipe.frontend_status, ConnectionStatus::Closed),
2385 "frontend_hup must mark the frontend Closed"
2386 );
2387 assert!(
2388 !pipe.backend_readiness.event.is_writable(),
2389 "the close branch must not arm backend WRITABLE when there is nothing queued"
2390 );
2391
2392 drop(frontend_peer);
2393 }
2394
2395 #[cfg(all(target_os = "linux", feature = "splice"))]
2404 #[test]
2405 fn splice_readable_eof_drains_kernel_pipe_bytes_before_closing() {
2406 use std::io::Read;
2407
2408 let (mut frontend_peer, frontend_socket) = connected_pair();
2409 let (mut backend_peer, backend_socket) = connected_pair();
2410
2411 let mut pool = Pool::with_capacity(2, 2, 4096);
2412 let backend_buffer = pool.checkout().expect("backend buffer");
2413 let frontend_buffer = pool.checkout().expect("frontend buffer");
2416 let address = "127.0.0.1:0".parse().expect("test address");
2417 let listener = Rc::new(RefCell::new(TestListener { address }));
2418
2419 let mut pipe = Pipe::new(
2420 backend_buffer,
2421 None,
2422 Some(TcpStream::from_std(backend_socket)),
2423 None,
2424 None,
2425 None,
2426 None,
2427 frontend_buffer,
2428 Token(0),
2429 TcpStream::from_std(frontend_socket),
2430 listener,
2431 Protocol::TCP,
2432 Ulid::generate(),
2433 Ulid::generate(),
2434 None,
2435 WebSocketContext::Tcp,
2436 );
2437 pipe.set_back_token(Token(1));
2438 pipe.frontend_readiness.interest = Ready::READABLE | Ready::HUP | Ready::ERROR;
2439 pipe.frontend_readiness.event = Ready::READABLE;
2440
2441 assert!(
2442 pipe.splice_pipe.is_some(),
2443 "Protocol::TCP must allocate the splice kernel pipe for this test to be meaningful"
2444 );
2445
2446 let payload = vec![0x5a_u8; 32 * 1024];
2449 frontend_peer
2450 .write_all(&payload)
2451 .expect("write frontend payload");
2452
2453 let mut metrics = SessionMetrics::new(Some(Duration::ZERO));
2457 for _ in 0..100_000 {
2458 if pipe.splice_in_pending() >= payload.len() {
2459 break;
2460 }
2461 let result = pipe.splice_readable(&mut metrics);
2462 assert_ne!(
2463 result,
2464 SessionResult::Close,
2465 "splice_readable must not close while splicing the payload in"
2466 );
2467 }
2468 assert_eq!(
2469 pipe.splice_in_pending(),
2470 payload.len(),
2471 "the whole payload must sit in the kernel in_pipe before EOF"
2472 );
2473
2474 drop(frontend_peer);
2477
2478 let mut saw_eof = false;
2481 for _ in 0..100_000 {
2482 let result = pipe.splice_readable(&mut metrics);
2483 assert_ne!(
2484 result,
2485 SessionResult::Close,
2486 "splice_readable must not close while {} kernel-pipe bytes remain",
2487 pipe.splice_in_pending()
2488 );
2489 if matches!(pipe.frontend_status, ConnectionStatus::WriteOpen) {
2490 saw_eof = true;
2491 break;
2492 }
2493 }
2494 assert!(
2495 saw_eof,
2496 "frontend EOF was never observed within the retry budget"
2497 );
2498 assert_eq!(
2499 pipe.splice_in_pending(),
2500 payload.len(),
2501 "the kernel-pipe bytes must survive the frontend EOF, not be dropped"
2502 );
2503 assert!(
2504 pipe.backend_readiness.interest.is_writable()
2505 && pipe.backend_readiness.event.is_writable(),
2506 "EOF with kernel-pipe bytes pending must arm backend WRITABLE to drain them"
2507 );
2508
2509 let mut received = Vec::with_capacity(payload.len());
2521 let mut closed_during_drain = false;
2522 for _ in 0..100_000 {
2523 let drain = pipe.splice_backend_writable(&mut metrics);
2524 if drain == SessionResult::Close {
2525 assert_eq!(
2526 pipe.splice_in_pending(),
2527 0,
2528 "the session must only close once the kernel pipe is fully drained, \
2529 never mid-flight"
2530 );
2531 closed_during_drain = true;
2532 }
2533 let mut buf = [0u8; 16384];
2534 match backend_peer.read(&mut buf) {
2535 Ok(0) => break,
2536 Ok(n) => received.extend_from_slice(&buf[..n]),
2537 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
2538 Err(_) => break,
2539 }
2540 if received.len() >= payload.len() {
2541 break;
2542 }
2543 }
2544 assert_eq!(
2545 received, payload,
2546 "the backend must receive the spliced tail byte-for-byte, not a truncation"
2547 );
2548 assert_eq!(
2549 pipe.splice_in_pending(),
2550 0,
2551 "the kernel in_pipe must be fully drained"
2552 );
2553 assert!(
2554 closed_during_drain,
2555 "the fix under test (sozu-proxy/sozu#1290) must close the session as soon as \
2556 the kernel pipe drains, without waiting for a separate re-observation event"
2557 );
2558
2559 assert!(
2565 !pipe.check_connections(),
2566 "with nothing inflight and the frontend closed, the session must be closeable"
2567 );
2568 assert_eq!(
2569 pipe.splice_readable(&mut metrics),
2570 SessionResult::Close,
2571 "re-observing EOF with nothing inflight must (still) close the session"
2572 );
2573 }
2574}