1use std::{
2 cell::RefCell,
3 collections::{BTreeMap, HashMap, hash_map::Entry},
4 io::ErrorKind,
5 net::{Shutdown, SocketAddr},
6 rc::Rc,
7 time::{Duration, Instant},
8};
9
10use mio::{
11 Interest, Registry, Token,
12 net::{TcpListener as MioTcpListener, TcpStream as MioTcpStream},
13};
14use rusty_ulid::Ulid;
15use socket2::SockRef;
16use sozu_command::{
17 ObjectKind,
18 config::{
19 DEFAULT_SNI_PREREAD_MAX_BYTES, DEFAULT_SNI_PREREAD_TIMEOUT, MAX_LOOP_ITERATIONS,
20 MIN_SNI_PREREAD_MAX_BYTES, validate_sni_pattern,
21 },
22 listener_key::ListenerKey,
23 logging::{EndpointRecord, LogContext, ansi_palette},
24 proto::command::request::RequestType,
25};
26
27use crate::metrics::names;
28use crate::router::pattern_trie::{InsertResult, TrieNode};
29use crate::{
30 AcceptError, BackendConnectAction, BackendConnectionError, BackendConnectionStatus, CachedTags,
31 ListenerError, ListenerHandler, Protocol, ProxyConfiguration, ProxyError, ProxySession,
32 Readiness, SessionIsToBeClosed, SessionMetrics, SessionResult, StateMachineBuilder,
33 backends::{Backend, BackendMap},
34 load_balancing::affinity_key_from_ip,
35 pool::{Checkout, Pool},
36 protocol::mux::router::cluster_reads_affinity_key,
37 protocol::{
38 Pipe,
39 pipe::WebSocketContext,
40 proxy_protocol::{
41 expect::ExpectProxyProtocol, relay::RelayProxyProtocol, send::SendProxyProtocol,
42 },
43 tcp_preread::{AlpnMatcher, PrereadConfig, shell::SniPreread},
44 },
45 retry::RetryPolicy,
46 server::{ListenToken, SessionManager, push_event},
47 socket::{server_bind, stats::socket_rtt},
48 sozu_command::{
49 proto::command::{
50 Event, EventKind, ProxyProtocolConfig, RequestTcpFrontend, TcpListenerConfig,
51 UpdateTcpListenerConfig, WorkerRequest, WorkerResponse,
52 },
53 ready::Ready,
54 state::ClusterId,
55 },
56 timer::TimeoutContainer,
57};
58
59StateMachineBuilder! {
60 enum TcpStateMachine {
66 Pipe(Pipe<MioTcpStream, TcpListener>),
67 SendProxyProtocol(SendProxyProtocol<MioTcpStream>),
68 RelayProxyProtocol(RelayProxyProtocol<MioTcpStream>),
69 ExpectProxyProtocol(ExpectProxyProtocol<MioTcpStream>),
70 SniPreread(SniPreread<MioTcpStream>),
71 }
72}
73
74macro_rules! log_context {
79 ($self:expr) => {{
80 let (open, reset, grey, gray, white) = ansi_palette();
81 format!(
82 "{gray}{ctx}{reset}\t{open}TCP{reset}\t{grey}Session{reset}({gray}frontend{reset}={white}{frontend}{reset}, {gray}backend{reset}={white}{backend}{reset})\t >>>",
83 open = open,
84 reset = reset,
85 grey = grey,
86 gray = gray,
87 white = white,
88 ctx = $self.log_context(),
89 frontend = $self.frontend_token.0,
90 backend = $self
91 .backend_token
92 .map(|token| token.0.to_string())
93 .unwrap_or_else(|| "<none>".to_string()),
94 )
95 }};
96}
97
98macro_rules! log_module_context {
106 () => {{
107 let (open, reset, _, _, _) = sozu_command::logging::ansi_palette();
108 format!("{open}TCP{reset}\t >>>", open = open, reset = reset)
109 }};
110}
111
112pub struct TcpSession {
113 backend_buffer: Option<Checkout>,
114 backend_connected: BackendConnectionStatus,
115 backend_id: Option<String>,
116 backend_address: Option<SocketAddr>,
120 backend_token: Option<Token>,
121 backend: Option<Rc<RefCell<Backend>>>,
122 cluster_id: Option<ClusterId>,
123 cluster_metrics_incarnation: crate::metrics::ClusterMetricsCapture,
127 configured_backend_timeout: Duration,
128 connection_attempt: u8,
129 container_backend_timeout: TimeoutContainer,
130 container_frontend_timeout: TimeoutContainer,
131 frontend_address: Option<SocketAddr>,
132 frontend_buffer: Option<Checkout>,
133 frontend_token: Token,
134 has_been_closed: SessionIsToBeClosed,
135 last_event: Instant,
136 listener: Rc<RefCell<TcpListener>>,
137 metrics: SessionMetrics,
138 proxy: Rc<RefCell<TcpProxy>>,
139 request_id: Ulid,
140 state: TcpStateMachine,
141 cluster_ip_tracked: bool,
150 routed_sni: Option<String>,
158 routed_alpn_label: Option<&'static str>,
165 tags_key: Option<String>,
174}
175
176impl TcpSession {
177 #[allow(clippy::too_many_arguments)]
178 fn new(
179 backend_buffer: Checkout,
180 backend_id: Option<String>,
181 cluster_id: Option<ClusterId>,
182 configured_backend_timeout: Duration,
183 configured_connect_timeout: Duration,
184 configured_frontend_timeout: Duration,
185 frontend_buffer: Checkout,
186 frontend_token: Token,
187 listener: Rc<RefCell<TcpListener>>,
188 proxy_protocol: Option<ProxyProtocolConfig>,
189 proxy: Rc<RefCell<TcpProxy>>,
190 socket: MioTcpStream,
191 peer: SocketAddr,
192 wait_time: Duration,
193 ) -> TcpSession {
194 let frontend_address = Some(peer);
196 let mut frontend_buffer_session = None;
197 let mut backend_buffer_session = None;
198
199 let request_id = Ulid::generate();
200
201 let container_frontend_timeout =
202 TimeoutContainer::new(configured_frontend_timeout, frontend_token);
203 let container_backend_timeout = TimeoutContainer::new_empty(configured_connect_timeout);
204 let cluster_metrics_incarnation = cluster_id
205 .as_deref()
206 .map_or_else(Default::default, crate::metrics::capture_cluster_metrics);
207
208 let state = match proxy_protocol {
209 Some(ProxyProtocolConfig::RelayHeader) => {
210 backend_buffer_session = Some(backend_buffer);
211 gauge_add!(names::protocol::PROXY_RELAY, 1);
212 TcpStateMachine::RelayProxyProtocol(RelayProxyProtocol::new(
213 socket,
214 frontend_token,
215 request_id,
216 None,
217 frontend_buffer,
218 ))
219 }
220 Some(ProxyProtocolConfig::ExpectHeader) => {
221 frontend_buffer_session = Some(frontend_buffer);
222 backend_buffer_session = Some(backend_buffer);
223 gauge_add!(names::protocol::PROXY_EXPECT, 1);
224 TcpStateMachine::ExpectProxyProtocol(ExpectProxyProtocol::new(
225 container_frontend_timeout.clone(),
226 socket,
227 frontend_token,
228 request_id,
229 ))
230 }
231 Some(ProxyProtocolConfig::SendHeader) => {
232 frontend_buffer_session = Some(frontend_buffer);
233 backend_buffer_session = Some(backend_buffer);
234 gauge_add!(names::protocol::PROXY_SEND, 1);
235 TcpStateMachine::SendProxyProtocol(SendProxyProtocol::new(
236 socket,
237 frontend_token,
238 request_id,
239 None,
240 ))
241 }
242 None => {
243 gauge_add!(names::protocol::TCP, 1);
244 let mut pipe = Pipe::new(
245 backend_buffer,
246 backend_id.clone(),
247 None,
248 None,
249 None,
250 None,
251 cluster_id.clone(),
252 frontend_buffer,
253 frontend_token,
254 socket,
255 listener.clone(),
256 Protocol::TCP,
257 request_id,
258 request_id,
259 frontend_address,
260 WebSocketContext::Tcp,
261 );
262 pipe.set_cluster_id(cluster_id.clone());
263 pipe.set_cluster_metrics_incarnation(cluster_metrics_incarnation);
264 TcpStateMachine::Pipe(pipe)
265 }
266 };
267
268 let metrics = SessionMetrics::new(Some(wait_time));
269 TcpSession {
272 backend_buffer: backend_buffer_session,
273 backend_connected: BackendConnectionStatus::NotConnected,
274 backend_id,
275 backend_address: None,
276 backend_token: None,
277 backend: None,
278 cluster_id,
279 cluster_metrics_incarnation,
280 configured_backend_timeout,
281 connection_attempt: 0,
282 container_backend_timeout,
283 container_frontend_timeout,
284 frontend_address,
285 frontend_buffer: frontend_buffer_session,
286 frontend_token,
287 has_been_closed: false,
288 last_event: Instant::now(),
289 listener,
290 metrics,
291 proxy,
292 request_id,
293 state,
294 cluster_ip_tracked: false,
295 routed_sni: None,
296 routed_alpn_label: None,
297 tags_key: None,
298 }
299 }
300
301 #[allow(clippy::too_many_arguments)]
310 fn new_sni_preread(
311 backend_buffer: Checkout,
312 configured_backend_timeout: Duration,
313 configured_connect_timeout: Duration,
314 frontend_buffer: Checkout,
315 frontend_token: Token,
316 listener: Rc<RefCell<TcpListener>>,
317 proxy: Rc<RefCell<TcpProxy>>,
318 socket: MioTcpStream,
319 peer: SocketAddr,
320 wait_time: Duration,
321 preread_timeout: Duration,
322 effective_max_bytes: usize,
323 ) -> TcpSession {
324 let frontend_address = Some(peer);
326 let request_id = Ulid::generate();
327
328 let container_frontend_timeout = TimeoutContainer::new(preread_timeout, frontend_token);
336 let container_backend_timeout = TimeoutContainer::new_empty(configured_connect_timeout);
337
338 let state = TcpStateMachine::SniPreread(SniPreread::new(
339 socket,
340 frontend_token,
341 request_id,
342 frontend_buffer,
343 effective_max_bytes,
344 ));
345
346 gauge_add!(names::tcp::sni_preread::ACTIVE, 1);
355
356 let metrics = SessionMetrics::new(Some(wait_time));
357
358 TcpSession {
359 backend_buffer: Some(backend_buffer),
360 backend_connected: BackendConnectionStatus::NotConnected,
361 backend_id: None,
362 backend_address: None,
363 backend_token: None,
364 backend: None,
365 cluster_id: None,
366 cluster_metrics_incarnation: Default::default(),
367 configured_backend_timeout,
368 connection_attempt: 0,
369 container_backend_timeout,
370 container_frontend_timeout,
371 frontend_address,
372 frontend_buffer: None,
373 frontend_token,
374 has_been_closed: false,
375 last_event: Instant::now(),
376 listener,
377 metrics,
378 proxy,
379 request_id,
380 state,
381 cluster_ip_tracked: false,
382 routed_sni: None,
383 routed_alpn_label: None,
384 tags_key: None,
385 }
386 }
387
388 fn effective_session_address(&self) -> Option<SocketAddr> {
396 match &self.state {
397 TcpStateMachine::Pipe(pipe) => pipe.get_session_address(),
398 TcpStateMachine::ExpectProxyProtocol(epp) => {
399 epp.addresses.as_ref().and_then(|pa| pa.source())
400 }
401 TcpStateMachine::RelayProxyProtocol(rpp) => {
402 rpp.addresses.as_ref().and_then(|pa| pa.source())
403 }
404 TcpStateMachine::SniPreread(preread) => preread.outcome().and_then(|o| o.proxy_source),
405 TcpStateMachine::SendProxyProtocol(_) | TcpStateMachine::FailedUpgrade(_) => None,
406 }
407 .or(self.frontend_address)
408 }
409
410 fn log_request(&self) {
411 let listener = self.listener.borrow();
412 let context = self.log_context();
413 self.metrics
414 .register_end_of_session_for_incarnation(&context, self.cluster_metrics_incarnation);
415 let address_key = TcpFrontendTagsKey::Address(*listener.get_addr()).to_string();
419 let tags_key = self.tags_key.as_deref().unwrap_or(&address_key);
420 info_access!(
421 on_failure: { incr!(names::access_logs::UNSENT) },
422 message: None,
423 context,
424 session_address: self.frontend_address,
425 backend_address: None,
426 protocol: "TCP",
427 endpoint: EndpointRecord::Tcp,
428 tags: listener.get_tags(tags_key),
429 client_rtt: socket_rtt(self.state.front_socket()),
430 server_rtt: None,
431 user_agent: None,
432 x_request_id: None,
433 tls_version: None,
440 tls_cipher: None,
441 tls_sni: None,
442 tls_alpn: None,
443 xff_chain: None,
444 service_time: self.metrics.service_time(),
445 response_time: self.metrics.backend_response_time(),
446 request_time: self.metrics.request_time(),
447 start_time_ns: self.metrics.start_wall_ns(),
448 bytes_in: self.metrics.bin,
449 bytes_out: self.metrics.bout,
450 otel: None,
451 );
452 }
453
454 fn front_hup(&mut self) -> SessionResult {
455 let listener = self.listener.borrow();
456 match &mut self.state {
457 TcpStateMachine::Pipe(pipe) => pipe.frontend_hup(&mut self.metrics),
458 TcpStateMachine::SniPreread(preread) => {
463 let cfg = listener.preread_config(preread.effective_max_bytes());
464 preread.on_front_closed(&cfg);
465 SessionResult::Close
466 }
467 _ => {
468 self.log_request();
469 SessionResult::Close
470 }
471 }
472 }
473
474 fn back_hup(&mut self) -> SessionResult {
475 match &mut self.state {
481 TcpStateMachine::Pipe(pipe) => pipe.backend_hup(&mut self.metrics),
482 _ => {
483 self.log_request();
484 SessionResult::Close
485 }
486 }
487 }
488
489 fn log_context(&self) -> LogContext<'_> {
490 LogContext {
491 session_id: self.request_id,
492 request_id: Some(self.request_id),
493 cluster_id: self.cluster_id.as_deref(),
494 backend_id: self.backend_id.as_deref(),
495 }
496 }
497
498 fn readable(&mut self) -> SessionResult {
499 if frontend_timeout_resets_on_readable(&self.state)
503 && !self.container_frontend_timeout.reset()
504 {
505 error!(
506 "{} Could not reset frontend timeout on readable",
507 log_context!(self)
508 );
509 }
510 if self.backend_connected == BackendConnectionStatus::Connected
511 && !self.container_backend_timeout.reset()
512 {
513 error!(
514 "{} Could not reset backend timeout on readable",
515 log_context!(self)
516 );
517 }
518 let listener = self.listener.borrow();
519 let result = match &mut self.state {
520 TcpStateMachine::Pipe(pipe) => pipe.readable(&mut self.metrics),
521 TcpStateMachine::RelayProxyProtocol(pp) => pp.readable(&mut self.metrics),
522 TcpStateMachine::ExpectProxyProtocol(pp) => pp.readable(&mut self.metrics),
523 TcpStateMachine::SendProxyProtocol(_) => SessionResult::Continue,
524 TcpStateMachine::SniPreread(preread) => {
525 let cfg = listener.preread_config(preread.effective_max_bytes());
526 preread.readable(&mut self.metrics, &cfg)
527 }
528 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
529 };
530 drop(listener);
531
532 if let TcpStateMachine::SniPreread(preread) = &self.state
536 && self.cluster_id.is_none()
537 && let Some(outcome) = preread.outcome()
538 {
539 self.cluster_id = Some(outcome.cluster.clone());
540 self.cluster_metrics_incarnation =
541 crate::metrics::capture_cluster_metrics(outcome.cluster.as_ref());
542 self.container_frontend_timeout
554 .set_duration(Duration::from_secs(
555 self.listener.borrow().config.front_timeout as u64,
556 ));
557 }
558
559 result
560 }
561
562 fn writable(&mut self) -> SessionResult {
563 match &mut self.state {
564 TcpStateMachine::Pipe(pipe) => pipe.writable(&mut self.metrics),
565 _ => SessionResult::Continue,
566 }
567 }
568
569 fn back_readable(&mut self) -> SessionResult {
570 if !self.container_frontend_timeout.reset() {
571 error!(
572 "{} Could not reset frontend timeout on back_readable",
573 log_context!(self)
574 );
575 }
576 if !self.container_backend_timeout.reset() {
577 error!(
578 "{} Could not reset backend timeout on back_readable",
579 log_context!(self)
580 );
581 }
582
583 match &mut self.state {
584 TcpStateMachine::Pipe(pipe) => pipe.backend_readable(&mut self.metrics),
585 _ => SessionResult::Continue,
586 }
587 }
588
589 fn back_writable(&mut self) -> SessionResult {
590 match &mut self.state {
591 TcpStateMachine::Pipe(pipe) => pipe.backend_writable(&mut self.metrics),
592 TcpStateMachine::RelayProxyProtocol(pp) => pp.back_writable(&mut self.metrics),
593 TcpStateMachine::SendProxyProtocol(pp) => pp.back_writable(&mut self.metrics),
594 TcpStateMachine::SniPreread(preread) => preread.back_writable(),
598 TcpStateMachine::ExpectProxyProtocol(_) => SessionResult::Continue,
599 TcpStateMachine::FailedUpgrade(_) => {
600 unreachable!()
601 }
602 }
603 }
604
605 fn back_socket_mut(&mut self) -> Option<&mut MioTcpStream> {
606 match &mut self.state {
607 TcpStateMachine::Pipe(pipe) => pipe.back_socket_mut(),
608 TcpStateMachine::SendProxyProtocol(pp) => pp.back_socket_mut(),
609 TcpStateMachine::RelayProxyProtocol(pp) => pp.back_socket_mut(),
610 TcpStateMachine::SniPreread(preread) => preread.back_socket_mut(),
611 TcpStateMachine::ExpectProxyProtocol(_) => None,
612 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
613 }
614 }
615
616 pub fn upgrade(&mut self) -> SessionIsToBeClosed {
617 let new_state = match self.state.take() {
618 TcpStateMachine::SendProxyProtocol(spp) => self.upgrade_send(spp),
619 TcpStateMachine::RelayProxyProtocol(rpp) => self.upgrade_relay(rpp),
620 TcpStateMachine::ExpectProxyProtocol(epp) => self.upgrade_expect(epp),
621 TcpStateMachine::SniPreread(preread) => self.upgrade_sni_preread(preread),
622 TcpStateMachine::Pipe(_) => None,
623 TcpStateMachine::FailedUpgrade(_) => todo!(),
624 };
625
626 match new_state {
627 Some(state) => {
628 self.state = state;
629 false
630 } None => true,
633 }
634 }
635
636 fn upgrade_send(
637 &mut self,
638 send_proxy_protocol: SendProxyProtocol<MioTcpStream>,
639 ) -> Option<TcpStateMachine> {
640 if self.backend_buffer.is_some() && self.frontend_buffer.is_some() {
641 let mut pipe = send_proxy_protocol.into_pipe(
642 self.frontend_buffer.take().unwrap(),
643 self.backend_buffer.take().unwrap(),
644 self.listener.clone(),
645 );
646
647 let frontend_event = pipe.frontend_readiness.event;
660 let backend_event = pipe.backend_readiness.event;
661 pipe.restore_readiness_events(frontend_event, backend_event);
662
663 pipe.set_cluster_id(self.cluster_id.clone());
664 pipe.set_cluster_metrics_incarnation(self.cluster_metrics_incarnation);
665 pipe.set_backend_address(self.backend_address);
666 if let Some(sni) = self.routed_sni.take() {
671 pipe.set_tls_metadata(None, None, Some(sni), self.routed_alpn_label.take());
672 }
673 pipe.set_tags_key(self.tags_key.clone());
676 gauge_add!(names::protocol::PROXY_SEND, -1);
677 gauge_add!(names::protocol::TCP, 1);
678 return Some(TcpStateMachine::Pipe(pipe));
679 }
680
681 error!(
682 "{} Missing the frontend or backend buffer queue, we can't switch to a pipe",
683 log_context!(self)
684 );
685 None
686 }
687
688 fn upgrade_relay(&mut self, rpp: RelayProxyProtocol<MioTcpStream>) -> Option<TcpStateMachine> {
689 if self.backend_buffer.is_some() {
690 let mut pipe =
691 rpp.into_pipe(self.backend_buffer.take().unwrap(), self.listener.clone());
692 pipe.set_cluster_id(self.cluster_id.clone());
693 pipe.set_cluster_metrics_incarnation(self.cluster_metrics_incarnation);
694 pipe.set_backend_address(self.backend_address);
695 gauge_add!(names::protocol::PROXY_RELAY, -1);
696 gauge_add!(names::protocol::TCP, 1);
697 return Some(TcpStateMachine::Pipe(pipe));
698 }
699
700 error!(
701 "{} Missing the backend buffer queue, we can't switch to a pipe",
702 log_context!(self)
703 );
704 None
705 }
706
707 fn upgrade_expect(
708 &mut self,
709 epp: ExpectProxyProtocol<MioTcpStream>,
710 ) -> Option<TcpStateMachine> {
711 if self.frontend_buffer.is_some() && self.backend_buffer.is_some() {
712 let mut pipe = epp.into_pipe(
713 self.frontend_buffer.take().unwrap(),
714 self.backend_buffer.take().unwrap(),
715 None,
716 None,
717 self.listener.clone(),
718 );
719
720 pipe.set_cluster_id(self.cluster_id.clone());
721 pipe.set_cluster_metrics_incarnation(self.cluster_metrics_incarnation);
722 pipe.set_backend_address(self.backend_address);
723 gauge_add!(names::protocol::PROXY_EXPECT, -1);
724 gauge_add!(names::protocol::TCP, 1);
725 return Some(TcpStateMachine::Pipe(pipe));
726 }
727
728 error!(
729 "{} Missing the backend buffer queue, we can't switch to a pipe",
730 log_context!(self)
731 );
732 None
733 }
734
735 fn abort_sni_preread_upgrade(
773 &self,
774 preread: &SniPreread<MioTcpStream>,
775 reason: &str,
776 ) -> Option<TcpStateMachine> {
777 error!("{} {}", log_context!(self), reason);
778 time!(
779 names::tcp::sni_preread::DURATION,
780 preread.started_at().elapsed().as_millis() as i64
781 );
782 None
783 }
784
785 fn upgrade_sni_preread(
786 &mut self,
787 mut preread: SniPreread<MioTcpStream>,
788 ) -> Option<TcpStateMachine> {
789 let Some(outcome) = preread.outcome().cloned() else {
800 return self.abort_sni_preread_upgrade(
801 &preread,
802 "upgrade_sni_preread called before a route decision",
803 );
804 };
805 let Some(backend_socket) = preread.backend.take() else {
806 return self.abort_sni_preread_upgrade(
807 &preread,
808 "SNI preread upgrade with no backend socket set",
809 );
810 };
811 let Some(backend_token) = preread.backend_token else {
812 return self.abort_sni_preread_upgrade(
813 &preread,
814 "SNI preread upgrade with no backend token set",
815 );
816 };
817 let Some(back_buffer) = self.backend_buffer.take() else {
818 return self.abort_sni_preread_upgrade(
819 &preread,
820 "SNI preread upgrade with no backend buffer queued",
821 );
822 };
823
824 gauge_add!(names::tcp::sni_preread::ACTIVE, -1);
825 time!(
826 names::tcp::sni_preread::DURATION,
827 preread.started_at().elapsed().as_millis() as i64
828 );
829
830 if self.cluster_id.as_ref() != Some(&outcome.cluster) {
834 self.cluster_id = Some(outcome.cluster.clone());
835 self.cluster_metrics_incarnation =
836 crate::metrics::capture_cluster_metrics(outcome.cluster.as_ref());
837 }
838 self.routed_sni = Some(outcome.sni.clone());
853 self.routed_alpn_label = known_alpn_label(&outcome.alpn);
854 self.tags_key = Some(sni_tags_key(
862 self.listener.borrow().get_addr(),
863 &outcome.matched_sni_pattern,
864 &alpn_matcher_protocols(&outcome.matched_alpn),
865 ));
866
867 let proxy_protocol = self
868 .proxy
869 .borrow()
870 .configs
871 .get(&outcome.cluster)
872 .and_then(|c| c.proxy_protocol);
873
874 let frontend_event = preread.frontend_readiness.event;
875 let backend_event = preread.backend_readiness.event;
876 let mut frontend_buffer = preread.frontend_buffer;
877 let frontend = preread.frontend;
878 let frontend_token = preread.frontend_token;
879 let request_id = preread.request_id;
880
881 match proxy_protocol {
882 Some(ProxyProtocolConfig::SendHeader) => {
883 frontend_buffer.consume(outcome.content_offset);
884 self.frontend_buffer = Some(frontend_buffer);
885 self.backend_buffer = Some(back_buffer);
886 gauge_add!(names::protocol::PROXY_SEND, 1);
887 let mut spp = SendProxyProtocol::new(
888 frontend,
889 frontend_token,
890 request_id,
891 Some(backend_socket),
892 );
893 spp.frontend_readiness.event = frontend_event;
894 spp.backend_readiness.event = backend_event;
895 spp.set_back_token(backend_token);
896 spp.set_back_connected(BackendConnectionStatus::Connected);
897 Some(TcpStateMachine::SendProxyProtocol(spp))
898 }
899 Some(ProxyProtocolConfig::ExpectHeader) | None => {
900 frontend_buffer.consume(outcome.content_offset);
901 Some(self.build_pipe_from_preread(
902 back_buffer,
903 frontend_buffer,
904 frontend,
905 frontend_token,
906 frontend_event,
907 backend_event,
908 backend_socket,
909 backend_token,
910 request_id,
911 outcome.proxy_source,
912 outcome.cluster,
913 ))
914 }
915 Some(ProxyProtocolConfig::RelayHeader) => Some(self.build_pipe_from_preread(
916 back_buffer,
917 frontend_buffer,
918 frontend,
919 frontend_token,
920 frontend_event,
921 backend_event,
922 backend_socket,
923 backend_token,
924 request_id,
925 outcome.proxy_source,
926 outcome.cluster,
927 )),
928 }
929 }
930
931 #[allow(clippy::too_many_arguments)]
932 fn build_pipe_from_preread(
933 &mut self,
934 back_buffer: Checkout,
935 frontend_buffer: Checkout,
936 frontend: MioTcpStream,
937 frontend_token: Token,
938 frontend_event: Ready,
939 backend_event: Ready,
940 backend_socket: MioTcpStream,
941 backend_token: Token,
942 request_id: Ulid,
943 proxy_source: Option<SocketAddr>,
944 cluster_id: ClusterId,
945 ) -> TcpStateMachine {
946 let addr = proxy_source.or(self.frontend_address);
947 let mut pipe = Pipe::new(
948 back_buffer,
949 self.backend_id.clone(),
950 Some(backend_socket),
951 None,
952 None,
953 None,
954 Some(cluster_id),
955 frontend_buffer,
956 frontend_token,
957 frontend,
958 self.listener.clone(),
959 Protocol::TCP,
960 request_id,
961 request_id,
962 addr,
963 WebSocketContext::Tcp,
964 );
965 pipe.set_cluster_metrics_incarnation(self.cluster_metrics_incarnation);
966 pipe.restore_readiness_events(frontend_event, backend_event);
978 pipe.set_back_token(backend_token);
979 pipe.set_backend_address(self.backend_address);
980 if let Some(sni) = self.routed_sni.take() {
984 pipe.set_tls_metadata(None, None, Some(sni), self.routed_alpn_label.take());
985 }
986 pipe.set_tags_key(self.tags_key.clone());
989 gauge_add!(names::protocol::TCP, 1);
990 TcpStateMachine::Pipe(pipe)
991 }
992
993 fn front_readiness(&mut self) -> &mut Readiness {
994 match &mut self.state {
995 TcpStateMachine::Pipe(pipe) => &mut pipe.frontend_readiness,
996 TcpStateMachine::SendProxyProtocol(pp) => &mut pp.frontend_readiness,
997 TcpStateMachine::RelayProxyProtocol(pp) => &mut pp.frontend_readiness,
998 TcpStateMachine::ExpectProxyProtocol(pp) => &mut pp.frontend_readiness,
999 TcpStateMachine::SniPreread(preread) => &mut preread.frontend_readiness,
1000 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
1001 }
1002 }
1003
1004 fn back_readiness(&mut self) -> Option<&mut Readiness> {
1005 match &mut self.state {
1006 TcpStateMachine::Pipe(pipe) => Some(&mut pipe.backend_readiness),
1007 TcpStateMachine::SendProxyProtocol(pp) => Some(&mut pp.backend_readiness),
1008 TcpStateMachine::RelayProxyProtocol(pp) => Some(&mut pp.backend_readiness),
1009 TcpStateMachine::SniPreread(preread) => Some(&mut preread.backend_readiness),
1010 TcpStateMachine::ExpectProxyProtocol(_) => None,
1011 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
1012 }
1013 }
1014
1015 fn set_back_socket(&mut self, socket: MioTcpStream) {
1016 match &mut self.state {
1017 TcpStateMachine::Pipe(pipe) => pipe.set_back_socket(socket),
1018 TcpStateMachine::SendProxyProtocol(pp) => pp.set_back_socket(socket),
1019 TcpStateMachine::RelayProxyProtocol(pp) => pp.set_back_socket(socket),
1020 TcpStateMachine::SniPreread(preread) => preread.set_back_socket(socket),
1021 TcpStateMachine::ExpectProxyProtocol(_) => {
1022 error!(
1023 "{} We should not set the back socket for the expect proxy protocol",
1024 log_context!(self)
1025 );
1026 panic!(
1027 "{} We should not set the back socket for the expect proxy protocol",
1028 log_context!(self)
1029 );
1030 }
1031 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
1032 }
1033 }
1034
1035 fn set_back_token(&mut self, token: Token) {
1036 debug_assert_ne!(
1040 token, self.frontend_token,
1041 "backend token must differ from the frontend token"
1042 );
1043 self.backend_token = Some(token);
1044
1045 match &mut self.state {
1046 TcpStateMachine::Pipe(pipe) => pipe.set_back_token(token),
1047 TcpStateMachine::SendProxyProtocol(pp) => pp.set_back_token(token),
1048 TcpStateMachine::SniPreread(preread) => preread.set_back_token(token),
1049 TcpStateMachine::RelayProxyProtocol(pp) => pp.set_back_token(token),
1050 TcpStateMachine::ExpectProxyProtocol(_) => self.backend_token = Some(token),
1051 TcpStateMachine::FailedUpgrade(_) => unreachable!(),
1052 }
1053
1054 debug_assert_eq!(
1058 self.backend_token,
1059 Some(token),
1060 "set_back_token must leave the session owning the registered token"
1061 );
1062 }
1063
1064 fn set_backend_id(&mut self, id: String) {
1065 self.backend_id = Some(id.clone());
1066 if let TcpStateMachine::Pipe(pipe) = &mut self.state {
1067 pipe.set_backend_id(Some(id));
1068 }
1069 }
1070
1071 fn set_backend_address(&mut self, address: SocketAddr) {
1074 self.backend_address = Some(address);
1075 if let TcpStateMachine::Pipe(pipe) = &mut self.state {
1076 pipe.set_backend_address(Some(address));
1077 }
1078 }
1079
1080 fn back_connected(&self) -> BackendConnectionStatus {
1081 self.backend_connected
1082 }
1083
1084 fn set_back_connected(&mut self, status: BackendConnectionStatus) {
1085 let last = self.backend_connected;
1086 debug_assert!(
1093 status != BackendConnectionStatus::Connected
1094 || last != BackendConnectionStatus::Connected,
1095 "set_back_connected(Connected) must not run on an already-Connected backend (gauge would double-count)"
1096 );
1097 self.backend_connected = status;
1098
1099 debug_assert_eq!(
1101 self.backend_connected, status,
1102 "set_back_connected must record the requested status"
1103 );
1104
1105 if status == BackendConnectionStatus::Connected {
1106 gauge_add!(names::backend::CONNECTIONS, 1);
1107 gauge_add!(
1108 names::backend::CONNECTIONS_PER_BACKEND,
1109 1,
1110 self.cluster_id.as_deref(),
1111 self.metrics.backend_id.as_deref(),
1112 self.cluster_metrics_incarnation
1113 );
1114
1115 self.container_backend_timeout
1118 .set_duration(self.configured_backend_timeout);
1119 self.container_frontend_timeout.reset();
1120
1121 if let TcpStateMachine::SendProxyProtocol(spp) = &mut self.state {
1122 spp.set_back_connected(BackendConnectionStatus::Connected);
1123 }
1124
1125 if let Some(backend) = self.backend.as_ref() {
1126 let mut backend = backend.borrow_mut();
1127
1128 if backend.retry_policy.is_down() {
1129 incr!(
1130 names::backend::UP,
1131 self.cluster_id.as_deref(),
1132 self.metrics.backend_id.as_deref(),
1133 self.cluster_metrics_incarnation
1134 );
1135 gauge!(
1136 names::backend::AVAILABLE,
1137 1,
1138 self.cluster_id.as_deref(),
1139 self.metrics.backend_id.as_deref(),
1140 self.cluster_metrics_incarnation
1141 );
1142 info!(
1143 "{} backend server {} at {} is up",
1144 log_context!(self),
1145 backend.backend_id,
1146 backend.address
1147 );
1148 push_event(Event {
1149 kind: EventKind::BackendUp as i32,
1150 backend_id: Some(backend.backend_id.to_owned()),
1151 address: Some(backend.address.into()),
1152 cluster_id: None,
1153 metric_detail: None,
1154 });
1155 }
1156
1157 if let BackendConnectionStatus::Connecting(start) = last {
1158 let now = Instant::now();
1159 backend.set_connection_time(now - start, now);
1160 }
1161
1162 backend.failures = 0;
1164 backend.retry_policy.succeed(Instant::now());
1165 }
1166 }
1167 }
1168
1169 fn remove_backend(&mut self) {
1170 if let Some(backend) = self.backend.take() {
1171 (*backend.borrow_mut()).dec_connections();
1172 }
1173
1174 self.backend_token = None;
1175
1176 debug_assert!(
1181 self.backend.is_none(),
1182 "remove_backend must release the backend handle"
1183 );
1184 debug_assert!(
1185 self.backend_token.is_none(),
1186 "remove_backend must clear the backend token"
1187 );
1188 }
1189
1190 fn fail_backend_connection(&mut self) {
1191 if let Some(backend) = self.backend.as_ref() {
1192 let backend = &mut *backend.borrow_mut();
1193 backend.failures += 1;
1194
1195 let already_unavailable = backend.retry_policy.is_down();
1196 backend.retry_policy.fail(Instant::now(), &mut rand::rng());
1199 incr!(
1200 names::backend::CONNECTIONS_ERROR,
1201 self.cluster_id.as_deref(),
1202 self.metrics.backend_id.as_deref(),
1203 self.cluster_metrics_incarnation
1204 );
1205 if !already_unavailable && backend.retry_policy.is_down() {
1206 error!(
1207 "{} backend server {} at {} is down",
1208 log_context!(self),
1209 backend.backend_id,
1210 backend.address
1211 );
1212 incr!(
1213 names::backend::DOWN,
1214 self.cluster_id.as_deref(),
1215 self.metrics.backend_id.as_deref(),
1216 self.cluster_metrics_incarnation
1217 );
1218 gauge!(
1219 names::backend::AVAILABLE,
1220 0,
1221 self.cluster_id.as_deref(),
1222 self.metrics.backend_id.as_deref(),
1223 self.cluster_metrics_incarnation
1224 );
1225
1226 push_event(Event {
1227 kind: EventKind::BackendDown as i32,
1228 backend_id: Some(backend.backend_id.to_owned()),
1229 address: Some(backend.address.into()),
1230 cluster_id: None,
1231 metric_detail: None,
1232 });
1233 }
1234 }
1235 }
1236
1237 pub fn test_back_socket(&mut self) -> SessionIsToBeClosed {
1238 match self.back_socket_mut() {
1239 Some(ref mut s) => {
1240 let mut tmp = [0u8; 1];
1241 let res = s.peek(&mut tmp[..]);
1242
1243 match res {
1244 Ok(0) => false,
1246 Ok(_) => true,
1247 Err(e) => matches!(e.kind(), std::io::ErrorKind::WouldBlock),
1248 }
1249 }
1250 None => false,
1251 }
1252 }
1253
1254 pub fn cancel_timeouts(&mut self) {
1255 self.container_frontend_timeout.cancel();
1256 self.container_backend_timeout.cancel();
1257 }
1258
1259 #[cfg(debug_assertions)]
1267 fn check_invariants(&self) {
1268 debug_assert!(
1276 u32::from(self.connection_attempt)
1277 <= sozu_command::config::MAX_CONNECTION_ATTEMPTS_UPPER_BOUND,
1278 "connection_attempt ({}) must never exceed the largest attempt budget",
1279 self.connection_attempt,
1280 );
1281
1282 if self.backend_connected == BackendConnectionStatus::Connected {
1290 debug_assert!(
1291 self.backend_token.is_some(),
1292 "a Connected backend must own a backend token"
1293 );
1294 }
1295
1296 if self.backend.is_some() {
1303 debug_assert!(
1304 self.backend_token.is_some(),
1305 "a live backend handle must have a backend token"
1306 );
1307 }
1308
1309 if self.has_been_closed {
1312 debug_assert!(
1313 self.backend.is_none(),
1314 "a closed session must have released its backend handle"
1315 );
1316 debug_assert!(
1317 !self.cluster_ip_tracked,
1318 "a closed session must have untracked its (cluster, source-IP) slot"
1319 );
1320 }
1321 }
1322
1323 fn attempt_backend_connect_if_needed(
1349 &mut self,
1350 session: &Rc<RefCell<dyn ProxySession>>,
1351 ) -> Option<SessionResult> {
1352 if self.back_connected() != BackendConnectionStatus::NotConnected {
1353 return None;
1354 }
1355 if matches!(&self.state, TcpStateMachine::SniPreread(preread) if !preread.is_routed()) {
1356 return None;
1357 }
1358 if matches!(&self.state, TcpStateMachine::ExpectProxyProtocol(_)) {
1359 return None;
1360 }
1361
1362 let connection_result = self.connect_to_backend(session.clone());
1363 if let Err(err) = &connection_result {
1364 match err {
1365 BackendConnectionError::MaxConnectionRetries(_) => trace!(
1368 "{} Error connecting to backend: {}",
1369 log_context!(self),
1370 err
1371 ),
1372 _ => warn!(
1373 "{} Error connecting to backend: {}",
1374 log_context!(self),
1375 err
1376 ),
1377 }
1378 }
1379 handle_connection_result(connection_result)
1380 }
1381
1382 fn ready_inner(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionResult {
1383 let mut counter = 0;
1384
1385 let back_connected = self.back_connected();
1386 if back_connected.is_connecting() {
1387 let Some(back_event) = self.back_readiness().map(|readiness| readiness.event) else {
1398 error!(
1399 "{} backend is connecting but the session state carries no backend readiness, closing",
1400 log_context!(self)
1401 );
1402 return SessionResult::Close;
1403 };
1404 if back_event.is_hup() && !self.test_back_socket() {
1405 debug!(
1407 "{} error connecting to backend, trying again",
1408 log_context!(self)
1409 );
1410 self.connection_attempt += 1;
1411 self.fail_backend_connection();
1412
1413 self.close_backend();
1415 let connection_result = self.connect_to_backend(session.clone());
1416 if let Err(err) = &connection_result {
1417 match err {
1418 BackendConnectionError::MaxConnectionRetries(_) => trace!(
1421 "{} Error connecting to backend: {}",
1422 log_context!(self),
1423 err
1424 ),
1425 _ => warn!(
1426 "{} Error connecting to backend: {}",
1427 log_context!(self),
1428 err
1429 ),
1430 }
1431 }
1432
1433 if let Some(state_result) = handle_connection_result(connection_result) {
1434 return state_result;
1435 }
1436 } else if back_event != Ready::EMPTY {
1437 self.connection_attempt = 0;
1438 self.set_back_connected(BackendConnectionStatus::Connected);
1439 }
1440 } else if back_connected == BackendConnectionStatus::NotConnected
1441 && let Some(state_result) = self.attempt_backend_connect_if_needed(&session)
1442 {
1443 return state_result;
1444 }
1445
1446 if let TcpStateMachine::ExpectProxyProtocol(_) = self.state {
1461 while (self.front_readiness().interest & self.front_readiness().event).is_readable()
1462 && self.front_readiness().event.is_hup()
1463 && counter < MAX_LOOP_ITERATIONS
1464 {
1465 let session_result = self.readable();
1466 if session_result != SessionResult::Continue {
1467 return session_result;
1468 }
1469 counter += 1;
1470 }
1471 }
1472
1473 if self.front_readiness().event.is_hup() {
1474 let session_result = self.front_hup();
1475 if session_result != SessionResult::Continue {
1476 return session_result;
1477 }
1478 self.front_readiness().event.remove(Ready::HUP);
1489 }
1490
1491 while counter < MAX_LOOP_ITERATIONS {
1492 let front_interest = self.front_readiness().interest & self.front_readiness().event;
1493 let back_interest = self
1494 .back_readiness()
1495 .map(|r| r.interest & r.event)
1496 .unwrap_or(Ready::EMPTY);
1497
1498 trace!(
1499 "{} Frontend interest({:?}) and backend interest({:?})",
1500 log_context!(self),
1501 front_interest,
1502 back_interest
1503 );
1504
1505 if front_interest == Ready::EMPTY && back_interest == Ready::EMPTY {
1506 break;
1507 }
1508
1509 if self
1510 .back_readiness()
1511 .map(|r| r.event.is_hup())
1512 .unwrap_or(false)
1513 && self.front_readiness().interest.is_writable()
1514 && !self.front_readiness().event.is_writable()
1515 {
1516 break;
1517 }
1518
1519 if front_interest.is_readable() {
1520 let session_result = self.readable();
1521 if session_result != SessionResult::Continue {
1522 return session_result;
1523 }
1524 if let Some(state_result) = self.attempt_backend_connect_if_needed(&session) {
1531 return state_result;
1532 }
1533 }
1534
1535 if back_interest.is_writable() {
1536 let session_result = self.back_writable();
1537 if session_result != SessionResult::Continue {
1538 return session_result;
1539 }
1540 }
1541
1542 if back_interest.is_readable() {
1543 let session_result = self.back_readable();
1544 if session_result != SessionResult::Continue {
1545 return session_result;
1546 }
1547 }
1548
1549 if front_interest.is_writable() {
1550 let session_result = self.writable();
1551 if session_result != SessionResult::Continue {
1552 return session_result;
1553 }
1554 }
1555
1556 if back_interest.is_hup() {
1557 let session_result = self.back_hup();
1558 if session_result != SessionResult::Continue {
1559 return session_result;
1560 }
1561 }
1562
1563 if front_interest.is_error() {
1564 error!(
1565 "{} Frontend socket error, disconnecting",
1566 log_context!(self)
1567 );
1568 self.front_readiness().interest = Ready::EMPTY;
1569 if let Some(r) = self.back_readiness() {
1570 r.interest = Ready::EMPTY;
1571 }
1572
1573 return SessionResult::Close;
1574 }
1575
1576 if back_interest.is_error() && self.back_hup() == SessionResult::Close {
1577 self.front_readiness().interest = Ready::EMPTY;
1578 if let Some(r) = self.back_readiness() {
1579 r.interest = Ready::EMPTY;
1580 }
1581
1582 error!("{} backend socket error, disconnecting", log_context!(self));
1583 return SessionResult::Close;
1584 }
1585
1586 counter += 1;
1587 }
1588
1589 if counter >= MAX_LOOP_ITERATIONS {
1590 error!(
1591 "{} Handling session went through {} iterations, there's a probable infinite loop bug, closing the connection",
1592 log_context!(self),
1593 MAX_LOOP_ITERATIONS
1594 );
1595
1596 incr!(names::tcp::INFINITE_LOOP_ERROR);
1597
1598 let front_interest = self.front_readiness().interest & self.front_readiness().event;
1599 let back_interest = self
1600 .back_readiness()
1601 .map(|r| r.interest & r.event)
1602 .unwrap_or(Ready::EMPTY);
1603
1604 let back = self.back_readiness().cloned();
1605
1606 error!(
1607 "{} readiness: front {:?} / back {:?} | front: {:?} | back: {:?} ",
1608 log_context!(self),
1609 self.front_readiness(),
1610 back,
1611 front_interest,
1612 back_interest
1613 );
1614
1615 self.print_session();
1616
1617 return SessionResult::Close;
1618 }
1619
1620 SessionResult::Continue
1621 }
1622
1623 fn close_backend(&mut self) {
1625 if let Some(token) = self.backend_token
1637 && self.back_socket_mut().is_some()
1638 {
1639 self.proxy
1640 .borrow()
1641 .sessions
1642 .borrow_mut()
1643 .slab
1644 .try_remove(token.0);
1645 }
1646 self.remove_backend();
1647
1648 let back_connected = self.back_connected();
1649 if back_connected != BackendConnectionStatus::NotConnected {
1650 if let Some(r) = self.back_readiness() {
1651 r.event = Ready::EMPTY;
1652 }
1653
1654 let log_context = log_context!(self);
1655 if let Some(sock) = self.back_socket_mut() {
1656 if let Err(e) = sock.shutdown(Shutdown::Both)
1661 && e.kind() != ErrorKind::NotConnected
1662 {
1663 error!(
1664 "{} Error closing back socket({:?}): {:?}",
1665 log_context, sock, e
1666 );
1667 }
1668 }
1669 }
1670
1671 if back_connected == BackendConnectionStatus::Connected {
1678 gauge_add!(names::backend::CONNECTIONS, -1);
1679 gauge_add!(
1680 names::backend::CONNECTIONS_PER_BACKEND,
1681 -1,
1682 self.cluster_id.as_deref(),
1683 self.metrics.backend_id.as_deref(),
1684 self.cluster_metrics_incarnation
1685 );
1686 }
1687
1688 self.set_back_connected(BackendConnectionStatus::NotConnected);
1689
1690 debug_assert_eq!(
1694 self.backend_connected,
1695 BackendConnectionStatus::NotConnected,
1696 "close_backend must leave the backend NotConnected"
1697 );
1698 debug_assert!(
1699 self.backend_token.is_none(),
1700 "close_backend must clear the backend token"
1701 );
1702 }
1703
1704 fn connect_to_backend(
1705 &mut self,
1706 session_rc: Rc<RefCell<dyn ProxySession>>,
1707 ) -> Result<BackendConnectAction, BackendConnectionError> {
1708 debug_assert!(
1713 u32::from(self.connection_attempt)
1714 <= sozu_command::config::MAX_CONNECTION_ATTEMPTS_UPPER_BOUND,
1715 "connection_attempt ({}) overflowed the largest attempt budget before the retry gate",
1716 self.connection_attempt,
1717 );
1718
1719 let cluster_id = self
1725 .cluster_id
1726 .clone()
1727 .or_else(|| self.listener.borrow().cluster_id.clone())
1728 .ok_or(BackendConnectionError::NotFound(ObjectKind::TcpCluster))?;
1729
1730 if self.cluster_id.is_none() {
1735 self.cluster_metrics_incarnation =
1736 crate::metrics::capture_cluster_metrics(cluster_id.as_ref());
1737 self.cluster_id = Some(cluster_id.clone());
1738 }
1739
1740 let max_attempts = {
1743 let proxy = self.proxy.borrow();
1744 let cluster_attempts = proxy
1745 .configs
1746 .get(&cluster_id)
1747 .and_then(|c| c.max_connection_attempts);
1748 proxy
1749 .sessions
1750 .borrow()
1751 .effective_max_connection_attempts(cluster_attempts)
1752 };
1753 if u32::from(self.connection_attempt) >= max_attempts {
1754 incr!(
1755 names::backend::CONNECT_RETRIES_EXHAUSTED,
1756 self.cluster_id.as_deref(),
1757 self.metrics.backend_id.as_deref(),
1758 self.cluster_metrics_incarnation
1759 );
1760 warn!(
1761 "{} Max connection attempt reached ({})",
1762 log_context!(self),
1763 self.connection_attempt
1764 );
1765 return Err(BackendConnectionError::MaxConnectionRetries(Some(
1766 cluster_id,
1767 )));
1768 }
1769
1770 if self.proxy.borrow().sessions.borrow().at_capacity() {
1771 return Err(BackendConnectionError::MaxSessionsMemory);
1772 }
1773
1774 let (cluster_max_connections_per_ip, cluster_max_connections_per_subnet, keyed) = self
1783 .proxy
1784 .borrow()
1785 .configs
1786 .get(&cluster_id)
1787 .map(|c| {
1788 (
1789 c.max_connections_per_ip,
1790 c.max_connections_per_subnet,
1791 c.reads_affinity_key,
1792 )
1793 })
1794 .unwrap_or((None, None, false));
1795 if let Some(ip) = self.effective_session_address().map(|sa| sa.ip()) {
1796 let sessions_rc = self.proxy.borrow().sessions.clone();
1797 let at_limit = sessions_rc.borrow().cluster_connection_at_limit(
1801 self.frontend_token,
1802 &cluster_id,
1803 &ip,
1804 cluster_max_connections_per_ip,
1805 cluster_max_connections_per_subnet,
1806 );
1807 if at_limit {
1808 debug!(
1809 "{} per-(cluster, source-IP/subnet) limit hit for cluster {} from {}",
1810 log_context!(self),
1811 cluster_id,
1812 ip
1813 );
1814 return Err(BackendConnectionError::TooManyConnectionsPerIp { cluster_id });
1815 }
1816 sessions_rc.borrow_mut().track_cluster_connection(
1817 self.frontend_token,
1818 cluster_id.clone(),
1819 ip,
1820 cluster_max_connections_per_subnet,
1821 );
1822 self.cluster_ip_tracked = true;
1823 }
1824
1825 let affinity_key = if keyed {
1831 self.effective_session_address()
1832 .map(|address| affinity_key_from_ip(address.ip()))
1833 } else {
1834 None
1835 };
1836 let (backend, mut stream) = self
1837 .proxy
1838 .borrow()
1839 .backends
1840 .borrow_mut()
1841 .backend_from_cluster_id(&cluster_id, affinity_key, Instant::now())
1842 .map_err(BackendConnectionError::Backend)?;
1843
1844 if backend.borrow().unix_path.is_none()
1846 && let Err(e) = stream.set_nodelay(true)
1847 {
1848 error!(
1849 "{} Error setting nodelay on back socket({:?}): {:?}",
1850 log_context!(self),
1851 stream,
1852 e
1853 );
1854 }
1855 self.backend = Some(Rc::clone(&backend));
1861 self.backend_connected = BackendConnectionStatus::Connecting(Instant::now());
1862
1863 let back_token = {
1864 let proxy = self.proxy.borrow();
1865 let mut s = proxy.sessions.borrow_mut();
1866 let entry = s.slab.vacant_entry();
1867 let back_token = Token(entry.key());
1868 let _entry = entry.insert(session_rc.clone());
1869 back_token
1870 };
1871
1872 if let Err(e) = self.proxy.borrow().registry.register(
1873 &mut stream,
1874 back_token,
1875 Interest::READABLE | Interest::WRITABLE,
1876 ) {
1877 error!(
1878 "{} Error registering back socket({:?}): {:?}",
1879 log_context!(self),
1880 stream,
1881 e
1882 );
1883 }
1884
1885 self.container_backend_timeout.set(back_token);
1886
1887 self.set_back_token(back_token);
1888 self.set_back_socket(stream);
1889
1890 self.metrics.backend_id = Some(Rc::from(backend.borrow().backend_id.as_str()));
1891 self.metrics.backend_start();
1892 self.set_backend_id(backend.borrow().backend_id.clone());
1893 self.set_backend_address(backend.borrow().address);
1894
1895 debug_assert!(
1900 self.backend_token.is_some(),
1901 "a New backend connection must own its backend token"
1902 );
1903 debug_assert!(
1904 self.backend_connected.is_connecting(),
1905 "a New backend connection must be in the Connecting state"
1906 );
1907
1908 Ok(BackendConnectAction::New)
1909 }
1910}
1911
1912impl ProxySession for TcpSession {
1913 fn close(&mut self) {
1914 if self.has_been_closed {
1915 return;
1916 }
1917
1918 debug_assert!(
1922 !self.has_been_closed,
1923 "close() body must only run on a not-yet-closed session"
1924 );
1925
1926 trace!("{} Closing TCP session", log_context!(self));
1928 self.metrics.service_stop();
1929
1930 if self.cluster_ip_tracked {
1936 self.proxy
1937 .borrow()
1938 .sessions
1939 .borrow_mut()
1940 .untrack_all_cluster_ip(self.frontend_token);
1941 self.cluster_ip_tracked = false;
1942 }
1943
1944 match self.state.marker() {
1953 StateMarker::Pipe => gauge_add!(names::protocol::TCP, -1),
1954 StateMarker::SendProxyProtocol => gauge_add!(names::protocol::PROXY_SEND, -1),
1955 StateMarker::RelayProxyProtocol => gauge_add!(names::protocol::PROXY_RELAY, -1),
1956 StateMarker::ExpectProxyProtocol => gauge_add!(names::protocol::PROXY_EXPECT, -1),
1957 StateMarker::SniPreread => {
1958 gauge_add!(names::tcp::sni_preread::ACTIVE, -1);
1959 if let TcpStateMachine::SniPreread(preread) = &self.state {
1960 time!(
1961 names::tcp::sni_preread::DURATION,
1962 preread.started_at().elapsed().as_millis() as i64
1963 );
1964 }
1965 }
1966 }
1967
1968 if self.state.failed() {
1969 match self.state.marker() {
1970 StateMarker::Pipe => incr!(names::tcp::UPGRADE_PIPE_FAILED),
1971 StateMarker::SendProxyProtocol => incr!(names::tcp::UPGRADE_SEND_FAILED),
1972 StateMarker::RelayProxyProtocol => incr!(names::tcp::UPGRADE_RELAY_FAILED),
1973 StateMarker::ExpectProxyProtocol => incr!(names::tcp::UPGRADE_EXPECT_FAILED),
1974 StateMarker::SniPreread => incr!(names::tcp::UPGRADE_SNI_PREREAD_FAILED),
1975 }
1976 return;
1977 }
1978
1979 self.cancel_timeouts();
1980
1981 let front_socket = self.state.front_socket();
1982 if let Err(e) = front_socket.shutdown(Shutdown::Both) {
1988 if e.kind() != ErrorKind::NotConnected {
1990 error!(
1991 "{} Error shutting down front socket({:?}): {:?}",
1992 log_context!(self),
1993 front_socket,
1994 e
1995 );
1996 }
1997 }
1998
1999 self.proxy
2003 .borrow()
2004 .sessions
2005 .borrow_mut()
2006 .slab
2007 .try_remove(self.frontend_token.0);
2008
2009 self.close_backend();
2010 self.has_been_closed = true;
2011
2012 debug_assert!(self.has_been_closed, "close() must mark the session closed");
2017 debug_assert!(
2018 self.backend_token.is_none(),
2019 "close() must leave no dangling backend token"
2020 );
2021 debug_assert!(
2022 !self.cluster_ip_tracked,
2023 "close() must untrack the (cluster, source-IP) slot"
2024 );
2025 }
2026
2027 fn timeout(&mut self, token: Token) -> SessionIsToBeClosed {
2028 debug_assert!(
2033 self.backend_token != Some(self.frontend_token),
2034 "frontend and backend tokens must never collide"
2035 );
2036 if self.frontend_token == token {
2037 self.container_frontend_timeout.triggered();
2038 if let TcpStateMachine::SniPreread(preread) = &mut self.state {
2053 if preread.is_routed() {
2054 debug!(
2055 "{} frontend timeout while a routed SNI-preread session was still \
2056 waiting on its backend connect",
2057 log_context!(self)
2058 );
2059 } else {
2060 let listener = self.listener.borrow();
2061 let cfg = listener.preread_config(preread.effective_max_bytes());
2062 preread.on_timeout(&cfg);
2063 }
2064 }
2065 return true;
2066 }
2067 if self.backend_token == Some(token) {
2068 self.container_backend_timeout.triggered();
2069 return true;
2070 }
2071 false
2073 }
2074
2075 fn protocol(&self) -> Protocol {
2076 Protocol::TCP
2077 }
2078
2079 fn update_readiness(&mut self, token: Token, events: Ready) {
2080 trace!(
2081 "{} token {:?} got event {}",
2082 log_context!(self),
2083 token,
2084 super::ready_to_string(events)
2085 );
2086
2087 self.last_event = Instant::now();
2088 self.metrics.wait_start();
2089
2090 if self.frontend_token == token {
2091 self.front_readiness().event = self.front_readiness().event | events;
2092 } else if self.backend_token == Some(token)
2093 && let Some(r) = self.back_readiness()
2094 {
2095 r.event |= events;
2096 }
2097 }
2098
2099 fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
2100 self.metrics.service_start();
2101
2102 let session_result = self.ready_inner(session.clone());
2103
2104 let to_bo_closed = match session_result {
2105 SessionResult::Close => true,
2106 SessionResult::Continue => false,
2107 SessionResult::Upgrade => match self.upgrade() {
2108 false => self.ready(session),
2109 true => true,
2110 },
2111 };
2112
2113 self.metrics.service_stop();
2114
2115 #[cfg(debug_assertions)]
2120 self.check_invariants();
2121
2122 to_bo_closed
2123 }
2124
2125 fn shutting_down(&mut self) -> SessionIsToBeClosed {
2126 true
2127 }
2128
2129 fn targets(&self, target: &crate::SessionTarget) -> bool {
2131 self.cluster_id.as_deref().is_some_and(|cluster_id| {
2132 target.matches(cluster_id, self.backend_id.as_deref(), self.backend_address)
2133 })
2134 }
2135
2136 fn last_event(&self) -> Instant {
2137 self.last_event
2138 }
2139
2140 fn print_session(&self) {
2141 let state: String = match &self.state {
2142 TcpStateMachine::ExpectProxyProtocol(_) => String::from("Expect"),
2143 TcpStateMachine::SendProxyProtocol(_) => String::from("Send"),
2144 TcpStateMachine::RelayProxyProtocol(_) => String::from("Relay"),
2145 TcpStateMachine::Pipe(_) => String::from("TCP"),
2146 TcpStateMachine::SniPreread(_) => String::from("SniPreread"),
2147 TcpStateMachine::FailedUpgrade(marker) => format!("FailedUpgrade({marker:?})"),
2148 };
2149
2150 let front_readiness = match &self.state {
2151 TcpStateMachine::ExpectProxyProtocol(expect) => Some(&expect.frontend_readiness),
2152 TcpStateMachine::SendProxyProtocol(send) => Some(&send.frontend_readiness),
2153 TcpStateMachine::RelayProxyProtocol(relay) => Some(&relay.frontend_readiness),
2154 TcpStateMachine::Pipe(pipe) => Some(&pipe.frontend_readiness),
2155 TcpStateMachine::SniPreread(preread) => Some(&preread.frontend_readiness),
2156 TcpStateMachine::FailedUpgrade(_) => None,
2157 };
2158
2159 let back_readiness = match &self.state {
2160 TcpStateMachine::SendProxyProtocol(send) => Some(&send.backend_readiness),
2161 TcpStateMachine::RelayProxyProtocol(relay) => Some(&relay.backend_readiness),
2162 TcpStateMachine::Pipe(pipe) => Some(&pipe.backend_readiness),
2163 TcpStateMachine::SniPreread(preread) => Some(&preread.backend_readiness),
2164 TcpStateMachine::ExpectProxyProtocol(_) => None,
2165 TcpStateMachine::FailedUpgrade(_) => None,
2166 };
2167
2168 error!(
2169 "\
2170{} Session ({:?})
2171\tFrontend:
2172\t\ttoken: {:?}\treadiness: {:?}
2173\tBackend:
2174\t\ttoken: {:?}\treadiness: {:?}\tstatus: {:?}\tcluster id: {:?}",
2175 log_context!(self),
2176 state,
2177 self.frontend_token,
2178 front_readiness,
2179 self.backend_token,
2180 back_readiness,
2181 self.backend_connected,
2182 self.cluster_id
2183 );
2184 error!("Metrics: {:?}", self.metrics);
2185 }
2186
2187 fn frontend_token(&self) -> Token {
2188 self.frontend_token
2189 }
2190}
2191
2192pub struct TcpListener {
2193 active: SessionIsToBeClosed,
2194 address: SocketAddr,
2195 cluster_id: Option<ClusterId>,
2196 config: TcpListenerConfig,
2197 listener: Option<MioTcpListener>,
2198 parked_listener: Option<MioTcpListener>,
2212 nodelay_backlog: bool,
2218 sni_routes: TrieNode<Vec<(AlpnMatcher, ClusterId)>>,
2225 tags: BTreeMap<String, CachedTags>,
2226 token: Token,
2227}
2228
2229impl ListenerHandler for TcpListener {
2230 fn get_addr(&self) -> &SocketAddr {
2231 &self.address
2232 }
2233
2234 fn get_tags(&self, key: &str) -> Option<&CachedTags> {
2235 self.tags.get(key)
2236 }
2237
2238 fn set_tags(&mut self, key: String, tags: Option<BTreeMap<String, String>>) {
2239 match tags {
2240 Some(tags) => self.tags.insert(key, CachedTags::new(tags)),
2241 None => self.tags.remove(&key),
2242 };
2243 }
2244
2245 fn protocol(&self) -> Protocol {
2246 Protocol::TCP
2247 }
2248
2249 fn public_address(&self) -> SocketAddr {
2250 self.config
2251 .public_address
2252 .map(|addr| addr.into())
2253 .unwrap_or(self.address)
2254 }
2255}
2256
2257impl TcpListener {
2258 pub fn key(&self) -> ListenerKey {
2261 ListenerKey::new(self.address, self.config.interface.as_deref())
2262 }
2263
2264 pub fn is(&self, key: &ListenerKey) -> bool {
2266 key.matches(&self.address, self.config.interface.as_deref())
2267 }
2268
2269 fn new(config: TcpListenerConfig, token: Token) -> Result<TcpListener, ListenerError> {
2270 Ok(TcpListener {
2271 cluster_id: None,
2272 listener: None,
2273 parked_listener: None,
2274 nodelay_backlog: false,
2275 token,
2276 address: config.address.into(),
2277 config,
2278 active: false,
2279 sni_routes: TrieNode::root(),
2280 tags: BTreeMap::new(),
2281 })
2282 }
2283
2284 pub fn validate_config(_config: &TcpListenerConfig) -> Result<(), ListenerError> {
2293 Ok(())
2294 }
2295
2296 fn preread_config(&self, effective_max_bytes: usize) -> PrereadConfig<'_> {
2303 PrereadConfig {
2304 routes: &self.sni_routes,
2305 inbound_proxy: self.config.expect_proxy,
2306 max_bytes: effective_max_bytes,
2307 timeout: Duration::from_secs(u64::from(
2308 self.config
2309 .sni_preread_timeout
2310 .unwrap_or(DEFAULT_SNI_PREREAD_TIMEOUT),
2311 )),
2312 accept_wildcard: true,
2313 }
2314 }
2315
2316 fn validate_new_tcp_front(&self, front: &RequestTcpFrontend) -> Result<(), ProxyError> {
2339 let reject = |reason: String| {
2340 Err(ProxyError::InvalidTcpFrontend {
2341 address: self.address,
2342 reason,
2343 })
2344 };
2345
2346 match &front.sni {
2347 None => {
2348 if !front.alpn.is_empty() {
2349 return reject(format!(
2350 "alpn = {:?} set without sni: alpn only matches within an SNI-scoped \
2351 preread, so a frontend without sni would silently ignore its alpn list",
2352 front.alpn
2353 ));
2354 }
2355 if !self.sni_routes.is_empty() {
2356 return reject(
2357 "a no-SNI frontend cannot be added to a listener that already has \
2358 SNI-scoped routes"
2359 .to_string(),
2360 );
2361 }
2362 }
2363 Some(sni) => {
2364 if self.cluster_id.is_some() {
2365 return reject(
2366 "an SNI-scoped frontend cannot be added to a listener that already has \
2367 a no-SNI catch-all cluster"
2368 .to_string(),
2369 );
2370 }
2371
2372 let normalized_sni = match validate_sni_pattern(sni) {
2385 Ok(normalized) => normalized,
2386 Err(config_error) => {
2387 return reject(format!(
2388 "sni {sni:?} failed SNI shape validation: {config_error}"
2389 ));
2390 }
2391 };
2392
2393 let key = normalized_sni.into_bytes();
2442 let accept_wildcard_for_self_lookup = key.starts_with(b"*.");
2443 if let Some((_, existing)) = self
2444 .sni_routes
2445 .domain_lookup(&key, accept_wildcard_for_self_lookup)
2446 {
2447 let new_is_catch_all = front.alpn.is_empty();
2448 for (matcher, _cluster_id) in existing {
2449 match matcher {
2450 AlpnMatcher::Any if new_is_catch_all => {
2451 return reject(format!(
2452 "sni {sni:?} already has a catch-all (empty alpn) \
2453 frontend: at most one frontend per (address, sni) may \
2454 omit alpn"
2455 ));
2456 }
2457 AlpnMatcher::OneOf(protocols) => {
2458 if let Some(overlap) = front
2459 .alpn
2460 .iter()
2461 .find(|protocol| protocols.contains(protocol.as_bytes()))
2462 {
2463 return reject(format!(
2464 "sni {sni:?} already has a frontend matching ALPN \
2465 protocol {overlap:?}: ALPN matchers for the same \
2466 (address, sni) must not overlap"
2467 ));
2468 }
2469 }
2470 AlpnMatcher::Any => {}
2471 }
2472 }
2473 }
2474 }
2475 }
2476
2477 Ok(())
2478 }
2479
2480 fn insert_sni_route(
2499 &mut self,
2500 sni: String,
2501 alpn: Vec<String>,
2502 cluster_id: ClusterId,
2503 ) -> Result<(), ProxyError> {
2504 let (key, matcher) = route_key_and_matcher(&sni, alpn);
2505 match self.sni_routes.domain_lookup_mut(&key, false) {
2510 Some((_, entries)) => {
2511 entries.push((matcher, cluster_id));
2512 Ok(())
2513 }
2514 None => {
2515 let insert_result = self
2516 .sni_routes
2517 .domain_insert(key, vec![(matcher, cluster_id)]);
2518 debug_assert_ne!(
2519 insert_result,
2520 InsertResult::Failed,
2521 "insert_sni_route's key must already be validated by validate_new_tcp_front"
2522 );
2523 if insert_result == InsertResult::Failed {
2524 error!(
2525 "{} SNI route insert failed for {:?} despite passing shape \
2526 validation -- rejecting to avoid a silently dead route",
2527 log_module_context!(),
2528 sni
2529 );
2530 return Err(ProxyError::InvalidTcpFrontend {
2531 address: self.address,
2532 reason: format!(
2533 "internal error: the route table rejected sni {sni:?} despite \
2534 passing shape validation"
2535 ),
2536 });
2537 }
2538 Ok(())
2539 }
2540 }
2541 }
2542
2543 fn remove_sni_route(&mut self, sni: String, alpn: Vec<String>, cluster_id: &str) {
2548 let (key, matcher) = route_key_and_matcher(&sni, alpn);
2549 if let Some((_, entries)) = self.sni_routes.domain_lookup_mut(&key, false) {
2553 entries.retain(|(m, c)| !(*m == matcher && **c == *cluster_id));
2554 if entries.is_empty() {
2555 self.sni_routes.domain_remove(&key);
2556 }
2557 }
2558 }
2559
2560 pub fn activate(
2561 &mut self,
2562 registry: &Registry,
2563 tcp_listener: Option<MioTcpListener>,
2564 ) -> Result<Token, ProxyError> {
2565 if self.active {
2566 return Ok(self.token);
2567 }
2568 let address: SocketAddr = self.config.address.into();
2569
2570 let parked = self.parked_listener.take();
2576 let mut listener = match (tcp_listener, parked) {
2577 (Some(inherited), Some(parked)) => {
2578 warn!(
2586 "{} closing the socket parked on the TCP listener for {}: an \
2587 inherited socket arrived for the same address and is the one being \
2588 activated",
2589 log_module_context!(),
2590 address
2591 );
2592 drop(parked);
2593 inherited
2594 }
2595 (Some(inherited), None) => inherited,
2596 (None, Some(parked)) => parked,
2597 (None, None) => server_bind(address, self.config.interface.as_deref())
2598 .map_err(|e| ProxyError::BindToSocket(address, e))?,
2599 };
2600
2601 if let Err(e) = SockRef::from(&listener).set_tcp_nodelay(true) {
2605 error!(
2606 "{} error setting nodelay on listen socket({:?}): {:?}",
2607 log_module_context!(),
2608 listener,
2609 e
2610 );
2611 }
2612
2613 let registration = registry
2614 .register(&mut listener, self.token, Interest::READABLE)
2615 .map_err(ProxyError::RegisterListener);
2616 if let Err(error) = registration {
2617 self.parked_listener = Some(listener);
2627 return Err(error);
2628 }
2629
2630 self.listener = Some(listener);
2631 self.nodelay_backlog = true;
2632 self.active = true;
2633 Ok(self.token)
2634 }
2635
2636 pub fn update_config(&mut self, patch: &UpdateTcpListenerConfig) -> Result<(), ListenerError> {
2640 if let Some(v) = patch.public_address {
2641 self.config.public_address = Some(v);
2642 }
2643 if let Some(v) = patch.expect_proxy {
2644 self.config.expect_proxy = v;
2645 }
2646 if let Some(v) = patch.front_timeout {
2647 self.config.front_timeout = v;
2648 }
2649 if let Some(v) = patch.back_timeout {
2650 self.config.back_timeout = v;
2651 }
2652 if let Some(v) = patch.connect_timeout {
2653 self.config.connect_timeout = v;
2654 }
2655 Ok(())
2656 }
2657}
2658
2659fn handle_connection_result(
2660 connection_result: Result<BackendConnectAction, BackendConnectionError>,
2661) -> Option<SessionResult> {
2662 match connection_result {
2663 Ok(BackendConnectAction::Reuse) => None,
2665 Ok(BackendConnectAction::New) | Ok(BackendConnectAction::Replace) => {
2666 Some(SessionResult::Continue)
2668 }
2669 Err(_) => {
2670 Some(SessionResult::Close)
2673 }
2674 }
2675}
2676
2677fn effective_sni_preread_max_bytes(configured: Option<u32>, buffer_capacity: usize) -> usize {
2692 (configured.unwrap_or(DEFAULT_SNI_PREREAD_MAX_BYTES) as usize)
2693 .min(buffer_capacity)
2694 .max(MIN_SNI_PREREAD_MAX_BYTES as usize)
2695}
2696
2697fn frontend_timeout_resets_on_readable(state: &TcpStateMachine) -> bool {
2710 !matches!(
2711 state,
2712 TcpStateMachine::SniPreread(preread) if preread.outcome().is_none()
2713 )
2714}
2715
2716fn known_alpn_label(offered: &[Vec<u8>]) -> Option<&'static str> {
2726 match offered.first().map(Vec::as_slice) {
2727 Some(b"h2") => Some("h2"),
2728 Some(b"http/1.1") => Some("http/1.1"),
2729 _ => None,
2730 }
2731}
2732
2733#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2749enum TcpFrontendTagsKey {
2750 Address(SocketAddr),
2753 Sni {
2759 address: SocketAddr,
2760 sni: String,
2761 alpn: Vec<String>,
2762 },
2763}
2764
2765impl TcpFrontendTagsKey {
2766 fn sni(address: SocketAddr, sni: &str, alpn: &[String]) -> Self {
2772 let mut alpn: Vec<String> = alpn.to_vec();
2773 alpn.sort_unstable();
2774 alpn.dedup();
2775 TcpFrontendTagsKey::Sni {
2776 address,
2777 sni: sni.to_ascii_lowercase(),
2778 alpn,
2779 }
2780 }
2781}
2782
2783impl std::fmt::Display for TcpFrontendTagsKey {
2784 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2785 match self {
2786 TcpFrontendTagsKey::Address(address) => write!(f, "{address}"),
2787 TcpFrontendTagsKey::Sni { address, sni, alpn } => {
2788 write!(f, "{address}|{sni}|")?;
2789 for protocol in alpn {
2790 write!(f, "{}:{protocol}", protocol.len())?;
2796 }
2797 Ok(())
2798 }
2799 }
2800 }
2801}
2802
2803fn sni_tags_key(address: &SocketAddr, sni: &str, alpn: &[String]) -> String {
2810 TcpFrontendTagsKey::sni(*address, sni, alpn).to_string()
2811}
2812
2813fn alpn_matcher_protocols(matcher: &AlpnMatcher) -> Vec<String> {
2819 match matcher {
2820 AlpnMatcher::Any => Vec::new(),
2821 AlpnMatcher::OneOf(set) => set
2822 .iter()
2823 .map(|protocol| String::from_utf8_lossy(protocol).into_owned())
2824 .collect(),
2825 }
2826}
2827
2828fn route_key_and_matcher(sni: &str, alpn: Vec<String>) -> (Vec<u8>, AlpnMatcher) {
2835 let key = sni.to_ascii_lowercase().into_bytes();
2836 let matcher = if alpn.is_empty() {
2837 AlpnMatcher::Any
2838 } else {
2839 AlpnMatcher::OneOf(alpn.into_iter().map(String::into_bytes).collect())
2840 };
2841 (key, matcher)
2842}
2843
2844#[derive(Debug)]
2845pub struct ClusterConfiguration {
2846 proxy_protocol: Option<ProxyProtocolConfig>,
2847 reads_affinity_key: bool,
2851 pub max_connections_per_ip: Option<u64>,
2857 pub max_connections_per_subnet: Option<u64>,
2863 pub max_connection_attempts: Option<u32>,
2868}
2869
2870pub struct TcpProxy {
2871 fronts: HashMap<ClusterId, Token>,
2872 backends: Rc<RefCell<BackendMap>>,
2873 listeners: HashMap<Token, Rc<RefCell<TcpListener>>>,
2874 configs: HashMap<ClusterId, ClusterConfiguration>,
2875 registry: Registry,
2876 sessions: Rc<RefCell<SessionManager>>,
2877 pool: Rc<RefCell<Pool>>,
2878}
2879
2880impl TcpProxy {
2881 pub fn new(
2882 registry: Registry,
2883 sessions: Rc<RefCell<SessionManager>>,
2884 pool: Rc<RefCell<Pool>>,
2885 backends: Rc<RefCell<BackendMap>>,
2886 ) -> TcpProxy {
2887 TcpProxy {
2888 backends,
2889 listeners: HashMap::new(),
2890 configs: HashMap::new(),
2891 fronts: HashMap::new(),
2892 registry,
2893 sessions,
2894 pool,
2895 }
2896 }
2897
2898 pub fn add_listener(
2899 &mut self,
2900 config: TcpListenerConfig,
2901 token: Token,
2902 ) -> Result<Token, ProxyError> {
2903 if self.listeners.contains_key(&token) {
2904 return Err(ProxyError::ListenerAlreadyPresent);
2905 }
2906 let mut tcp_listener = TcpListener::new(config, token).map_err(ProxyError::AddListener)?;
2907 if let Some(sibling) = self
2912 .listeners_at(tcp_listener.address)
2913 .ok()
2914 .and_then(|listeners| listeners.into_iter().next())
2915 {
2916 let sibling = sibling.borrow();
2917 tcp_listener.cluster_id.clone_from(&sibling.cluster_id);
2918 tcp_listener.sni_routes = sibling.sni_routes.clone();
2919 tcp_listener.tags = sibling.tags.clone();
2920 }
2921 match self.listeners.entry(token) {
2922 Entry::Vacant(entry) => {
2923 entry.insert(Rc::new(RefCell::new(tcp_listener)));
2924 Ok(token)
2925 }
2926 _ => Err(ProxyError::ListenerAlreadyPresent),
2927 }
2928 }
2929
2930 pub(crate) fn add_listener_with_retained_frontends(
2938 &mut self,
2939 config: TcpListenerConfig,
2940 token: Token,
2941 retained_frontends: Vec<RequestTcpFrontend>,
2942 ) -> Result<Token, ProxyError> {
2943 let address = config.address.into();
2944 let had_sibling = self.listeners_at(address).is_ok();
2945 self.add_listener(config, token)?;
2946
2947 if !had_sibling {
2948 for frontend in retained_frontends {
2949 if let Err(error) = self.add_tcp_front(frontend) {
2950 self.listeners.remove(&token);
2951 self.fronts
2952 .retain(|_, listener_token| *listener_token != token);
2953 return Err(error);
2954 }
2955 }
2956 }
2957
2958 Ok(token)
2959 }
2960
2961 pub fn remove_listener(&mut self, address: &ListenerKey) -> SessionIsToBeClosed {
2962 let len = self.listeners.len();
2963
2964 self.listeners.retain(|_, l| !l.borrow().is(address));
2965 self.listeners.len() < len
2966 }
2967
2968 pub fn inherited_socket_fate(&self, addr: &ListenerKey) -> crate::InheritedSocketFate {
2976 use crate::InheritedSocketFate;
2977 match self
2978 .listeners
2979 .values()
2980 .find(|listener| listener.borrow().is(addr))
2981 {
2982 None => InheritedSocketFate::Unclaimed,
2983 Some(listener) if listener.borrow().active => InheritedSocketFate::Refused,
2984 Some(_) => InheritedSocketFate::Adopted,
2985 }
2986 }
2987
2988 pub fn activate_listener(
2989 &self,
2990 addr: &ListenerKey,
2991 tcp_listener: Option<MioTcpListener>,
2992 ) -> Result<Token, ProxyError> {
2993 let listener = self
2994 .listeners
2995 .values()
2996 .find(|listener| listener.borrow().is(addr))
2997 .ok_or(ProxyError::NoListenerFound(addr.clone()))?;
2998
2999 listener.borrow_mut().activate(&self.registry, tcp_listener)
3000 }
3001
3002 pub fn listener_token(&self, address: &ListenerKey) -> Option<Token> {
3011 self.listeners
3012 .iter()
3013 .find(|(_, listener)| listener.borrow().is(address))
3014 .map(|(token, _)| *token)
3015 }
3016
3017 #[cfg(test)]
3020 pub(crate) fn listener_cluster(&self, address: &ListenerKey) -> Option<ClusterId> {
3021 let token = self.listener_token(address)?;
3022 self.listeners[&token].borrow().cluster_id.clone()
3023 }
3024
3025 pub fn give_back_listeners(&mut self) -> Vec<(ListenerKey, MioTcpListener)> {
3026 self.listeners
3027 .values()
3028 .filter_map(|listener| {
3029 let mut owned = listener.borrow_mut();
3030 if let Some(listener) = owned.listener.take() {
3031 owned.active = false;
3034 return Some((owned.key(), listener));
3035 }
3036
3037 None
3038 })
3039 .collect()
3040 }
3041
3042 pub fn give_back_listener(
3043 &mut self,
3044 address: &ListenerKey,
3045 ) -> Result<(Token, MioTcpListener), ProxyError> {
3046 let listener = self
3047 .listeners
3048 .values()
3049 .find(|listener| listener.borrow().is(address))
3050 .ok_or(ProxyError::NoListenerFound(address.clone()))?;
3051
3052 let mut owned = listener.borrow_mut();
3053
3054 let taken_listener = owned
3055 .listener
3056 .take()
3057 .ok_or(ProxyError::UnactivatedListener)?;
3058
3059 owned.active = false;
3062
3063 Ok((owned.token, taken_listener))
3064 }
3065
3066 pub fn update_listener(&mut self, patch: UpdateTcpListenerConfig) -> Result<(), ProxyError> {
3068 let address: SocketAddr = patch.address.into();
3069 let listener = self
3070 .listeners
3071 .values()
3072 .find(|l| l.borrow().address == address)
3073 .ok_or(ProxyError::NoListenerFound(address.into()))?;
3074 listener
3075 .borrow_mut()
3076 .update_config(&patch)
3077 .map_err(|listener_error| ProxyError::ListenerActivation {
3078 address,
3079 listener_error,
3080 })
3081 }
3082
3083 fn listeners_at(
3087 &self,
3088 address: SocketAddr,
3089 ) -> Result<Vec<Rc<RefCell<TcpListener>>>, ProxyError> {
3090 let mut listeners: Vec<_> = self
3091 .listeners
3092 .iter()
3093 .filter(|(_, listener)| listener.borrow().address == address)
3094 .map(|(token, listener)| (*token, listener.clone()))
3095 .collect();
3096 listeners.sort_by_key(|(token, _)| *token);
3098 let listeners: Vec<_> = listeners
3099 .into_iter()
3100 .map(|(_, listener)| listener)
3101 .collect();
3102 if listeners.is_empty() {
3103 return Err(ProxyError::NoListenerFound(address.into()));
3104 }
3105 Ok(listeners)
3106 }
3107
3108 pub fn add_tcp_front(&mut self, front: RequestTcpFrontend) -> Result<(), ProxyError> {
3109 let address = front.address.into();
3110 let listeners = self.listeners_at(address)?;
3111
3112 for listener in &listeners {
3121 listener.borrow().validate_new_tcp_front(&front)?;
3122 }
3123
3124 let cluster_id = ClusterId::from(front.cluster_id);
3125 for listener in listeners {
3126 let mut listener = listener.borrow_mut();
3127 self.fronts.insert(cluster_id.clone(), listener.token);
3128
3129 match &front.sni {
3130 Some(sni) => {
3131 listener.set_tags(
3135 sni_tags_key(&address, sni, &front.alpn),
3136 Some(front.tags.clone()),
3137 );
3138 listener.insert_sni_route(
3139 sni.to_owned(),
3140 front.alpn.clone(),
3141 cluster_id.clone(),
3142 )?;
3143 }
3144 None => {
3145 listener.set_tags(
3146 TcpFrontendTagsKey::Address(address).to_string(),
3147 Some(front.tags.clone()),
3148 );
3149 listener.cluster_id = Some(cluster_id.clone());
3150 }
3151 }
3152
3153 debug_assert!(
3157 listener.cluster_id.is_none() || listener.sni_routes.is_empty(),
3158 "a TCP listener must never mix a no-SNI catch-all cluster with SNI-scoped routes"
3159 );
3160 }
3161
3162 Ok(())
3163 }
3164
3165 pub fn remove_tcp_front(&mut self, front: RequestTcpFrontend) -> Result<(), ProxyError> {
3166 let address = front.address.into();
3167
3168 for listener in self.listeners_at(address)? {
3169 let mut listener = listener.borrow_mut();
3170 match &front.sni {
3171 Some(sni) => {
3172 listener.set_tags(sni_tags_key(&address, sni, &front.alpn), None);
3176 listener.remove_sni_route(
3177 sni.to_owned(),
3178 front.alpn.clone(),
3179 &front.cluster_id,
3180 );
3181 self.fronts.remove(front.cluster_id.as_str());
3182 }
3183 None => {
3184 listener.set_tags(TcpFrontendTagsKey::Address(address).to_string(), None);
3185 if let Some(cluster_id) = listener.cluster_id.take() {
3186 self.fronts.remove(&cluster_id);
3187 }
3188 }
3189 }
3190 }
3191
3192 Ok(())
3193 }
3194}
3195
3196impl ProxyConfiguration for TcpProxy {
3197 fn notify(&mut self, message: WorkerRequest) -> WorkerResponse {
3198 let request_type = match message.content.request_type {
3199 Some(t) => t,
3200 None => return WorkerResponse::error(message.id, "Empty request"),
3201 };
3202 match request_type {
3203 RequestType::AddTcpFrontend(front) => {
3204 if let Err(err) = self.add_tcp_front(front) {
3205 return WorkerResponse::error(message.id, err);
3206 }
3207
3208 WorkerResponse::ok(message.id)
3209 }
3210 RequestType::RemoveTcpFrontend(front) => {
3211 if let Err(err) = self.remove_tcp_front(front) {
3212 return WorkerResponse::error(message.id, err);
3213 }
3214
3215 WorkerResponse::ok(message.id)
3216 }
3217 RequestType::SoftStop(_) => {
3218 info!(
3219 "{} {} processing soft shutdown",
3220 log_module_context!(),
3221 message.id
3222 );
3223 let listeners = std::mem::take(&mut self.listeners);
3224 for l in listeners.values() {
3225 l.borrow_mut()
3226 .listener
3227 .take()
3228 .map(|mut sock| self.registry.deregister(&mut sock));
3229 }
3230 WorkerResponse::processing(message.id)
3231 }
3232 RequestType::HardStop(_) => {
3233 info!("{} {} hard shutdown", log_module_context!(), message.id);
3234 for (_, l) in std::mem::take(&mut self.listeners) {
3235 l.borrow_mut()
3236 .listener
3237 .take()
3238 .map(|mut sock| self.registry.deregister(&mut sock));
3239 }
3240 WorkerResponse::ok(message.id)
3241 }
3242 RequestType::Status(_) => {
3243 info!("{} {} status", log_module_context!(), message.id);
3244 WorkerResponse::ok(message.id)
3245 }
3246 RequestType::AddCluster(cluster) => {
3247 let config = ClusterConfiguration {
3248 proxy_protocol: cluster
3249 .proxy_protocol
3250 .and_then(|n| ProxyProtocolConfig::try_from(n).ok()),
3251 reads_affinity_key: cluster_reads_affinity_key(&cluster),
3252 max_connections_per_ip: cluster.max_connections_per_ip,
3253 max_connections_per_subnet: cluster.max_connections_per_subnet,
3254 max_connection_attempts: cluster.max_connection_attempts,
3255 };
3256 self.configs.insert(cluster.cluster_id.into(), config);
3257 WorkerResponse::ok(message.id)
3258 }
3259 RequestType::RemoveCluster(cluster_id) => {
3260 self.configs.remove(cluster_id.as_str());
3261 WorkerResponse::ok(message.id)
3262 }
3263 RequestType::RemoveListener(remove) => {
3264 if !self.remove_listener(&remove.listener_key()) {
3265 WorkerResponse::error(
3266 message.id,
3267 format!("no TCP listener to remove at address {:?}", remove.address),
3268 )
3269 } else {
3270 WorkerResponse::ok(message.id)
3271 }
3272 }
3273 command => {
3274 debug!(
3275 "{} {} unsupported message for TCP proxy, ignoring {:?}",
3276 log_module_context!(),
3277 message.id,
3278 command
3279 );
3280 WorkerResponse::error(message.id, "unsupported message")
3281 }
3282 }
3283 }
3284
3285 fn accept(&mut self, token: ListenToken) -> Result<(MioTcpStream, SocketAddr), AcceptError> {
3286 let internal_token = Token(token.0);
3287 if let Some(listener) = self.listeners.get(&internal_token) {
3288 let mut listener = listener.borrow_mut();
3289 let accepted = match &listener.listener {
3290 Some(tcp_listener) => tcp_listener.accept(),
3291 None => return Err(AcceptError::IoError),
3292 };
3293 match accepted {
3294 Ok((frontend_sock, peer)) => {
3295 if listener.nodelay_backlog
3296 && let Err(e) = frontend_sock.set_nodelay(true)
3297 {
3298 error!(
3299 "{} error setting nodelay on front socket({:?}): {:?}",
3300 log_module_context!(),
3301 frontend_sock,
3302 e
3303 );
3304 }
3305 Ok((frontend_sock, peer))
3306 }
3307 Err(e) if e.kind() == ErrorKind::WouldBlock => {
3308 listener.nodelay_backlog = false;
3310 Err(AcceptError::WouldBlock)
3311 }
3312 Err(e) => {
3313 error!("{} accept() IO error: {:?}", log_module_context!(), e);
3314 Err(AcceptError::IoError)
3315 }
3316 }
3317 } else {
3318 Err(AcceptError::IoError)
3319 }
3320 }
3321
3322 fn create_session(
3323 &mut self,
3324 mut frontend_sock: MioTcpStream,
3325 peer: SocketAddr,
3326 token: ListenToken,
3327 wait_time: Duration,
3328 proxy: Rc<RefCell<Self>>,
3329 ) -> Result<(), AcceptError> {
3330 let listener_token = Token(token.0);
3331
3332 let listener = self
3333 .listeners
3334 .get(&listener_token)
3335 .ok_or(AcceptError::IoError)?;
3336
3337 let owned = listener.borrow();
3338 let mut pool = self.pool.borrow_mut();
3339
3340 let (front_buffer, back_buffer) = match (pool.checkout(), pool.checkout()) {
3341 (Some(fb), Some(bb)) => (fb, bb),
3342 _ => {
3343 error!("{} could not get buffers from pool", log_module_context!());
3344 error!(
3345 "{} Buffer capacity has been reached, stopping to accept new connections for now",
3346 log_module_context!()
3347 );
3348 gauge!(names::accept_queue::BACKPRESSURE, 1);
3349 self.sessions.borrow_mut().can_accept = false;
3350
3351 return Err(AcceptError::BufferCapacityReached);
3352 }
3353 };
3354
3355 if owned.cluster_id.is_none() && owned.sni_routes.is_empty() {
3359 error!(
3360 "{} listener at address {:?} has no linked cluster",
3361 log_module_context!(),
3362 owned.address
3363 );
3364 return Err(AcceptError::IoError);
3365 }
3366
3367 let mut session_manager = self.sessions.borrow_mut();
3372 let entry = session_manager.slab.vacant_entry();
3373 let frontend_token = Token(entry.key());
3374
3375 if let Err(register_error) = self.registry.register(
3376 &mut frontend_sock,
3377 frontend_token,
3378 Interest::READABLE | Interest::WRITABLE,
3379 ) {
3380 error!(
3381 "{} error registering front socket({:?}): {:?}",
3382 log_module_context!(),
3383 frontend_sock,
3384 register_error
3385 );
3386 return Err(AcceptError::RegisterError);
3387 }
3388
3389 let session = if !owned.sni_routes.is_empty() {
3390 let effective_max_bytes = effective_sni_preread_max_bytes(
3394 owned.config.sni_preread_max_bytes,
3395 front_buffer.capacity(),
3396 );
3397 let preread_timeout = Duration::from_secs(u64::from(
3398 owned
3399 .config
3400 .sni_preread_timeout
3401 .unwrap_or(DEFAULT_SNI_PREREAD_TIMEOUT),
3402 ));
3403 TcpSession::new_sni_preread(
3404 back_buffer,
3405 Duration::from_secs(owned.config.back_timeout as u64),
3406 Duration::from_secs(owned.config.connect_timeout as u64),
3407 front_buffer,
3408 frontend_token,
3409 listener.clone(),
3410 proxy,
3411 frontend_sock,
3412 peer,
3413 wait_time,
3414 preread_timeout,
3415 effective_max_bytes,
3416 )
3417 } else {
3418 let proxy_protocol = self
3419 .configs
3420 .get(owned.cluster_id.as_ref().unwrap())
3421 .and_then(|c| c.proxy_protocol);
3422 TcpSession::new(
3423 back_buffer,
3424 None,
3425 owned.cluster_id.clone(),
3426 Duration::from_secs(owned.config.back_timeout as u64),
3427 Duration::from_secs(owned.config.connect_timeout as u64),
3428 Duration::from_secs(owned.config.front_timeout as u64),
3429 front_buffer,
3430 frontend_token,
3431 listener.clone(),
3432 proxy_protocol,
3433 proxy,
3434 frontend_sock,
3435 peer,
3436 wait_time,
3437 )
3438 };
3439 incr!(names::tcp::REQUESTS);
3440
3441 let session = Rc::new(RefCell::new(session));
3442 entry.insert(session);
3443
3444 Ok(())
3445 }
3446}
3447
3448pub mod testing {
3449 use crate::testing::*;
3450
3451 pub fn start_tcp_worker(
3453 config: TcpListenerConfig,
3454 max_buffers: usize,
3455 buffer_size: usize,
3456 channel: ProxyChannel,
3457 ) -> anyhow::Result<()> {
3458 let address = config.address.into();
3459
3460 let ServerParts {
3461 event_loop,
3462 registry,
3463 sessions,
3464 pool,
3465 backends,
3466 client_scm_socket: _,
3467 server_scm_socket,
3468 server_config,
3469 } = prebuild_server(max_buffers, buffer_size, true)?;
3470
3471 let token = {
3472 let mut sessions = sessions.borrow_mut();
3473 let entry = sessions.slab.vacant_entry();
3474 let key = entry.key();
3475 let _ = entry.insert(Rc::new(RefCell::new(ListenSession {
3476 protocol: Protocol::TCPListen,
3477 })));
3478 Token(key)
3479 };
3480
3481 let mut proxy = TcpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
3482 proxy
3483 .add_listener(config, token)
3484 .with_context(|| "Failed at creating adding the listener")?;
3485 proxy
3486 .activate_listener(&address, None)
3487 .with_context(|| "Failed at creating activating the listener")?;
3488
3489 let mut server = Server::new(
3490 event_loop,
3491 channel,
3492 server_scm_socket,
3493 sessions,
3494 pool,
3495 backends,
3496 None,
3497 None,
3498 Some(proxy),
3499 server_config,
3500 None,
3501 false,
3502 )
3503 .with_context(|| "Failed at creating server")?;
3504
3505 debug!("{} starting event loop", log_module_context!());
3506 server.run();
3507 debug!("{} ending event loop", log_module_context!());
3508 Ok(())
3509 }
3510}
3511
3512#[cfg(test)]
3513mod tests {
3514 use std::{
3515 io::{Read, Write},
3516 net::{Shutdown, TcpListener, TcpStream},
3517 str,
3518 sync::{
3519 Arc, Barrier,
3520 atomic::{AtomicBool, Ordering},
3521 },
3522 thread,
3523 time::Duration,
3524 };
3525
3526 use sozu_command::{
3527 channel::Channel,
3528 config::ListenerBuilder,
3529 proto::command::{
3530 LoadBalancingParams, RequestTcpFrontend, SocketAddress, SoftStop, WorkerRequest,
3531 WorkerResponse, request::RequestType,
3532 },
3533 };
3534
3535 use super::testing::start_tcp_worker;
3536 use crate::testing::*;
3537
3538 #[test]
3552 fn round_trip() {
3553 setup_test_logger!();
3554 let barrier = Arc::new(Barrier::new(2));
3555 let test_finished = Arc::new(AtomicBool::new(false));
3556
3557 let front_port1 = provide_port();
3558 let front_port2 = provide_port();
3559
3560 let backend_port = start_server(barrier.clone(), test_finished.clone());
3561 let mut command =
3562 start_proxy(backend_port, front_port1, front_port2).expect("Could not start proxy");
3563 barrier.wait();
3564
3565 thread::scope(|_s| {
3566 let front_addr = format!("127.0.0.1:{front_port1}");
3567
3568 let mut s1 = TcpStream::connect(&front_addr).expect("could not connect");
3569 s1.set_read_timeout(Some(Duration::from_secs(5)))
3570 .expect("could not set read timeout on s1");
3571
3572 let s3 = TcpStream::connect(&front_addr).expect("could not connect");
3573
3574 let mut s2 = TcpStream::connect(&front_addr).expect("could not connect");
3575 s2.set_read_timeout(Some(Duration::from_secs(5)))
3576 .expect("could not set read timeout on s2");
3577
3578 s1.write_all(b"hello ").expect("could not write to s1");
3579 println!("s1 sent");
3580
3581 s2.write_all(b"pouet pouet").expect("could not write to s2");
3582 println!("s2 sent");
3583
3584 let mut res = [0; 128];
3585 s1.write_all(b"coucou").expect("could not write to s1");
3586
3587 s3.shutdown(Shutdown::Both).expect("could not shutdown s3");
3588
3589 let sz2 = s2
3590 .read(&mut res[..])
3591 .expect("could not read from socket s2");
3592 println!("s2 received {:?}", str::from_utf8(&res[..sz2]));
3593 assert_eq!(&res[..sz2], &b"pouet pouet"[..]);
3594
3595 let expected = b"hello coucou";
3600 let mut total = 0;
3601 while total < expected.len() {
3602 let sz = s1
3603 .read(&mut res[total..])
3604 .expect("could not read from socket s1");
3605 assert!(sz > 0, "connection closed before receiving all data");
3606 total += sz;
3607 }
3608 println!(
3609 "s1 received again({}): {:?}",
3610 total,
3611 str::from_utf8(&res[..total])
3612 );
3613 assert_eq!(&res[..total], &expected[..]);
3614
3615 test_finished.store(true, Ordering::Relaxed);
3617
3618 command
3620 .write_message(&WorkerRequest {
3621 id: "ID_SOFTSTOP".to_owned(),
3622 content: RequestType::SoftStop(SoftStop {}).into(),
3623 })
3624 .expect("could not send SoftStop to sozu worker");
3625 });
3626 }
3627
3628 fn start_server(barrier: Arc<Barrier>, test_finished: Arc<AtomicBool>) -> u16 {
3631 let listener =
3632 TcpListener::bind("127.0.0.1:0").expect("could not bind echo server listener");
3633 let port = listener
3634 .local_addr()
3635 .expect("could not get echo server local address")
3636 .port();
3637
3638 listener
3639 .set_nonblocking(true)
3640 .expect("could not set echo server listener to non-blocking");
3641
3642 thread::spawn(move || {
3643 barrier.wait();
3644 let mut count: u8 = 0;
3645 loop {
3646 match listener.accept() {
3647 Ok((mut stream, _)) => {
3648 let finished = test_finished.clone();
3649 thread::spawn(move || {
3650 println!("got a new client: {count}");
3651 stream
3652 .set_read_timeout(Some(Duration::from_secs(2)))
3653 .expect("could not set read timeout on echo client");
3654 let mut buf = [0; 128];
3655 loop {
3656 match stream.read(&mut buf[..]) {
3657 Ok(0) => break,
3658 Ok(sz) => {
3659 println!(
3660 "ECHO[{count}] got \"{:?}\"",
3661 str::from_utf8(&buf[..sz])
3662 );
3663 stream
3664 .write_all(&buf[..sz])
3665 .expect("could not echo data back");
3666 }
3667 Err(ref e)
3668 if e.kind() == std::io::ErrorKind::WouldBlock
3669 || e.kind() == std::io::ErrorKind::TimedOut =>
3670 {
3671 if finished.load(Ordering::Relaxed) {
3672 println!("backend server stopping (client handler)");
3673 break;
3674 }
3675 }
3676 Err(_) => break,
3677 }
3678 }
3679 });
3680 count = count.wrapping_add(1);
3681 }
3682 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
3683 if test_finished.load(Ordering::Relaxed) {
3684 println!("backend server stopping (accept loop)");
3685 break;
3686 }
3687 thread::sleep(Duration::from_millis(50));
3688 }
3689 Err(e) => {
3690 println!("connection failed: {e:?}");
3691 }
3692 }
3693 }
3694 });
3695
3696 port
3697 }
3698
3699 fn start_proxy(
3701 backend_port: u16,
3702 front_port1: u16,
3703 front_port2: u16,
3704 ) -> anyhow::Result<Channel<WorkerRequest, WorkerResponse>> {
3705 let config = ListenerBuilder::new_tcp(SocketAddress::new_v4(127, 0, 0, 1, front_port1))
3706 .to_tcp(None)
3707 .expect("could not create listener config");
3708
3709 let (mut command, channel) =
3710 Channel::generate(1000, 10000).with_context(|| "should create a channel")?;
3711 let _jg = thread::spawn(move || {
3712 setup_test_logger!();
3713 start_tcp_worker(config, 100, 16384, channel).expect("could not start the tcp server");
3714 });
3715
3716 command
3717 .blocking()
3718 .expect("could not set command channel to blocking");
3719 {
3720 let front = RequestTcpFrontend {
3721 cluster_id: "yolo".to_owned(),
3722 address: SocketAddress::new_v4(127, 0, 0, 1, front_port1),
3723 ..Default::default()
3724 };
3725 let backend = sozu_command_lib::response::Backend {
3726 cluster_id: "yolo".into(),
3727 backend_id: "yolo-0".to_owned(),
3728 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
3729 load_balancing_parameters: Some(LoadBalancingParams::default()),
3730 sticky_id: None,
3731 backup: None,
3732 unix_path: None,
3733 };
3734
3735 command
3737 .write_message(&WorkerRequest {
3738 id: "ID_YOLO0".to_owned(),
3739 content: RequestType::AddCluster(sozu_command::proto::command::Cluster {
3740 cluster_id: "yolo".to_owned(),
3741 ..Default::default()
3742 })
3743 .into(),
3744 })
3745 .expect("could not send AddCluster");
3746 command
3747 .write_message(&WorkerRequest {
3748 id: "ID_YOLO1".to_owned(),
3749 content: RequestType::AddTcpFrontend(front).into(),
3750 })
3751 .expect("could not send AddTcpFrontend for front1");
3752 command
3753 .write_message(&WorkerRequest {
3754 id: "ID_YOLO2".to_owned(),
3755 content: RequestType::AddBackend(backend.to_add_backend()).into(),
3756 })
3757 .expect("could not send AddBackend for front1");
3758 }
3759 {
3760 let front = RequestTcpFrontend {
3761 cluster_id: "yolo".to_owned(),
3762 address: SocketAddress::new_v4(127, 0, 0, 1, front_port2),
3763 ..Default::default()
3764 };
3765 let backend = sozu_command::response::Backend {
3766 cluster_id: "yolo".into(),
3767 backend_id: "yolo-0".to_owned(),
3768 address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
3769 load_balancing_parameters: Some(LoadBalancingParams::default()),
3770 sticky_id: None,
3771 backup: None,
3772 unix_path: None,
3773 };
3774 command
3775 .write_message(&WorkerRequest {
3776 id: "ID_YOLO3".to_owned(),
3777 content: RequestType::AddTcpFrontend(front).into(),
3778 })
3779 .expect("could not send AddTcpFrontend for front2");
3780 command
3781 .write_message(&WorkerRequest {
3782 id: "ID_YOLO4".to_owned(),
3783 content: RequestType::AddBackend(backend.to_add_backend()).into(),
3784 })
3785 .expect("could not send AddBackend for front2");
3786 }
3787
3788 for _ in 0..5 {
3789 println!(
3790 "read_message: {:?}",
3791 command
3792 .read_message()
3793 .with_context(|| "could not read message")?
3794 );
3795 }
3796
3797 Ok(command)
3798 }
3799}
3800
3801#[cfg(test)]
3810mod sni_routing_tests {
3811 use sozu_command::{config::ListenerBuilder, proto::command::SocketAddress};
3812
3813 use super::*;
3814 use crate::testing::{ServerParts, prebuild_server, provide_port};
3815
3816 fn test_listener() -> TcpListener {
3817 let config = ListenerBuilder::new_tcp(SocketAddress::new_v4(127, 0, 0, 1, provide_port()))
3818 .to_tcp(None)
3819 .expect("could not build a TcpListenerConfig for the test");
3820 TcpListener::new(config, Token(0)).expect("could not build a bare TcpListener for the test")
3821 }
3822
3823 fn frontend(cluster_id: &str, sni: Option<&str>, alpn: &[&str]) -> RequestTcpFrontend {
3824 RequestTcpFrontend {
3825 cluster_id: cluster_id.to_owned(),
3826 address: SocketAddress::new_v4(127, 0, 0, 1, provide_port()),
3827 sni: sni.map(str::to_owned),
3828 alpn: alpn.iter().map(|p| p.to_string()).collect(),
3829 ..Default::default()
3830 }
3831 }
3832
3833 #[test]
3836 fn effective_max_bytes_falls_back_to_default_when_unconfigured() {
3837 assert_eq!(
3838 effective_sni_preread_max_bytes(None, 65536),
3839 DEFAULT_SNI_PREREAD_MAX_BYTES as usize
3840 );
3841 }
3842
3843 #[test]
3844 fn effective_max_bytes_is_the_min_of_knob_and_capacity() {
3845 assert_eq!(effective_sni_preread_max_bytes(Some(8192), 16384), 8192);
3846 assert_eq!(effective_sni_preread_max_bytes(Some(32768), 16384), 16384);
3847 assert_eq!(effective_sni_preread_max_bytes(Some(16384), 16384), 16384);
3848 }
3849
3850 #[test]
3851 fn effective_max_bytes_never_below_the_floor() {
3852 assert_eq!(
3860 effective_sni_preread_max_bytes(Some(0), 16384),
3861 MIN_SNI_PREREAD_MAX_BYTES as usize,
3862 "a 0 knob must degrade to the floor, never 0 (would spin the preread)"
3863 );
3864 assert_eq!(
3865 effective_sni_preread_max_bytes(Some(3), 16384),
3866 MIN_SNI_PREREAD_MAX_BYTES as usize,
3867 "any sub-floor knob must be raised to the floor"
3868 );
3869 assert_eq!(
3871 effective_sni_preread_max_bytes(Some(MIN_SNI_PREREAD_MAX_BYTES), 16384),
3872 MIN_SNI_PREREAD_MAX_BYTES as usize
3873 );
3874 assert_eq!(
3875 effective_sni_preread_max_bytes(Some(MIN_SNI_PREREAD_MAX_BYTES + 1), 16384),
3876 (MIN_SNI_PREREAD_MAX_BYTES + 1) as usize
3877 );
3878 }
3879
3880 #[test]
3883 fn known_alpn_label_picks_the_clients_first_offer() {
3884 assert_eq!(
3885 known_alpn_label(&[b"h2".to_vec(), b"http/1.1".to_vec()]),
3886 Some("h2")
3887 );
3888 assert_eq!(
3889 known_alpn_label(&[b"http/1.1".to_vec(), b"h2".to_vec()]),
3890 Some("http/1.1"),
3891 "client preference order must win, not a fixed h2-first priority"
3892 );
3893 }
3894
3895 #[test]
3896 fn known_alpn_label_is_none_for_empty_or_unrecognized_offers() {
3897 assert_eq!(known_alpn_label(&[]), None);
3898 assert_eq!(known_alpn_label(&[b"spdy/1".to_vec()]), None);
3899 }
3900
3901 #[test]
3904 fn empty_alpn_maps_to_any_non_empty_maps_to_one_of() {
3905 let mut listener = test_listener();
3906 listener
3907 .insert_sni_route("example.com".to_owned(), vec![], "cluster-any".into())
3908 .expect("insert_sni_route must succeed for a valid test SNI");
3909 listener
3910 .insert_sni_route(
3911 "h2.example.com".to_owned(),
3912 vec!["h2".to_owned(), "http/1.1".to_owned()],
3913 "cluster-h2".into(),
3914 )
3915 .expect("insert_sni_route must succeed for a valid test SNI");
3916
3917 let (_, any_entries) = listener
3918 .sni_routes
3919 .domain_lookup(b"example.com", true)
3920 .expect("example.com must be routable");
3921 assert_eq!(any_entries, &vec![(AlpnMatcher::Any, "cluster-any".into())]);
3922
3923 let (_, h2_entries) = listener
3924 .sni_routes
3925 .domain_lookup(b"h2.example.com", true)
3926 .expect("h2.example.com must be routable");
3927 assert_eq!(
3928 h2_entries,
3929 &vec![(
3930 AlpnMatcher::OneOf([b"h2".to_vec(), b"http/1.1".to_vec()].into_iter().collect()),
3931 "cluster-h2".into()
3932 )]
3933 );
3934 }
3935
3936 #[test]
3937 fn insert_sni_route_appends_under_the_same_sni() {
3938 let mut listener = test_listener();
3939 listener
3940 .insert_sni_route(
3941 "example.com".to_owned(),
3942 vec!["h2".to_owned()],
3943 "cluster-h2".into(),
3944 )
3945 .expect("insert_sni_route must succeed for a valid test SNI");
3946 listener
3947 .insert_sni_route("example.com".to_owned(), vec![], "cluster-default".into())
3948 .expect("insert_sni_route must succeed for a valid test SNI");
3949
3950 let (_, entries) = listener
3951 .sni_routes
3952 .domain_lookup(b"example.com", true)
3953 .expect("example.com must be routable");
3954 assert_eq!(entries.len(), 2, "both fronts must share the SNI's Vec");
3955 }
3956
3957 #[test]
3958 fn remove_sni_route_drops_only_the_matching_entry() {
3959 let mut listener = test_listener();
3960 listener
3961 .insert_sni_route(
3962 "example.com".to_owned(),
3963 vec!["h2".to_owned()],
3964 "cluster-h2".into(),
3965 )
3966 .expect("insert_sni_route must succeed for a valid test SNI");
3967 listener
3968 .insert_sni_route("example.com".to_owned(), vec![], "cluster-default".into())
3969 .expect("insert_sni_route must succeed for a valid test SNI");
3970
3971 listener.remove_sni_route(
3972 "example.com".to_owned(),
3973 vec!["h2".to_owned()],
3974 "cluster-h2",
3975 );
3976
3977 let (_, entries) = listener
3978 .sni_routes
3979 .domain_lookup(b"example.com", true)
3980 .expect("example.com must still be routable via the remaining entry");
3981 assert_eq!(
3982 entries,
3983 &vec![(AlpnMatcher::Any, "cluster-default".into())],
3984 "removing one entry must not disturb the other"
3985 );
3986 }
3987
3988 #[test]
3989 fn remove_sni_route_empties_the_trie_key_when_the_last_entry_goes() {
3990 let mut listener = test_listener();
3991 listener
3992 .insert_sni_route("example.com".to_owned(), vec![], "cluster-a".into())
3993 .expect("insert_sni_route must succeed for a valid test SNI");
3994 assert!(!listener.sni_routes.is_empty());
3995
3996 listener.remove_sni_route("example.com".to_owned(), vec![], "cluster-a");
3997
3998 assert!(
3999 listener.sni_routes.is_empty(),
4000 "domain_remove must run once the SNI's Vec empties, leaving no stranded key"
4001 );
4002 assert!(
4003 listener
4004 .sni_routes
4005 .domain_lookup(b"example.com", true)
4006 .is_none()
4007 );
4008 }
4009
4010 #[test]
4011 fn remove_sni_route_on_an_absent_sni_is_a_harmless_no_op() {
4012 let mut listener = test_listener();
4013 listener
4014 .insert_sni_route("example.com".to_owned(), vec![], "cluster-a".into())
4015 .expect("insert_sni_route must succeed for a valid test SNI");
4016
4017 listener.remove_sni_route("other.example.net".to_owned(), vec![], "cluster-a");
4020
4021 assert!(
4022 listener
4023 .sni_routes
4024 .domain_lookup(b"example.com", true)
4025 .is_some()
4026 );
4027 }
4028
4029 #[test]
4033 fn insert_sni_route_creates_a_distinct_node_for_an_exact_key_over_a_sibling_wildcard() {
4034 let mut listener = test_listener();
4035 listener
4037 .insert_sni_route(
4038 "*.example.com".to_owned(),
4039 vec![],
4040 "cluster-wildcard".into(),
4041 )
4042 .expect("insert_sni_route must succeed for a valid test SNI");
4043 listener
4045 .insert_sni_route(
4046 "a.example.com".to_owned(),
4047 vec!["h2".to_owned()],
4048 "cluster-a-h2".into(),
4049 )
4050 .expect("insert_sni_route must succeed for a valid test SNI");
4051
4052 let (_, a_entries) = listener
4057 .sni_routes
4058 .domain_lookup(b"a.example.com", false)
4059 .expect("a.example.com must have a distinct exact-key node");
4060 assert_eq!(
4061 a_entries,
4062 &vec![(
4063 AlpnMatcher::OneOf([b"h2".to_vec()].into_iter().collect()),
4064 "cluster-a-h2".into()
4065 )],
4066 "the exact key's own Vec must hold only its own entry, not the wildcard's"
4067 );
4068
4069 let (_, b_entries) = listener
4073 .sni_routes
4074 .domain_lookup(b"b.example.com", true)
4075 .expect("b.example.com must fall back to the wildcard catch-all");
4076 assert_eq!(
4077 b_entries,
4078 &vec![(AlpnMatcher::Any, "cluster-wildcard".into())],
4079 "an unrelated subdomain must see ONLY the wildcard's entry"
4080 );
4081 }
4082
4083 #[test]
4084 fn validate_new_tcp_front_accepts_an_exact_catch_all_sibling_of_a_wildcard_catch_all() {
4085 let mut listener = test_listener();
4086 listener
4087 .insert_sni_route(
4088 "*.example.com".to_owned(),
4089 vec![],
4090 "cluster-wildcard".into(),
4091 )
4092 .expect("insert_sni_route must succeed for a valid test SNI");
4093
4094 let front = frontend("cluster-a", Some("a.example.com"), &[]);
4100 assert!(
4101 listener.validate_new_tcp_front(&front).is_ok(),
4102 "an exact catch-all must be accepted when only a SIBLING wildcard has a catch-all"
4103 );
4104 }
4105
4106 #[test]
4107 fn validate_new_tcp_front_rejects_a_duplicate_wildcard_catch_all() {
4108 let mut listener = test_listener();
4109 listener
4110 .insert_sni_route(
4111 "*.example.com".to_owned(),
4112 vec![],
4113 "cluster-wildcard".into(),
4114 )
4115 .expect("insert_sni_route must succeed for a valid test SNI");
4116
4117 let front = frontend("cluster-dup", Some("*.example.com"), &[]);
4125 assert!(
4126 listener.validate_new_tcp_front(&front).is_err(),
4127 "a second catch-all on the same wildcard SNI must be rejected as a duplicate"
4128 );
4129 }
4130
4131 #[test]
4132 fn validate_new_tcp_front_rejects_overlapping_alpn_on_the_same_wildcard() {
4133 let mut listener = test_listener();
4134 listener
4135 .insert_sni_route(
4136 "*.example.com".to_owned(),
4137 vec!["h2".to_owned()],
4138 "cluster-wildcard-h2".into(),
4139 )
4140 .expect("insert_sni_route must succeed for a valid test SNI");
4141
4142 let front = frontend("cluster-dup", Some("*.example.com"), &["h2"]);
4145 assert!(
4146 listener.validate_new_tcp_front(&front).is_err(),
4147 "an overlapping ALPN matcher on the same wildcard SNI must be rejected"
4148 );
4149 }
4150
4151 #[test]
4152 fn validate_new_tcp_front_accepts_a_disjoint_alpn_addition_on_the_same_wildcard() {
4153 let mut listener = test_listener();
4154 listener
4155 .insert_sni_route(
4156 "*.example.com".to_owned(),
4157 vec![],
4158 "cluster-wildcard".into(),
4159 )
4160 .expect("insert_sni_route must succeed for a valid test SNI");
4161
4162 let front = frontend("cluster-h2", Some("*.example.com"), &["h2"]);
4166 assert!(
4167 listener.validate_new_tcp_front(&front).is_ok(),
4168 "a disjoint ALPN addition on the same wildcard SNI must be accepted"
4169 );
4170 }
4171
4172 #[test]
4178 fn validate_new_tcp_front_rejects_malformed_sni_shapes() {
4179 let listener = test_listener();
4180
4181 for bad in [
4182 "/[a-z]+/.example.com",
4183 "foo/bar.example.com",
4184 "a.*.example.com",
4185 "*.*.example.com",
4186 "*",
4187 ] {
4188 let front = frontend("cluster-a", Some(bad), &[]);
4189 assert!(
4190 listener.validate_new_tcp_front(&front).is_err(),
4191 "malformed SNI shape {bad:?} must be rejected at the worker boundary"
4192 );
4193 }
4194
4195 for good in ["a.example.com", "*.example.com"] {
4197 let front = frontend("cluster-a", Some(good), &[]);
4198 assert!(
4199 listener.validate_new_tcp_front(&front).is_ok(),
4200 "legal SNI shape {good:?} must still be accepted"
4201 );
4202 }
4203 }
4204
4205 #[test]
4216 fn add_tcp_front_enforces_the_shared_sni_validator_at_the_worker_boundary() {
4217 let mut proxy = test_proxy();
4218 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4219 let config = ListenerBuilder::new_tcp(address)
4220 .to_tcp(None)
4221 .expect("could not build listener config");
4222 let token = Token(0);
4223 proxy
4224 .add_listener(config, token)
4225 .expect("could not add listener");
4226
4227 for bad in [
4228 ".example.com", "example.com.", "a..b.example.com", "", "exämple.com", ] {
4234 let front = frontend("cluster-a", Some(bad), &[]);
4235 let front = RequestTcpFrontend { address, ..front };
4236 match proxy.add_tcp_front(front) {
4237 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
4238 other => panic!("malformed sni {bad:?} must be rejected, got {other:?}"),
4239 }
4240
4241 let listener = proxy
4242 .listeners
4243 .get(&token)
4244 .expect("listener must be present")
4245 .borrow();
4246 assert!(
4247 listener.sni_routes.is_empty(),
4248 "a rejected sni {bad:?} must leave sni_routes empty"
4249 );
4250 assert!(
4251 listener.cluster_id.is_none(),
4252 "a rejected sni {bad:?} must leave cluster_id unset"
4253 );
4254 }
4255
4256 let space_front = frontend("cluster-space", Some("exa mple.com"), &[]);
4264 let space_front = RequestTcpFrontend {
4265 address,
4266 ..space_front
4267 };
4268 assert!(
4269 proxy.add_tcp_front(space_front).is_ok(),
4270 "the shared SNI validator does not reject an embedded space -- \
4271 it is valid ASCII with no empty label"
4272 );
4273 }
4274
4275 #[test]
4278 fn sni_fronts_keep_distinct_tags_under_distinct_keys() {
4279 let mut proxy = test_proxy();
4280 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4281 let config = ListenerBuilder::new_tcp(address)
4282 .to_tcp(None)
4283 .expect("could not build listener config");
4284 let token = Token(0);
4285 proxy
4286 .add_listener(config, token)
4287 .expect("could not add listener");
4288
4289 let front_a = RequestTcpFrontend {
4290 cluster_id: "cluster-a".to_owned(),
4291 address,
4292 sni: Some("a.example.com".to_owned()),
4293 alpn: vec![],
4294 tags: std::collections::BTreeMap::from([("team".to_owned(), "alpha".to_owned())]),
4295 };
4296 let front_b = RequestTcpFrontend {
4297 cluster_id: "cluster-b".to_owned(),
4298 address,
4299 sni: Some("b.example.com".to_owned()),
4300 alpn: vec!["h2".to_owned()],
4301 tags: std::collections::BTreeMap::from([("team".to_owned(), "beta".to_owned())]),
4302 };
4303 proxy
4304 .add_tcp_front(front_a)
4305 .expect("add_tcp_front A must succeed");
4306 proxy
4307 .add_tcp_front(front_b)
4308 .expect("add_tcp_front B must succeed");
4309
4310 let std_address: SocketAddr = address.into();
4311 let key_a = sni_tags_key(&std_address, "a.example.com", &[]);
4312 let key_b = sni_tags_key(&std_address, "b.example.com", &["h2".to_owned()]);
4313
4314 let listener = proxy
4315 .listeners
4316 .get(&token)
4317 .expect("listener must be present")
4318 .borrow();
4319 let tags_a = listener
4320 .get_tags(&key_a)
4321 .expect("front A's tags must live under its own composed key");
4322 assert_eq!(
4323 tags_a.tags.get("team").map(String::as_str),
4324 Some("alpha"),
4325 "front A's tags must survive front B's add, not be clobbered by it"
4326 );
4327 let tags_b = listener
4328 .get_tags(&key_b)
4329 .expect("front B's tags must live under its own composed key");
4330 assert_eq!(tags_b.tags.get("team").map(String::as_str), Some("beta"));
4331 }
4332
4333 #[test]
4334 fn removing_one_sni_front_clears_only_its_own_tags() {
4335 let mut proxy = test_proxy();
4336 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4337 let config = ListenerBuilder::new_tcp(address)
4338 .to_tcp(None)
4339 .expect("could not build listener config");
4340 let token = Token(0);
4341 proxy
4342 .add_listener(config, token)
4343 .expect("could not add listener");
4344
4345 let front_a = RequestTcpFrontend {
4346 cluster_id: "cluster-a".to_owned(),
4347 address,
4348 sni: Some("a.example.com".to_owned()),
4349 alpn: vec![],
4350 tags: std::collections::BTreeMap::from([("team".to_owned(), "alpha".to_owned())]),
4351 };
4352 let front_b = RequestTcpFrontend {
4353 cluster_id: "cluster-b".to_owned(),
4354 address,
4355 sni: Some("b.example.com".to_owned()),
4356 alpn: vec!["h2".to_owned()],
4357 tags: std::collections::BTreeMap::from([("team".to_owned(), "beta".to_owned())]),
4358 };
4359 proxy
4360 .add_tcp_front(front_a.clone())
4361 .expect("add_tcp_front A must succeed");
4362 proxy
4363 .add_tcp_front(front_b)
4364 .expect("add_tcp_front B must succeed");
4365
4366 proxy
4367 .remove_tcp_front(front_a)
4368 .expect("remove_tcp_front A must succeed");
4369
4370 let std_address: SocketAddr = address.into();
4371 let key_a = sni_tags_key(&std_address, "a.example.com", &[]);
4372 let key_b = sni_tags_key(&std_address, "b.example.com", &["h2".to_owned()]);
4373
4374 let listener = proxy
4375 .listeners
4376 .get(&token)
4377 .expect("listener must be present")
4378 .borrow();
4379 assert!(
4380 listener.get_tags(&key_a).is_none(),
4381 "removing front A must clear its own tags entry"
4382 );
4383 assert!(
4384 listener.get_tags(&key_b).is_some(),
4385 "removing front A must NOT clear sibling front B's tags"
4386 );
4387 }
4388
4389 #[test]
4396 fn route_time_tags_key_rebuild_matches_the_add_time_key() {
4397 let address: SocketAddr = "127.0.0.1:9000".parse().expect("test address");
4398
4399 let add_time = sni_tags_key(
4401 &address,
4402 "*.Example.COM",
4403 &["http/1.1".to_owned(), "h2".to_owned()],
4404 );
4405 let matcher =
4406 AlpnMatcher::OneOf([b"h2".to_vec(), b"http/1.1".to_vec()].into_iter().collect());
4407 let route_time = sni_tags_key(
4408 &address,
4409 "*.example.com", &alpn_matcher_protocols(&matcher),
4411 );
4412 assert_eq!(
4413 add_time, route_time,
4414 "add-time and route-time keys must agree for the same front"
4415 );
4416
4417 assert_eq!(
4419 sni_tags_key(&address, "a.example.com", &[]),
4420 sni_tags_key(
4421 &address,
4422 "a.example.com",
4423 &alpn_matcher_protocols(&AlpnMatcher::Any)
4424 )
4425 );
4426
4427 assert_ne!(add_time, address.to_string());
4429 }
4430
4431 #[test]
4438 fn alpn_sets_differing_only_by_an_embedded_separator_get_distinct_tags_keys() {
4439 let address: SocketAddr = "127.0.0.1:9001".parse().expect("test address");
4440 let key_joined = sni_tags_key(&address, "example.com", &["a,b".to_owned()]);
4441 let key_split = sni_tags_key(&address, "example.com", &["a".to_owned(), "b".to_owned()]);
4442 assert_ne!(
4443 key_joined, key_split,
4444 "a single \"a,b\" protocol must not collide with the disjoint [\"a\", \"b\"] pair"
4445 );
4446 }
4447
4448 #[test]
4454 fn alpn_sets_differing_only_by_an_embedded_separator_keep_distinct_tags() {
4455 let mut proxy = test_proxy();
4456 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4457 let config = ListenerBuilder::new_tcp(address)
4458 .to_tcp(None)
4459 .expect("could not build listener config");
4460 let token = Token(0);
4461 proxy
4462 .add_listener(config, token)
4463 .expect("could not add listener");
4464
4465 let front_joined = RequestTcpFrontend {
4466 cluster_id: "cluster-joined".to_owned(),
4467 address,
4468 sni: Some("example.com".to_owned()),
4469 alpn: vec!["a,b".to_owned()],
4470 tags: std::collections::BTreeMap::from([("variant".to_owned(), "joined".to_owned())]),
4471 };
4472 let front_split = RequestTcpFrontend {
4473 cluster_id: "cluster-split".to_owned(),
4474 address,
4475 sni: Some("example.com".to_owned()),
4476 alpn: vec!["a".to_owned(), "b".to_owned()],
4477 tags: std::collections::BTreeMap::from([("variant".to_owned(), "split".to_owned())]),
4478 };
4479
4480 proxy
4481 .add_tcp_front(front_joined.clone())
4482 .expect("the single \"a,b\" protocol front must be accepted");
4483 proxy.add_tcp_front(front_split.clone()).expect(
4484 "the disjoint [\"a\", \"b\"] front must be accepted -- it is NOT the same ALPN \
4485 set as [\"a,b\"]",
4486 );
4487
4488 let std_address: SocketAddr = address.into();
4489 let key_joined = sni_tags_key(&std_address, "example.com", &["a,b".to_owned()]);
4490 let key_split = sni_tags_key(
4491 &std_address,
4492 "example.com",
4493 &["a".to_owned(), "b".to_owned()],
4494 );
4495 assert_ne!(key_joined, key_split);
4496
4497 {
4498 let listener = proxy
4499 .listeners
4500 .get(&token)
4501 .expect("listener must be present")
4502 .borrow();
4503 assert_eq!(
4504 listener
4505 .get_tags(&key_joined)
4506 .and_then(|t| t.tags.get("variant"))
4507 .map(String::as_str),
4508 Some("joined"),
4509 "the joined front's tags must live under its own composed key"
4510 );
4511 assert_eq!(
4512 listener
4513 .get_tags(&key_split)
4514 .and_then(|t| t.tags.get("variant"))
4515 .map(String::as_str),
4516 Some("split"),
4517 "the split front's tags must live under its own DISTINCT composed key"
4518 );
4519 }
4520
4521 proxy
4522 .remove_tcp_front(front_joined)
4523 .expect("remove the joined front");
4524
4525 let listener = proxy
4526 .listeners
4527 .get(&token)
4528 .expect("listener must be present")
4529 .borrow();
4530 assert!(
4531 listener.get_tags(&key_joined).is_none(),
4532 "removing the joined front must clear its own tags entry"
4533 );
4534 assert_eq!(
4535 listener
4536 .get_tags(&key_split)
4537 .and_then(|t| t.tags.get("variant"))
4538 .map(String::as_str),
4539 Some("split"),
4540 "removing the joined front must not disturb its sibling's tags"
4541 );
4542 }
4543
4544 #[test]
4545 fn remove_sni_route_for_an_absent_exact_key_does_not_strip_a_sibling_wildcards_entry() {
4546 let mut listener = test_listener();
4547 listener
4548 .insert_sni_route(
4549 "*.example.com".to_owned(),
4550 vec![],
4551 "cluster-wildcard".into(),
4552 )
4553 .expect("insert_sni_route must succeed for a valid test SNI");
4554
4555 listener.remove_sni_route("a.example.com".to_owned(), vec![], "cluster-wildcard");
4561
4562 let (_, wildcard_entries) = listener
4563 .sni_routes
4564 .domain_lookup(b"b.example.com", true)
4565 .expect(
4566 "the wildcard catch-all must survive a remove targeting an unrelated exact key",
4567 );
4568 assert_eq!(
4569 wildcard_entries,
4570 &vec![(AlpnMatcher::Any, "cluster-wildcard".into())]
4571 );
4572 }
4573
4574 #[test]
4577 fn a_no_sni_front_leaves_the_route_table_empty() {
4578 let mut listener = test_listener();
4579 listener.cluster_id = Some("legacy-catch-all".into());
4580 assert!(
4581 listener.sni_routes.is_empty(),
4582 "a listener with only a no-SNI front must never populate sni_routes"
4583 );
4584 }
4585
4586 #[test]
4587 fn an_sni_scoped_front_leaves_cluster_id_unset() {
4588 let mut listener = test_listener();
4589 listener
4590 .insert_sni_route("example.com".to_owned(), vec![], "cluster-a".into())
4591 .expect("insert_sni_route must succeed for a valid test SNI");
4592 assert!(
4593 listener.cluster_id.is_none(),
4594 "a listener with only SNI-scoped fronts must never populate the legacy cluster_id"
4595 );
4596 }
4597
4598 fn test_proxy() -> TcpProxy {
4601 let ServerParts {
4602 registry,
4603 sessions,
4604 pool,
4605 backends,
4606 ..
4607 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
4608 TcpProxy::new(registry, sessions, pool, backends)
4609 }
4610
4611 #[cfg(target_os = "linux")]
4617 #[test]
4618 fn listeners_sharing_an_address_on_different_interfaces_coexist() {
4619 use socket2::SockRef;
4620
4621 let mut proxy = test_proxy();
4622 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4623 let bare = ListenerKey::from(address);
4624 let on_lo = ListenerKey::new(address, Some("lo"));
4625 for (token, interface) in [(Token(0), None), (Token(1), Some("lo"))] {
4626 let config = ListenerBuilder::new_tcp(address)
4627 .with_interface(interface)
4628 .to_tcp(None)
4629 .expect("could not build listener config");
4630 proxy
4631 .add_listener(config, token)
4632 .expect("a listener on another interface must not collide");
4633 }
4634 assert_eq!(proxy.listener_token(&bare), Some(Token(0)));
4635 assert_eq!(proxy.listener_token(&on_lo), Some(Token(1)));
4636 assert_eq!(
4637 proxy.listener_token(&ListenerKey::new(address, Some("wg0"))),
4638 None,
4639 "an interface nobody added names no listener"
4640 );
4641
4642 proxy
4643 .add_tcp_front(RequestTcpFrontend {
4644 cluster_id: "cluster-a".to_owned(),
4645 address,
4646 ..Default::default()
4647 })
4648 .expect("add_tcp_front must succeed");
4649 for token in [Token(0), Token(1)] {
4650 assert_eq!(
4651 proxy.listeners[&token].borrow().cluster_id.as_deref(),
4652 Some("cluster-a"),
4653 "the frontend must reach the listener at {token:?}"
4654 );
4655 }
4656
4657 for key in [&bare, &on_lo] {
4658 if let Err(error) = proxy.activate_listener(key, None) {
4659 let message = error.to_string();
4660 if message.contains("Operation not permitted") {
4661 eprintln!(
4662 "SKIPPED listeners_sharing_an_address_on_different_interfaces_coexist: \
4663 this environment may not bind a socket to an interface: {message}"
4664 );
4665 return;
4666 }
4667 panic!("activating {key}: {message}");
4668 }
4669 }
4670 let mut handed_back = proxy.give_back_listeners();
4671 handed_back.sort_by(|(a, _), (b, _)| a.cmp(b));
4672 let keys: Vec<&ListenerKey> = handed_back.iter().map(|(key, _)| key).collect();
4673 assert_eq!(keys, vec![&bare, &on_lo]);
4674 for (key, socket) in &handed_back {
4675 assert_eq!(
4676 SockRef::from(socket)
4677 .device()
4678 .expect("getsockopt(SO_BINDTODEVICE)"),
4679 key.interface
4680 .as_ref()
4681 .map(|interface| interface.as_bytes().to_vec()),
4682 "the socket handed back for {key} must be bound to its interface"
4683 );
4684 }
4685
4686 proxy.remove_listener(&on_lo);
4687 assert_eq!(proxy.listener_token(&on_lo), None);
4688 assert_eq!(
4689 proxy.listener_token(&bare),
4690 Some(Token(0)),
4691 "removing the lo listener must keep the one without interface"
4692 );
4693 }
4694
4695 #[test]
4700 fn a_listener_added_next_to_a_sibling_takes_over_its_routes() {
4701 let mut proxy = test_proxy();
4702 let catch_all = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
4703 let by_sni = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
4704 for (token, address, sni) in [
4705 (Token(0), catch_all, None),
4706 (Token(2), by_sni, Some("example.com".to_owned())),
4707 ] {
4708 let config = ListenerBuilder::new_tcp(address)
4709 .to_tcp(None)
4710 .expect("could not build listener config");
4711 proxy.add_listener(config, token).expect("add the listener");
4712 proxy
4713 .add_tcp_front(RequestTcpFrontend {
4714 cluster_id: "cluster-a".to_owned(),
4715 address,
4716 sni,
4717 ..Default::default()
4718 })
4719 .expect("add_tcp_front must succeed");
4720 }
4721 for (token, address) in [(Token(1), catch_all), (Token(3), by_sni)] {
4722 let config = ListenerBuilder::new_tcp(address)
4723 .with_interface(Some("lo"))
4724 .to_tcp(None)
4725 .expect("could not build listener config");
4726 proxy.add_listener(config, token).expect("add the sibling");
4727 }
4728
4729 assert_eq!(
4730 proxy.listeners[&Token(1)].borrow().cluster_id.as_deref(),
4731 Some("cluster-a"),
4732 "the listener added after the frontend must route to its cluster"
4733 );
4734 let sibling = proxy.listeners[&Token(3)].borrow();
4735 let (_, entries) = sibling
4736 .sni_routes
4737 .domain_lookup(b"example.com", true)
4738 .expect("the listener added after the SNI frontend must route it");
4739 assert_eq!(entries, &vec![(AlpnMatcher::Any, "cluster-a".into())]);
4740 }
4741
4742 #[cfg(target_os = "linux")]
4749 #[test]
4750 fn traffic_through_an_interface_reaches_the_listener_bound_to_it() {
4751 let mut proxy = test_proxy();
4752 let port = provide_port();
4753 let address = SocketAddress::new_v4(0, 0, 0, 0, port);
4754 for (token, interface) in [(Token(0), None), (Token(1), Some("lo"))] {
4755 let config = ListenerBuilder::new_tcp(address)
4756 .with_interface(interface)
4757 .to_tcp(None)
4758 .expect("could not build listener config");
4759 proxy.add_listener(config, token).expect("add the listener");
4760 let key = ListenerKey::new(address, interface);
4761 if let Err(error) = proxy.activate_listener(&key, None) {
4762 let message = error.to_string();
4763 if message.contains("Operation not permitted") {
4764 eprintln!(
4765 "SKIPPED traffic_through_an_interface_reaches_the_listener_bound_to_it: \
4766 this environment may not bind a socket to an interface: {message}"
4767 );
4768 return;
4769 }
4770 panic!("activating {key}: {message}");
4771 }
4772 }
4773
4774 const CONNECTIONS: usize = 8;
4778 let _clients: Vec<_> = (0..CONNECTIONS)
4779 .map(|_| {
4780 std::net::TcpStream::connect(("127.0.0.1", port))
4781 .expect("connect through the loopback interface")
4782 })
4783 .collect();
4784 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2);
4787 let mut accepted = 0;
4788 while accepted < CONNECTIONS {
4789 match proxy.accept(ListenToken(1)) {
4790 Ok(_) => accepted += 1,
4791 Err(AcceptError::WouldBlock) if std::time::Instant::now() < deadline => {
4792 std::thread::sleep(std::time::Duration::from_millis(10));
4793 }
4794 Err(error) => panic!(
4795 "the lo listener must accept every connection, got {accepted} of \
4796 {CONNECTIONS}: {error:?}"
4797 ),
4798 }
4799 }
4800 assert!(
4801 matches!(proxy.accept(ListenToken(0)), Err(AcceptError::WouldBlock)),
4802 "the listener bound to no interface must not receive loopback traffic"
4803 );
4804 }
4805
4806 #[test]
4807 fn add_then_remove_sni_front_round_trips_through_tcp_proxy() {
4808 let mut proxy = test_proxy();
4809 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4810 let config = ListenerBuilder::new_tcp(address)
4811 .to_tcp(None)
4812 .expect("could not build listener config");
4813 let token = Token(0);
4814 proxy
4815 .add_listener(config, token)
4816 .expect("could not add listener");
4817
4818 let front = RequestTcpFrontend {
4819 cluster_id: "cluster-a".to_owned(),
4820 address,
4821 sni: Some("Example.COM".to_owned()),
4822 alpn: vec![],
4823 ..Default::default()
4824 };
4825 proxy
4826 .add_tcp_front(front.clone())
4827 .expect("add_tcp_front must succeed");
4828
4829 {
4830 let listener = proxy
4831 .listeners
4832 .get(&token)
4833 .expect("listener must be present")
4834 .borrow();
4835 assert!(listener.cluster_id.is_none());
4836 let (_, entries) = listener
4837 .sni_routes
4838 .domain_lookup(b"example.com", true)
4840 .expect("example.com must be routable after add_tcp_front");
4841 assert_eq!(entries, &vec![(AlpnMatcher::Any, "cluster-a".into())]);
4842 }
4843 assert_eq!(proxy.fronts.get("cluster-a"), Some(&token));
4844
4845 proxy
4846 .remove_tcp_front(front)
4847 .expect("remove_tcp_front must succeed");
4848
4849 {
4850 let listener = proxy
4851 .listeners
4852 .get(&token)
4853 .expect("listener must be present")
4854 .borrow();
4855 assert!(
4856 listener.sni_routes.is_empty(),
4857 "remove_tcp_front must leave no stranded route"
4858 );
4859 }
4860 assert_eq!(
4861 proxy.fronts.get("cluster-a"),
4862 None,
4863 "remove_tcp_front must undo add_tcp_front's self.fronts bookkeeping"
4864 );
4865 }
4866
4867 #[test]
4868 fn add_then_remove_legacy_no_sni_front_round_trips() {
4869 let mut proxy = test_proxy();
4870 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4871 let config = ListenerBuilder::new_tcp(address)
4872 .to_tcp(None)
4873 .expect("could not build listener config");
4874 let token = Token(0);
4875 proxy
4876 .add_listener(config, token)
4877 .expect("could not add listener");
4878
4879 let front = frontend("cluster-legacy", None, &[]);
4880 let front = RequestTcpFrontend { address, ..front };
4881 proxy
4882 .add_tcp_front(front.clone())
4883 .expect("add_tcp_front must succeed");
4884
4885 assert_eq!(
4886 proxy
4887 .listeners
4888 .get(&token)
4889 .expect("listener must be present")
4890 .borrow()
4891 .cluster_id,
4892 Some("cluster-legacy".into())
4893 );
4894
4895 proxy
4896 .remove_tcp_front(front)
4897 .expect("remove_tcp_front must succeed");
4898
4899 assert_eq!(
4900 proxy
4901 .listeners
4902 .get(&token)
4903 .expect("listener must be present")
4904 .borrow()
4905 .cluster_id,
4906 None
4907 );
4908 assert_eq!(proxy.fronts.get("cluster-legacy"), None);
4909 }
4910
4911 #[test]
4920 fn add_tcp_front_rejects_alpn_without_sni() {
4921 let mut proxy = test_proxy();
4922 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4923 let config = ListenerBuilder::new_tcp(address)
4924 .to_tcp(None)
4925 .expect("could not build listener config");
4926 proxy
4927 .add_listener(config, Token(0))
4928 .expect("could not add listener");
4929
4930 let front = frontend("cluster-a", None, &["h2"]);
4931 let front = RequestTcpFrontend { address, ..front };
4932 match proxy.add_tcp_front(front) {
4933 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
4934 other => panic!("expected InvalidTcpFrontend, got {other:?}"),
4935 }
4936 }
4937
4938 #[test]
4939 fn add_tcp_front_rejects_no_sni_front_on_listener_with_sni_routes() {
4940 let mut proxy = test_proxy();
4941 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4942 let config = ListenerBuilder::new_tcp(address)
4943 .to_tcp(None)
4944 .expect("could not build listener config");
4945 proxy
4946 .add_listener(config, Token(0))
4947 .expect("could not add listener");
4948
4949 let sni_front = frontend("cluster-a", Some("example.com"), &[]);
4950 let sni_front = RequestTcpFrontend {
4951 address,
4952 ..sni_front
4953 };
4954 proxy
4955 .add_tcp_front(sni_front)
4956 .expect("the first, SNI-scoped frontend must be accepted");
4957
4958 let no_sni_front = frontend("cluster-b", None, &[]);
4959 let no_sni_front = RequestTcpFrontend {
4960 address,
4961 ..no_sni_front
4962 };
4963 match proxy.add_tcp_front(no_sni_front) {
4964 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
4965 other => panic!("expected InvalidTcpFrontend, got {other:?}"),
4966 }
4967 }
4968
4969 #[test]
4970 fn add_tcp_front_rejects_sni_front_on_listener_with_no_sni_cluster() {
4971 let mut proxy = test_proxy();
4972 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4973 let config = ListenerBuilder::new_tcp(address)
4974 .to_tcp(None)
4975 .expect("could not build listener config");
4976 proxy
4977 .add_listener(config, Token(0))
4978 .expect("could not add listener");
4979
4980 let no_sni_front = frontend("cluster-a", None, &[]);
4981 let no_sni_front = RequestTcpFrontend {
4982 address,
4983 ..no_sni_front
4984 };
4985 proxy
4986 .add_tcp_front(no_sni_front)
4987 .expect("the first, no-SNI frontend must be accepted");
4988
4989 let sni_front = frontend("cluster-b", Some("example.com"), &[]);
4990 let sni_front = RequestTcpFrontend {
4991 address,
4992 ..sni_front
4993 };
4994 match proxy.add_tcp_front(sni_front) {
4995 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
4996 other => panic!("expected InvalidTcpFrontend, got {other:?}"),
4997 }
4998 }
4999
5000 #[test]
5001 fn add_tcp_front_rejects_alpn_overlap_on_same_sni() {
5002 let mut proxy = test_proxy();
5003 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5004 let config = ListenerBuilder::new_tcp(address)
5005 .to_tcp(None)
5006 .expect("could not build listener config");
5007 proxy
5008 .add_listener(config, Token(0))
5009 .expect("could not add listener");
5010
5011 let first = frontend("cluster-a", Some("example.com"), &["h2"]);
5012 let first = RequestTcpFrontend { address, ..first };
5013 proxy
5014 .add_tcp_front(first)
5015 .expect("the first frontend must be accepted");
5016
5017 let second = frontend("cluster-b", Some("example.com"), &["h2", "http/1.1"]);
5019 let second = RequestTcpFrontend { address, ..second };
5020 match proxy.add_tcp_front(second) {
5021 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
5022 other => panic!("expected InvalidTcpFrontend, got {other:?}"),
5023 }
5024 }
5025
5026 #[test]
5027 fn add_tcp_front_rejects_duplicate_catch_all_on_same_sni() {
5028 let mut proxy = test_proxy();
5029 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5030 let config = ListenerBuilder::new_tcp(address)
5031 .to_tcp(None)
5032 .expect("could not build listener config");
5033 proxy
5034 .add_listener(config, Token(0))
5035 .expect("could not add listener");
5036
5037 let first = frontend("cluster-a", Some("example.com"), &[]);
5038 let first = RequestTcpFrontend { address, ..first };
5039 proxy
5040 .add_tcp_front(first)
5041 .expect("the first catch-all frontend must be accepted");
5042
5043 let second = frontend("cluster-b", Some("example.com"), &[]);
5044 let second = RequestTcpFrontend { address, ..second };
5045 match proxy.add_tcp_front(second) {
5046 Err(ProxyError::InvalidTcpFrontend { .. }) => {}
5047 other => panic!("expected InvalidTcpFrontend, got {other:?}"),
5048 }
5049 }
5050
5051 #[test]
5056 fn add_tcp_front_accepts_disjoint_alpn_and_catch_all_on_same_sni() {
5057 let mut proxy = test_proxy();
5058 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5059 let config = ListenerBuilder::new_tcp(address)
5060 .to_tcp(None)
5061 .expect("could not build listener config");
5062 proxy
5063 .add_listener(config, Token(0))
5064 .expect("could not add listener");
5065
5066 let h2 = frontend("cluster-h2", Some("example.com"), &["h2"]);
5067 let h2 = RequestTcpFrontend { address, ..h2 };
5068 proxy
5069 .add_tcp_front(h2)
5070 .expect("disjoint alpn frontend must be accepted");
5071
5072 let http11 = frontend("cluster-http11", Some("example.com"), &["http/1.1"]);
5073 let http11 = RequestTcpFrontend { address, ..http11 };
5074 proxy
5075 .add_tcp_front(http11)
5076 .expect("second disjoint alpn frontend must be accepted");
5077
5078 let catch_all = frontend("cluster-default", Some("example.com"), &[]);
5079 let catch_all = RequestTcpFrontend {
5080 address,
5081 ..catch_all
5082 };
5083 proxy
5084 .add_tcp_front(catch_all)
5085 .expect("a catch-all alongside specific-protocol entries must be accepted");
5086 }
5087
5088 fn sni_preread_active_gauge() -> i64 {
5095 use sozu_command::proto::command::filtered_metrics::Inner;
5096 crate::metrics::METRICS.with(|metrics| {
5097 metrics
5098 .borrow_mut()
5099 .dump_local_proxy_metrics()
5100 .get(names::tcp::sni_preread::ACTIVE)
5101 .and_then(|fm| fm.inner.as_ref())
5102 .and_then(|inner| match inner {
5103 Inner::Gauge(v) => Some(*v as i64),
5104 _ => None,
5105 })
5106 .unwrap_or(0)
5107 })
5108 }
5109
5110 #[test]
5111 fn entering_sni_preread_increments_the_active_gauge() {
5112 let ServerParts {
5132 registry,
5133 sessions,
5134 pool,
5135 backends,
5136 ..
5137 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
5138
5139 let proxy = Rc::new(RefCell::new(TcpProxy::new(
5140 registry,
5141 sessions,
5142 pool.clone(),
5143 backends,
5144 )));
5145 let listener = Rc::new(RefCell::new(test_listener()));
5146
5147 let (front_buffer, back_buffer) = {
5148 let mut pool = pool.borrow_mut();
5149 (
5150 pool.checkout().expect("front buffer checkout must succeed"),
5151 pool.checkout().expect("back buffer checkout must succeed"),
5152 )
5153 };
5154
5155 let peer: SocketAddr = format!("127.0.0.1:{}", provide_port())
5159 .parse()
5160 .expect("loopback address must parse");
5161 let socket = MioTcpStream::connect(peer).expect("mio connect must return a socket handle");
5162
5163 let before = sni_preread_active_gauge();
5164 let session = TcpSession::new_sni_preread(
5165 back_buffer,
5166 Duration::from_secs(30),
5167 Duration::from_secs(30),
5168 front_buffer,
5169 Token(0),
5170 listener,
5171 proxy,
5172 socket,
5173 peer,
5174 Duration::from_millis(0),
5175 Duration::from_secs(3),
5176 16384,
5177 );
5178 let after = sni_preread_active_gauge();
5179
5180 assert!(matches!(session.state, TcpStateMachine::SniPreread(_)));
5183
5184 assert_eq!(
5185 after - before,
5186 1,
5187 "entering the SniPreread state must increment tcp.sni_preread.active by exactly one"
5188 );
5189 }
5190
5191 #[test]
5198 fn frontend_timeout_reset_is_gated_on_sni_preread_decision() {
5199 let mut pool = crate::pool::Pool::with_capacity(1, 1, 16 * 1024);
5200 let frontend_buffer = pool.checkout().expect("frontend buffer");
5201 let socket = MioTcpStream::connect(
5202 format!("127.0.0.1:{}", provide_port())
5203 .parse()
5204 .expect("loopback address must parse"),
5205 )
5206 .expect("mio connect must return a socket handle");
5207
5208 let undecided = TcpStateMachine::SniPreread(SniPreread::new(
5209 socket,
5210 Token(0),
5211 Ulid::generate(),
5212 frontend_buffer,
5213 16384,
5214 ));
5215 assert!(
5216 !frontend_timeout_resets_on_readable(&undecided),
5217 "an undecided SniPreread must not have its absolute deadline reset"
5218 );
5219
5220 let socket2 = MioTcpStream::connect(
5228 format!("127.0.0.1:{}", provide_port())
5229 .parse()
5230 .expect("loopback address must parse"),
5231 )
5232 .expect("mio connect must return a socket handle");
5233 let container = crate::timer::TimeoutContainer::new_empty(Duration::from_secs(5));
5234 let other = TcpStateMachine::ExpectProxyProtocol(ExpectProxyProtocol::new(
5235 container,
5236 socket2,
5237 Token(1),
5238 Ulid::generate(),
5239 ));
5240 assert!(
5241 frontend_timeout_resets_on_readable(&other),
5242 "every non-preread-undecided state must keep resetting its frontend timeout"
5243 );
5244 }
5245
5246 fn minimal_client_hello_wire(host: &str) -> Vec<u8> {
5253 let mut name_list = vec![0u8]; name_list.extend_from_slice(&(host.len() as u16).to_be_bytes());
5255 name_list.extend_from_slice(host.as_bytes());
5256 let mut sni_ext_data = Vec::new();
5257 sni_ext_data.extend_from_slice(&(name_list.len() as u16).to_be_bytes());
5258 sni_ext_data.extend_from_slice(&name_list);
5259 let mut sni_ext = Vec::new();
5260 sni_ext.extend_from_slice(&0x0000u16.to_be_bytes()); sni_ext.extend_from_slice(&(sni_ext_data.len() as u16).to_be_bytes());
5262 sni_ext.extend_from_slice(&sni_ext_data);
5263
5264 let mut body = Vec::new();
5265 body.extend_from_slice(&[0x03, 0x03]); body.extend_from_slice(&[0u8; 32]); body.push(0); body.extend_from_slice(&[0x00, 0x02, 0x13, 0x01]); body.push(1); body.push(0); body.extend_from_slice(&(sni_ext.len() as u16).to_be_bytes()); body.extend_from_slice(&sni_ext);
5273
5274 let mut handshake = Vec::new();
5275 handshake.push(1u8); let hs_len = body.len() as u32;
5277 handshake.extend_from_slice(&hs_len.to_be_bytes()[1..4]);
5278 handshake.extend_from_slice(&body);
5279
5280 let mut record = Vec::new();
5281 record.push(22u8); record.extend_from_slice(&[0x03, 0x03]); record.extend_from_slice(&(handshake.len() as u16).to_be_bytes());
5284 record.extend_from_slice(&handshake);
5285 record
5286 }
5287
5288 fn sni_preread_routed_count() -> i64 {
5292 use sozu_command::proto::command::filtered_metrics::Inner;
5293 crate::metrics::METRICS.with(|metrics| {
5294 metrics
5295 .borrow_mut()
5296 .dump_local_proxy_metrics()
5297 .get(names::tcp::sni_preread::ROUTED)
5298 .and_then(|fm| fm.inner.as_ref())
5299 .and_then(|inner| match inner {
5300 Inner::Count(v) => Some(*v),
5301 _ => None,
5302 })
5303 .unwrap_or(0)
5304 })
5305 }
5306
5307 #[test]
5322 fn frontend_timeout_restored_and_timeout_after_route_is_not_double_counted() {
5323 let ServerParts {
5324 registry,
5325 sessions,
5326 pool,
5327 backends,
5328 ..
5329 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
5330 let proxy = Rc::new(RefCell::new(TcpProxy::new(
5331 registry,
5332 sessions,
5333 pool.clone(),
5334 backends,
5335 )));
5336
5337 let mut bare_listener = test_listener();
5338 bare_listener
5339 .insert_sni_route("example.com".to_owned(), vec![], "cluster-a".into())
5340 .expect("insert_sni_route must succeed for a valid test SNI");
5341 let configured_front_timeout = bare_listener.config.front_timeout;
5342 let listener = Rc::new(RefCell::new(bare_listener));
5343
5344 let std_listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind test listener");
5345 let addr = std_listener.local_addr().expect("listener local addr");
5346 let mut client = std::net::TcpStream::connect(addr).expect("connect test client");
5347 let (server, server_peer) = std_listener.accept().expect("accept test server");
5348 server.set_nonblocking(true).expect("server nonblocking");
5349
5350 let (front_buffer, back_buffer) = {
5351 let mut pool = pool.borrow_mut();
5352 (
5353 pool.checkout().expect("front buffer checkout must succeed"),
5354 pool.checkout().expect("back buffer checkout must succeed"),
5355 )
5356 };
5357
5358 let mut session = TcpSession::new_sni_preread(
5359 back_buffer,
5360 Duration::from_secs(30),
5361 Duration::from_secs(30),
5362 front_buffer,
5363 Token(0),
5364 listener,
5365 proxy,
5366 MioTcpStream::from_std(server),
5367 server_peer,
5368 Duration::from_millis(0),
5369 Duration::from_secs(3),
5370 16384,
5371 );
5372
5373 {
5374 use std::io::Write as _;
5375 client
5376 .write_all(&minimal_client_hello_wire("example.com"))
5377 .expect("write ClientHello");
5378 client.flush().ok();
5379 }
5380
5381 let routed_before = sni_preread_routed_count();
5382 for _ in 0..10 {
5383 if session.cluster_id.is_some() {
5384 break;
5385 }
5386 let _ = session.readable();
5387 }
5388 assert_eq!(
5389 session.cluster_id.as_deref(),
5390 Some("cluster-a"),
5391 "the session must have routed on a valid ClientHello for a configured SNI"
5392 );
5393 assert_eq!(
5394 sni_preread_routed_count() - routed_before,
5395 1,
5396 "routing must count tcp.sni_preread.routed exactly once"
5397 );
5398
5399 assert_eq!(
5401 session.container_frontend_timeout.duration(),
5402 Duration::from_secs(configured_front_timeout as u64),
5403 "the frontend timeout must already carry the configured front_timeout right after \
5404 routing, not only once the backend connects"
5405 );
5406
5407 let _ = session.timeout(Token(0));
5412 assert_eq!(
5413 sni_preread_routed_count() - routed_before,
5414 1,
5415 "a timeout firing after the route already latched must not double-count \
5416 tcp.sni_preread.routed"
5417 );
5418
5419 drop(client);
5420 }
5421
5422 fn proxy_protocol_v2_ipv4_header() -> [u8; 28] {
5427 [
5428 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54,
5429 0x0A, 0x21, 0x11, 0x00, 0x0C, 127, 0, 0, 1, 127, 0, 0, 1, 0x1F, 0x90, 0x10, 0x68, ]
5438 }
5439
5440 struct ExpectProxyFixture {
5443 session: Rc<RefCell<TcpSession>>,
5444 proxy_session: Rc<RefCell<dyn ProxySession>>,
5448 frontend_token: Token,
5449 client: std::net::TcpStream,
5450 _frontend_listener: std::net::TcpListener,
5451 _backend_listener: std::net::TcpListener,
5452 }
5453
5454 fn expect_proxy_fixture() -> ExpectProxyFixture {
5461 let ServerParts {
5462 registry,
5463 sessions,
5464 pool,
5465 backends,
5466 ..
5467 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
5468
5469 let backend_listener =
5470 std::net::TcpListener::bind("127.0.0.1:0").expect("bind test backend");
5471 let backend_address = backend_listener
5472 .local_addr()
5473 .expect("test backend local addr");
5474 backends.borrow_mut().add_backend(
5475 "cluster-expect",
5476 Backend::new("cluster-expect-1", backend_address, None, None, None),
5477 );
5478
5479 let proxy = Rc::new(RefCell::new(TcpProxy::new(
5480 registry,
5481 sessions.clone(),
5482 pool.clone(),
5483 backends,
5484 )));
5485 let listener = Rc::new(RefCell::new(test_listener()));
5486
5487 let frontend_listener =
5488 std::net::TcpListener::bind("127.0.0.1:0").expect("bind test frontend listener");
5489 let frontend_address = frontend_listener
5490 .local_addr()
5491 .expect("test frontend local addr");
5492 let client = std::net::TcpStream::connect(frontend_address).expect("connect test client");
5493 let (frontend, frontend_peer) = frontend_listener
5494 .accept()
5495 .expect("accept the client connection");
5496 frontend
5497 .set_nonblocking(true)
5498 .expect("frontend socket nonblocking");
5499
5500 let (frontend_buffer, backend_buffer) = {
5501 let mut pool = pool.borrow_mut();
5502 (
5503 pool.checkout().expect("front buffer checkout must succeed"),
5504 pool.checkout().expect("back buffer checkout must succeed"),
5505 )
5506 };
5507
5508 let frontend_token = {
5509 let mut session_manager = sessions.borrow_mut();
5510 let entry = session_manager.slab.vacant_entry();
5511 let token = Token(entry.key());
5512 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
5513 protocol: Protocol::TCPListen,
5514 })));
5515 token
5516 };
5517
5518 let session = Rc::new(RefCell::new(TcpSession::new(
5519 backend_buffer,
5520 None,
5521 Some("cluster-expect".into()),
5522 Duration::from_secs(30),
5523 Duration::from_secs(30),
5524 Duration::from_secs(30),
5525 frontend_buffer,
5526 frontend_token,
5527 listener,
5528 Some(ProxyProtocolConfig::ExpectHeader),
5529 proxy,
5530 MioTcpStream::from_std(frontend),
5531 frontend_peer,
5532 Duration::from_millis(0),
5533 )));
5534 let proxy_session: Rc<RefCell<dyn ProxySession>> = session.clone();
5535 sessions.borrow_mut().slab[frontend_token.0] = proxy_session.clone();
5536
5537 assert!(
5538 matches!(
5539 session.borrow().state,
5540 TcpStateMachine::ExpectProxyProtocol(_)
5541 ),
5542 "an ExpectHeader cluster must start its session in the expect state"
5543 );
5544
5545 ExpectProxyFixture {
5546 session,
5547 proxy_session,
5548 frontend_token,
5549 client,
5550 _frontend_listener: frontend_listener,
5551 _backend_listener: backend_listener,
5552 }
5553 }
5554
5555 #[test]
5576 fn expect_proxy_dials_the_backend_only_after_the_header_is_parsed() {
5577 use std::io::Write as _;
5578
5579 let mut fixture = expect_proxy_fixture();
5580 let (session, proxy_session, frontend_token) = (
5581 fixture.session.clone(),
5582 fixture.proxy_session.clone(),
5583 fixture.frontend_token,
5584 );
5585
5586 session
5589 .borrow_mut()
5590 .update_readiness(frontend_token, Ready::WRITABLE);
5591 let closed = session.borrow_mut().ready(proxy_session.clone());
5592 assert!(
5593 !closed,
5594 "a session still waiting for its PROXY header must stay open"
5595 );
5596 {
5597 let session = session.borrow();
5598 assert!(
5599 matches!(session.state, TcpStateMachine::ExpectProxyProtocol(_)),
5600 "the session must still be expecting its PROXY header"
5601 );
5602 assert_eq!(
5603 session.back_connected(),
5604 BackendConnectionStatus::NotConnected,
5605 "no backend may be dialed while the PROXY header is still unparsed"
5606 );
5607 assert!(
5608 session.backend_token.is_none(),
5609 "no backend token may be wired while the PROXY header is still unparsed"
5610 );
5611 }
5612
5613 fixture
5616 .client
5617 .write_all(&proxy_protocol_v2_ipv4_header())
5618 .expect("write the PROXY-v2 header");
5619 fixture.client.flush().ok();
5620 session
5621 .borrow_mut()
5622 .update_readiness(frontend_token, Ready::READABLE);
5623 let _ = session.borrow_mut().ready(proxy_session.clone());
5624 {
5625 let session = session.borrow();
5626 assert!(
5627 !matches!(session.state, TcpStateMachine::ExpectProxyProtocol(_)),
5628 "a parsed PROXY header must move the session out of the expect state"
5629 );
5630 assert_ne!(
5631 session.back_connected(),
5632 BackendConnectionStatus::NotConnected,
5633 "the backend must be dialed once the header has been parsed"
5634 );
5635 assert!(
5636 session.backend_token.is_some(),
5637 "a dialed backend must own its backend token"
5638 );
5639 }
5640 }
5641
5642 #[cfg(target_os = "linux")]
5653 fn half_close_and_observe_frontend(fixture: &mut ExpectProxyFixture, wire: &[u8]) -> Ready {
5654 use std::{io::Write as _, net::Shutdown, os::fd::AsRawFd, time::Duration};
5655
5656 use mio::{Events, Interest, Poll, Token, unix::SourceFd};
5657
5658 fixture
5659 .client
5660 .write_all(wire)
5661 .expect("write the request stream");
5662 fixture
5663 .client
5664 .shutdown(Shutdown::Write)
5665 .expect("half-close the client write side");
5666
5667 let frontend_fd = fixture.session.borrow().state.front_socket().as_raw_fd();
5668 let mut source = SourceFd(&frontend_fd);
5669 let mut poll = Poll::new().expect("create frontend poll");
5670 let observed_token = Token(7);
5671 poll.registry()
5672 .register(
5673 &mut source,
5674 observed_token,
5675 Interest::READABLE | Interest::WRITABLE,
5676 )
5677 .expect("register accepted frontend socket");
5678 let mut events = Events::with_capacity(4);
5679 poll.poll(&mut events, Some(Duration::from_secs(2)))
5680 .expect("poll accepted frontend socket");
5681 let ready = events
5682 .iter()
5683 .find(|event| event.token() == observed_token)
5684 .map(Ready::from)
5685 .expect("the queued request and FIN produce an epoll event");
5686 assert!(
5687 ready.is_readable(),
5688 "the queued FIN must be reported readable: {ready:?}"
5689 );
5690 assert!(
5691 ready.is_hup(),
5692 "the peer FIN must be reported as HUP: {ready:?}"
5693 );
5694 assert!(
5695 !ready.is_write_closed(),
5696 "EPOLLRDHUP leaves the response direction open: {ready:?}"
5697 );
5698 ready
5699 }
5700
5701 #[cfg(target_os = "linux")]
5714 #[test]
5715 fn expect_proxy_half_close_drains_payload_to_the_backend() {
5716 use std::{io::Read as _, time::Duration};
5717
5718 let mut fixture = expect_proxy_fixture();
5719
5720 let payload = b"request-after-proxy-header";
5721 let mut wire = proxy_protocol_v2_ipv4_header().to_vec();
5722 wire.extend_from_slice(payload);
5723 let ready = half_close_and_observe_frontend(&mut fixture, &wire);
5724
5725 fixture
5726 .session
5727 .borrow_mut()
5728 .update_readiness(fixture.frontend_token, ready);
5729 let closed = fixture
5730 .session
5731 .borrow_mut()
5732 .ready(fixture.proxy_session.clone());
5733 assert!(
5734 !closed,
5735 "READABLE|HUP must parse the header and retain the response direction"
5736 );
5737 assert!(
5738 !matches!(
5739 fixture.session.borrow().state,
5740 TcpStateMachine::ExpectProxyProtocol(_)
5741 ),
5742 "the complete header must upgrade the expect state"
5743 );
5744
5745 let (mut backend, _) = fixture
5746 ._backend_listener
5747 .accept()
5748 .expect("the parsed header triggers the backend dial");
5749 backend
5750 .set_read_timeout(Some(Duration::from_secs(2)))
5751 .expect("set backend read timeout");
5752 backend
5753 .set_write_timeout(Some(Duration::from_secs(2)))
5754 .expect("set backend write timeout");
5755
5756 let backend_token = fixture
5757 .session
5758 .borrow()
5759 .backend_token
5760 .expect("the upgraded session owns a backend token");
5761 fixture
5768 .session
5769 .borrow_mut()
5770 .update_readiness(backend_token, Ready::WRITABLE);
5771 let _ = fixture
5772 .session
5773 .borrow_mut()
5774 .ready(fixture.proxy_session.clone());
5775
5776 let mut backend_received = vec![0; payload.len()];
5777 backend
5778 .read_exact(&mut backend_received)
5779 .expect("backend receives the payload queued before FIN");
5780 assert_eq!(backend_received, payload);
5781 }
5782
5783 #[cfg(target_os = "linux")]
5790 #[test]
5791 fn expect_proxy_zero_byte_half_close_closes_without_dialing() {
5792 let mut fixture = expect_proxy_fixture();
5793 let ready = half_close_and_observe_frontend(&mut fixture, &[]);
5794
5795 fixture
5796 .session
5797 .borrow_mut()
5798 .update_readiness(fixture.frontend_token, ready);
5799 let closed = fixture
5800 .session
5801 .borrow_mut()
5802 .ready(fixture.proxy_session.clone());
5803 assert!(
5804 closed,
5805 "a zero-byte client half-close must close the session"
5806 );
5807 let session = fixture.session.borrow();
5808 assert_eq!(
5809 session.back_connected(),
5810 BackendConnectionStatus::NotConnected,
5811 "a zero-byte health check must never dial the backend"
5812 );
5813 assert!(
5814 session.backend_token.is_none(),
5815 "a zero-byte health check must never wire a backend token"
5816 );
5817 }
5818
5819 #[cfg(target_os = "linux")]
5824 #[test]
5825 fn expect_proxy_partial_header_half_close_closes_without_dialing() {
5826 let mut fixture = expect_proxy_fixture();
5827 let header = proxy_protocol_v2_ipv4_header();
5828 let ready = half_close_and_observe_frontend(&mut fixture, &header[..10]);
5829
5830 fixture
5831 .session
5832 .borrow_mut()
5833 .update_readiness(fixture.frontend_token, ready);
5834 let closed = fixture
5835 .session
5836 .borrow_mut()
5837 .ready(fixture.proxy_session.clone());
5838 assert!(
5839 closed,
5840 "an incomplete PROXY header followed by FIN must close the session"
5841 );
5842 let session = fixture.session.borrow();
5843 assert_eq!(
5844 session.back_connected(),
5845 BackendConnectionStatus::NotConnected,
5846 "an incomplete PROXY header must never dial the backend"
5847 );
5848 }
5849
5850 #[test]
5866 fn connecting_backend_without_readiness_closes_instead_of_panicking() {
5867 let fixture = expect_proxy_fixture();
5868 let (session, proxy_session) = (fixture.session.clone(), fixture.proxy_session.clone());
5869
5870 session.borrow_mut().backend_connected =
5873 BackendConnectionStatus::Connecting(Instant::now());
5874 assert!(
5875 session.borrow_mut().back_readiness().is_none(),
5876 "the expect state must have no backend readiness -- the premise of this test"
5877 );
5878
5879 let closed = session.borrow_mut().ready(proxy_session.clone());
5880 assert!(
5881 closed,
5882 "a connecting backend with no readiness slot must close the session"
5883 );
5884 }
5885
5886 fn expect_proxy_fixture_dialed() -> ExpectProxyFixture {
5889 use std::io::Write as _;
5890
5891 let mut fixture = expect_proxy_fixture();
5892 fixture
5893 .client
5894 .write_all(&proxy_protocol_v2_ipv4_header())
5895 .expect("write the PROXY-v2 header");
5896 fixture
5897 .session
5898 .borrow_mut()
5899 .update_readiness(fixture.frontend_token, Ready::READABLE);
5900 let proxy_session = fixture.proxy_session.clone();
5901 let _ = fixture.session.borrow_mut().ready(proxy_session);
5902 {
5903 let session = fixture.session.borrow();
5904 assert!(
5905 matches!(session.state, TcpStateMachine::Pipe(_)),
5906 "precondition: the parsed header upgrades the session to Pipe"
5907 );
5908 assert!(
5909 session.backend_token.is_some(),
5910 "precondition: the Pipe dialed its backend"
5911 );
5912 }
5913 fixture
5914 }
5915
5916 #[test]
5923 fn a_dialed_pipe_reports_the_backend_address_it_was_given() {
5924 let fixture = expect_proxy_fixture_dialed();
5925 let backend_address = fixture
5926 ._backend_listener
5927 .local_addr()
5928 .expect("test backend local addr");
5929 let session = fixture.session.borrow();
5930 let TcpStateMachine::Pipe(pipe) = &session.state else {
5931 unreachable!("expect_proxy_fixture_dialed checked the Pipe state");
5932 };
5933 assert_eq!(
5934 pipe.get_backend_address(),
5935 Some(backend_address),
5936 "the pipe must report the backend address the session dialed"
5937 );
5938 }
5939
5940 #[cfg(target_os = "linux")]
5952 #[test]
5953 fn closed_tcp_sessions_leave_their_sockets_to_close() {
5954 use std::os::fd::AsRawFd as _;
5955
5956 use crate::protocol::mux::test_support::epoll_watches;
5957
5958 let fixture = expect_proxy_fixture_dialed();
5959 let session = fixture.session.clone();
5960 let frontend_token = fixture.frontend_token;
5961 let registry = session
5962 .borrow()
5963 .proxy
5964 .borrow()
5965 .registry
5966 .try_clone()
5967 .expect("the test registry must clone");
5968
5969 let (front_fd, back_fd) = {
5972 let mut session = session.borrow_mut();
5973 let front_fd = session.state.front_socket().as_raw_fd();
5974 registry
5975 .register(
5976 &mut mio::unix::SourceFd(&front_fd),
5977 frontend_token,
5978 Interest::READABLE | Interest::WRITABLE,
5979 )
5980 .expect("register the front socket");
5981 let back_fd = session
5982 .back_socket_mut()
5983 .expect("the dialed Pipe owns a backend socket")
5984 .as_raw_fd();
5985 (front_fd, back_fd)
5986 };
5987 assert!(
5988 epoll_watches(®istry, front_fd) && epoll_watches(®istry, back_fd),
5989 "precondition: both sockets are in the epoll set"
5990 );
5991
5992 let removed = session
5994 .borrow()
5995 .proxy
5996 .borrow()
5997 .sessions
5998 .borrow_mut()
5999 .slab
6000 .remove(frontend_token.0);
6001 session.borrow_mut().close();
6002 assert!(
6003 epoll_watches(®istry, front_fd),
6004 "TcpSession::close must not deregister a front socket it is about \
6005 to close: the close removes it, the EPOLL_CTL_DEL is a wasted syscall"
6006 );
6007 assert!(
6008 epoll_watches(®istry, back_fd),
6009 "TcpSession::close_backend must not deregister a backend socket it \
6010 is about to close: the close removes it"
6011 );
6012
6013 drop(removed);
6014 drop(session);
6015 drop(fixture);
6016 assert!(
6017 !epoll_watches(®istry, front_fd),
6018 "dropping the session closes the front socket's only descriptor, \
6019 which must take it out of the epoll set"
6020 );
6021 assert!(
6022 !epoll_watches(®istry, back_fd),
6023 "dropping the session closes the backend socket's only descriptor, \
6024 which must take it out of the epoll set"
6025 );
6026 }
6027
6028 struct DialFixture {
6031 session: Rc<RefCell<TcpSession>>,
6032 proxy_session: Rc<RefCell<dyn ProxySession>>,
6033 frontend_token: Token,
6034 backend_address: SocketAddr,
6035 client: std::net::TcpStream,
6036 _frontend_listener: std::net::TcpListener,
6037 _backend_listener: std::net::TcpListener,
6038 }
6039
6040 fn dial_fixture(
6045 sni_route: bool,
6046 build: impl FnOnce(
6047 Checkout,
6048 Checkout,
6049 Token,
6050 Rc<RefCell<TcpListener>>,
6051 Rc<RefCell<TcpProxy>>,
6052 MioTcpStream,
6053 SocketAddr,
6054 ) -> TcpSession,
6055 ) -> DialFixture {
6056 let ServerParts {
6057 registry,
6058 sessions,
6059 pool,
6060 backends,
6061 ..
6062 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
6063
6064 let backend_listener =
6065 std::net::TcpListener::bind("127.0.0.1:0").expect("bind test backend");
6066 let backend_address = backend_listener
6067 .local_addr()
6068 .expect("test backend local addr");
6069 backends.borrow_mut().add_backend(
6070 "cluster-dial",
6071 Backend::new("cluster-dial-1", backend_address, None, None, None),
6072 );
6073
6074 let proxy = Rc::new(RefCell::new(TcpProxy::new(
6075 registry,
6076 sessions.clone(),
6077 pool.clone(),
6078 backends,
6079 )));
6080 let mut bare_listener = test_listener();
6081 if sni_route {
6082 bare_listener
6083 .insert_sni_route("example.com".to_owned(), vec![], "cluster-dial".into())
6084 .expect("insert_sni_route must succeed for a valid test SNI");
6085 }
6086 let listener = Rc::new(RefCell::new(bare_listener));
6087
6088 let frontend_listener =
6089 std::net::TcpListener::bind("127.0.0.1:0").expect("bind test frontend listener");
6090 let client = std::net::TcpStream::connect(
6091 frontend_listener
6092 .local_addr()
6093 .expect("test frontend local addr"),
6094 )
6095 .expect("connect test client");
6096 let (frontend, frontend_peer) = frontend_listener
6097 .accept()
6098 .expect("accept the client connection");
6099 frontend
6100 .set_nonblocking(true)
6101 .expect("frontend socket nonblocking");
6102
6103 let (frontend_buffer, backend_buffer) = {
6104 let mut pool = pool.borrow_mut();
6105 (
6106 pool.checkout().expect("front buffer checkout must succeed"),
6107 pool.checkout().expect("back buffer checkout must succeed"),
6108 )
6109 };
6110
6111 let frontend_token = {
6112 let mut session_manager = sessions.borrow_mut();
6113 let entry = session_manager.slab.vacant_entry();
6114 let token = Token(entry.key());
6115 entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
6116 protocol: Protocol::TCPListen,
6117 })));
6118 token
6119 };
6120
6121 let session = Rc::new(RefCell::new(build(
6122 backend_buffer,
6123 frontend_buffer,
6124 frontend_token,
6125 listener,
6126 proxy,
6127 MioTcpStream::from_std(frontend),
6128 frontend_peer,
6129 )));
6130 let proxy_session: Rc<RefCell<dyn ProxySession>> = session.clone();
6131 sessions.borrow_mut().slab[frontend_token.0] = proxy_session.clone();
6132
6133 DialFixture {
6134 session,
6135 proxy_session,
6136 frontend_token,
6137 backend_address,
6138 client,
6139 _frontend_listener: frontend_listener,
6140 _backend_listener: backend_listener,
6141 }
6142 }
6143
6144 fn proxy_protocol_session_dialed(proxy_protocol: ProxyProtocolConfig) -> DialFixture {
6148 let fixture = dial_fixture(
6149 false,
6150 |back_buffer, front_buffer, token, listener, proxy, socket, peer| {
6151 TcpSession::new(
6152 back_buffer,
6153 None,
6154 Some("cluster-dial".into()),
6155 Duration::from_secs(30),
6156 Duration::from_secs(30),
6157 Duration::from_secs(30),
6158 front_buffer,
6159 token,
6160 listener,
6161 Some(proxy_protocol),
6162 proxy,
6163 socket,
6164 peer,
6165 Duration::from_millis(0),
6166 )
6167 },
6168 );
6169 fixture
6170 .session
6171 .borrow_mut()
6172 .update_readiness(fixture.frontend_token, Ready::WRITABLE);
6173 let closed = fixture
6174 .session
6175 .borrow_mut()
6176 .ready(fixture.proxy_session.clone());
6177 assert!(!closed, "precondition: the dial keeps the session open");
6178 fixture
6179 }
6180
6181 #[test]
6182 fn static_tcp_route_keeps_its_capture_after_cluster_replacement() {
6183 let fixture = dial_fixture(
6184 false,
6185 |back_buffer, front_buffer, token, listener, proxy, socket, peer| {
6186 TcpSession::new(
6187 back_buffer,
6188 None,
6189 Some("cluster-dial".into()),
6190 Duration::from_secs(30),
6191 Duration::from_secs(30),
6192 Duration::from_secs(30),
6193 front_buffer,
6194 token,
6195 listener,
6196 None,
6197 proxy,
6198 socket,
6199 peer,
6200 Duration::from_millis(0),
6201 )
6202 },
6203 );
6204 let initial_capture = fixture.session.borrow().cluster_metrics_incarnation;
6205 assert!(
6206 matches!(
6207 initial_capture,
6208 crate::metrics::ClusterMetricsCapture::Active(_)
6209 ),
6210 "precondition: the static route captured an implicit active identity",
6211 );
6212
6213 crate::metrics::METRICS.with(|metrics| {
6214 let mut metrics = metrics.borrow_mut();
6215 metrics.remove_cluster("cluster-dial");
6216 metrics.add_cluster("cluster-dial");
6217 let replacement = metrics
6218 .cluster_incarnation("cluster-dial")
6219 .expect("replacement cluster identity");
6220 assert_ne!(
6221 initial_capture,
6222 crate::metrics::ClusterMetricsCapture::Active(replacement),
6223 );
6224 });
6225 fixture
6226 .session
6227 .borrow_mut()
6228 .update_readiness(fixture.frontend_token, Ready::WRITABLE);
6229 let closed = fixture
6230 .session
6231 .borrow_mut()
6232 .ready(fixture.proxy_session.clone());
6233 assert!(!closed, "the backend dial must keep the session open");
6234 assert_eq!(
6235 fixture.session.borrow().cluster_metrics_incarnation,
6236 initial_capture,
6237 "a retry after replacement must keep the identity captured at static routing time",
6238 );
6239 crate::metrics::METRICS.with(|metrics| metrics.borrow_mut().remove_cluster("cluster-dial"));
6240 }
6241
6242 #[test]
6243 fn sni_tcp_route_keeps_its_capture_after_cluster_replacement() {
6244 use std::io::Write as _;
6245
6246 let mut fixture = dial_fixture(
6247 true,
6248 |back_buffer, front_buffer, token, listener, proxy, socket, peer| {
6249 TcpSession::new_sni_preread(
6250 back_buffer,
6251 Duration::from_secs(30),
6252 Duration::from_secs(30),
6253 front_buffer,
6254 token,
6255 listener,
6256 proxy,
6257 socket,
6258 peer,
6259 Duration::from_millis(0),
6260 Duration::from_secs(3),
6261 16384,
6262 )
6263 },
6264 );
6265 fixture
6266 .client
6267 .write_all(&minimal_client_hello_wire("example.com"))
6268 .expect("write ClientHello");
6269 fixture.client.flush().ok();
6270 for _ in 0..10 {
6271 if fixture.session.borrow().cluster_id.is_some() {
6272 break;
6273 }
6274 let _ = fixture.session.borrow_mut().readable();
6275 }
6276 assert_eq!(
6277 fixture.session.borrow().cluster_id.as_deref(),
6278 Some("cluster-dial")
6279 );
6280 let initial_capture = fixture.session.borrow().cluster_metrics_incarnation;
6281 assert!(
6282 matches!(
6283 initial_capture,
6284 crate::metrics::ClusterMetricsCapture::Active(_)
6285 ),
6286 "precondition: SNI routing captured an implicit active identity",
6287 );
6288
6289 crate::metrics::METRICS.with(|metrics| {
6290 let mut metrics = metrics.borrow_mut();
6291 metrics.remove_cluster("cluster-dial");
6292 metrics.add_cluster("cluster-dial");
6293 let replacement = metrics
6294 .cluster_incarnation("cluster-dial")
6295 .expect("replacement cluster identity");
6296 assert_ne!(
6297 initial_capture,
6298 crate::metrics::ClusterMetricsCapture::Active(replacement),
6299 );
6300 });
6301 let closed = fixture
6302 .session
6303 .borrow_mut()
6304 .ready(fixture.proxy_session.clone());
6305 assert!(
6306 !closed,
6307 "the SNI-routed backend dial must keep the session open"
6308 );
6309 assert_eq!(
6310 fixture.session.borrow().cluster_metrics_incarnation,
6311 initial_capture,
6312 "SNI upgrade and retry after replacement must keep the route-time identity",
6313 );
6314 crate::metrics::METRICS.with(|metrics| metrics.borrow_mut().remove_cluster("cluster-dial"));
6315 }
6316
6317 fn assert_upgraded_pipe_reports_the_dialed_address(fixture: &DialFixture) {
6321 {
6322 let session = fixture.session.borrow();
6323 assert!(
6324 !matches!(session.state, TcpStateMachine::Pipe(_)),
6325 "precondition: the backend was dialed before the Pipe existed"
6326 );
6327 assert!(
6328 session.backend_token.is_some(),
6329 "precondition: the session dialed its backend"
6330 );
6331 }
6332 let closed = fixture.session.borrow_mut().upgrade();
6333 assert!(!closed, "the upgrade to Pipe must succeed");
6334 let session = fixture.session.borrow();
6335 let TcpStateMachine::Pipe(pipe) = &session.state else {
6336 panic!("the upgrade must install a Pipe");
6337 };
6338 assert_eq!(
6339 pipe.get_backend_address(),
6340 Some(fixture.backend_address),
6341 "the Pipe must report the backend address dialed before the upgrade"
6342 );
6343 }
6344
6345 #[test]
6351 fn a_send_proxy_protocol_pipe_reports_the_dialed_backend_address() {
6352 let fixture = proxy_protocol_session_dialed(ProxyProtocolConfig::SendHeader);
6353 assert!(
6354 matches!(
6355 fixture.session.borrow().state,
6356 TcpStateMachine::SendProxyProtocol(_)
6357 ),
6358 "precondition: a SendHeader cluster starts in the send state"
6359 );
6360 assert_upgraded_pipe_reports_the_dialed_address(&fixture);
6361 }
6362
6363 #[test]
6369 fn a_relay_proxy_protocol_pipe_reports_the_dialed_backend_address() {
6370 let fixture = proxy_protocol_session_dialed(ProxyProtocolConfig::RelayHeader);
6371 assert!(
6372 matches!(
6373 fixture.session.borrow().state,
6374 TcpStateMachine::RelayProxyProtocol(_)
6375 ),
6376 "precondition: a RelayHeader cluster starts in the relay state"
6377 );
6378 assert_upgraded_pipe_reports_the_dialed_address(&fixture);
6379 }
6380
6381 #[test]
6389 fn an_sni_preread_pipe_reports_the_dialed_backend_address() {
6390 use std::io::Write as _;
6391
6392 let mut fixture = dial_fixture(
6393 true,
6394 |back_buffer, front_buffer, token, listener, proxy, socket, peer| {
6395 TcpSession::new_sni_preread(
6396 back_buffer,
6397 Duration::from_secs(30),
6398 Duration::from_secs(30),
6399 front_buffer,
6400 token,
6401 listener,
6402 proxy,
6403 socket,
6404 peer,
6405 Duration::from_millis(0),
6406 Duration::from_secs(3),
6407 16384,
6408 )
6409 },
6410 );
6411 fixture
6412 .client
6413 .write_all(&minimal_client_hello_wire("example.com"))
6414 .expect("write ClientHello");
6415 fixture.client.flush().ok();
6416 for _ in 0..10 {
6417 if fixture.session.borrow().cluster_id.is_some() {
6418 break;
6419 }
6420 let _ = fixture.session.borrow_mut().readable();
6421 }
6422 assert_eq!(
6423 fixture.session.borrow().cluster_id.as_deref(),
6424 Some("cluster-dial"),
6425 "precondition: the ClientHello routes the session"
6426 );
6427 let closed = fixture
6428 .session
6429 .borrow_mut()
6430 .ready(fixture.proxy_session.clone());
6431 assert!(!closed, "precondition: the dial keeps the session open");
6432 assert!(
6433 matches!(
6434 fixture.session.borrow().state,
6435 TcpStateMachine::SniPreread(_)
6436 ),
6437 "precondition: the routed session dials while still in SniPreread"
6438 );
6439 assert_upgraded_pipe_reports_the_dialed_address(&fixture);
6440 }
6441}
6442
6443#[cfg(test)]
6450mod relay_upgrade_tests {
6451 use std::net::TcpListener as StdTcpListener;
6452
6453 use sozu_command::{config::ListenerBuilder, proto::command::SocketAddress};
6454
6455 use super::*;
6456 use crate::{pool::Pool, testing::provide_port};
6457
6458 fn test_listener() -> TcpListener {
6459 let config = ListenerBuilder::new_tcp(SocketAddress::new_v4(127, 0, 0, 1, provide_port()))
6460 .to_tcp(None)
6461 .expect("could not build a TcpListenerConfig for the test");
6462 TcpListener::new(config, Token(0)).expect("could not build a bare TcpListener for the test")
6463 }
6464
6465 fn connected_pair() -> (std::net::TcpStream, std::net::TcpStream) {
6466 let listener = StdTcpListener::bind("127.0.0.1:0").expect("bind the test listener");
6467 let address = listener
6468 .local_addr()
6469 .expect("the test listener must expose its address");
6470 let local = std::net::TcpStream::connect(address).expect("connect the test pair");
6471 let (peer, _addr) = listener.accept().expect("accept the test pair");
6472 local
6473 .set_nonblocking(true)
6474 .expect("the local end must be non-blocking");
6475 peer.set_nonblocking(true)
6476 .expect("the peer end must be non-blocking");
6477 (local, peer)
6478 }
6479
6480 #[test]
6500 fn into_pipe_rearms_the_backend_write_for_pipelined_payload() {
6501 let listener = Rc::new(RefCell::new(test_listener()));
6502 let (frontend, _front_peer) = connected_pair();
6503 let (backend, _backend_peer) = connected_pair();
6504
6505 let mut pool = Pool::with_capacity(2, 2, 4096);
6506 let mut frontend_buffer = pool.checkout().expect("frontend buffer");
6507 let backend_buffer = pool.checkout().expect("backend buffer");
6508 let payload = b"pipelined-payload";
6511 frontend_buffer.space()[..payload.len()].copy_from_slice(payload);
6512 frontend_buffer.fill(payload.len());
6513
6514 let mut relay = RelayProxyProtocol::new(
6515 MioTcpStream::from_std(frontend),
6516 Token(0),
6517 Ulid::generate(),
6518 Some(MioTcpStream::from_std(backend)),
6519 frontend_buffer,
6520 );
6521 relay.header_size = Some(28);
6524 relay.set_back_token(Token(1));
6525 relay.backend_readiness.event = Ready::EMPTY;
6529 relay.frontend_readiness.event = Ready::EMPTY;
6530
6531 let pipe = relay.into_pipe(backend_buffer, listener);
6532
6533 assert!(
6534 pipe.backend_readiness.event.is_writable(),
6535 "a non-empty inherited frontend buffer must leave a backend WRITABLE event queued, or the pipelined payload is never flushed"
6536 );
6537 assert!(
6538 pipe.backend_readiness.interest.is_writable(),
6539 "the inherited-write arm must also hold the backend WRITABLE interest"
6540 );
6541 }
6542}