1use std::{
3 cell::RefCell,
4 collections::{HashMap, HashSet, VecDeque, hash_map::Entry},
5 hash::{DefaultHasher, Hash, Hasher},
6 io::Error as IoError,
7 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
8 os::unix::io::{AsRawFd, FromRawFd, RawFd},
9 rc::Rc,
10 str::FromStr,
11 sync::LazyLock,
12 time::{Duration, Instant},
13};
14
15use mio::{
16 Events, Interest, Poll, Token,
17 net::{TcpListener as MioTcpListener, TcpStream, UdpSocket as MioUdpSocket},
18};
19use slab::Slab;
20use sozu_command::{
21 channel::Channel,
22 config::MetricDetailLevel,
23 listener_key::ListenerKey,
24 logging,
25 proto::command::{
26 ActivateListener, AddBackend, CertificatesWithFingerprints, Cluster, ClusterHashes,
27 ClusterInformations, DeactivateListener, Event, EventKind, HttpListenerConfig,
28 HttpsListenerConfig, InitialState, ListenerType, LoadBalancingAlgorithms, LoadMetric,
29 MetricDetail, MetricsConfiguration, RemoveBackend, Request, ResponseContent,
30 ResponseStatus, ServerConfig, TcpListenerConfig as CommandTcpListener,
31 UdpListenerConfig as CommandUdpListener, UpdateHttpListenerConfig,
32 UpdateHttpsListenerConfig, UpdateTcpListenerConfig, UpdateUdpListenerConfig, WorkerRequest,
33 WorkerResponse, request::RequestType, response_content::ContentType,
34 },
35 ready::Ready,
36 scm_socket::{Listeners, ScmSocket, ScmSocketError},
37 state::{ClusterId, ConfigState, StateError},
38};
39
40use crate::metrics::names;
41use crate::{
42 AcceptError, Protocol, ProxyConfiguration, ProxySession, SessionIsToBeClosed,
43 backends::{Backend, BackendMap},
44 features::FEATURES,
45 health_check::HealthChecker,
46 http, https,
47 load_balancing::ShuffleSharding,
48 metrics::METRICS,
49 pool::Pool,
50 tcp,
51 timer::Timer,
52 udp,
53};
54
55pub const PER_SOURCE_BUCKETS: usize = 256;
69
70static PER_SOURCE_BUCKET_KEYS: LazyLock<[&'static str; PER_SOURCE_BUCKETS]> = LazyLock::new(|| {
75 let mut keys: [&'static str; PER_SOURCE_BUCKETS] = [""; PER_SOURCE_BUCKETS];
76 for (i, slot) in keys.iter_mut().enumerate() {
77 let owned = format!("client.connect.per_source.bucket_{i:03}");
79 *slot = Box::leak(owned.into_boxed_str());
80 }
81 keys
82});
83
84fn per_source_bucket(peer: &SocketAddr) -> &'static str {
89 let mut hasher = DefaultHasher::new();
90 match peer.ip() {
91 IpAddr::V4(v4) => {
92 let octets = v4.octets();
93 let masked = Ipv4Addr::new(octets[0], octets[1], octets[2], 0);
95 masked.hash(&mut hasher);
96 }
97 IpAddr::V6(v6) => {
98 let octets = v6.octets();
99 let mut masked_octets = [0u8; 16];
101 masked_octets[..6].copy_from_slice(&octets[..6]);
102 Ipv6Addr::from(masked_octets).hash(&mut hasher);
103 }
104 }
105 let idx = (hasher.finish() as usize) % PER_SOURCE_BUCKETS;
106 PER_SOURCE_BUCKET_KEYS[idx]
107}
108
109const ACCEPT_SATURATION_TICK: Duration = Duration::from_secs(1);
114
115pub type ProxyChannel = Channel<WorkerResponse, WorkerRequest>;
116
117thread_local! {
118 pub static QUEUE: RefCell<VecDeque<WorkerResponse>> = const { RefCell::new(VecDeque::new()) };
119}
120
121thread_local! {
122 pub static TIMER: RefCell<Timer<Token>> = RefCell::new(Timer::default());
123}
124
125pub fn push_queue(message: WorkerResponse) {
126 QUEUE.with(|queue| {
127 (*queue.borrow_mut()).push_back(message);
128 });
129}
130
131pub fn push_event(event: Event) {
132 QUEUE.with(|queue| {
133 (*queue.borrow_mut()).push_back(WorkerResponse {
134 id: "EVENT".to_string(),
135 message: String::new(),
136 status: ResponseStatus::Processing.into(),
137 content: Some(ContentType::Event(event).into()),
138 });
139 });
140}
141
142fn is_frontend_removal(request: &Request) -> bool {
144 matches!(
145 request.request_type,
146 Some(
147 RequestType::RemoveHttpFrontend(_)
148 | RequestType::RemoveHttpsFrontend(_)
149 | RequestType::RemoveTcpFrontend(_)
150 | RequestType::RemoveUdpFrontend(_)
151 )
152 )
153}
154
155fn worker_metric_detail_status_content(
161 configured: MetricDetailLevel,
162 effective: MetricDetailLevel,
163 previous_effective: MetricDetailLevel,
164 active_lease_count: u32,
165) -> ResponseContent {
166 use sozu_command::proto::command::WorkerMetricDetailStatus;
167 ContentType::WorkerMetricDetailStatus(WorkerMetricDetailStatus {
168 configured: MetricDetail::from(configured) as i32,
169 effective: MetricDetail::from(effective) as i32,
170 previous_effective: MetricDetail::from(previous_effective) as i32,
171 active_lease_count,
172 })
173 .into()
174}
175
176fn push_metric_detail_transition(
183 previous: MetricDetailLevel,
184 effective: MetricDetailLevel,
185 transition_kind: &'static str,
186 client_id: Option<String>,
187) {
188 use sozu_command::proto::command::MetricDetailTransition;
189 if previous == effective {
194 return;
195 }
196 push_event(Event {
197 kind: EventKind::MetricDetailChanged as i32,
198 cluster_id: None,
199 backend_id: None,
200 address: None,
201 metric_detail: Some(MetricDetailTransition {
202 previous_effective: MetricDetail::from(previous) as i32,
203 effective: MetricDetail::from(effective) as i32,
204 transition_kind: transition_kind.to_owned(),
205 client_id,
206 }),
207 });
208}
209
210fn tick_metric_detail_leases(now: Instant) {
218 let lease_tick_transition = METRICS.with(|metrics| {
225 let mut m = metrics.borrow_mut();
226 if !m.lease_tick_due(now) {
227 return None;
228 }
229 let previous = m.lease_tick(now)?;
230 let effective = m.detail_effective();
231 Some((previous, effective))
232 });
233 if let Some((previous, effective)) = lease_tick_transition {
234 push_metric_detail_transition(previous, effective, "lease_tick_expired", None);
239 }
240}
241
242#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
243pub struct ListenToken(pub usize);
244#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
245pub struct SessionToken(pub usize);
246
247impl From<usize> for ListenToken {
248 fn from(val: usize) -> ListenToken {
249 ListenToken(val)
250 }
251}
252
253impl From<ListenToken> for usize {
254 fn from(val: ListenToken) -> usize {
255 val.0
256 }
257}
258
259impl From<usize> for SessionToken {
260 fn from(val: usize) -> SessionToken {
261 SessionToken(val)
262 }
263}
264
265impl From<SessionToken> for usize {
266 fn from(val: SessionToken) -> usize {
267 val.0
268 }
269}
270
271fn mask_ipv4(address: Ipv4Addr, prefix: u32) -> Ipv4Addr {
285 if prefix >= u32::BITS {
286 return address;
287 }
288 if prefix == 0 {
289 return Ipv4Addr::UNSPECIFIED;
290 }
291 debug_assert!(
294 prefix > 0 && prefix < u32::BITS,
295 "both shift-overflowing ends must have returned before the shift"
296 );
297 let mask = u32::MAX << (u32::BITS - prefix);
298 let masked = Ipv4Addr::from(u32::from(address) & mask);
299 debug_assert_eq!(
303 u32::from(masked),
304 u32::from(address) & mask,
305 "masking must keep exactly the network bits of the source address"
306 );
307 debug_assert_eq!(
308 u32::from(masked) & !mask,
309 0,
310 "masking must clear every host bit below the prefix"
311 );
312 masked
313}
314
315fn mask_ipv6(address: Ipv6Addr, prefix: u32) -> Ipv6Addr {
319 if prefix >= u128::BITS {
320 return address;
321 }
322 if prefix == 0 {
323 return Ipv6Addr::UNSPECIFIED;
324 }
325 debug_assert!(
326 prefix > 0 && prefix < u128::BITS,
327 "both shift-overflowing ends must have returned before the shift"
328 );
329 let mask = u128::MAX << (u128::BITS - prefix);
330 let masked = Ipv6Addr::from(u128::from(address) & mask);
331 debug_assert_eq!(
332 u128::from(masked),
333 u128::from(address) & mask,
334 "masking must keep exactly the network bits of the source address"
335 );
336 debug_assert_eq!(
337 u128::from(masked) & !mask,
338 0,
339 "masking must clear every host bit below the prefix"
340 );
341 masked
342}
343
344pub fn subnet_key(address: &IpAddr, ipv4_prefix: u32, ipv6_prefix: u32) -> IpAddr {
355 let canonical_v4 = match address {
356 IpAddr::V4(v4) => Some(*v4),
357 IpAddr::V6(v6) => v6.to_ipv4_mapped(),
358 };
359 let key = match (canonical_v4, address) {
360 (Some(v4), _) => IpAddr::V4(mask_ipv4(v4, ipv4_prefix)),
361 (None, IpAddr::V6(v6)) => IpAddr::V6(mask_ipv6(*v6, ipv6_prefix)),
362 (None, IpAddr::V4(v4)) => IpAddr::V4(mask_ipv4(*v4, ipv4_prefix)),
364 };
365 debug_assert_eq!(
369 key.is_ipv4(),
370 canonical_v4.is_some(),
371 "subnet key family must follow the canonicalised source family"
372 );
373 debug_assert_eq!(
374 key.is_ipv6(),
375 canonical_v4.is_none(),
376 "a genuine IPv6 source must never produce an IPv4 subnet key"
377 );
378 key
379}
380
381pub struct SessionManager {
382 pub max_connections: usize,
383 pub nb_connections: usize,
384 pub can_accept: bool,
385 pub slab: Slab<Rc<RefCell<dyn ProxySession>>>,
386 pub max_connections_per_ip: u64,
390 pub retry_after: u32,
394 connections_per_cluster_ip: HashMap<ClusterId, HashMap<IpAddr, usize>>,
418 cluster_ip_tracks: HashMap<Token, HashMap<ClusterId, HashSet<IpAddr>>>,
425 pub max_connections_per_subnet: u64,
439 pub subnet_ipv4_prefix: u32,
443 pub subnet_ipv6_prefix: u32,
446 connections_per_cluster_subnet: HashMap<ClusterId, HashMap<IpAddr, usize>>,
459 cluster_subnet_tracks: HashMap<Token, HashMap<ClusterId, HashSet<IpAddr>>>,
462 pub max_connection_attempts: u32,
469}
470
471impl SessionManager {
472 pub fn new(
473 slab: Slab<Rc<RefCell<dyn ProxySession>>>,
474 max_connections: usize,
475 max_connections_per_ip: u64,
476 retry_after: u32,
477 max_connections_per_subnet: u64,
478 subnet_ipv4_prefix: u32,
479 subnet_ipv6_prefix: u32,
480 ) -> Rc<RefCell<Self>> {
481 Rc::new(RefCell::new(SessionManager {
482 max_connections,
483 nb_connections: 0,
484 can_accept: true,
485 slab,
486 max_connections_per_ip,
487 retry_after,
488 connections_per_cluster_ip: HashMap::new(),
489 cluster_ip_tracks: HashMap::new(),
490 max_connections_per_subnet,
491 subnet_ipv4_prefix,
492 subnet_ipv6_prefix,
493 connections_per_cluster_subnet: HashMap::new(),
494 cluster_subnet_tracks: HashMap::new(),
495 max_connection_attempts: sozu_command::config::DEFAULT_MAX_CONNECTION_ATTEMPTS,
496 }))
497 }
498
499 pub fn effective_max_connection_attempts(&self, override_value: Option<u32>) -> u32 {
503 override_value.unwrap_or(self.max_connection_attempts)
504 }
505
506 pub fn effective_max_connections_per_ip(&self, override_value: Option<u64>) -> u64 {
511 override_value.unwrap_or(self.max_connections_per_ip)
512 }
513
514 pub fn effective_retry_after(&self, override_value: Option<u32>) -> u32 {
518 override_value.unwrap_or(self.retry_after)
519 }
520
521 pub fn effective_max_connections_per_subnet(&self, override_value: Option<u64>) -> u64 {
526 override_value.unwrap_or(self.max_connections_per_subnet)
527 }
528
529 pub fn subnet_key(&self, ip: &IpAddr) -> IpAddr {
532 subnet_key(ip, self.subnet_ipv4_prefix, self.subnet_ipv6_prefix)
533 }
534
535 pub fn cluster_subnet_at_limit(
545 &self,
546 token: Token,
547 cluster_id: &str,
548 ip: &IpAddr,
549 override_value: Option<u64>,
550 ) -> bool {
551 let limit = self.effective_max_connections_per_subnet(override_value);
552 if limit == 0 {
553 return false;
554 }
555 debug_assert!(
556 limit > 0,
557 "limit==0 (unlimited) must have returned before reaching the bounded check"
558 );
559 let subnet = self.subnet_key(ip);
560 let already_tracked = self
561 .cluster_subnet_tracks
562 .get(&token)
563 .and_then(|by_cluster| by_cluster.get(cluster_id))
564 .is_some_and(|subnets| subnets.contains(&subnet));
565 if already_tracked {
566 debug_assert!(
567 self.connections_per_cluster_subnet
568 .get(cluster_id)
569 .and_then(|by_subnet| by_subnet.get(&subnet))
570 .is_some_and(|c| *c > 0),
571 "a tracked (token, cluster, subnet) slot must have a positive forward count"
572 );
573 return false;
574 }
575 self.connections_per_cluster_subnet
576 .get(cluster_id)
577 .and_then(|by_subnet| by_subnet.get(&subnet))
578 .is_some_and(|c| (*c as u64) >= limit)
579 }
580
581 pub fn cluster_connection_at_limit(
592 &self,
593 token: Token,
594 cluster_id: &str,
595 ip: &IpAddr,
596 ip_override: Option<u64>,
597 subnet_override: Option<u64>,
598 ) -> bool {
599 self.cluster_ip_at_limit(token, cluster_id, ip, ip_override)
600 || self.cluster_subnet_at_limit(token, cluster_id, ip, subnet_override)
601 }
602
603 pub fn cluster_ip_at_limit(
615 &self,
616 token: Token,
617 cluster_id: &str,
618 ip: &IpAddr,
619 override_value: Option<u64>,
620 ) -> bool {
621 let limit = self.effective_max_connections_per_ip(override_value);
622 if limit == 0 {
623 return false;
624 }
625 debug_assert!(
628 limit > 0,
629 "limit==0 (unlimited) must have returned before reaching the bounded check"
630 );
631 let already_tracked = self
632 .cluster_ip_tracks
633 .get(&token)
634 .and_then(|by_cluster| by_cluster.get(cluster_id))
635 .is_some_and(|ips| ips.contains(ip));
636 if already_tracked {
637 debug_assert!(
641 self.connections_per_cluster_ip
642 .get(cluster_id)
643 .and_then(|by_ip| by_ip.get(ip))
644 .is_some_and(|c| *c > 0),
645 "a tracked (token, cluster, ip) slot must have a positive forward count"
646 );
647 return false;
648 }
649 self.connections_per_cluster_ip
650 .get(cluster_id)
651 .and_then(|by_ip| by_ip.get(ip))
652 .is_some_and(|c| (*c as u64) >= limit)
653 }
654
655 pub fn track_cluster_ip(&mut self, token: Token, cluster_id: ClusterId, ip: IpAddr) {
735 let count_before = self
739 .connections_per_cluster_ip
740 .get(&cluster_id)
741 .and_then(|by_ip| by_ip.get(&ip))
742 .copied()
743 .unwrap_or(0);
744 let inserted = self
745 .cluster_ip_tracks
746 .entry(token)
747 .or_default()
748 .entry(cluster_id.clone())
749 .or_default()
750 .insert(ip);
751 if inserted {
752 *self
753 .connections_per_cluster_ip
754 .entry(cluster_id.clone())
755 .or_default()
756 .entry(ip)
757 .or_insert(0) += 1;
758 }
759 debug_assert!(
763 self.cluster_ip_tracks
764 .get(&token)
765 .and_then(|by_cluster| by_cluster.get(&cluster_id))
766 .is_some_and(|ips| ips.contains(&ip)),
767 "track must leave the (token, cluster, ip) recorded in the reverse index"
768 );
769 debug_assert_eq!(
770 self.connections_per_cluster_ip
771 .get(&cluster_id)
772 .and_then(|by_ip| by_ip.get(&ip))
773 .copied()
774 .unwrap_or(0),
775 count_before + inserted as usize,
776 "forward count must advance by exactly 1 on first track, 0 on a repeat"
777 );
778 #[cfg(debug_assertions)]
779 self.check_invariants();
780 }
781
782 pub fn track_cluster_subnet(
796 &mut self,
797 token: Token,
798 cluster_id: ClusterId,
799 ip: IpAddr,
800 override_value: Option<u64>,
801 ) {
802 if self.effective_max_connections_per_subnet(override_value) == 0 {
803 return;
811 }
812 let subnet = self.subnet_key(&ip);
813 let count_before = self
817 .connections_per_cluster_subnet
818 .get(&cluster_id)
819 .and_then(|by_subnet| by_subnet.get(&subnet))
820 .copied()
821 .unwrap_or(0);
822 let inserted = self
823 .cluster_subnet_tracks
824 .entry(token)
825 .or_default()
826 .entry(cluster_id.clone())
827 .or_default()
828 .insert(subnet);
829 if inserted {
830 *self
831 .connections_per_cluster_subnet
832 .entry(cluster_id.clone())
833 .or_default()
834 .entry(subnet)
835 .or_insert(0) += 1;
836 }
837 debug_assert!(
838 self.cluster_subnet_tracks
839 .get(&token)
840 .and_then(|by_cluster| by_cluster.get(&cluster_id))
841 .is_some_and(|subnets| subnets.contains(&subnet)),
842 "track must leave the (token, cluster, subnet) recorded in the reverse index"
843 );
844 debug_assert_eq!(
845 self.connections_per_cluster_subnet
846 .get(&cluster_id)
847 .and_then(|by_subnet| by_subnet.get(&subnet))
848 .copied()
849 .unwrap_or(0),
850 count_before + inserted as usize,
851 "forward count must advance by exactly 1 on first track, 0 on a repeat"
852 );
853 #[cfg(debug_assertions)]
854 self.check_invariants();
855 }
856
857 pub fn track_cluster_connection(
863 &mut self,
864 token: Token,
865 cluster_id: ClusterId,
866 ip: IpAddr,
867 subnet_override: Option<u64>,
868 ) {
869 self.track_cluster_ip(token, cluster_id.clone(), ip);
870 self.track_cluster_subnet(token, cluster_id, ip, subnet_override);
871 }
872
873 pub fn untrack_all_cluster_ip(&mut self, token: Token) {
887 self.untrack_all_cluster_subnet(token);
888 let Some(by_cluster) = self.cluster_ip_tracks.remove(&token) else {
889 return;
890 };
891 debug_assert!(
894 !self.cluster_ip_tracks.contains_key(&token),
895 "untrack_all must evict the token from the reverse index"
896 );
897 for (cluster_id, ips) in by_cluster {
898 let Entry::Occupied(mut outer) = self.connections_per_cluster_ip.entry(cluster_id)
899 else {
900 continue;
901 };
902 for ip in ips {
903 if let Entry::Occupied(mut inner) = outer.get_mut().entry(ip) {
904 let count = inner.get_mut();
905 *count = count.saturating_sub(1);
906 if *count == 0 {
907 inner.remove();
908 }
909 }
910 }
911 if outer.get().is_empty() {
912 outer.remove();
913 }
914 }
915 debug_assert!(
918 self.connections_per_cluster_ip
919 .values()
920 .all(|by_ip| !by_ip.is_empty() && by_ip.values().all(|&c| c > 0)),
921 "untrack_all must not leave empty inner maps or zero-count ips behind"
922 );
923 #[cfg(debug_assertions)]
924 self.check_invariants();
925 }
926
927 fn untrack_all_cluster_subnet(&mut self, token: Token) {
932 let Some(by_cluster) = self.cluster_subnet_tracks.remove(&token) else {
933 return;
934 };
935 debug_assert!(
936 !self.cluster_subnet_tracks.contains_key(&token),
937 "untrack_all must evict the token from the subnet reverse index"
938 );
939 for (cluster_id, subnets) in by_cluster {
940 let Entry::Occupied(mut outer) = self.connections_per_cluster_subnet.entry(cluster_id)
941 else {
942 continue;
943 };
944 for subnet in subnets {
945 if let Entry::Occupied(mut inner) = outer.get_mut().entry(subnet) {
946 let count = inner.get_mut();
947 *count = count.saturating_sub(1);
948 if *count == 0 {
949 inner.remove();
950 }
951 }
952 }
953 if outer.get().is_empty() {
954 outer.remove();
955 }
956 }
957 debug_assert!(
958 self.connections_per_cluster_subnet
959 .values()
960 .all(|by_subnet| !by_subnet.is_empty() && by_subnet.values().all(|&c| c > 0)),
961 "untrack_all must not leave empty inner maps or zero-count subnets behind"
962 );
963 }
964
965 pub fn subnet_tracking_is_empty(&self) -> bool {
971 self.cluster_subnet_tracks.is_empty() && self.connections_per_cluster_subnet.is_empty()
972 }
973
974 pub fn clear_cluster_subnet_tracking(&mut self) {
980 self.cluster_subnet_tracks.clear();
981 self.connections_per_cluster_subnet.clear();
982 debug_assert!(
983 self.cluster_subnet_tracks.is_empty() && self.connections_per_cluster_subnet.is_empty(),
984 "clear must wipe both the subnet reverse index and the subnet forward count map"
985 );
986 #[cfg(debug_assertions)]
987 self.check_invariants();
988 }
989
990 pub fn clear_cluster_ip_tracking(&mut self) {
995 self.cluster_ip_tracks.clear();
996 self.connections_per_cluster_ip.clear();
997 debug_assert!(
1000 self.cluster_ip_tracks.is_empty() && self.connections_per_cluster_ip.is_empty(),
1001 "clear must wipe both the reverse index and the forward count map"
1002 );
1003 #[cfg(debug_assertions)]
1004 self.check_invariants();
1005 }
1006
1007 pub fn at_capacity(&self) -> bool {
1009 self.slab.len() >= self.accept_slab_threshold()
1010 }
1011
1012 pub fn accept_slab_threshold(&self) -> usize {
1024 let threshold = 10 + 2 * self.max_connections;
1025 debug_assert!(
1029 threshold > self.max_connections,
1030 "accept gate must sit strictly above max_connections to reserve system slots"
1031 );
1032 threshold
1033 }
1034
1035 pub fn check_limits(&mut self) -> bool {
1038 debug_assert!(
1041 self.nb_connections <= self.max_connections,
1042 "nb_connections must never exceed max_connections"
1043 );
1044 if self.nb_connections >= self.max_connections {
1045 error!("max number of session connection reached, flushing the accept queue");
1046 gauge!(names::accept_queue::BACKPRESSURE, 1);
1047 self.can_accept = false;
1048 debug_assert!(
1050 !self.can_accept,
1051 "refusing at the cap must clear can_accept"
1052 );
1053 return false;
1054 }
1055
1056 if self.at_capacity() {
1057 error!("not enough memory to accept another session, flushing the accept queue");
1058 error!(
1059 "nb_connections: {}, max_connections: {}",
1060 self.nb_connections, self.max_connections
1061 );
1062 gauge!(names::accept_queue::BACKPRESSURE, 1);
1063 self.can_accept = false;
1064
1065 debug_assert!(
1066 !self.can_accept,
1067 "refusing at slab capacity must clear can_accept"
1068 );
1069 return false;
1070 }
1071
1072 debug_assert!(
1074 self.nb_connections < self.max_connections && !self.at_capacity(),
1075 "check_limits returned room while a gate was actually saturated"
1076 );
1077 true
1078 }
1079
1080 pub fn to_session(token: Token) -> SessionToken {
1081 SessionToken(token.0)
1082 }
1083
1084 pub fn incr(&mut self) {
1085 let before = self.nb_connections;
1086 self.nb_connections += 1;
1087 assert!(self.nb_connections <= self.max_connections);
1088 debug_assert_eq!(
1090 self.nb_connections,
1091 before + 1,
1092 "incr must raise nb_connections by exactly one"
1093 );
1094 gauge!(names::client::CONNECTIONS, self.nb_connections);
1100 }
1101
1102 pub fn decr(&mut self) {
1105 assert!(self.nb_connections != 0);
1106 let before = self.nb_connections;
1107 self.nb_connections -= 1;
1108 debug_assert_eq!(
1110 self.nb_connections,
1111 before - 1,
1112 "decr must lower nb_connections by exactly one"
1113 );
1114 gauge!(names::client::CONNECTIONS, self.nb_connections);
1115
1116 if !self.can_accept && self.nb_connections < self.max_connections * 90 / 100 {
1118 debug!(
1119 "nb_connections = {}, max_connections = {}, starting to accept again",
1120 self.nb_connections, self.max_connections
1121 );
1122 gauge!(names::accept_queue::BACKPRESSURE, 0);
1123 self.can_accept = true;
1124 }
1125 }
1126
1127 #[cfg(debug_assertions)]
1132 fn check_invariants(&self) {
1133 debug_assert!(
1135 self.nb_connections <= self.max_connections,
1136 "nb_connections {} exceeds max_connections {}",
1137 self.nb_connections,
1138 self.max_connections
1139 );
1140 debug_assert!(
1143 self.connections_per_cluster_ip
1144 .values()
1145 .all(|by_ip| !by_ip.is_empty() && by_ip.values().all(|&c| c > 0)),
1146 "connections_per_cluster_ip holds an empty inner map or a zero count"
1147 );
1148 debug_assert!(
1153 self.cluster_ip_tracks.values().all(|by_cluster| {
1154 by_cluster.iter().all(|(cluster_id, ips)| {
1155 ips.iter().all(|ip| {
1156 self.connections_per_cluster_ip
1157 .get(cluster_id)
1158 .and_then(|by_ip| by_ip.get(ip))
1159 .is_some_and(|&c| c > 0)
1160 })
1161 })
1162 }),
1163 "a tracked (token, cluster, ip) slot has no positive forward count"
1164 );
1165 debug_assert!(
1167 self.cluster_ip_tracks.values().all(|by_cluster| {
1168 !by_cluster.is_empty() && by_cluster.values().all(|ips| !ips.is_empty())
1169 }),
1170 "cluster_ip_tracks retains an empty per-token or per-cluster entry"
1171 );
1172 debug_assert!(
1174 self.connections_per_cluster_subnet
1175 .values()
1176 .all(|by_subnet| !by_subnet.is_empty() && by_subnet.values().all(|&c| c > 0)),
1177 "connections_per_cluster_subnet holds an empty inner map or a zero count"
1178 );
1179 debug_assert!(
1180 self.cluster_subnet_tracks.values().all(|by_cluster| {
1181 by_cluster.iter().all(|(cluster_id, subnets)| {
1182 subnets.iter().all(|subnet| {
1183 self.connections_per_cluster_subnet
1184 .get(cluster_id)
1185 .and_then(|by_subnet| by_subnet.get(subnet))
1186 .is_some_and(|&c| c > 0)
1187 })
1188 })
1189 }),
1190 "a tracked (token, cluster, subnet) slot has no positive forward count"
1191 );
1192 debug_assert!(
1193 self.cluster_subnet_tracks.values().all(|by_cluster| {
1194 !by_cluster.is_empty() && by_cluster.values().all(|subnets| !subnets.is_empty())
1195 }),
1196 "cluster_subnet_tracks retains an empty per-token or per-cluster entry"
1197 );
1198 }
1199}
1200
1201#[derive(thiserror::Error, Debug)]
1202pub enum ServerError {
1203 #[error("could not create event loop with MIO poll: {0}")]
1204 CreatePoll(IoError),
1205 #[error("could not clone the MIO registry: {0}")]
1206 CloneRegistry(IoError),
1207 #[error("could not register the channel: {0}")]
1208 RegisterChannel(IoError),
1209 #[error("{msg}:{scm_err}")]
1210 ScmSocket {
1211 msg: String,
1212 scm_err: ScmSocketError,
1213 },
1214}
1215
1216pub struct Server {
1233 accept_queue_timeout: Duration,
1234 accept_queue: VecDeque<(TcpStream, ListenToken, Protocol, Instant, SocketAddr)>,
1241 evict_on_queue_full: bool,
1245 accept_ready: HashSet<ListenToken>,
1246 backends: Rc<RefCell<BackendMap>>,
1247 base_sessions_count: usize,
1248 channel: ProxyChannel,
1249 config_state: ConfigState,
1250 current_poll_errors: i32,
1251 health_checker: HealthChecker,
1252 http: Rc<RefCell<http::HttpProxy>>,
1253 https: Rc<RefCell<https::HttpsProxy>>,
1254 last_sessions_len: usize,
1255 last_shutting_down_message: Option<Instant>,
1256 last_zombie_check: Instant,
1257 loop_start: Instant,
1258 started_at: Instant,
1262 last_saturation_tick: Instant,
1267 max_poll_errors: i32, pool: Rc<RefCell<Pool>>,
1274 pub poll: Poll,
1275 poll_timeout: Option<Duration>, scm_listeners: Option<Listeners>,
1277 scm: ScmSocket,
1278 sessions: Rc<RefCell<SessionManager>>,
1279 should_poll_at: Option<Instant>,
1280 shutting_down: Option<String>,
1281 tcp: Rc<RefCell<tcp::TcpProxy>>,
1282 udp: Rc<RefCell<udp::UdpProxy>>,
1283 zombie_check_interval: Duration,
1284}
1285
1286impl Server {
1287 pub fn try_new_from_config(
1288 worker_to_main_channel: ProxyChannel,
1289 worker_to_main_scm: ScmSocket,
1290 config: ServerConfig,
1291 initial_state: InitialState,
1292 expects_initial_status: bool,
1293 ) -> Result<Self, ServerError> {
1294 let event_loop = Poll::new().map_err(ServerError::CreatePoll)?;
1295 if let Some(cap) = config.basic_auth_max_credential_bytes {
1302 crate::protocol::mux::auth::set_max_decoded_credential_bytes(cap as usize);
1303 }
1304 #[cfg(all(target_os = "linux", feature = "splice"))]
1311 if let Some(cap) = config.splice_pipe_capacity_bytes {
1312 crate::splice::set_pipe_capacity(cap as usize);
1313 }
1314 let pool = Rc::new(RefCell::new(Pool::with_capacity(
1315 config.min_buffers as usize,
1316 config.max_buffers as usize,
1317 config.buffer_size as usize,
1318 )));
1319 let backends = Rc::new(RefCell::new(BackendMap::new()));
1320
1321 let sessions: Rc<RefCell<SessionManager>> = SessionManager::new(
1326 Slab::with_capacity(config.slab_capacity() as usize),
1327 config.max_connections as usize,
1328 config
1329 .max_connections_per_ip
1330 .unwrap_or(sozu_command::config::DEFAULT_MAX_CONNECTIONS_PER_IP),
1331 config
1332 .retry_after
1333 .unwrap_or(sozu_command::config::DEFAULT_RETRY_AFTER),
1334 config
1335 .max_connections_per_subnet
1336 .unwrap_or(sozu_command::config::DEFAULT_MAX_CONNECTIONS_PER_SUBNET),
1337 config
1338 .subnet_ipv4_prefix
1339 .unwrap_or(sozu_command::config::DEFAULT_SUBNET_IPV4_PREFIX),
1340 config
1341 .subnet_ipv6_prefix
1342 .unwrap_or(sozu_command::config::DEFAULT_SUBNET_IPV6_PREFIX),
1343 );
1344 sessions.borrow_mut().max_connection_attempts = match config.max_connection_attempts {
1348 Some(attempts)
1349 if sozu_command::config::validate_max_connection_attempts(attempts).is_ok() =>
1350 {
1351 attempts
1352 }
1353 Some(attempts) => {
1354 error!(
1355 "max_connection_attempts = {} is out of 1..=255, keeping {}",
1356 attempts,
1357 sozu_command::config::DEFAULT_MAX_CONNECTION_ATTEMPTS
1358 );
1359 sozu_command::config::DEFAULT_MAX_CONNECTION_ATTEMPTS
1360 }
1361 None => sozu_command::config::DEFAULT_MAX_CONNECTION_ATTEMPTS,
1362 };
1363 {
1364 let mut s = sessions.borrow_mut();
1365 let entry = s.slab.vacant_entry();
1366 trace!("taking token {:?} for channel", SessionToken(entry.key()));
1367 entry.insert(Rc::new(RefCell::new(ListenSession {
1368 protocol: Protocol::Channel,
1369 })));
1370 }
1371 {
1372 let mut s = sessions.borrow_mut();
1373 let entry = s.slab.vacant_entry();
1374 trace!("taking token {:?} for metrics", SessionToken(entry.key()));
1375 entry.insert(Rc::new(RefCell::new(ListenSession {
1376 protocol: Protocol::Timer,
1377 })));
1378 }
1379 {
1380 let mut s = sessions.borrow_mut();
1381 let entry = s.slab.vacant_entry();
1382 trace!("taking token {:?} for metrics", SessionToken(entry.key()));
1383 entry.insert(Rc::new(RefCell::new(ListenSession {
1384 protocol: Protocol::Metrics,
1385 })));
1386 }
1387
1388 Server::new(
1389 event_loop,
1390 worker_to_main_channel,
1391 worker_to_main_scm,
1392 sessions,
1393 pool,
1394 backends,
1395 None,
1396 None,
1397 None,
1398 config,
1399 Some(initial_state),
1400 expects_initial_status,
1401 )
1402 }
1403
1404 #[allow(clippy::too_many_arguments)]
1405 pub fn new(
1406 poll: Poll,
1407 mut channel: ProxyChannel,
1408 scm: ScmSocket,
1409 sessions: Rc<RefCell<SessionManager>>,
1410 pool: Rc<RefCell<Pool>>,
1411 backends: Rc<RefCell<BackendMap>>,
1412 http: Option<http::HttpProxy>,
1413 https: Option<https::HttpsProxy>,
1414 tcp: Option<tcp::TcpProxy>,
1415 server_config: ServerConfig,
1416 initial_state: Option<InitialState>,
1417 expects_initial_status: bool,
1418 ) -> Result<Self, ServerError> {
1419 FEATURES.with(|_features| {
1420 });
1422
1423 poll.registry()
1424 .register(
1425 &mut channel,
1426 Token(0),
1427 Interest::READABLE | Interest::WRITABLE,
1428 )
1429 .map_err(ServerError::RegisterChannel)?;
1430
1431 METRICS.with(|metrics| {
1432 if let Some(sock) = (*metrics.borrow_mut()).socket_mut() {
1433 poll.registry()
1434 .register(sock, Token(2), Interest::WRITABLE)
1435 .expect("should register the metrics socket");
1436 }
1437 });
1438
1439 let base_sessions_count = sessions.borrow().slab.len();
1440
1441 let http = Rc::new(RefCell::new(match http {
1442 Some(http) => http,
1443 None => {
1444 let registry = poll
1445 .registry()
1446 .try_clone()
1447 .map_err(ServerError::CloneRegistry)?;
1448
1449 http::HttpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone())
1450 }
1451 }));
1452
1453 let https = Rc::new(RefCell::new(match https {
1454 Some(https) => https,
1455 None => {
1456 let registry = poll
1457 .registry()
1458 .try_clone()
1459 .map_err(ServerError::CloneRegistry)?;
1460
1461 https::HttpsProxy::new(registry, sessions.clone(), pool.clone(), backends.clone())
1462 }
1463 }));
1464
1465 let tcp = Rc::new(RefCell::new(match tcp {
1466 Some(tcp) => tcp,
1467 None => {
1468 let registry = poll
1469 .registry()
1470 .try_clone()
1471 .map_err(ServerError::CloneRegistry)?;
1472
1473 tcp::TcpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone())
1474 }
1475 }));
1476
1477 let udp = Rc::new(RefCell::new({
1481 let registry = poll
1482 .registry()
1483 .try_clone()
1484 .map_err(ServerError::CloneRegistry)?;
1485
1486 udp::UdpProxy::new(
1487 registry,
1488 sessions.clone(),
1489 pool.clone(),
1490 backends.clone(),
1491 server_config.max_connections as usize,
1492 server_config.buffer_size as usize,
1493 )
1494 }));
1495
1496 let mut server = Server {
1497 accept_queue_timeout: Duration::from_secs(u64::from(
1498 server_config.accept_queue_timeout,
1499 )),
1500 accept_queue: VecDeque::new(),
1501 evict_on_queue_full: server_config.evict_on_queue_full.unwrap_or(false),
1502 accept_ready: HashSet::new(),
1503 backends,
1504 base_sessions_count,
1505 channel,
1506 config_state: ConfigState::new(),
1507 current_poll_errors: 0,
1508 health_checker: HealthChecker::new(),
1509 http,
1510 https,
1511 last_sessions_len: 0, last_shutting_down_message: None,
1513 last_zombie_check: Instant::now(), loop_start: Instant::now(), started_at: Instant::now(), last_saturation_tick: Instant::now(), max_poll_errors: 10000, pool,
1519 poll_timeout: Some(Duration::from_millis(1000)), poll,
1521 scm_listeners: None,
1522 scm,
1523 sessions,
1524 should_poll_at: None,
1525 shutting_down: None,
1526 tcp,
1527 udp,
1528 zombie_check_interval: Duration::from_secs(u64::from(
1529 server_config.zombie_check_interval,
1530 )),
1531 };
1532
1533 info!("will try to receive listeners");
1554 server
1555 .scm
1556 .set_blocking(true)
1557 .map_err(|scm_err| ServerError::ScmSocket {
1558 msg: "Could not set the scm socket to blocking".to_string(),
1559 scm_err,
1560 })?;
1561 let listeners =
1562 server
1563 .scm
1564 .receive_listeners()
1565 .map_err(|scm_err| ServerError::ScmSocket {
1566 msg: "could not receive listeners from the scm socket".to_string(),
1567 scm_err,
1568 })?;
1569 server
1570 .scm
1571 .set_blocking(false)
1572 .map_err(|scm_err| ServerError::ScmSocket {
1573 msg: "Could not set the scm socket to unblocking".to_string(),
1574 scm_err,
1575 })?;
1576 info!("received listeners: {:?}", listeners);
1577 server.scm_listeners = Some(listeners);
1578
1579 if let Some(state) = initial_state {
1581 for request in state.requests {
1582 trace!("generating initial config request: {:#?}", request);
1583 server.notify_proxys(request);
1584 }
1585
1586 QUEUE.with(|queue| {
1588 (*queue.borrow_mut()).clear();
1589 });
1590 }
1591
1592 server.report_unclaimed_inherited_sockets();
1593
1594 if expects_initial_status {
1595 server.block_channel();
1598 let msg = server.channel.read_message();
1599 debug!("got message: {:?}", msg);
1600
1601 if let Ok(WorkerRequest {
1602 id,
1603 content:
1604 Request {
1605 request_type: Some(RequestType::Status(_)),
1606 },
1607 }) = msg
1608 {
1609 if let Err(e) = server.channel.write_message(&WorkerResponse::ok(id)) {
1610 error!("Could not send an ok to the main process: {}", e);
1611 }
1612 } else {
1613 panic!(
1614 "plz give me a status request first when I start, you sent me this instead: {msg:?}"
1615 );
1616 }
1617 server.unblock_channel();
1618 }
1619
1620 Ok(server)
1621 }
1622
1623 pub fn run(&mut self) {
1625 let mut events = Events::with_capacity(1024); let mut event_owners: Vec<Option<Rc<RefCell<dyn ProxySession>>>> =
1630 Vec::with_capacity(events.capacity());
1631 self.last_sessions_len = self.sessions.borrow().slab.len();
1632
1633 self.last_zombie_check = Instant::now();
1634 self.loop_start = Instant::now();
1635
1636 loop {
1637 self.check_for_poll_errors();
1638
1639 let timeout = self.reset_loop_time_and_get_timeout();
1640
1641 match self.poll.poll(&mut events, timeout) {
1642 Ok(_) => self.current_poll_errors = 0,
1643 Err(error) => {
1644 error!("Error while polling events: {:?}", error);
1645 self.current_poll_errors += 1;
1646 continue;
1647 }
1648 }
1649
1650 let after_epoll = Instant::now();
1651 time!(
1652 names::event_loop::EPOLL_TIME,
1653 (after_epoll - self.loop_start).as_millis()
1654 );
1655 self.loop_start = after_epoll;
1656
1657 tick_metric_detail_leases(after_epoll);
1659
1660 self.send_queue();
1661
1662 self.snapshot_event_owners(events.iter().map(|event| event.token()), &mut event_owners);
1663 for (event, owner) in events.iter().zip(event_owners.drain(..)) {
1664 match event.token() {
1665 Token(0) => {
1667 if event.is_error() {
1668 error!("error reading from command channel");
1669 continue;
1670 }
1671 if event.is_read_closed() || event.is_write_closed() {
1672 error!("command channel was closed");
1673 return;
1674 }
1675 let ready = Ready::from(event);
1676 self.channel.handle_events(ready);
1677
1678 loop {
1680 QUEUE.with(|queue| {
1681 if !(*queue.borrow()).is_empty() {
1682 self.channel.interest.insert(Ready::WRITABLE);
1683 }
1684 });
1685
1686 if self.channel.readiness() == Ready::EMPTY {
1689 break;
1690 }
1691
1692 if self.read_channel_messages_and_notify() {
1694 return;
1695 }
1696
1697 QUEUE.with(|queue| {
1698 if !(*queue.borrow()).is_empty() {
1699 self.channel.interest.insert(Ready::WRITABLE);
1700 }
1701 });
1702
1703 self.send_queue();
1704 }
1705 }
1706 Token(1) => {
1708 while let Some(t) = TIMER.with(|timer| timer.borrow_mut().poll()) {
1709 self.timeout(t);
1710 }
1711 }
1712 Token(2) => METRICS.with(|metrics| {
1714 (*metrics.borrow_mut()).writable();
1715 }),
1716 token if self.health_checker.owns_token(token) => {
1719 self.health_checker.ready(token);
1720 }
1721 token if self.udp.borrow().health_owns_token(token) => {
1722 self.udp.borrow_mut().health_ready(token);
1723 }
1724 token => self.ready_if_still_owned(token, Ready::from(event), owner),
1725 }
1726 }
1727
1728 if let Some(t) = self.should_poll_at.as_ref()
1729 && *t <= Instant::now()
1730 {
1731 while let Some(t) = TIMER.with(|timer| timer.borrow_mut().poll()) {
1732 self.timeout(t);
1734 }
1735 }
1736 self.handle_remaining_readiness();
1737 self.create_sessions();
1738
1739 self.should_poll_at = TIMER.with(|timer| timer.borrow().next_poll_date());
1740
1741 self.zombie_check();
1742 self.health_checker
1743 .poll(&self.backends, self.poll.registry());
1744 self.udp.borrow_mut().health_poll();
1748
1749 {
1762 let sessions = self.sessions.borrow();
1763 let nb_connections = sessions.nb_connections;
1764 let max_connections = sessions.max_connections;
1765 let slab_len = sessions.slab.len();
1766 let slab_capacity = sessions.slab.capacity();
1767 let accept_threshold = sessions.accept_slab_threshold();
1768
1769 gauge!(names::client::CONNECTIONS, nb_connections);
1770 gauge!(names::client::CONNECTIONS_MAX, max_connections);
1771 if let Some(percent) = (nb_connections * 100).checked_div(max_connections) {
1772 gauge!(names::client::CONNECTIONS_PERCENT, percent);
1773 }
1774
1775 gauge!(names::slab::ENTRIES, slab_len);
1776 gauge!(names::slab::CAPACITY, slab_capacity);
1777 if let Some(percent) = (slab_len * 100).checked_div(slab_capacity) {
1778 gauge!(names::slab::USAGE_PERCENT, percent);
1779 }
1780 if let Some(percent) = (slab_len * 100).checked_div(accept_threshold) {
1781 gauge!(names::slab::ACCEPT_THRESHOLD_PERCENT, percent);
1782 }
1783 }
1784 {
1790 let pool = self.pool.borrow();
1791 let used = pool.inner.used();
1792 let capacity = pool.inner.capacity();
1793 gauge!(names::buffer::IN_USE, used);
1794 gauge!(names::buffer::CAPACITY, capacity);
1795 if let Some(percent) = (used * 100).checked_div(capacity) {
1796 gauge!(names::buffer::USAGE_PERCENT, percent);
1797 }
1798 }
1799 let now = Instant::now();
1807 if now.duration_since(self.last_saturation_tick) >= ACCEPT_SATURATION_TICK {
1808 if !self.sessions.borrow().can_accept {
1809 incr!(names::accept_queue::SATURATED_SECONDS);
1810 }
1811 self.last_saturation_tick = now;
1812 }
1813 gauge!(
1817 names::process::UPTIME_SECONDS,
1818 self.started_at.elapsed().as_secs() as usize
1819 );
1820 gauge!(
1825 names::server::LIVE,
1826 if self.shutting_down.is_some() { 0 } else { 1 }
1827 );
1828 METRICS.with(|metrics| {
1829 (*metrics.borrow_mut()).send_data();
1830 });
1831
1832 if self.shutting_down.is_some() && self.shut_down_sessions() {
1833 return;
1834 }
1835 }
1836 }
1837
1838 fn check_for_poll_errors(&mut self) {
1839 if self.current_poll_errors >= self.max_poll_errors {
1840 error!(
1841 "Something is going very wrong. Last {} poll() calls failed, crashing..",
1842 self.current_poll_errors
1843 );
1844 panic!(
1845 "poll() calls failed {} times in a row",
1846 self.current_poll_errors
1847 );
1848 }
1849 }
1850
1851 fn reset_loop_time_and_get_timeout(&mut self) -> Option<Duration> {
1852 let now = Instant::now();
1853 time!(
1854 names::event_loop::EVENT_LOOP_TIME,
1855 (now - self.loop_start).as_millis()
1856 );
1857
1858 let mut timeout = match self.should_poll_at.as_ref() {
1859 None => self.poll_timeout,
1860 Some(i) => {
1861 if *i <= now {
1862 self.poll_timeout
1863 } else {
1864 let dur = *i - now;
1865 match self.poll_timeout {
1866 None => Some(dur),
1867 Some(t) => {
1868 if t < dur {
1869 Some(t)
1870 } else {
1871 Some(dur)
1872 }
1873 }
1874 }
1875 }
1876 }
1877 };
1878
1879 if self.shutting_down.is_some() {
1880 let shutdown_tick = Duration::from_millis(100);
1881 timeout = match timeout {
1882 None => Some(shutdown_tick),
1883 Some(current) => Some(current.min(shutdown_tick)),
1884 };
1885 }
1886
1887 self.loop_start = now;
1888 timeout
1889 }
1890
1891 fn read_channel_messages_and_notify(&mut self) -> bool {
1893 if !self.channel.readiness().is_readable() {
1894 return false;
1895 }
1896
1897 if let Err(e) = self.channel.readable() {
1898 error!("error reading from channel: {:?}", e);
1899 }
1900
1901 loop {
1902 let request = self.channel.read_message();
1903 debug!("Received request {:?}", request);
1904 match request {
1905 Ok(request) => match request.content.request_type {
1906 Some(RequestType::HardStop(_)) => {
1907 let req_id = request.id.clone();
1908 self.notify(request);
1909 if let Err(e) = self.channel.write_message(&WorkerResponse::ok(req_id)) {
1910 error!("Could not send ok response to the main process: {}", e);
1911 }
1912 if let Err(e) = self.channel.run() {
1913 error!("Error while running the server channel: {}", e);
1914 }
1915 return true;
1916 }
1917 Some(RequestType::SoftStop(_)) => {
1918 self.shutting_down = Some(request.id.clone());
1919 self.last_sessions_len = self.sessions.borrow().slab.len();
1920 self.notify(request);
1921 }
1922 Some(RequestType::ReturnListenSockets(_)) => {
1923 info!("received ReturnListenSockets order");
1924 match self.return_listen_sockets() {
1925 Ok(_) => push_queue(WorkerResponse::ok(request.id)),
1926 Err(error) => push_queue(worker_response_error(
1927 request.id,
1928 format!("Could not send listeners on scm socket: {error:?}"),
1929 )),
1930 }
1931 }
1932 _ => self.notify(request),
1933 },
1934 Err(_) => {
1936 if (self.channel.interest & self.channel.readiness).is_readable() {
1938 if let Err(e) = self.channel.readable() {
1939 error!("error reading from channel: {:?}", e);
1940 }
1941 continue;
1942 }
1943 break;
1944 }
1945 }
1946 }
1947 false
1948 }
1949
1950 fn zombie_check(&mut self) {
1952 let now = Instant::now();
1953 if now - self.last_zombie_check < self.zombie_check_interval {
1954 return;
1955 }
1956 info!("zombie check");
1957 debug_assert!(
1960 now >= self.last_zombie_check,
1961 "zombie-check timestamp must never move backwards"
1962 );
1963 self.last_zombie_check = now;
1964
1965 let mut zombie_tokens = HashSet::new();
1966
1967 for (_index, session) in self
1969 .sessions
1970 .borrow_mut()
1971 .slab
1972 .iter_mut()
1973 .filter(|(_, c)| now - c.borrow().last_event() > self.zombie_check_interval)
1974 {
1975 let session_token = session.borrow().frontend_token();
1976 if !zombie_tokens.contains(&session_token) {
1977 session.borrow().print_session();
1978 zombie_tokens.insert(session_token);
1979 }
1980 }
1981
1982 debug_assert!(
1987 !self.sessions.borrow().slab.iter().any(|(_, session)| {
1988 let s = session.borrow();
1989 zombie_tokens.contains(&s.frontend_token())
1990 && matches!(
1991 s.protocol(),
1992 Protocol::HTTPListen | Protocol::HTTPSListen | Protocol::TCPListen
1993 )
1994 }),
1995 "zombie reaping must never target a listener session"
1996 );
1997
1998 let zombie_count = zombie_tokens.len() as i64;
1999 count!(names::misc::ZOMBIES, zombie_count);
2000
2001 let remaining_count = self.shut_down_sessions_by_frontend_tokens(zombie_tokens);
2002 info!(
2003 "removing {} zombies ({} remaining entries after close)",
2004 zombie_count, remaining_count
2005 );
2006 }
2007
2008 fn shut_down_sessions_by_frontend_tokens(&self, tokens: HashSet<Token>) -> usize {
2011 if tokens.is_empty() {
2012 return 0;
2013 }
2014
2015 for token in &tokens {
2017 if self.sessions.borrow().slab.contains(token.0) {
2018 let slab_before = self.sessions.borrow().slab.len();
2019 let session = { self.sessions.borrow_mut().slab.remove(token.0) };
2020 session.borrow_mut().close();
2021 self.sessions.borrow_mut().decr();
2022 debug_assert!(
2027 !self.sessions.borrow().slab.contains(token.0),
2028 "removed token must be absent from the slab"
2029 );
2030 debug_assert!(
2031 self.sessions.borrow().slab.len() < slab_before,
2032 "removing a present session must free at least its own slab slot"
2033 );
2034 }
2035 }
2036
2037 let mut dangling_entries = HashSet::new();
2039 for (entry_key, session) in &self.sessions.borrow().slab {
2040 if tokens.contains(&session.borrow().frontend_token()) {
2041 dangling_entries.insert(entry_key);
2042 }
2043 }
2044
2045 let mut dangling_entries_count = 0;
2047 for entry_key in dangling_entries {
2048 let mut sessions = self.sessions.borrow_mut();
2049 if sessions.slab.contains(entry_key) {
2050 sessions.slab.remove(entry_key);
2051 dangling_entries_count += 1;
2052 }
2053 }
2054 debug_assert!(
2058 !self
2059 .sessions
2060 .borrow()
2061 .slab
2062 .iter()
2063 .any(|(_, session)| tokens.contains(&session.borrow().frontend_token())),
2064 "no slab entry may reference a closed frontend token after teardown"
2065 );
2066 dangling_entries_count
2067 }
2068
2069 fn shut_down_sessions(&mut self) -> bool {
2071 let sessions_count = self.sessions.borrow().slab.len();
2072 let mut sessions_to_shut_down = HashSet::new();
2073
2074 for (_key, session) in &self.sessions.borrow().slab {
2075 let mut session = session.borrow_mut();
2076 if session.shutting_down() {
2077 debug!(
2078 "Server killing session from shutting_down: token={:?}, protocol={:?}",
2079 session.frontend_token(),
2080 session.protocol()
2081 );
2082 sessions_to_shut_down.insert(Token(session.frontend_token().0));
2083 }
2084 }
2085 let _ = self.shut_down_sessions_by_frontend_tokens(sessions_to_shut_down);
2086
2087 let new_sessions_count = self.sessions.borrow().slab.len();
2088
2089 if new_sessions_count < sessions_count {
2090 let now = Instant::now();
2091 if let Some(last) = self.last_shutting_down_message
2092 && (now - last) > Duration::from_secs(5)
2093 {
2094 info!(
2095 "closed {} sessions, {} sessions left, base_sessions_count = {}",
2096 sessions_count - new_sessions_count,
2097 new_sessions_count,
2098 self.base_sessions_count
2099 );
2100 }
2101 self.last_shutting_down_message = Some(now);
2102 }
2103
2104 if new_sessions_count <= self.base_sessions_count {
2105 info!("last session stopped, shutting down!");
2106 if let Err(e) = self.channel.run() {
2107 error!("Error while running the server channel: {}", e);
2108 }
2109 let id = self
2111 .shutting_down
2112 .take()
2113 .expect("should have shut down correctly"); debug!("Responding OK to main process for request {}", id);
2116
2117 let proxy_response = WorkerResponse::ok(id);
2118 if let Err(e) = self.channel.write_message(&proxy_response) {
2119 error!("Could not write response to the main process: {}", e);
2120 }
2121 if let Err(e) = self.channel.run() {
2122 error!("Error while running the server channel: {}", e);
2123 }
2124 return true;
2125 }
2126
2127 if new_sessions_count < self.last_sessions_len {
2128 info!(
2129 "shutting down, {} slab elements remaining (base: {})",
2130 new_sessions_count - self.base_sessions_count,
2131 self.base_sessions_count
2132 );
2133 self.last_sessions_len = new_sessions_count;
2134 }
2135
2136 false
2137 }
2138
2139 fn kill_session(&self, session: Rc<RefCell<dyn ProxySession>>) {
2140 let token = session.borrow().frontend_token();
2141 let _ = self.shut_down_sessions_by_frontend_tokens(HashSet::from([token]));
2142 }
2143
2144 fn send_queue(&mut self) {
2145 if self.channel.readiness.is_writable() {
2146 QUEUE.with(|q| {
2147 let mut queue = q.borrow_mut();
2148 loop {
2149 if let Some(resp) = queue.pop_front() {
2150 debug!("Sending response {:?}", resp);
2151 if let Err(e) = self.channel.write_message(&resp) {
2152 error!("Could not write message {} on the channel: {}", resp, e);
2153 queue.push_front(resp);
2154 }
2155 }
2156
2157 if self.channel.back_buf.available_data() > 0
2158 && let Err(e) = self.channel.writable()
2159 {
2160 error!("error writing to channel: {:?}", e);
2161 }
2162
2163 if !self.channel.readiness.is_writable() {
2164 break;
2165 }
2166
2167 if self.channel.back_buf.available_data() == 0 && queue.is_empty() {
2168 break;
2169 }
2170 }
2171 });
2172 }
2173 }
2174
2175 fn notify(&mut self, message: WorkerRequest) {
2176 let now = std::time::Instant::now();
2182 tick_metric_detail_leases(now);
2183 match &message.content.request_type {
2184 Some(RequestType::ConfigureMetrics(configuration)) => {
2185 match MetricsConfiguration::try_from(*configuration) {
2186 Ok(metrics_config) => {
2187 METRICS.with(|metrics| {
2188 (*metrics.borrow_mut()).configure(&metrics_config);
2189 push_queue(WorkerResponse::ok(message.id));
2190 });
2191 }
2192 Err(e) => {
2193 error!("Error configuring metrics: {}", e);
2194 push_queue(WorkerResponse::error(message.id, e));
2195 }
2196 }
2197 return;
2198 }
2199 Some(RequestType::QueryMetrics(query_metrics_options)) => {
2200 METRICS.with(|metrics| {
2201 match (*metrics.borrow_mut()).query(query_metrics_options) {
2202 Ok(c) => push_queue(WorkerResponse::ok_with_content(message.id, c)),
2203 Err(e) => {
2204 error!("Error querying metrics: {}", e);
2205 push_queue(WorkerResponse::error(message.id, e))
2206 }
2207 }
2208 });
2209 return;
2210 }
2211 Some(RequestType::SetMetricDetail(req)) => {
2219 let presented_binding = crate::metrics::PeerBinding {
2226 pid: req.peer_pid,
2227 session_ulid: req.peer_session_ulid.as_deref().and_then(|s| {
2233 rusty_ulid::Ulid::from_str(s)
2234 .map(u128::from)
2235 .ok()
2236 .or_else(|| u128::from_str_radix(s.trim_start_matches("0x"), 16).ok())
2237 }),
2238 };
2239 if req.clear.unwrap_or(false) {
2240 if req.client_id.len() > crate::metrics::LEASE_CLIENT_ID_MAX_BYTES {
2251 let msg = format!(
2252 "SetMetricDetail: clear client_id length {} exceeds {} bytes",
2253 req.client_id.len(),
2254 crate::metrics::LEASE_CLIENT_ID_MAX_BYTES,
2255 );
2256 error!("{}", msg);
2257 push_queue(WorkerResponse::error(message.id.clone(), msg));
2258 return;
2259 }
2260 let (outcome, effective_after, configured_after, lease_count_after) = METRICS
2268 .with(|metrics| {
2269 let mut m = metrics.borrow_mut();
2270 let outcome = m.lease_clear(&req.client_id, presented_binding);
2271 (
2272 outcome,
2273 m.detail_effective(),
2274 m.detail_configured(),
2275 m.lease_count(),
2276 )
2277 });
2278 match outcome {
2279 crate::metrics::LeaseClearOutcome::Cleared { previous_effective } => {
2280 push_metric_detail_transition(
2281 previous_effective,
2282 effective_after,
2283 "lease_clear",
2284 Some(req.client_id.clone()),
2285 );
2286 push_queue(WorkerResponse::ok_with_content(
2287 message.id.clone(),
2288 worker_metric_detail_status_content(
2289 configured_after,
2290 effective_after,
2291 previous_effective,
2292 lease_count_after,
2293 ),
2294 ));
2295 }
2296 crate::metrics::LeaseClearOutcome::NotFound => {
2297 push_queue(WorkerResponse::ok_with_content(
2301 message.id.clone(),
2302 worker_metric_detail_status_content(
2303 configured_after,
2304 effective_after,
2305 effective_after,
2306 lease_count_after,
2307 ),
2308 ));
2309 }
2310 crate::metrics::LeaseClearOutcome::Unauthorized => {
2311 let msg = "SetMetricDetail: clear refused (peer \
2322 binding does not match the apply-time owner)"
2323 .to_owned();
2324 error!("{}", msg);
2325 push_queue(WorkerResponse::error(message.id.clone(), msg));
2326 }
2327 }
2328 return;
2329 }
2330 let detail_proto = match req.detail {
2331 Some(d) => d,
2332 None => {
2333 let msg = "SetMetricDetail without `detail` and without `clear`".to_owned();
2340 error!("{}", msg);
2341 push_queue(WorkerResponse::error(message.id.clone(), msg));
2342 return;
2343 }
2344 };
2345 let detail_enum = match MetricDetail::try_from(detail_proto) {
2346 Ok(d) => d,
2347 Err(e) => {
2348 let msg =
2349 format!("SetMetricDetail: invalid MetricDetail variant {detail_proto}");
2350 error!("{}: {}", msg, e);
2351 push_queue(WorkerResponse::error(message.id.clone(), msg));
2352 return;
2353 }
2354 };
2355 let level = MetricDetailLevel::from(detail_enum);
2356 if let Some(t) = req.ttl_seconds
2365 && u64::from(t) > crate::metrics::LEASE_TTL_MAX.as_secs()
2366 {
2367 let msg = format!(
2368 "SetMetricDetail: ttl_seconds={t} exceeds LEASE_TTL_MAX={}",
2369 crate::metrics::LEASE_TTL_MAX.as_secs()
2370 );
2371 error!("{}", msg);
2372 push_queue(WorkerResponse::error(message.id.clone(), msg));
2373 return;
2374 }
2375 let ttl_seconds = req.ttl_seconds.filter(|&t| t > 0).unwrap_or_else(|| {
2376 u32::try_from(crate::metrics::LEASE_TTL_DEFAULT.as_secs()).unwrap_or(60)
2383 });
2384 let ttl = std::time::Duration::from_secs(ttl_seconds.into());
2385 let (outcome, configured_after, lease_count_after) = METRICS.with(|metrics| {
2386 let mut m = metrics.borrow_mut();
2387 let outcome =
2388 m.lease_apply(req.client_id.clone(), level, ttl, presented_binding, now);
2389 (outcome, m.detail_configured(), m.lease_count())
2390 });
2391 match outcome {
2392 crate::metrics::LeaseApplyOutcome::Applied {
2393 previous_effective,
2394 new_effective,
2395 } => {
2396 push_metric_detail_transition(
2397 previous_effective,
2398 new_effective,
2399 "lease_apply",
2400 Some(req.client_id.clone()),
2401 );
2402 push_queue(WorkerResponse::ok_with_content(
2403 message.id.clone(),
2404 worker_metric_detail_status_content(
2405 configured_after,
2406 new_effective,
2407 previous_effective,
2408 lease_count_after,
2409 ),
2410 ));
2411 }
2412 crate::metrics::LeaseApplyOutcome::ClientIdTooLong => {
2413 let msg = format!(
2414 "SetMetricDetail: client_id length {} exceeds {} bytes",
2415 req.client_id.len(),
2416 crate::metrics::LEASE_CLIENT_ID_MAX_BYTES,
2417 );
2418 error!("{}", msg);
2419 push_queue(WorkerResponse::error(message.id.clone(), msg));
2420 }
2421 crate::metrics::LeaseApplyOutcome::TableFull => {
2422 let msg = format!(
2429 "SetMetricDetail: lease table at capacity ({} entries); reject new \
2430 apply — operators must retry after an active lease expires or is \
2431 cleared",
2432 crate::metrics::LEASE_TABLE_CAP,
2433 );
2434 error!("{}", msg);
2435 push_queue(WorkerResponse::error(message.id.clone(), msg));
2436 }
2437 crate::metrics::LeaseApplyOutcome::TtlOutOfRange => {
2438 let msg = format!(
2444 "SetMetricDetail: ttl exceeds LEASE_TTL_MAX={} (internal contract \
2445 violation: dispatch gate should have rejected)",
2446 crate::metrics::LEASE_TTL_MAX.as_secs(),
2447 );
2448 error!("{}", msg);
2449 push_queue(WorkerResponse::error(message.id.clone(), msg));
2450 }
2451 crate::metrics::LeaseApplyOutcome::Unauthorized => {
2452 let msg = "SetMetricDetail: renewal refused (peer binding does not \
2461 match the apply-time owner)"
2462 .to_owned();
2463 error!("{}", msg);
2464 push_queue(WorkerResponse::error(message.id.clone(), msg));
2465 }
2466 }
2467 return;
2468 }
2469 Some(RequestType::Logging(logging_filter)) => {
2470 info!(
2471 "{} changing logging filter to {}",
2472 message.id, logging_filter
2473 );
2474 let (directives, _errors) = logging::parse_logging_spec(logging_filter);
2476 logging::LOGGER.with(|logger| {
2477 logger.borrow_mut().set_directives(directives);
2478 });
2479 push_queue(WorkerResponse::ok(message.id));
2480 return;
2481 }
2482 Some(RequestType::QueryClustersHashes(_)) => {
2483 push_queue(WorkerResponse::ok_with_content(
2484 message.id.clone(),
2485 ContentType::ClusterHashes(ClusterHashes {
2486 map: self
2487 .config_state
2488 .hash_state()
2489 .into_iter()
2490 .map(|(cluster_id, hash)| (cluster_id.to_string(), hash))
2491 .collect(),
2492 })
2493 .into(),
2494 ));
2495 return;
2496 }
2497 Some(RequestType::QueryClusterById(cluster_id)) => {
2498 push_queue(WorkerResponse::ok_with_content(
2499 message.id.clone(),
2500 ContentType::Clusters(ClusterInformations {
2501 vec: self
2502 .config_state
2503 .cluster_state(cluster_id)
2504 .map_or(vec![], |ci| vec![ci]),
2505 })
2506 .into(),
2507 ));
2508 }
2509 Some(RequestType::SetMaxConnectionsPerIp(limit)) => {
2510 let mut sessions = self.sessions.borrow_mut();
2511 let previous = sessions.max_connections_per_ip;
2512 sessions.max_connections_per_ip = *limit;
2513 if *limit == 0 {
2518 sessions.clear_cluster_ip_tracking();
2519 }
2520 info!(
2521 "{} updated global max_connections_per_ip from {} to {}",
2522 message.id, previous, limit
2523 );
2524 push_queue(WorkerResponse::ok(message.id));
2525 return;
2526 }
2527 Some(RequestType::QueryMaxConnectionsPerIp(_)) => {
2528 let limit = self.sessions.borrow().max_connections_per_ip;
2529 push_queue(WorkerResponse::ok_with_content(
2530 message.id,
2531 ContentType::MaxConnectionsPerIpLimit(
2532 sozu_command::proto::command::MaxConnectionsPerIpLimit { limit },
2533 )
2534 .into(),
2535 ));
2536 return;
2537 }
2538 Some(RequestType::SetMaxConnectionsPerSubnet(limit)) => {
2539 let mut sessions = self.sessions.borrow_mut();
2540 let previous = sessions.max_connections_per_subnet;
2541 sessions.max_connections_per_subnet = *limit;
2542 if *limit == 0 {
2548 sessions.clear_cluster_subnet_tracking();
2549 }
2550 info!(
2551 "{} updated global max_connections_per_subnet from {} to {}",
2552 message.id, previous, limit
2553 );
2554 push_queue(WorkerResponse::ok(message.id));
2555 return;
2556 }
2557 Some(RequestType::SetMaxConnectionAttempts(attempts)) => {
2558 if let Err(reason) =
2559 sozu_command::config::validate_max_connection_attempts(*attempts)
2560 {
2561 push_queue(WorkerResponse::error(message.id, reason));
2562 return;
2563 }
2564 let mut sessions = self.sessions.borrow_mut();
2565 let previous = sessions.max_connection_attempts;
2566 sessions.max_connection_attempts = *attempts;
2567 info!(
2568 "{} updated global max_connection_attempts from {} to {}",
2569 message.id, previous, attempts
2570 );
2571 push_queue(WorkerResponse::ok(message.id));
2572 return;
2573 }
2574 Some(RequestType::QueryMaxConnectionsPerSubnet(_)) => {
2575 let sessions = self.sessions.borrow();
2576 let limit = sessions.max_connections_per_subnet;
2577 let ipv4_prefix = sessions.subnet_ipv4_prefix;
2581 let ipv6_prefix = sessions.subnet_ipv6_prefix;
2582 drop(sessions);
2583 push_queue(WorkerResponse::ok_with_content(
2584 message.id,
2585 ContentType::MaxConnectionsPerSubnetLimit(
2586 sozu_command::proto::command::MaxConnectionsPerSubnetLimit {
2587 limit,
2588 ipv4_prefix,
2589 ipv6_prefix,
2590 },
2591 )
2592 .into(),
2593 ));
2594 return;
2595 }
2596 Some(RequestType::QueryClustersByDomain(domain)) => {
2597 let cluster_ids = self
2598 .config_state
2599 .get_cluster_ids_by_domain(domain.hostname.clone(), domain.path.clone());
2600 let vec = cluster_ids
2601 .iter()
2602 .filter_map(|cluster_id| self.config_state.cluster_state(cluster_id))
2603 .collect();
2604
2605 push_queue(WorkerResponse::ok_with_content(
2606 message.id.clone(),
2607 ContentType::Clusters(ClusterInformations { vec }).into(),
2608 ));
2609 return;
2610 }
2611 Some(RequestType::QueryCertificatesFromWorkers(filters))
2622 if filters.fingerprint.is_some() =>
2623 {
2624 let certs = self.config_state.get_certificates(filters.clone());
2625 let response = if !certs.is_empty() {
2626 WorkerResponse::ok_with_content(
2627 message.id.clone(),
2628 ContentType::CertificatesWithFingerprints(CertificatesWithFingerprints {
2629 certs,
2630 })
2631 .into(),
2632 )
2633 } else {
2634 worker_response_error(
2635 message.id.clone(),
2636 "Could not find certificate for this fingerprint",
2637 )
2638 };
2639 push_queue(response);
2640 return;
2641 }
2642 _other_request => {}
2645 }
2646 self.notify_proxys(message);
2647 }
2648
2649 pub fn notify_proxys(&mut self, request: WorkerRequest) {
2650 let cluster_objects = match &request.content.request_type {
2654 Some(RequestType::RemoveCluster(cluster_id)) => {
2655 self.config_state.cluster_removal_requests(cluster_id)
2656 }
2657 _ => Vec::new(),
2658 };
2659
2660 let rollback_candidate = request
2664 .content
2665 .request_type
2666 .as_ref()
2667 .and_then(|request_type| self.config_state.rollback_for(request_type));
2668 let mut rollback = None;
2669 if let Err(e) = self.config_state.dispatch(&request.content) {
2670 if self
2671 .config_state
2672 .removed_with_its_cluster(&request.content, &e)
2673 {
2674 debug!("{} already removed with its cluster: {}", request.id, e);
2680 push_queue(WorkerResponse::ok(request.id));
2681 return;
2682 }
2683 if is_frontend_removal(&request.content)
2684 && matches!(e, StateError::NotFound { .. } | StateError::NoChange)
2685 {
2686 push_queue(WorkerResponse::error(
2696 request.id,
2697 format!("Could not execute order on config state: {e}"),
2698 ));
2699 return;
2700 }
2701 error!("Could not execute order on config state: {}", e);
2702 } else {
2703 rollback = rollback_candidate;
2704 }
2705
2706 let req_id = request.id.clone();
2707
2708 match request.content.request_type {
2709 Some(RequestType::AddCluster(ref cluster)) => {
2710 if let Some(hc) = cluster.health_check.as_ref()
2718 && let Err(reason) = sozu_command::config::validate_health_check_config(hc)
2719 {
2720 push_queue(worker_response_error(req_id, reason));
2721 return;
2722 }
2723 if let Err(reason) = sozu_command::config::validate_shuffle_sharding(
2726 cluster.shard_percent,
2727 cluster.shard_min_backends,
2728 cluster.shard_mode,
2729 ) {
2730 push_queue(worker_response_error(req_id, reason));
2731 return;
2732 }
2733 if let Some(Err(reason)) = cluster
2734 .max_connection_attempts
2735 .map(sozu_command::config::validate_max_connection_attempts)
2736 {
2737 push_queue(worker_response_error(req_id, reason));
2738 return;
2739 }
2740 self.add_cluster(cluster);
2741 if cluster.health_check.is_none() {
2745 self.health_checker.remove_cluster(&cluster.cluster_id);
2746 }
2747 METRICS.with(|metrics| {
2752 (*metrics.borrow_mut()).add_cluster(&cluster.cluster_id);
2753 });
2754 }
2756 Some(RequestType::RemoveCluster(ref cluster_id)) => {
2757 self.remove_cluster_objects(&req_id, cluster_id, cluster_objects);
2758 self.remove_health_check_state(cluster_id);
2759 METRICS.with(|metrics| {
2760 (*metrics.borrow_mut()).remove_cluster(cluster_id);
2761 });
2762 }
2764 Some(RequestType::SetHealthCheck(ref set)) => {
2765 if let Err(reason) = sozu_command::config::validate_health_check_config(&set.config)
2766 {
2767 push_queue(worker_response_error(req_id, reason));
2768 return;
2769 }
2770 self.backends
2771 .borrow_mut()
2772 .set_health_check_config(&set.cluster_id, Some(set.config.to_owned()));
2773 push_queue(WorkerResponse::ok(req_id));
2774 return;
2775 }
2776 Some(RequestType::RemoveHealthCheck(ref cluster_id)) => {
2777 self.remove_health_check_state(cluster_id);
2778 push_queue(WorkerResponse::ok(req_id));
2779 return;
2780 }
2781 Some(RequestType::AddBackend(ref backend)) => {
2782 push_queue(self.add_backend(&req_id, backend));
2783 return;
2784 }
2785 Some(RequestType::RemoveBackend(ref remove_backend)) => {
2786 push_queue(self.remove_backend(&req_id, remove_backend));
2787 return;
2788 }
2789 _ => {}
2790 };
2791
2792 let proxy_destinations = request.content.get_destinations();
2793 let mut notify_response = None;
2794 if proxy_destinations.to_http_proxy {
2795 notify_response = Some(self.http.borrow_mut().notify(request.clone()));
2796 }
2797 if proxy_destinations.to_https_proxy {
2798 let http_proxy_response = self.https.borrow_mut().notify(request.clone());
2799 if http_proxy_response.is_failure() || notify_response.is_none() {
2800 notify_response = Some(http_proxy_response);
2801 }
2802 }
2803 if proxy_destinations.to_tcp_proxy {
2804 let tcp_proxy_response = self.tcp.borrow_mut().notify(request.clone());
2805 if tcp_proxy_response.is_failure() || notify_response.is_none() {
2806 notify_response = Some(tcp_proxy_response);
2807 }
2808 }
2809 if proxy_destinations.to_udp_proxy {
2810 let udp_proxy_response = self.udp.borrow_mut().notify(request.clone());
2811 if udp_proxy_response.is_failure() || notify_response.is_none() {
2812 notify_response = Some(udp_proxy_response);
2813 }
2814 }
2815 if let Some(response) = notify_response {
2816 self.finish_proxy_order(&mut rollback, response);
2817 }
2818
2819 match request.content.request_type {
2820 Some(RequestType::AddHttpListener(listener)) => {
2822 let response = self.notify_add_http_listener(&req_id, listener);
2823 self.finish_proxy_order(&mut rollback, response);
2824 }
2825 Some(
2826 RequestType::AddHttpsListener(listener)
2827 | RequestType::AddHttpsListenerWithClientAuth(listener),
2828 ) => {
2829 let response = self.notify_add_https_listener(&req_id, listener);
2830 self.finish_proxy_order(&mut rollback, response);
2831 }
2832 Some(RequestType::AddTcpListener(listener)) => {
2833 let response = self.notify_add_tcp_listener(&req_id, listener);
2834 self.finish_proxy_order(&mut rollback, response);
2835 }
2836 Some(RequestType::AddUdpListener(listener)) => {
2837 let response = self.notify_add_udp_listener(&req_id, listener);
2838 self.finish_proxy_order(&mut rollback, response);
2839 }
2840 Some(RequestType::UpdateHttpListener(patch)) => {
2841 let response = self.notify_update_http_listener(&req_id, patch);
2842 self.finish_proxy_order(&mut rollback, response);
2843 }
2844 Some(
2845 RequestType::UpdateHttpsListener(patch)
2846 | RequestType::UpdateHttpsListenerWithClientAuth(patch),
2847 ) => {
2848 let response = self.notify_update_https_listener(&req_id, patch);
2849 self.finish_proxy_order(&mut rollback, response);
2850 }
2851 Some(RequestType::UpdateTcpListener(patch)) => {
2852 let response = self.notify_update_tcp_listener(&req_id, patch);
2853 self.finish_proxy_order(&mut rollback, response);
2854 }
2855 Some(RequestType::UpdateUdpListener(patch)) => {
2856 let response = self.notify_update_udp_listener(&req_id, patch);
2857 self.finish_proxy_order(&mut rollback, response);
2858 }
2859 Some(RequestType::RemoveListener(ref remove)) => {
2860 debug!("{} remove {:?} listener {:?}", req_id, remove.proxy, remove);
2861 debug_assert!(
2864 self.base_sessions_count > 0,
2865 "removing a listener with base_sessions_count == 0 would underflow"
2866 );
2867 self.base_sessions_count -= 1;
2868 let listener_type = ListenerType::try_from(remove.proxy);
2869 let address = remove.listener_key();
2877 let listen_token = match listener_type {
2878 Ok(ListenerType::Http) => self.http.borrow().listener_token(&address),
2879 Ok(ListenerType::Https) => self.https.borrow().listener_token(&address),
2880 Ok(ListenerType::Tcp) => self.tcp.borrow().listener_token(&address),
2881 Ok(ListenerType::Udp) => self.udp.borrow().listener_token(&address),
2882 Err(_) => None,
2883 };
2884 let response = match listener_type {
2885 Ok(ListenerType::Http) => self.http.borrow_mut().notify(request),
2886 Ok(ListenerType::Https) => self.https.borrow_mut().notify(request),
2887 Ok(ListenerType::Tcp) => self.tcp.borrow_mut().notify(request),
2888 Ok(ListenerType::Udp) => self.udp.borrow_mut().notify(request),
2889 Err(_) => WorkerResponse::error(req_id, "Wrong variant ListenerType"),
2890 };
2891 if let Some(token) = listen_token {
2892 self.sessions.borrow_mut().slab.try_remove(token.0);
2893 self.accept_ready.remove(&ListenToken(token.0));
2896 info!("released listen token {:?}", token);
2897 }
2898 self.finish_proxy_order(&mut rollback, response);
2899 }
2900 Some(RequestType::ActivateListener(ref activate)) => {
2901 let response = self.notify_activate_listener(&req_id, activate);
2902 self.finish_proxy_order(&mut rollback, response);
2903 }
2904 Some(RequestType::DeactivateListener(ref deactivate)) => {
2905 let response = self.notify_deactivate_listener(&req_id, deactivate);
2906 self.finish_proxy_order(&mut rollback, response);
2907 }
2908 _other_request => {}
2909 };
2910 }
2911
2912 fn finish_proxy_order(&mut self, rollback: &mut Option<Request>, response: WorkerResponse) {
2915 if response.is_failure()
2916 && let Some(rollback) = rollback.take()
2917 {
2918 debug_assert!(
2919 rollback.request_type.is_some(),
2920 "a ConfigState rollback must carry one request"
2921 );
2922 if let Err(error) = self.config_state.dispatch(&rollback) {
2923 error!(
2924 "Could not roll back worker config state after a proxy refused an order: {}",
2925 error
2926 );
2927 }
2928 debug_assert!(
2929 rollback.request_type.is_some(),
2930 "dispatching a rollback must not consume its request type"
2931 );
2932 }
2933
2934 push_queue(response);
2935 }
2936
2937 fn add_cluster(&mut self, cluster: &Cluster) {
2938 let mut backends = self.backends.borrow_mut();
2939 backends.set_load_balancing_policy_for_cluster(
2940 &cluster.cluster_id,
2941 LoadBalancingAlgorithms::try_from(cluster.load_balancing).unwrap_or_default(),
2942 cluster
2943 .load_metric
2944 .and_then(|n| LoadMetric::try_from(n).ok()),
2945 );
2946 backends.set_shuffle_sharding_for_cluster(
2947 &cluster.cluster_id,
2948 ShuffleSharding::from_cluster(
2949 cluster.shard_percent,
2950 cluster.shard_min_backends,
2951 cluster.shard_mode,
2952 ),
2953 );
2954 backends.set_health_check_config(&cluster.cluster_id, cluster.health_check.to_owned());
2955 backends.set_cluster_http2(&cluster.cluster_id, cluster.http2.unwrap_or(false));
2956 }
2957
2958 fn add_backend(&mut self, req_id: &str, add_backend: &AddBackend) -> WorkerResponse {
2959 let new_backend = Backend::new(
2960 &add_backend.backend_id,
2961 add_backend.address.into(),
2962 add_backend.sticky_id.clone(),
2963 add_backend.load_balancing_parameters,
2964 add_backend.backup,
2965 );
2966 self.backends
2967 .borrow_mut()
2968 .add_backend(&add_backend.cluster_id, new_backend);
2969
2970 WorkerResponse::ok(req_id)
2971 }
2972
2973 fn remove_cluster_objects(&mut self, req_id: &str, cluster_id: &str, requests: Vec<Request>) {
2985 for request in requests {
2986 let result = match request.request_type {
2987 Some(RequestType::RemoveHttpFrontend(front)) => {
2988 self.http.borrow_mut().remove_http_frontend(front)
2989 }
2990 Some(RequestType::RemoveHttpsFrontend(front)) => self
2991 .https
2992 .borrow_mut()
2993 .remove_https_frontend(front)
2994 .map(|_| ()),
2995 Some(RequestType::RemoveTcpFrontend(front)) => {
2996 self.tcp.borrow_mut().remove_tcp_front(front)
2997 }
2998 Some(RequestType::RemoveUdpFrontend(front)) => {
2999 self.udp.borrow_mut().remove_udp_front(front)
3000 }
3001 Some(RequestType::RemoveBackend(backend)) => {
3002 let response = self.remove_backend(req_id, &backend);
3003 debug_assert_eq!(
3004 response.status,
3005 ResponseStatus::Ok as i32,
3006 "Server::remove_backend always answers ok"
3007 );
3008 Ok(())
3009 }
3010 other => {
3011 debug_assert!(
3012 false,
3013 "cluster_removal_requests only returns frontend and backend removals, got {other:?}"
3014 );
3015 Ok(())
3016 }
3017 };
3018 if let Err(error) = result {
3019 warn!(
3020 "{} removing cluster {}: could not remove one of its objects: {}",
3021 req_id, cluster_id, error
3022 );
3023 }
3024 }
3025 self.backends.borrow_mut().remove_cluster(cluster_id);
3026 debug_assert!(
3027 !self.backends.borrow().backends.contains_key(cluster_id),
3028 "a removed cluster must leave no backend list behind"
3029 );
3030 }
3031
3032 fn remove_health_check_state(&mut self, cluster_id: &str) {
3037 self.health_checker.remove_cluster(cluster_id);
3038 self.backends
3039 .borrow_mut()
3040 .set_health_check_config(cluster_id, None);
3041 }
3042
3043 fn remove_backend(&mut self, req_id: &str, backend: &RemoveBackend) -> WorkerResponse {
3044 let address = backend.address.into();
3045 let id_still_live = {
3052 let mut backends = self.backends.borrow_mut();
3053 backends.remove_backend(&backend.cluster_id, &backend.backend_id, &address);
3054 backends.has_backend_id(&backend.cluster_id, &backend.backend_id)
3055 };
3056 if !id_still_live {
3057 METRICS.with(|metrics| {
3058 (*metrics.borrow_mut()).remove_backend(&backend.cluster_id, &backend.backend_id);
3059 });
3060 }
3061
3062 WorkerResponse::ok(req_id)
3063 }
3064
3065 fn notify_add_http_listener(
3066 &mut self,
3067 req_id: &str,
3068 listener: HttpListenerConfig,
3069 ) -> WorkerResponse {
3070 debug!("{} add http listener {:?}", req_id, listener);
3071
3072 if self.sessions.borrow().at_capacity() {
3073 return worker_response_error(req_id, "session list is full, cannot add a listener");
3074 }
3075
3076 let mut session_manager = self.sessions.borrow_mut();
3077 let slab_before = session_manager.slab.len();
3079 debug_assert!(
3080 !session_manager
3081 .slab
3082 .contains(session_manager.slab.vacant_key()),
3083 "the next vacant slab key must be free before insertion"
3084 );
3085 let entry = session_manager.slab.vacant_entry();
3086 let token = Token(entry.key());
3087
3088 match self.http.borrow_mut().add_listener(listener, token) {
3089 Ok(_token) => {
3090 entry.insert(Rc::new(RefCell::new(ListenSession {
3091 protocol: Protocol::HTTPListen,
3092 })));
3093 debug_assert!(
3096 session_manager.slab.contains(token.0),
3097 "listener insert must occupy the token's slab key"
3098 );
3099 debug_assert_eq!(
3100 session_manager.slab.len(),
3101 slab_before + 1,
3102 "adding a listener must occupy exactly one slab slot"
3103 );
3104 self.base_sessions_count += 1;
3105 WorkerResponse::ok(req_id)
3106 }
3107 Err(e) => worker_response_error(req_id, format!("Could not add HTTP listener: {e}")),
3108 }
3109 }
3110
3111 fn notify_add_https_listener(
3112 &mut self,
3113 req_id: &str,
3114 listener: HttpsListenerConfig,
3115 ) -> WorkerResponse {
3116 debug!("{} add https listener {:?}", req_id, listener);
3117
3118 if self.sessions.borrow().at_capacity() {
3119 return worker_response_error(req_id, "session list is full, cannot add a listener");
3120 }
3121
3122 let mut session_manager = self.sessions.borrow_mut();
3123 let slab_before = session_manager.slab.len();
3124 debug_assert!(
3125 !session_manager
3126 .slab
3127 .contains(session_manager.slab.vacant_key()),
3128 "the next vacant slab key must be free before insertion"
3129 );
3130 let entry = session_manager.slab.vacant_entry();
3131 let token = Token(entry.key());
3132
3133 match self
3134 .https
3135 .borrow_mut()
3136 .add_listener(listener.clone(), token)
3137 {
3138 Ok(_token) => {
3139 entry.insert(Rc::new(RefCell::new(ListenSession {
3140 protocol: Protocol::HTTPSListen,
3141 })));
3142 debug_assert!(
3143 session_manager.slab.contains(token.0),
3144 "listener insert must occupy the token's slab key"
3145 );
3146 debug_assert_eq!(
3147 session_manager.slab.len(),
3148 slab_before + 1,
3149 "adding a listener must occupy exactly one slab slot"
3150 );
3151 self.base_sessions_count += 1;
3152 WorkerResponse::ok(req_id)
3153 }
3154 Err(e) => worker_response_error(req_id, format!("Could not add HTTPS listener: {e}")),
3155 }
3156 }
3157
3158 fn notify_add_tcp_listener(
3159 &mut self,
3160 req_id: &str,
3161 listener: CommandTcpListener,
3162 ) -> WorkerResponse {
3163 debug!("{} add tcp listener {:?}", req_id, listener);
3164
3165 if self.sessions.borrow().at_capacity() {
3166 return worker_response_error(req_id, "session list is full, cannot add a listener");
3167 }
3168
3169 let mut session_manager = self.sessions.borrow_mut();
3170 let slab_before = session_manager.slab.len();
3171 debug_assert!(
3172 !session_manager
3173 .slab
3174 .contains(session_manager.slab.vacant_key()),
3175 "the next vacant slab key must be free before insertion"
3176 );
3177 let entry = session_manager.slab.vacant_entry();
3178 let token = Token(entry.key());
3179
3180 match self.tcp.borrow_mut().add_listener(listener, token) {
3181 Ok(_token) => {
3182 entry.insert(Rc::new(RefCell::new(ListenSession {
3183 protocol: Protocol::TCPListen,
3184 })));
3185 debug_assert!(
3186 session_manager.slab.contains(token.0),
3187 "listener insert must occupy the token's slab key"
3188 );
3189 debug_assert_eq!(
3190 session_manager.slab.len(),
3191 slab_before + 1,
3192 "adding a listener must occupy exactly one slab slot"
3193 );
3194 self.base_sessions_count += 1;
3195 WorkerResponse::ok(req_id)
3196 }
3197 Err(e) => worker_response_error(req_id, format!("Could not add TCP listener: {e}")),
3198 }
3199 }
3200
3201 fn notify_add_udp_listener(
3202 &mut self,
3203 req_id: &str,
3204 listener: CommandUdpListener,
3205 ) -> WorkerResponse {
3206 debug!("{} add udp listener {:?}", req_id, listener);
3207
3208 if self.sessions.borrow().at_capacity() {
3209 return worker_response_error(req_id, "session list is full, cannot add a listener");
3210 }
3211
3212 let mut session_manager = self.sessions.borrow_mut();
3213 let entry = session_manager.slab.vacant_entry();
3214 let token = Token(entry.key());
3215
3216 match self.udp.borrow_mut().add_listener(listener, token) {
3217 Ok(_token) => {
3218 entry.insert(Rc::new(RefCell::new(ListenSession {
3219 protocol: Protocol::UDPListen,
3220 })));
3221 self.base_sessions_count += 1;
3222 WorkerResponse::ok(req_id)
3223 }
3224 Err(e) => worker_response_error(req_id, format!("Could not add UDP listener: {e}")),
3225 }
3226 }
3227
3228 fn notify_update_udp_listener(
3229 &mut self,
3230 req_id: &str,
3231 patch: UpdateUdpListenerConfig,
3232 ) -> WorkerResponse {
3233 debug!("{} update udp listener {:?}", req_id, patch.address);
3234 match self.udp.borrow_mut().update_listener(patch) {
3235 Ok(()) => WorkerResponse::ok(req_id),
3236 Err(e) => worker_response_error(req_id, format!("Could not update UDP listener: {e}")),
3237 }
3238 }
3239
3240 fn notify_update_http_listener(
3241 &mut self,
3242 req_id: &str,
3243 patch: UpdateHttpListenerConfig,
3244 ) -> WorkerResponse {
3245 debug!("{} update http listener {:?}", req_id, patch.address);
3246 match self.http.borrow_mut().update_listener(patch) {
3247 Ok(()) => WorkerResponse::ok(req_id),
3248 Err(e) => worker_response_error(req_id, format!("Could not update HTTP listener: {e}")),
3249 }
3250 }
3251
3252 fn notify_update_https_listener(
3253 &mut self,
3254 req_id: &str,
3255 patch: UpdateHttpsListenerConfig,
3256 ) -> WorkerResponse {
3257 debug!("{} update https listener {:?}", req_id, patch.address);
3258 match self.https.borrow_mut().update_listener(patch) {
3259 Ok(()) => WorkerResponse::ok(req_id),
3260 Err(e) => {
3261 worker_response_error(req_id, format!("Could not update HTTPS listener: {e}"))
3262 }
3263 }
3264 }
3265
3266 fn notify_update_tcp_listener(
3267 &mut self,
3268 req_id: &str,
3269 patch: UpdateTcpListenerConfig,
3270 ) -> WorkerResponse {
3271 debug!("{} update tcp listener {:?}", req_id, patch.address);
3272 match self.tcp.borrow_mut().update_listener(patch) {
3273 Ok(()) => WorkerResponse::ok(req_id),
3274 Err(e) => worker_response_error(req_id, format!("Could not update TCP listener: {e}")),
3275 }
3276 }
3277
3278 fn reserve_listen_token(&mut self, token: Token, protocol: Protocol) {
3313 let mut sessions = self.sessions.borrow_mut();
3314 match sessions.slab.get_mut(token.0) {
3315 Some(slot) => {
3316 *slot = Rc::new(RefCell::new(ListenSession { protocol }));
3317 info!("reserved listen token {:?} for {:?}", token, protocol);
3318 }
3319 None => error!(
3323 "listen token {:?} ({:?}) has no slab slot to reserve; the listener would not be reachable if reactivated",
3324 token, protocol
3325 ),
3326 }
3327 }
3328
3329 fn report_unclaimed_inherited_sockets(&self) {
3349 let Some(scm_listeners) = self.scm_listeners.as_ref() else {
3350 return;
3351 };
3352 let leftovers: [(&str, &Vec<(ListenerKey, RawFd)>); 4] = [
3353 ("HTTP", &scm_listeners.http),
3354 ("HTTPS", &scm_listeners.tls),
3355 ("TCP", &scm_listeners.tcp),
3356 ("UDP", &scm_listeners.udp),
3357 ];
3358 for (label, table) in leftovers {
3359 for (address, fd) in table {
3360 let has_listener = match label {
3361 "HTTP" => self.http.borrow().listener_token(address).is_some(),
3362 "HTTPS" => self.https.borrow().listener_token(address).is_some(),
3363 "TCP" => self.tcp.borrow().listener_token(address).is_some(),
3364 _ => self.udp.borrow().listener_token(address).is_some(),
3365 };
3366 if has_listener {
3367 info!(
3368 "inherited {} listening socket for {} (fd {}) is held for an \
3369 ActivateListener this worker has not processed yet",
3370 label, address, fd
3371 );
3372 } else {
3373 warn!(
3374 "inherited {} listening socket for {} (fd {}) is held with no listener \
3375 at that address: until an AddListener plus ActivateListener for {} \
3376 adopts it, it is expected to keep taking a share of new connections \
3377 that nothing accepts (inferred from SO_REUSEPORT semantics, not \
3378 measured here)",
3379 label, address, fd, address
3380 );
3381 }
3382 }
3383 }
3384 }
3385
3386 fn notify_activate_listener(
3387 &mut self,
3388 req_id: &str,
3389 activate: &ActivateListener,
3390 ) -> WorkerResponse {
3391 use crate::InheritedSocketFate;
3392
3393 debug!(
3394 "{} activate {:?} listener {:?}",
3395 req_id, activate.proxy, activate
3396 );
3397
3398 let address = activate.listener_key();
3399
3400 match ListenerType::try_from(activate.proxy) {
3401 Ok(ListenerType::Http) => {
3402 let listener = match self.http.borrow().inherited_socket_fate(&address) {
3403 InheritedSocketFate::Adopted => self
3404 .scm_listeners
3405 .as_mut()
3406 .and_then(|listeners| listeners.get_http(&address))
3407 .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3412 InheritedSocketFate::Refused => {
3413 discard_inherited_socket::<MioTcpListener>(
3414 self.scm_listeners
3415 .as_mut()
3416 .and_then(|listeners| listeners.get_http(&address)),
3417 &address,
3418 "HTTP",
3419 );
3420 None
3421 }
3422 InheritedSocketFate::Unclaimed => None,
3425 };
3426
3427 let activated_token = self.http.borrow_mut().activate_listener(&address, listener);
3428 match activated_token {
3429 Ok(token) => {
3430 self.accept(ListenToken(token.0), Protocol::HTTPListen);
3431 WorkerResponse::ok(req_id)
3432 }
3433 Err(activate_error) => worker_response_error(
3434 req_id,
3435 format!("Could not activate HTTP listener: {activate_error}"),
3436 ),
3437 }
3438 }
3439 Ok(ListenerType::Https) => {
3440 let listener = match self.https.borrow().inherited_socket_fate(&address) {
3441 InheritedSocketFate::Adopted => self
3442 .scm_listeners
3443 .as_mut()
3444 .and_then(|listeners| listeners.get_https(&address))
3445 .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3450 InheritedSocketFate::Refused => {
3451 discard_inherited_socket::<MioTcpListener>(
3452 self.scm_listeners
3453 .as_mut()
3454 .and_then(|listeners| listeners.get_https(&address)),
3455 &address,
3456 "HTTPS",
3457 );
3458 None
3459 }
3460 InheritedSocketFate::Unclaimed => None,
3463 };
3464
3465 let activated_token = self
3466 .https
3467 .borrow_mut()
3468 .activate_listener(&address, listener);
3469 match activated_token {
3470 Ok(token) => {
3471 self.accept(ListenToken(token.0), Protocol::HTTPSListen);
3472 WorkerResponse::ok(req_id)
3473 }
3474 Err(activate_error) => worker_response_error(
3475 req_id,
3476 format!("Could not activate HTTPS listener: {activate_error}"),
3477 ),
3478 }
3479 }
3480 Ok(ListenerType::Tcp) => {
3481 let listener = match self.tcp.borrow().inherited_socket_fate(&address) {
3482 InheritedSocketFate::Adopted => self
3483 .scm_listeners
3484 .as_mut()
3485 .and_then(|listeners| listeners.get_tcp(&address))
3486 .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3491 InheritedSocketFate::Refused => {
3492 discard_inherited_socket::<MioTcpListener>(
3493 self.scm_listeners
3494 .as_mut()
3495 .and_then(|listeners| listeners.get_tcp(&address)),
3496 &address,
3497 "TCP",
3498 );
3499 None
3500 }
3501 InheritedSocketFate::Unclaimed => None,
3504 };
3505
3506 let listener_token = self.tcp.borrow_mut().activate_listener(&address, listener);
3507 match listener_token {
3508 Ok(token) => {
3509 self.accept(ListenToken(token.0), Protocol::TCPListen);
3510 WorkerResponse::ok(req_id)
3511 }
3512 Err(activate_error) => worker_response_error(
3513 req_id,
3514 format!("Could not activate TCP listener: {activate_error}"),
3515 ),
3516 }
3517 }
3518 Ok(ListenerType::Udp) => {
3519 let socket = match self.udp.borrow().inherited_socket_fate(&address) {
3520 InheritedSocketFate::Adopted => self
3521 .scm_listeners
3522 .as_mut()
3523 .and_then(|listeners| listeners.get_udp(&address))
3524 .map(|fd| unsafe { MioUdpSocket::from_raw_fd(fd) }),
3532 InheritedSocketFate::Refused => {
3533 discard_inherited_socket::<MioUdpSocket>(
3534 self.scm_listeners
3535 .as_mut()
3536 .and_then(|listeners| listeners.get_udp(&address)),
3537 &address,
3538 "UDP",
3539 );
3540 None
3541 }
3542 InheritedSocketFate::Unclaimed => None,
3545 };
3546
3547 let activated_token = self.udp.borrow_mut().activate_listener(&address, socket);
3548 match activated_token {
3549 Ok(token) => {
3550 let session_installed = self.udp.borrow().has_listener_session(token)
3600 && self.sessions.borrow().slab.contains(token.0);
3601 if !session_installed {
3602 let session = match self.udp.borrow_mut().build_session(token) {
3609 Some(session) => session,
3610 None => {
3611 return worker_response_error(
3612 req_id,
3613 format!(
3614 "Could not build the UDP listener session for {address}"
3615 ),
3616 );
3617 }
3618 };
3619 let mut sessions = self.sessions.borrow_mut();
3620 match sessions.slab.get_mut(token.0) {
3621 Some(slot) => *slot = session,
3622 None => {
3623 return worker_response_error(
3624 req_id,
3625 format!(
3626 "UDP listener {address} has no session slot at {token:?}"
3627 ),
3628 );
3629 }
3630 }
3631 }
3632 WorkerResponse::ok(req_id)
3633 }
3634 Err(activate_error) => worker_response_error(
3635 req_id,
3636 format!("Could not activate UDP listener: {activate_error}"),
3637 ),
3638 }
3639 }
3640 Err(_) => worker_response_error(req_id, "Wrong variant for ListenerType on request"),
3641 }
3642 }
3643
3644 fn notify_deactivate_listener(
3645 &mut self,
3646 req_id: &str,
3647 deactivate: &DeactivateListener,
3648 ) -> WorkerResponse {
3649 debug!(
3650 "{} deactivate {:?} listener {:?}",
3651 req_id, deactivate.proxy, deactivate
3652 );
3653
3654 let address = deactivate.listener_key();
3655
3656 match ListenerType::try_from(deactivate.proxy) {
3657 Ok(ListenerType::Http) => {
3658 let (token, mut listener) =
3659 match self.http.borrow_mut().give_back_listener(&address) {
3660 Ok((token, listener)) => (token, listener),
3661 Err(e) => {
3662 return worker_response_error(
3663 req_id,
3664 format!("Couldn't deactivate HTTP listener at {address}: {e}"),
3665 );
3666 }
3667 };
3668
3669 if let Err(e) = self.poll.registry().deregister(&mut listener) {
3670 error!(
3671 "error deregistering HTTP listen socket({:?}): {:?}",
3672 deactivate, e
3673 );
3674 }
3675
3676 self.reserve_listen_token(token, Protocol::HTTPListen);
3681 self.accept_ready.remove(&ListenToken(token.0));
3687
3688 if deactivate.to_scm {
3689 self.unblock_scm_socket();
3690 let listeners = Listeners {
3691 http: vec![(address.clone(), listener.as_raw_fd())],
3692 tls: vec![],
3693 tcp: vec![],
3694 udp: vec![],
3695 };
3696 info!("sending HTTP listener: {:?}", listeners);
3697 let res = self.scm.send_listeners(&listeners);
3698
3699 self.block_scm_socket();
3700
3701 info!("sent HTTP listener: {:?}", res);
3702 }
3703 WorkerResponse::ok(req_id)
3704 }
3705 Ok(ListenerType::Https) => {
3706 let (token, mut listener) =
3707 match self.https.borrow_mut().give_back_listener(&address) {
3708 Ok((token, listener)) => (token, listener),
3709 Err(e) => {
3710 return worker_response_error(
3711 req_id,
3712 format!("Couldn't deactivate HTTPS listener at {address}: {e}",),
3713 );
3714 }
3715 };
3716 if let Err(e) = self.poll.registry().deregister(&mut listener) {
3717 error!(
3718 "error deregistering HTTPS listen socket({:?}): {:?}",
3719 deactivate, e
3720 );
3721 }
3722 self.reserve_listen_token(token, Protocol::HTTPSListen);
3725 self.accept_ready.remove(&ListenToken(token.0));
3726
3727 if deactivate.to_scm {
3728 self.unblock_scm_socket();
3729 let listeners = Listeners {
3730 http: vec![],
3731 tls: vec![(address.clone(), listener.as_raw_fd())],
3732 tcp: vec![],
3733 udp: vec![],
3734 };
3735 info!("sending HTTPS listener: {:?}", listeners);
3736 let res = self.scm.send_listeners(&listeners);
3737
3738 self.block_scm_socket();
3739
3740 info!("sent HTTPS listener: {:?}", res);
3741 }
3742 WorkerResponse::ok(req_id)
3743 }
3744 Ok(ListenerType::Tcp) => {
3745 let (token, mut listener) = match self.tcp.borrow_mut().give_back_listener(&address)
3746 {
3747 Ok((token, listener)) => (token, listener),
3748 Err(e) => {
3749 return worker_response_error(
3750 req_id,
3751 format!("Could not deactivate TCP listener at {address}: {e}"),
3752 );
3753 }
3754 };
3755
3756 if let Err(e) = self.poll.registry().deregister(&mut listener) {
3757 error!(
3758 "error deregistering TCP listen socket({:?}): {:?}",
3759 deactivate, e
3760 );
3761 }
3762 self.reserve_listen_token(token, Protocol::TCPListen);
3765 self.accept_ready.remove(&ListenToken(token.0));
3766
3767 if deactivate.to_scm {
3768 self.unblock_scm_socket();
3769 let listeners = Listeners {
3770 http: vec![],
3771 tls: vec![],
3772 tcp: vec![(address.clone(), listener.as_raw_fd())],
3773 udp: vec![],
3774 };
3775 info!("sending TCP listener: {:?}", listeners);
3776 let res = self.scm.send_listeners(&listeners);
3777
3778 self.block_scm_socket();
3779
3780 info!("sent TCP listener: {:?}", res);
3781 }
3782 WorkerResponse::ok(req_id)
3783 }
3784 Ok(ListenerType::Udp) => {
3785 let (token, mut listener) = match self.udp.borrow_mut().give_back_listener(&address)
3786 {
3787 Ok((token, listener)) => (token, listener),
3788 Err(e) => {
3789 return worker_response_error(
3790 req_id,
3791 format!("Could not deactivate UDP listener at {address}: {e}"),
3792 );
3793 }
3794 };
3795
3796 if let Err(e) = self.poll.registry().deregister(&mut listener) {
3797 error!(
3798 "error deregistering UDP listen socket({:?}): {:?}",
3799 deactivate, e
3800 );
3801 }
3802 self.reserve_listen_token(token, Protocol::UDPListen);
3807 self.accept_ready.remove(&ListenToken(token.0));
3811
3812 if deactivate.to_scm {
3813 self.unblock_scm_socket();
3814 let listeners = Listeners {
3815 http: vec![],
3816 tls: vec![],
3817 tcp: vec![],
3818 udp: vec![(address.clone(), listener.as_raw_fd())],
3819 };
3820 info!("sending UDP listener: {:?}", listeners);
3821 let res = self.scm.send_listeners(&listeners);
3822
3823 self.block_scm_socket();
3824
3825 info!("sent UDP listener: {:?}", res);
3826 }
3827 WorkerResponse::ok(req_id)
3828 }
3829 Err(_) => worker_response_error(req_id, "Wrong variant for ListenerType on request"),
3830 }
3831 }
3832
3833 pub fn return_listen_sockets(&mut self) -> Result<(), ScmSocketError> {
3835 self.unblock_scm_socket();
3836
3837 let mut http_listeners = self.http.borrow_mut().give_back_listeners();
3838 for &mut (_, ref mut sock) in http_listeners.iter_mut() {
3839 if let Err(e) = self.poll.registry().deregister(sock) {
3840 error!(
3841 "error deregistering HTTP listen socket({:?}): {:?}",
3842 sock, e
3843 );
3844 }
3845 }
3846
3847 let mut https_listeners = self.https.borrow_mut().give_back_listeners();
3848 for &mut (_, ref mut sock) in https_listeners.iter_mut() {
3849 if let Err(e) = self.poll.registry().deregister(sock) {
3850 error!(
3851 "error deregistering HTTPS listen socket({:?}): {:?}",
3852 sock, e
3853 );
3854 }
3855 }
3856
3857 let mut tcp_listeners = self.tcp.borrow_mut().give_back_listeners();
3858 for &mut (_, ref mut sock) in tcp_listeners.iter_mut() {
3859 if let Err(e) = self.poll.registry().deregister(sock) {
3860 error!("error deregistering TCP listen socket({:?}): {:?}", sock, e);
3861 }
3862 }
3863
3864 let mut udp_listeners = self.udp.borrow_mut().give_back_listeners();
3865 for &mut (_, ref mut sock) in udp_listeners.iter_mut() {
3866 if let Err(e) = self.poll.registry().deregister(sock) {
3867 error!("error deregistering UDP listen socket({:?}): {:?}", sock, e);
3868 }
3869 }
3870
3871 let listeners = Listeners {
3873 http: http_listeners
3874 .iter()
3875 .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3876 .collect(),
3877 tls: https_listeners
3878 .iter()
3879 .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3880 .collect(),
3881 tcp: tcp_listeners
3882 .iter()
3883 .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3884 .collect(),
3885 udp: udp_listeners
3886 .iter()
3887 .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3888 .collect(),
3889 };
3890 debug_assert_eq!(
3894 listeners.http.len(),
3895 http_listeners.len(),
3896 "every HTTP listener must be collected exactly once"
3897 );
3898 debug_assert_eq!(
3899 listeners.tls.len(),
3900 https_listeners.len(),
3901 "every HTTPS listener must be collected exactly once"
3902 );
3903 debug_assert_eq!(
3904 listeners.tcp.len(),
3905 tcp_listeners.len(),
3906 "every TCP listener must be collected exactly once"
3907 );
3908 info!("sending default listeners: {:?}", listeners);
3909 let res = self.scm.send_listeners(&listeners);
3910
3911 self.block_scm_socket();
3912
3913 info!("sent default listeners: {:?}", res);
3914 res
3915 }
3916
3917 fn block_scm_socket(&mut self) {
3918 if let Err(e) = self.scm.set_blocking(true) {
3919 error!("Could not block scm socket: {}", e);
3920 }
3921 }
3922
3923 fn unblock_scm_socket(&mut self) {
3924 if let Err(e) = self.scm.set_blocking(false) {
3925 error!("Could not unblock scm socket: {}", e);
3926 }
3927 }
3928
3929 pub fn to_session(&self, token: Token) -> SessionToken {
3930 SessionToken(token.0)
3931 }
3932
3933 pub fn from_session(&self, token: SessionToken) -> Token {
3934 Token(token.0)
3935 }
3936
3937 pub fn accept(&mut self, token: ListenToken, protocol: Protocol) {
3938 let (proto_key, accepted_protocol) = match protocol {
3951 Protocol::TCPListen => ("listener.accepted.tcp", Protocol::TCPListen),
3952 Protocol::HTTPListen => ("listener.accepted.http", Protocol::HTTPListen),
3953 Protocol::HTTPSListen => ("listener.accepted.https", Protocol::HTTPSListen),
3954 other => {
3955 warn!(
3956 "accept() called with non-listen protocol {:?} on token {:?}; skipping",
3957 other, token
3958 );
3959 return;
3960 }
3961 };
3962
3963 debug_assert!(
3966 matches!(
3967 accepted_protocol,
3968 Protocol::TCPListen | Protocol::HTTPListen | Protocol::HTTPSListen
3969 ),
3970 "accept dispatch must run with a listen protocol only"
3971 );
3972
3973 loop {
3974 let result = match accepted_protocol {
3975 Protocol::TCPListen => self.tcp.borrow_mut().accept(token),
3976 Protocol::HTTPListen => self.http.borrow_mut().accept(token),
3977 Protocol::HTTPSListen => self.https.borrow_mut().accept(token),
3978 other => unreachable!(
3982 "accept dispatch reached non-listen protocol {:?} after outer guard",
3983 other
3984 ),
3985 };
3986 match result {
3987 Ok((sock, peer)) => {
3988 incr!(names::listener::ACCEPTED_TOTAL);
3992 incr!(proto_key);
3993 incr!(per_source_bucket(&peer));
3994 let queue_before = self.accept_queue.len();
3995 self.accept_queue.push_back((
3996 sock,
3997 token,
3998 accepted_protocol,
3999 Instant::now(),
4000 peer,
4001 ));
4002 debug_assert_eq!(
4004 self.accept_queue.len(),
4005 queue_before + 1,
4006 "each accepted socket must enqueue exactly one entry"
4007 );
4008 }
4009 Err(AcceptError::WouldBlock) => {
4010 self.accept_ready.remove(&token);
4011 break;
4012 }
4013 Err(other) => {
4014 error!(
4015 "error accepting {:?} sockets: {:?}",
4016 accepted_protocol, other
4017 );
4018 self.accept_ready.remove(&token);
4019 break;
4020 }
4021 }
4022 }
4023
4024 gauge!(names::accept_queue::CONNECTIONS, self.accept_queue.len());
4025 }
4026
4027 pub fn create_sessions(&mut self) {
4028 while let Some((sock, token, protocol, timestamp, peer)) = self.accept_queue.pop_back() {
4029 let wait_time = Instant::now() - timestamp;
4030 time!(names::accept_queue::WAIT_TIME, wait_time.as_millis());
4031 if wait_time > self.accept_queue_timeout {
4032 incr!(names::accept_queue::TIMEOUT);
4033 continue;
4034 }
4035
4036 if !self.sessions.borrow_mut().check_limits() {
4037 if !self.evict_on_queue_full {
4043 incr!(names::listener::CONNECTION_CAPPED);
4044 break;
4045 }
4046
4047 if self.shutting_down.is_some() {
4051 incr!(names::listener::CONNECTION_CAPPED);
4052 break;
4053 }
4054
4055 let to_evict = (self.sessions.borrow().max_connections / 100).max(1);
4064 let evicted = self.evict_least_active_sessions(to_evict);
4065 if evicted == 0 {
4066 warn!("evict_on_queue_full enabled but no candidate sessions to evict");
4072 incr!(names::listener::CONNECTION_CAPPED);
4073 break;
4074 }
4075
4076 count!(names::sessions::EVICTED, evicted as i64);
4077 warn!(
4078 "evicted {} least recently active sessions to make room",
4079 evicted
4080 );
4081
4082 if !self.sessions.borrow_mut().check_limits() {
4083 incr!(names::listener::CONNECTION_CAPPED);
4084 break;
4085 }
4086 }
4087
4088 debug_assert!(
4095 matches!(
4096 protocol,
4097 Protocol::TCPListen | Protocol::HTTPListen | Protocol::HTTPSListen
4098 ),
4099 "accept queue must only hold listen protocols, got {protocol:?}"
4100 );
4101 match protocol {
4102 Protocol::TCPListen => {
4103 let proxy = self.tcp.clone();
4104 if self
4105 .tcp
4106 .borrow_mut()
4107 .create_session(sock, peer, token, wait_time, proxy)
4108 .is_err()
4109 {
4110 break;
4111 }
4112 }
4113 Protocol::HTTPListen => {
4114 let proxy = self.http.clone();
4115 if self
4116 .http
4117 .borrow_mut()
4118 .create_session(sock, peer, token, wait_time, proxy)
4119 .is_err()
4120 {
4121 break;
4122 }
4123 }
4124 Protocol::HTTPSListen => {
4125 if self
4126 .https
4127 .borrow_mut()
4128 .create_session(sock, peer, token, wait_time, self.https.clone())
4129 .is_err()
4130 {
4131 break;
4132 }
4133 }
4134 _ => panic!("should not call accept() on a HTTP, HTTPS or TCP session"),
4135 };
4136 let nb_before = self.sessions.borrow().nb_connections;
4137 self.sessions.borrow_mut().incr();
4138 debug_assert_eq!(
4140 self.sessions.borrow().nb_connections,
4141 nb_before + 1,
4142 "create_sessions must account exactly one new connection per created session"
4143 );
4144 }
4145
4146 gauge!(names::accept_queue::CONNECTIONS, self.accept_queue.len());
4147 }
4148
4149 fn snapshot_event_owners(
4152 &self,
4153 tokens: impl Iterator<Item = Token>,
4154 owners: &mut Vec<Option<Rc<RefCell<dyn ProxySession>>>>,
4155 ) {
4156 debug_assert!(
4157 owners.is_empty(),
4158 "the previous turn must have consumed every event owner"
4159 );
4160 let sessions = self.sessions.borrow();
4161 owners.extend(tokens.map(|token| sessions.slab.get(token.0).cloned()));
4162 }
4163
4164 fn ready_if_still_owned(
4186 &mut self,
4187 token: Token,
4188 events: Ready,
4189 owner: Option<Rc<RefCell<dyn ProxySession>>>,
4190 ) {
4191 let still_owned = owner.is_some_and(|owner| {
4192 self.sessions
4193 .borrow()
4194 .slab
4195 .get(token.0)
4196 .is_some_and(|current| Rc::ptr_eq(current, &owner))
4197 });
4198 if !still_owned {
4199 trace!(
4200 "PROXY\t{:?} dropping events {:?} from a socket whose session closed earlier in this epoll result",
4201 token, events
4202 );
4203 return;
4204 }
4205 self.ready(token, events);
4206 }
4207
4208 pub fn ready(&mut self, token: Token, events: Ready) {
4209 trace!("PROXY\t{:?} got events: {:?}", token, events);
4210
4211 let session_token = token.0;
4212 if self.sessions.borrow().slab.contains(session_token) {
4213 let protocol = self.sessions.borrow().slab[session_token]
4215 .borrow()
4216 .protocol();
4217 match protocol {
4226 Protocol::HTTPListen | Protocol::HTTPSListen | Protocol::TCPListen => {
4227 if events.is_readable() {
4229 self.accept_ready.insert(ListenToken(token.0));
4230 if self.sessions.borrow().can_accept {
4231 self.accept(ListenToken(token.0), protocol);
4232 }
4233 return;
4234 }
4235
4236 if events.is_writable() {
4237 error!(
4238 "received writable for listener {:?}, this should not happen",
4239 token
4240 );
4241 return;
4242 }
4243
4244 if events.is_hup() {
4245 error!("should not happen: server {:?} closed", token);
4246 return;
4247 }
4248
4249 unreachable!();
4250 }
4251 _ => {}
4252 }
4253
4254 let session = self.sessions.borrow_mut().slab[session_token].clone();
4255 session.borrow_mut().update_readiness(token, events);
4256 if session.borrow_mut().ready(session.clone()) {
4257 debug!(
4258 "Server killing session from ready: token={:?}, protocol={:?}, events={:?}",
4259 token, protocol, events
4260 );
4261 self.kill_session(session);
4262 }
4263 }
4264 }
4265
4266 pub fn timeout(&mut self, token: Token) {
4267 trace!("PROXY\t{:?} got timeout", token);
4268
4269 let session_token = token.0;
4270 if self.sessions.borrow().slab.contains(session_token) {
4271 let session = self.sessions.borrow_mut().slab[session_token].clone();
4272 if session.borrow_mut().timeout(token) {
4273 debug!(
4274 "Server killing session from timeout: token={:?}, protocol={:?}",
4275 token,
4276 session.borrow().protocol()
4277 );
4278 self.kill_session(session);
4279 } else if session.borrow().needs_ready_pass() {
4280 if session.borrow_mut().ready(session.clone()) {
4285 debug!(
4286 "Server killing session from ready after timeout: token={:?}, protocol={:?}",
4287 token,
4288 session.borrow().protocol()
4289 );
4290 self.kill_session(session);
4291 }
4292 }
4293 }
4294 }
4295
4296 pub fn handle_remaining_readiness(&mut self) {
4297 if self.sessions.borrow().can_accept && !self.accept_ready.is_empty() {
4300 while let Some(token) = self
4301 .accept_ready
4302 .iter()
4303 .next()
4304 .map(|token| ListenToken(token.0))
4305 {
4306 let protocol = self
4323 .sessions
4324 .borrow()
4325 .slab
4326 .get(token.0)
4327 .map(|session| session.borrow().protocol());
4328 let Some(protocol) = protocol else {
4329 error!(
4330 "accept_ready holds listen token {:?}, which no longer has a session; dropping it",
4331 token
4332 );
4333 self.accept_ready.remove(&token);
4334 continue;
4335 };
4336 self.accept(token, protocol);
4337 if !self.sessions.borrow().can_accept || self.accept_ready.is_empty() {
4338 break;
4339 }
4340 }
4341 }
4342 }
4343 fn block_channel(&mut self) {
4344 if let Err(e) = self.channel.blocking() {
4345 error!("Could not block channel: {}", e);
4346 }
4347 }
4348 fn unblock_channel(&mut self) {
4349 if let Err(e) = self.channel.nonblocking() {
4350 error!("Could not block channel: {}", e);
4351 }
4352 }
4353
4354 fn evict_least_active_sessions(&self, count: usize) -> usize {
4366 if count == 0 {
4367 return 0;
4368 }
4369
4370 let tokens = {
4371 let sessions = self.sessions.borrow();
4372 let mut candidates: Vec<(Token, Instant)> = sessions
4373 .slab
4374 .iter()
4375 .filter(|(_, session)| {
4376 !matches!(
4377 session.borrow().protocol(),
4378 Protocol::HTTPListen
4379 | Protocol::HTTPSListen
4380 | Protocol::TCPListen
4381 | Protocol::UDPListen
4382 | Protocol::Channel
4383 | Protocol::Metrics
4384 | Protocol::Timer
4385 )
4386 })
4387 .map(|(_, session)| {
4388 let s = session.borrow();
4389 (s.frontend_token(), s.last_event())
4390 })
4391 .collect();
4392
4393 if candidates.is_empty() {
4396 return 0;
4397 }
4398
4399 let pivot = count.min(candidates.len()) - 1;
4400 candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4401
4402 candidates[..=pivot]
4403 .iter()
4404 .map(|&(token, _)| token)
4405 .collect::<HashSet<Token>>()
4406 };
4407
4408 let evicted = tokens.len();
4409 self.shut_down_sessions_by_frontend_tokens(tokens);
4410 evicted
4411 }
4412}
4413
4414fn discard_inherited_socket<T: FromRawFd>(
4434 fd: Option<RawFd>,
4435 address: &ListenerKey,
4436 protocol: &str,
4437) {
4438 let Some(fd) = fd else {
4439 return;
4440 };
4441 warn!(
4442 "closing the inherited {} listening socket for {}: this worker already has an active \
4443 listener there, so nothing will adopt this descriptor unless that listener is \
4444 deactivated and reactivated, and until then it is expected to keep taking a share of \
4445 new connections that nothing accepts (inferred from SO_REUSEPORT semantics, not \
4446 measured here)",
4447 protocol, address
4448 );
4449 drop(unsafe { T::from_raw_fd(fd) });
4453}
4454
4455fn worker_response_error<S: ToString, T: ToString>(request_id: S, error: T) -> WorkerResponse {
4458 error!(
4459 "error on request {}, {}",
4460 request_id.to_string(),
4461 error.to_string()
4462 );
4463 WorkerResponse::error(request_id, error)
4464}
4465
4466pub struct ListenSession {
4467 pub protocol: Protocol,
4468}
4469
4470impl ProxySession for ListenSession {
4471 fn last_event(&self) -> Instant {
4472 Instant::now()
4473 }
4474
4475 fn print_session(&self) {}
4476
4477 fn frontend_token(&self) -> Token {
4478 Token(0)
4479 }
4480
4481 fn protocol(&self) -> Protocol {
4482 self.protocol
4483 }
4484
4485 fn ready(&mut self, _session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4486 false
4487 }
4488
4489 fn shutting_down(&mut self) -> SessionIsToBeClosed {
4490 false
4491 }
4492
4493 fn update_readiness(&mut self, _token: Token, _events: Ready) {}
4494
4495 fn close(&mut self) {}
4496
4497 fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4498 error!(
4499 "called ProxySession::timeout(token={:?}, time) on ListenSession {{ protocol: {:?} }}",
4500 _token, self.protocol
4501 );
4502 false
4503 }
4504}
4505
4506#[cfg(test)]
4507mod accept_telemetry_tests {
4508 use super::*;
4509
4510 #[test]
4513 fn per_source_bucket_collapses_ipv4_slash24() {
4514 let a: SocketAddr = "203.0.113.5:1234".parse().unwrap();
4515 let b: SocketAddr = "203.0.113.250:9999".parse().unwrap();
4516 assert_eq!(
4517 per_source_bucket(&a),
4518 per_source_bucket(&b),
4519 "addresses in the same /24 must land in the same bucket"
4520 );
4521 }
4522
4523 #[test]
4525 fn per_source_bucket_collapses_ipv6_slash48() {
4526 let a: SocketAddr = "[2001:db8:1234::1]:443".parse().unwrap();
4527 let b: SocketAddr = "[2001:db8:1234:abcd::ffff]:8443".parse().unwrap();
4528 assert_eq!(
4529 per_source_bucket(&a),
4530 per_source_bucket(&b),
4531 "addresses in the same /48 must land in the same bucket"
4532 );
4533 }
4534
4535 #[test]
4538 fn per_source_bucket_keys_are_bounded() {
4539 assert_eq!(PER_SOURCE_BUCKET_KEYS.len(), PER_SOURCE_BUCKETS);
4540 for (i, key) in PER_SOURCE_BUCKET_KEYS.iter().enumerate() {
4541 let expected = format!("client.connect.per_source.bucket_{i:03}");
4542 assert_eq!(*key, expected.as_str());
4543 }
4544 }
4545
4546 #[test]
4549 fn per_source_bucket_distributes_distinct_subnets() {
4550 let mut hits = std::collections::HashSet::new();
4551 for i in 0..200u8 {
4552 let addr: SocketAddr = format!("10.0.{i}.42:80").parse().unwrap();
4553 hits.insert(per_source_bucket(&addr));
4554 }
4555 assert!(
4559 hits.len() >= 100,
4560 "expected at least 100 distinct buckets across 200 /24s, got {}",
4561 hits.len()
4562 );
4563 }
4564}
4565
4566#[cfg(test)]
4567mod state_error_log_tests {
4568 use sozu_command::state::StateError;
4569
4570 #[test]
4571 fn worker_state_dispatch_error_sink_bounds_the_reason() {
4572 const REASON_SECRET: &str = "WORKER_STATE_ERROR_REASON_SECRET_SENTINEL";
4573
4574 let reason = format!("{REASON_SECRET}{}", "x".repeat(4096));
4575 let reason_len = reason.len();
4576 let output = crate::capture_test_logs(move || {
4577 let error = StateError::InvalidTcpFrontend {
4578 address: "127.0.0.1:443"
4579 .parse()
4580 .expect("test TCP frontend address must parse"),
4581 reason,
4582 };
4583 error!("Could not execute order on config state: {}", error);
4584 });
4585
4586 assert!(
4587 !output.contains(REASON_SECRET),
4588 "worker state-error sink leaked the reason: {output}"
4589 );
4590 assert!(
4591 output.contains(&format!("reason_bytes={reason_len}")),
4592 "worker state-error sink omitted bounded reason metadata: {output}"
4593 );
4594 assert!(
4595 output.len() <= 512,
4596 "worker state-error sink is not bounded: {} bytes",
4597 output.len()
4598 );
4599 }
4600}
4601
4602#[cfg(test)]
4603mod eviction_tests {
4604 use std::collections::HashSet;
4605 use std::time::{Duration, Instant};
4606
4607 use mio::Token;
4608
4609 #[test]
4613 fn select_nth_finds_oldest_sessions() {
4614 let now = Instant::now();
4615 let mut candidates = [
4616 (Token(1), now - Duration::from_secs(10)), (Token(2), now - Duration::from_secs(50)), (Token(3), now - Duration::from_secs(5)), (Token(4), now - Duration::from_secs(30)), (Token(5), now - Duration::from_secs(20)), ];
4622
4623 let count = 2;
4624 let pivot = count.min(candidates.len()) - 1;
4625 candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4626
4627 let selected: HashSet<Token> = candidates[..=pivot]
4628 .iter()
4629 .map(|&(token, _)| token)
4630 .collect();
4631
4632 assert_eq!(selected.len(), 2);
4633 assert!(
4634 selected.contains(&Token(2)),
4635 "should contain 50s-old session"
4636 );
4637 assert!(
4638 selected.contains(&Token(4)),
4639 "should contain 30s-old session"
4640 );
4641 }
4642
4643 #[test]
4647 fn select_nth_with_count_exceeding_candidates() {
4648 let now = Instant::now();
4649 let mut candidates = [(Token(1), now - Duration::from_secs(10))];
4650
4651 let count = 5;
4652 let pivot = count.min(candidates.len()) - 1;
4653 candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4654
4655 let selected: HashSet<Token> = candidates[..=pivot]
4656 .iter()
4657 .map(|&(token, _)| token)
4658 .collect();
4659
4660 assert_eq!(selected.len(), 1);
4661 assert!(selected.contains(&Token(1)));
4662 }
4663}
4664
4665#[cfg(test)]
4666mod accept_ready_tests {
4667 use sozu_command::{
4668 config::ListenerBuilder,
4669 proto::command::{DeactivateListener, ListenerType, SocketAddress},
4670 };
4671
4672 use super::*;
4673 use crate::testing::{ServerParts, prebuild_server, provide_port};
4674
4675 pub(super) fn server_with_tcp_listener() -> (Server, Token, SocketAddress) {
4680 let ServerParts {
4681 event_loop,
4682 registry,
4683 sessions,
4684 pool,
4685 backends,
4686 server_scm_socket,
4687 server_config,
4688 ..
4689 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
4694 let (_command_channel, proxy_channel) =
4695 Channel::generate(1000, 10000).expect("could not generate a test channel");
4696
4697 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4698 let listener_config = ListenerBuilder::new_tcp(address)
4699 .to_tcp(None)
4700 .expect("could not build a TcpListenerConfig for the test");
4701
4702 let listen_token = {
4703 let mut session_manager = sessions.borrow_mut();
4704 let entry = session_manager.slab.vacant_entry();
4705 let token = Token(entry.key());
4706 entry.insert(Rc::new(RefCell::new(ListenSession {
4707 protocol: Protocol::TCPListen,
4708 })));
4709 token
4710 };
4711
4712 let mut tcp_proxy =
4713 tcp::TcpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
4714 tcp_proxy
4715 .add_listener(listener_config, listen_token)
4716 .expect("could not add the test TCP listener");
4717 tcp_proxy
4718 .activate_listener(&address.into(), None)
4719 .expect("could not activate the test TCP listener");
4720
4721 let server = Server::new(
4722 event_loop,
4723 proxy_channel,
4724 server_scm_socket,
4725 sessions,
4726 pool,
4727 backends,
4728 None,
4729 None,
4730 Some(tcp_proxy),
4731 server_config,
4732 None,
4733 false,
4734 )
4735 .expect("could not build the test server");
4736
4737 (server, listen_token, address)
4740 }
4741
4742 #[test]
4754 fn deactivating_a_listener_drops_its_pending_accept_token() {
4755 let (mut server, listen_token, address) = server_with_tcp_listener();
4756
4757 server.accept_ready.insert(ListenToken(listen_token.0));
4759
4760 let response = server.notify_deactivate_listener(
4761 "test-deactivate",
4762 &DeactivateListener {
4763 interface: None,
4764 address,
4765 proxy: ListenerType::Tcp as i32,
4766 to_scm: false,
4767 },
4768 );
4769 assert_eq!(
4770 response.status,
4771 ResponseStatus::Ok as i32,
4772 "deactivating an activated TCP listener must succeed: {response:?}"
4773 );
4774 assert!(
4775 server.sessions.borrow().slab.contains(listen_token.0),
4776 "deactivating a listener must KEEP its slab slot reserved — see \
4777 `reserve_listen_token`; the accept purge below is what stops the \
4778 deferred pass from accepting on a socket-less listener"
4779 );
4780 assert!(
4781 !server.accept_ready.contains(&ListenToken(listen_token.0)),
4782 "a deactivated listener must not stay queued for a deferred accept"
4783 );
4784
4785 server.handle_remaining_readiness();
4787 }
4788
4789 #[test]
4800 fn a_stale_accept_token_is_dropped_instead_of_panicking() {
4801 let (mut server, listen_token, _address) = server_with_tcp_listener();
4802
4803 server.sessions.borrow_mut().slab.remove(listen_token.0);
4804 server.accept_ready.insert(ListenToken(listen_token.0));
4805
4806 server.handle_remaining_readiness();
4807
4808 assert!(
4809 server.accept_ready.is_empty(),
4810 "a listen token with no session must be dropped from accept_ready"
4811 );
4812 }
4813}
4814#[cfg(test)]
4819mod event_owner_tests {
4820 use super::*;
4821
4822 struct ClosingSession {
4825 sessions: Rc<RefCell<SessionManager>>,
4826 frontend: Token,
4827 backend: Token,
4828 }
4829
4830 impl ProxySession for ClosingSession {
4831 fn protocol(&self) -> Protocol {
4832 Protocol::TCP
4833 }
4834 fn ready(&mut self, _session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4835 true
4836 }
4837 fn shutting_down(&mut self) -> SessionIsToBeClosed {
4838 true
4839 }
4840 fn update_readiness(&mut self, _token: Token, _events: Ready) {}
4841 fn close(&mut self) {
4842 self.sessions.borrow_mut().slab.try_remove(self.backend.0);
4843 }
4844 fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4845 false
4846 }
4847 fn last_event(&self) -> Instant {
4848 Instant::now()
4849 }
4850 fn print_session(&self) {}
4851 fn frontend_token(&self) -> Token {
4852 self.frontend
4853 }
4854 }
4855
4856 struct DialingSession {
4860 sessions: Rc<RefCell<SessionManager>>,
4861 frontend: Token,
4862 backend: Option<Token>,
4863 delivered: Vec<Token>,
4864 }
4865
4866 impl ProxySession for DialingSession {
4867 fn protocol(&self) -> Protocol {
4868 Protocol::TCP
4869 }
4870 fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4871 if self.backend.is_none() {
4872 self.backend = Some(Token(self.sessions.borrow_mut().slab.insert(session)));
4873 }
4874 false
4875 }
4876 fn shutting_down(&mut self) -> SessionIsToBeClosed {
4877 true
4878 }
4879 fn update_readiness(&mut self, token: Token, _events: Ready) {
4880 self.delivered.push(token);
4881 }
4882 fn close(&mut self) {}
4883 fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4884 false
4885 }
4886 fn last_event(&self) -> Instant {
4887 Instant::now()
4888 }
4889 fn print_session(&self) {}
4890 fn frontend_token(&self) -> Token {
4891 self.frontend
4892 }
4893 }
4894
4895 #[test]
4903 fn an_event_of_a_closed_session_is_not_delivered_to_the_session_reusing_its_token() {
4904 let (mut server, _listen_token, _address) =
4905 super::accept_ready_tests::server_with_tcp_listener();
4906 let sessions = server.sessions.clone();
4907
4908 let closing = Rc::new(RefCell::new(ClosingSession {
4909 sessions: sessions.clone(),
4910 frontend: Token(0),
4911 backend: Token(0),
4912 }));
4913 let dialing = Rc::new(RefCell::new(DialingSession {
4914 sessions: sessions.clone(),
4915 frontend: Token(0),
4916 backend: None,
4917 delivered: Vec::new(),
4918 }));
4919 {
4920 let mut sessions = sessions.borrow_mut();
4921 let closing_session: Rc<RefCell<dyn ProxySession>> = closing.clone();
4922 let frontend = Token(sessions.slab.insert(closing_session.clone()));
4923 let backend = Token(sessions.slab.insert(closing_session));
4924 let mut closing = closing.borrow_mut();
4925 closing.frontend = frontend;
4926 closing.backend = backend;
4927 let dialing_session: Rc<RefCell<dyn ProxySession>> = dialing.clone();
4928 dialing.borrow_mut().frontend = Token(sessions.slab.insert(dialing_session));
4929 sessions.incr();
4930 sessions.incr();
4931 }
4932 let closed_frontend = closing.borrow().frontend;
4933 let closed_backend = closing.borrow().backend;
4934 let dialing_frontend = dialing.borrow().frontend;
4935
4936 let batch = [
4937 (closed_frontend, Ready::READABLE | Ready::HUP),
4938 (dialing_frontend, Ready::READABLE | Ready::WRITABLE),
4939 (
4940 closed_backend,
4941 Ready::READABLE | Ready::WRITABLE | Ready::HUP,
4942 ),
4943 ];
4944 let mut owners = Vec::new();
4945 server.snapshot_event_owners(batch.iter().map(|(token, _)| *token), &mut owners);
4946 for ((token, events), owner) in batch.into_iter().zip(owners.drain(..)) {
4947 server.ready_if_still_owned(token, events, owner);
4948 }
4949
4950 assert_eq!(
4951 dialing.borrow().backend,
4952 Some(closed_backend),
4953 "precondition: the dial must reuse the key the closed session freed in this batch"
4954 );
4955 assert!(
4956 !sessions.borrow().slab.contains(closed_frontend.0),
4957 "the closed session must have left the slab"
4958 );
4959 assert_eq!(
4960 dialing.borrow().delivered,
4961 vec![dialing_frontend],
4962 "the closed backend's HUP must not reach the session that reused its token"
4963 );
4964 }
4965}
4966#[cfg(test)]
4971mod listener_lifecycle_tests {
4972 use std::net::UdpSocket as StdUdpSocket;
4973
4974 use sozu_command::{
4975 config::ListenerBuilder,
4976 proto::command::{
4977 ActivateListener, DeactivateListener, ListenerType, LoadBalancingParams,
4978 RemoveListener, RequestUdpFrontend, SocketAddress, filtered_metrics,
4979 },
4980 };
4981
4982 use super::accept_ready_tests::server_with_tcp_listener;
4983 use super::*;
4984 use crate::{
4985 metrics::{METRICS, names},
4986 testing::{ServerParts, prebuild_server, provide_port},
4987 };
4988
4989 pub(super) fn bare_server() -> Server {
4994 let ServerParts {
4995 event_loop,
4996 sessions,
4997 pool,
4998 backends,
4999 server_scm_socket,
5000 server_config,
5001 ..
5002 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
5003 let (_command_channel, proxy_channel) =
5004 Channel::generate(1000, 10000).expect("could not generate a test channel");
5005
5006 Server::new(
5007 event_loop,
5008 proxy_channel,
5009 server_scm_socket,
5010 sessions,
5011 pool,
5012 backends,
5013 None,
5014 None,
5015 None,
5016 server_config,
5017 None,
5018 false,
5019 )
5020 .expect("could not build the test server")
5021 }
5022
5023 fn add_udp_listener(server: &mut Server, address: SocketAddress) -> Token {
5024 let listener_config = ListenerBuilder::new_udp(address)
5025 .to_udp(None)
5026 .expect("could not build a UdpListenerConfig for the test");
5027 let response = server.notify_add_udp_listener("test-add-udp", listener_config);
5028 assert_eq!(
5029 response.status,
5030 ResponseStatus::Ok as i32,
5031 "adding a UDP listener must succeed: {response:?}"
5032 );
5033 server
5034 .udp
5035 .borrow()
5036 .listener_token(&address.into())
5037 .expect("the added UDP listener must own a token")
5038 }
5039
5040 fn activate(server: &mut Server, address: SocketAddress, proxy: ListenerType) {
5041 let response = server.notify_activate_listener(
5042 "test-activate",
5043 &ActivateListener {
5044 interface: None,
5045 address,
5046 proxy: proxy as i32,
5047 from_scm: false,
5048 },
5049 );
5050 assert_eq!(
5051 response.status,
5052 ResponseStatus::Ok as i32,
5053 "activating a {proxy:?} listener must succeed: {response:?}"
5054 );
5055 }
5056
5057 fn deactivate(server: &mut Server, address: SocketAddress, proxy: ListenerType) {
5058 let response = server.notify_deactivate_listener(
5059 "test-deactivate",
5060 &DeactivateListener {
5061 interface: None,
5062 address,
5063 proxy: proxy as i32,
5064 to_scm: false,
5065 },
5066 );
5067 assert_eq!(
5068 response.status,
5069 ResponseStatus::Ok as i32,
5070 "deactivating a {proxy:?} listener must succeed: {response:?}"
5071 );
5072 }
5073
5074 fn slot_state(server: &Server, token: Token) -> Option<(Protocol, bool)> {
5080 let sessions = server.sessions.borrow();
5081 let session = sessions.slab.get(token.0)?;
5082 let session = session.borrow();
5083 Some((session.protocol(), session.frontend_token() == Token(0)))
5084 }
5085
5086 #[test]
5107 fn a_reactivated_tcp_listener_is_reachable_from_the_event_loop() {
5108 let (mut server, listen_token, address) = server_with_tcp_listener();
5109
5110 deactivate(&mut server, address, ListenerType::Tcp);
5111 activate(&mut server, address, ListenerType::Tcp);
5112
5113 server.sessions.borrow_mut().can_accept = false;
5114 server.ready(listen_token, Ready::READABLE);
5115
5116 assert!(
5117 server.accept_ready.contains(&ListenToken(listen_token.0)),
5118 "a reactivated listener must still be dispatched by `ready()`"
5119 );
5120 }
5121
5122 #[test]
5134 fn a_reactivated_udp_listener_gets_its_session_back() {
5135 let mut server = bare_server();
5136 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5137 let listen_token = add_udp_listener(&mut server, address);
5138
5139 assert_eq!(
5140 slot_state(&server, listen_token),
5141 Some((Protocol::UDPListen, true)),
5142 "an added, not-yet-activated UDP listener holds the placeholder"
5143 );
5144
5145 activate(&mut server, address, ListenerType::Udp);
5146 assert_eq!(
5147 slot_state(&server, listen_token),
5148 Some((Protocol::UDPListen, false)),
5149 "activating must install the real UdpListenerSession"
5150 );
5151
5152 deactivate(&mut server, address, ListenerType::Udp);
5153 activate(&mut server, address, ListenerType::Udp);
5154
5155 assert_eq!(
5156 slot_state(&server, listen_token),
5157 Some((Protocol::UDPListen, false)),
5158 "reactivating must reinstall the real UdpListenerSession"
5159 );
5160 }
5161
5162 #[test]
5186 fn a_deactivated_udp_listeners_key_is_not_handed_to_another_session() {
5187 let mut server = bare_server();
5188 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5189 let listen_token = add_udp_listener(&mut server, address);
5190 activate(&mut server, address, ListenerType::Udp);
5191 deactivate(&mut server, address, ListenerType::Udp);
5192
5193 let interloper = {
5196 let mut sessions = server.sessions.borrow_mut();
5197 let entry = sessions.slab.vacant_entry();
5198 let key = entry.key();
5199 entry.insert(Rc::new(RefCell::new(ListenSession {
5200 protocol: Protocol::Channel,
5201 })));
5202 Token(key)
5203 };
5204 assert_ne!(
5205 interloper, listen_token,
5206 "a deactivated listener's key must not be handed to another session"
5207 );
5208
5209 activate(&mut server, address, ListenerType::Udp);
5210
5211 assert_eq!(
5212 slot_state(&server, interloper),
5213 Some((Protocol::Channel, true)),
5214 "reactivating a listener must not overwrite another session's slot"
5215 );
5216 }
5217
5218 const UDP_FLOW_CLUSTER: &str = "udp-duplicate-activate";
5220
5221 fn slab_session(server: &Server, token: Token) -> Rc<RefCell<dyn ProxySession>> {
5227 server
5228 .sessions
5229 .borrow()
5230 .slab
5231 .get(token.0)
5232 .expect("the listener must own a slab slot")
5233 .clone()
5234 }
5235
5236 fn active_flows_gauge() -> i64 {
5240 METRICS.with(|metrics| {
5241 metrics
5242 .borrow_mut()
5243 .dump_local_proxy_metrics()
5244 .get(names::udp::ACTIVE_FLOWS)
5245 .and_then(|metric| match metric.inner {
5246 Some(filtered_metrics::Inner::Gauge(value)) => Some(value as i64),
5247 _ => None,
5248 })
5249 .unwrap_or(0)
5250 })
5251 }
5252
5253 fn udp_worker_with_backend(
5259 server: &mut Server,
5260 address: SocketAddress,
5261 ) -> (StdUdpSocket, StdUdpSocket) {
5262 server.notify_proxys(WorkerRequest {
5263 id: "test-add-udp-front".to_owned(),
5264 content: RequestType::AddUdpFrontend(RequestUdpFrontend {
5265 cluster_id: UDP_FLOW_CLUSTER.to_owned(),
5266 address,
5267 tags: Default::default(),
5268 })
5269 .into(),
5270 });
5271
5272 let backend_socket =
5273 StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test UDP backend");
5274 backend_socket
5275 .set_read_timeout(Some(Duration::from_millis(500)))
5276 .expect("could not set the backend read timeout");
5277 let backend_address = backend_socket
5278 .local_addr()
5279 .expect("the test UDP backend must have a local address");
5280
5281 server.notify_proxys(WorkerRequest {
5282 id: "test-add-udp-backend".to_owned(),
5283 content: RequestType::AddBackend(AddBackend {
5284 cluster_id: UDP_FLOW_CLUSTER.to_owned(),
5285 backend_id: format!("{UDP_FLOW_CLUSTER}-0"),
5286 address: backend_address.into(),
5287 load_balancing_parameters: Some(LoadBalancingParams::default()),
5288 sticky_id: None,
5289 backup: None,
5290 })
5291 .into(),
5292 });
5293
5294 let client_socket =
5295 StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test UDP client");
5296 (client_socket, backend_socket)
5297 }
5298
5299 fn forward_one(
5305 server: &mut Server,
5306 listen_token: Token,
5307 listen_address: SocketAddress,
5308 client: &StdUdpSocket,
5309 backend: &StdUdpSocket,
5310 payload: &[u8],
5311 ) -> Option<Vec<u8>> {
5312 let target: std::net::SocketAddr = listen_address.into();
5313 client
5314 .send_to(payload, target)
5315 .expect("the test client must reach the UDP listener");
5316 server.ready(listen_token, Ready::READABLE);
5317
5318 let mut received = [0u8; 64];
5319 backend
5320 .recv_from(&mut received)
5321 .ok()
5322 .map(|(len, _)| received[..len].to_vec())
5323 }
5324
5325 #[test]
5340 fn a_duplicate_udp_activate_keeps_the_live_listener_session() {
5341 let mut server = bare_server();
5342 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5343 let listen_token = add_udp_listener(&mut server, address);
5344 activate(&mut server, address, ListenerType::Udp);
5345
5346 let installed = slab_session(&server, listen_token);
5347
5348 let response = server.notify_activate_listener(
5349 "test-activate-twice",
5350 &ActivateListener {
5351 interface: None,
5352 address,
5353 proxy: ListenerType::Udp as i32,
5354 from_scm: false,
5355 },
5356 );
5357 assert_eq!(
5364 response.status,
5365 ResponseStatus::Ok as i32,
5366 "a repeated ActivateListener on an active UDP listener must answer ok: {response:?}"
5367 );
5368 assert_eq!(
5369 slot_state(&server, listen_token),
5370 Some((Protocol::UDPListen, false)),
5371 "the real UdpListenerSession must still be the one in the slab"
5372 );
5373 assert!(
5374 Rc::ptr_eq(&installed, &slab_session(&server, listen_token)),
5375 "a repeated ActivateListener must not replace the live UDP listener session"
5376 );
5377 }
5378
5379 #[test]
5391 fn in_flight_udp_flows_survive_a_duplicate_udp_activate() {
5392 let mut server = bare_server();
5393 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5394 let listen_token = add_udp_listener(&mut server, address);
5395 activate(&mut server, address, ListenerType::Udp);
5396 let (client, backend) = udp_worker_with_backend(&mut server, address);
5397
5398 assert_eq!(
5399 forward_one(
5400 &mut server,
5401 listen_token,
5402 address,
5403 &client,
5404 &backend,
5405 b"before"
5406 )
5407 .as_deref(),
5408 Some(&b"before"[..]),
5409 "the flow must be established before the duplicate activate"
5410 );
5411 assert_eq!(
5412 active_flows_gauge(),
5413 1,
5414 "one established flow must be on the gauge"
5415 );
5416
5417 activate(&mut server, address, ListenerType::Udp);
5418
5419 assert_eq!(
5424 active_flows_gauge(),
5425 1,
5426 "a repeated ActivateListener must neither open nor evict a flow"
5427 );
5428 assert_eq!(
5429 forward_one(
5430 &mut server,
5431 listen_token,
5432 address,
5433 &client,
5434 &backend,
5435 b"after"
5436 )
5437 .as_deref(),
5438 Some(&b"after"[..]),
5439 "an in-flight UDP flow must survive a repeated ActivateListener"
5440 );
5441 }
5442
5443 #[test]
5454 fn a_duplicate_udp_activate_strands_no_upstream_slab_slot() {
5455 let mut server = bare_server();
5456 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5457 let listen_token = add_udp_listener(&mut server, address);
5458 activate(&mut server, address, ListenerType::Udp);
5459 let (client, backend) = udp_worker_with_backend(&mut server, address);
5460
5461 let baseline = server.sessions.borrow().slab.len();
5464
5465 assert!(
5466 forward_one(
5467 &mut server,
5468 listen_token,
5469 address,
5470 &client,
5471 &backend,
5472 b"flow"
5473 )
5474 .is_some(),
5475 "the flow must be established before the duplicate activate"
5476 );
5477 assert_eq!(
5478 server.sessions.borrow().slab.len(),
5479 baseline + 1,
5480 "an established flow owns exactly one upstream slab slot"
5481 );
5482
5483 activate(&mut server, address, ListenerType::Udp);
5484 deactivate(&mut server, address, ListenerType::Udp);
5485
5486 assert_eq!(
5487 server.sessions.borrow().slab.len(),
5488 baseline,
5489 "tearing the listener down must release every per-flow slab slot"
5490 );
5491 assert_eq!(
5492 active_flows_gauge(),
5493 0,
5494 "tearing the listener down must return the active-flows gauge to zero"
5495 );
5496 }
5497
5498 #[test]
5514 fn replaying_activate_listener_is_a_clean_no_op_for_an_active_udp_listener() {
5515 let mut server = bare_server();
5516 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5517 let listener_config = ListenerBuilder::new_udp(address)
5518 .to_udp(None)
5519 .expect("could not build a UdpListenerConfig for the test");
5520
5521 server.notify_proxys(WorkerRequest {
5524 id: "test-add-udp".to_owned(),
5525 content: RequestType::AddUdpListener(listener_config).into(),
5526 });
5527 server.notify_proxys(WorkerRequest {
5528 id: "test-activate".to_owned(),
5529 content: RequestType::ActivateListener(ActivateListener {
5530 interface: None,
5531 address,
5532 proxy: ListenerType::Udp as i32,
5533 from_scm: false,
5534 })
5535 .into(),
5536 });
5537 let listen_token = server
5538 .udp
5539 .borrow()
5540 .listener_token(&address.into())
5541 .expect("the added UDP listener must own a token");
5542 let installed = slab_session(&server, listen_token);
5543
5544 let replayed: Vec<ActivateListener> = server
5545 .config_state
5546 .generate_activate_requests()
5547 .into_iter()
5548 .filter_map(|request| match request.request_type {
5549 Some(RequestType::ActivateListener(activate)) => Some(activate),
5550 _ => None,
5551 })
5552 .collect();
5553 assert_eq!(
5554 replayed.len(),
5555 1,
5556 "an active UDP listener must be replayed exactly once: {replayed:?}"
5557 );
5558
5559 for activate in &replayed {
5560 let response = server.notify_activate_listener("test-replay", activate);
5561 assert_eq!(
5562 response.status,
5563 ResponseStatus::Ok as i32,
5564 "a replayed ActivateListener must answer ok: {response:?}"
5565 );
5566 }
5567
5568 assert_eq!(
5569 slot_state(&server, listen_token),
5570 Some((Protocol::UDPListen, false)),
5571 "the replay must leave the real UdpListenerSession in the slab"
5572 );
5573 assert!(
5574 Rc::ptr_eq(&installed, &slab_session(&server, listen_token)),
5575 "replaying ActivateListener must leave the live UDP listener session in place"
5576 );
5577 }
5578
5579 #[test]
5598 fn removing_a_listener_releases_its_reserved_slab_slot() {
5599 let mut server = bare_server();
5600 let baseline = server.sessions.borrow().slab.len();
5601 let base_sessions_count = server.base_sessions_count;
5602
5603 for cycle in 0..3 {
5604 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5605 let listen_token = add_udp_listener(&mut server, address);
5606 activate(&mut server, address, ListenerType::Udp);
5607 if cycle % 2 == 0 {
5608 deactivate(&mut server, address, ListenerType::Udp);
5609 }
5610
5611 server.notify_proxys(WorkerRequest {
5612 id: "test-remove".to_owned(),
5613 content: RequestType::RemoveListener(RemoveListener {
5614 interface: None,
5615 address,
5616 proxy: ListenerType::Udp as i32,
5617 })
5618 .into(),
5619 });
5620
5621 assert!(
5622 !server.sessions.borrow().slab.contains(listen_token.0),
5623 "cycle {cycle}: removing a listener must release its reserved slot"
5624 );
5625 assert_eq!(
5626 server.sessions.borrow().slab.len(),
5627 baseline,
5628 "cycle {cycle}: the slab must return to its baseline occupancy"
5629 );
5630 assert_eq!(
5631 server.base_sessions_count, base_sessions_count,
5632 "cycle {cycle}: base_sessions_count must track the reserved slots"
5633 );
5634 }
5635 }
5636
5637 #[test]
5648 fn udp_flow_limits_come_from_the_cluster_not_the_global_defaults() {
5649 use std::{net::SocketAddr, time::Instant};
5650
5651 use crate::protocol::udp::{
5652 BackendId, BackendSource, ManagerInput, MetricEvent, Output, UdpManager,
5653 };
5654
5655 struct OneBackend;
5656 impl BackendSource for OneBackend {
5657 fn select(
5658 &mut self,
5659 _cluster: &str,
5660 _key: Option<u64>,
5661 _now: Instant,
5662 ) -> Option<(BackendId, SocketAddr)> {
5663 Some(("b".to_owned(), SocketAddr::from(([127, 0, 0, 1], 5353))))
5664 }
5665 }
5666 fn admit(manager: &RefCell<UdpManager>, port: u16) -> bool {
5669 let mut manager = manager.borrow_mut();
5670 manager.handle_input(
5671 ManagerInput::ClientDatagram {
5672 src: SocketAddr::from(([192, 0, 2, 1], port)),
5673 payload: b"q",
5674 backends: &mut OneBackend,
5675 },
5676 Instant::now(),
5677 );
5678 let mut opened = false;
5679 let mut shed = false;
5680 while let Some(out) = manager.poll_output() {
5681 opened |= matches!(out, Output::OpenUpstream { .. });
5682 shed |= matches!(out, Output::Metric(MetricEvent::FlowShedSourceLimit));
5683 }
5684 assert_ne!(opened, shed, "a datagram is either admitted or shed");
5685 opened
5686 }
5687 fn add_cluster(server: &mut Server, max_connections_per_ip: Option<u64>) {
5688 server.notify_proxys(WorkerRequest {
5689 id: "test-add-cluster".to_owned(),
5690 content: RequestType::AddCluster(Cluster {
5691 cluster_id: "udp-limits".to_owned(),
5692 max_connections_per_ip,
5693 ..Default::default()
5694 })
5695 .into(),
5696 });
5697 }
5698
5699 let mut server = bare_server();
5700 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5701 let token = add_udp_listener(&mut server, address);
5702 server.notify(WorkerRequest {
5703 id: "test-set-per-ip".to_owned(),
5704 content: RequestType::SetMaxConnectionsPerIp(1).into(),
5705 });
5706 server.notify(WorkerRequest {
5707 id: "test-set-per-subnet".to_owned(),
5708 content: RequestType::SetMaxConnectionsPerSubnet(1).into(),
5709 });
5710 add_cluster(&mut server, None);
5711 server.notify_proxys(WorkerRequest {
5712 id: "test-add-udp-front".to_owned(),
5713 content: RequestType::AddUdpFrontend(RequestUdpFrontend {
5714 cluster_id: "udp-limits".to_owned(),
5715 address,
5716 tags: Default::default(),
5717 })
5718 .into(),
5719 });
5720 let manager = server
5721 .udp
5722 .borrow()
5723 .manager(token)
5724 .expect("the UDP listener must own a flow manager");
5725
5726 {
5727 let manager = manager.borrow();
5728 let cfg = manager.cluster_config();
5729 assert_eq!(
5730 cfg.max_flows_per_ip, 0,
5731 "the global per-IP default must not apply to UDP"
5732 );
5733 assert_eq!(
5734 cfg.max_flows_per_subnet, 0,
5735 "the global per-subnet default must not apply to UDP"
5736 );
5737 }
5738 assert!(admit(&manager, 1000));
5739 assert!(
5740 admit(&manager, 1001),
5741 "a global limit of 1 must not shed a second UDP flow"
5742 );
5743
5744 add_cluster(&mut server, Some(2));
5745 assert_eq!(manager.borrow().cluster_config().max_flows_per_ip, 2);
5746 assert!(
5747 !admit(&manager, 1002),
5748 "the cluster's own limit of 2 counts the two open flows and sheds a third"
5749 );
5750 }
5751}
5752
5753#[cfg(test)]
5754mod add_cluster_validation_tests {
5755 use sozu_command::proto::command::{LoadBalancingParams, ShardMode};
5756
5757 use super::listener_lifecycle_tests::bare_server;
5758 use super::*;
5759 use crate::{backends::ShardOutcome, load_balancing::affinity_key_from_ip};
5760
5761 const CLUSTER: &str = "sharded";
5762 const ADD_CLUSTER_ID: &str = "test-add-invalid-shard";
5763
5764 #[test]
5774 fn an_add_cluster_with_an_invalid_shard_percent_is_refused_and_unsharded() {
5775 let mut server = bare_server();
5776 server.notify_proxys(WorkerRequest {
5777 id: ADD_CLUSTER_ID.to_owned(),
5778 content: RequestType::AddCluster(Cluster {
5779 cluster_id: CLUSTER.to_owned(),
5780 load_balancing: LoadBalancingAlgorithms::RoundRobin as i32,
5781 shard_percent: Some(0),
5784 shard_min_backends: Some(2),
5785 shard_mode: Some(ShardMode::Strict as i32),
5786 ..Default::default()
5787 })
5788 .into(),
5789 });
5790 let response = QUEUE
5791 .with(|queue| {
5792 queue
5793 .borrow()
5794 .iter()
5795 .find(|response| response.id == ADD_CLUSTER_ID)
5796 .cloned()
5797 })
5798 .expect("the worker must answer the AddCluster");
5799 assert_eq!(
5800 response.status,
5801 ResponseStatus::Failure as i32,
5802 "an invalid shard_percent must be refused: {response:?}"
5803 );
5804 assert_eq!(response.message, "shard_percent must lie in 1..=100");
5805
5806 for index in 0..4u16 {
5807 server.notify_proxys(WorkerRequest {
5808 id: format!("test-add-backend-{index}"),
5809 content: RequestType::AddBackend(AddBackend {
5810 cluster_id: CLUSTER.to_owned(),
5811 backend_id: format!("{CLUSTER}-{index}"),
5812 address: SocketAddr::from(([127, 0, 0, 1], 23_000 + index)).into(),
5813 load_balancing_parameters: Some(LoadBalancingParams::default()),
5814 sticky_id: None,
5815 backup: None,
5816 })
5817 .into(),
5818 });
5819 }
5820 let key = affinity_key_from_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
5821 let mut backends = server.backends.borrow_mut();
5822 let list = backends
5823 .backends
5824 .get_mut(CLUSTER)
5825 .expect("the backends must have created the cluster's list");
5826 assert_eq!(
5827 list.select_with_key(Some(key), Instant::now()).1,
5828 ShardOutcome::Unsharded,
5829 "a refused AddCluster must not arm a shard"
5830 );
5831 }
5832}
5833
5834#[cfg(test)]
5837mod remove_health_check_tests {
5838 use sozu_command::proto::command::{HealthCheckConfig, LoadBalancingParams};
5839
5840 use super::listener_lifecycle_tests::bare_server;
5841 use super::*;
5842
5843 const CLUSTER: &str = "health-checked";
5844
5845 fn health_check() -> HealthCheckConfig {
5846 HealthCheckConfig {
5847 uri: "/livez".to_owned(),
5848 interval: 1,
5849 timeout: 1,
5850 healthy_threshold: 1,
5851 unhealthy_threshold: 1,
5852 ..Default::default()
5853 }
5854 }
5855
5856 fn add_cluster(server: &mut Server, health_check: Option<HealthCheckConfig>) {
5857 server.notify_proxys(WorkerRequest {
5858 id: "test-add-cluster".to_owned(),
5859 content: RequestType::AddCluster(Cluster {
5860 cluster_id: CLUSTER.to_owned(),
5861 health_check,
5862 ..Default::default()
5863 })
5864 .into(),
5865 });
5866 }
5867
5868 fn server_with_down_backend() -> Server {
5871 let mut server = bare_server();
5872 add_cluster(&mut server, Some(health_check()));
5873 server.notify_proxys(WorkerRequest {
5874 id: "test-add-backend".to_owned(),
5875 content: RequestType::AddBackend(AddBackend {
5876 cluster_id: CLUSTER.to_owned(),
5877 backend_id: format!("{CLUSTER}-0"),
5878 address: SocketAddr::from(([127, 0, 0, 1], 23_100)).into(),
5879 load_balancing_parameters: Some(LoadBalancingParams::default()),
5880 sticky_id: None,
5881 backup: None,
5882 })
5883 .into(),
5884 });
5885 server.backends.borrow().backends[CLUSTER].backends[0]
5886 .borrow_mut()
5887 .health
5888 .record_failure(1);
5889 assert!(!backend_is_healthy(&server), "test setup: backend DOWN");
5890 server
5891 }
5892
5893 fn backend_is_healthy(server: &Server) -> bool {
5894 server.backends.borrow().backends[CLUSTER].backends[0]
5895 .borrow()
5896 .health
5897 .is_healthy()
5898 }
5899
5900 #[test]
5904 fn remove_health_check_resets_a_backend_marked_down() {
5905 let mut server = server_with_down_backend();
5906 server.notify_proxys(WorkerRequest {
5907 id: "test-remove-health-check".to_owned(),
5908 content: RequestType::RemoveHealthCheck(CLUSTER.to_owned()).into(),
5909 });
5910 assert!(
5911 !server
5912 .backends
5913 .borrow()
5914 .health_check_configs
5915 .contains_key(CLUSTER),
5916 "RemoveHealthCheck must forget the cluster's configuration"
5917 );
5918 assert!(
5919 backend_is_healthy(&server),
5920 "RemoveHealthCheck must reset a backend a probe marked DOWN"
5921 );
5922 }
5923
5924 #[test]
5930 fn add_cluster_without_health_check_resets_a_backend_marked_down() {
5931 let mut server = server_with_down_backend();
5932 add_cluster(&mut server, None);
5933 assert!(
5934 !server
5935 .backends
5936 .borrow()
5937 .health_check_configs
5938 .contains_key(CLUSTER),
5939 "an AddCluster without a health check must forget the configuration"
5940 );
5941 assert!(
5942 backend_is_healthy(&server),
5943 "an AddCluster without a health check must reset a backend marked DOWN"
5944 );
5945 }
5946}
5947
5948#[cfg(test)]
5955mod remove_cluster_cascade_tests {
5956 use sozu_command::{
5957 config::ListenerBuilder,
5958 proto::command::{
5959 LoadBalancingParams, PathRule, RequestHttpFrontend, RequestTcpFrontend,
5960 RequestUdpFrontend, RulePosition, SocketAddress,
5961 },
5962 };
5963
5964 use super::listener_lifecycle_tests::bare_server;
5965 use super::*;
5966 use crate::{L7ListenerHandler, protocol::http::parser::Method, testing::provide_port};
5967
5968 const REMOVED: &str = "cascade_removed";
5969 const KEPT: &str = "cascade_kept";
5970
5971 fn send(server: &mut Server, id: &str, request: RequestType) -> Vec<WorkerResponse> {
5975 server.notify_proxys(WorkerRequest {
5976 id: id.to_owned(),
5977 content: request.into(),
5978 });
5979 QUEUE.with(|queue| {
5980 queue
5981 .borrow_mut()
5982 .drain(..)
5983 .filter(|response| response.id != "EVENT")
5984 .collect()
5985 })
5986 }
5987
5988 fn http_front(cluster_id: Option<&str>, hostname: &str, path: &str) -> RequestHttpFrontend {
5989 RequestHttpFrontend {
5990 cluster_id: cluster_id.map(str::to_owned),
5991 hostname: hostname.to_owned(),
5992 path: PathRule::prefix(path.to_owned()),
5993 address: SocketAddress::new_v4(127, 0, 0, 1, 0),
5994 ..Default::default()
5995 }
5996 }
5997
5998 #[test]
6003 fn a_proxy_rejection_rolls_back_reversible_worker_state() {
6004 let cluster_id = "worker-rollback";
6005 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6006 let http = http_front(Some(cluster_id), "rollback.test", "/");
6007
6008 let requests = [
6009 RequestType::AddHttpFrontend(RequestHttpFrontend {
6010 address,
6011 ..http.clone()
6012 }),
6013 RequestType::AddHttpsFrontend(RequestHttpFrontend { address, ..http }),
6014 RequestType::AddTcpFrontend(RequestTcpFrontend {
6015 cluster_id: cluster_id.to_owned(),
6016 address,
6017 ..Default::default()
6018 }),
6019 RequestType::AddUdpFrontend(RequestUdpFrontend {
6020 cluster_id: cluster_id.to_owned(),
6021 address,
6022 ..Default::default()
6023 }),
6024 ];
6025
6026 for (index, request) in requests.into_iter().enumerate() {
6027 let mut server = bare_server();
6028 for response in send(
6029 &mut server,
6030 &format!("cluster-{index}"),
6031 RequestType::AddCluster(Cluster {
6032 cluster_id: cluster_id.to_owned(),
6033 ..Default::default()
6034 }),
6035 ) {
6036 assert_eq!(response.status, ResponseStatus::Ok as i32);
6037 }
6038
6039 let responses = send(&mut server, &format!("frontend-{index}"), request);
6040 assert_eq!(
6041 responses
6042 .iter()
6043 .map(|response| response.status)
6044 .collect::<Vec<_>>(),
6045 vec![ResponseStatus::Failure as i32],
6046 "a frontend with no listener must be refused: {responses:?}"
6047 );
6048
6049 let cluster = server
6050 .config_state
6051 .cluster_state(cluster_id)
6052 .expect("the setup cluster must stay in worker state");
6053 assert!(
6054 cluster.http_frontends.is_empty()
6055 && cluster.https_frontends.is_empty()
6056 && cluster.tcp_frontends.is_empty()
6057 && cluster.udp_frontends.is_empty(),
6058 "a refused frontend must not remain in worker ConfigState: {cluster:?}"
6059 );
6060 }
6061 }
6062
6063 #[test]
6068 fn a_refused_frontend_removal_restores_the_exact_stored_frontend() {
6069 let cluster_id = "worker-remove-rollback";
6070 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6071 let tags = std::collections::BTreeMap::from([("owner".to_owned(), "preserved".to_owned())]);
6072 let mut http = http_front(Some(cluster_id), "remove.test", "/api");
6073 http.address = address;
6074 http.method = Some("POST".to_owned());
6075 http.position = RulePosition::Post.into();
6076 http.tags = tags.clone();
6077 http.rewrite_host = Some("internal.test".to_owned());
6078
6079 let mut http_selector = http.clone();
6080 http_selector.position = RulePosition::Tree.into();
6081 http_selector.tags.clear();
6082 http_selector.rewrite_host = None;
6083
6084 let mut https = http.clone();
6085 https.hostname = "secure-remove.test".to_owned();
6086 let mut https_selector = https.clone();
6087 https_selector.position = RulePosition::Tree.into();
6088 https_selector.tags.clear();
6089 https_selector.rewrite_host = None;
6090
6091 let tcp = RequestTcpFrontend {
6092 cluster_id: cluster_id.to_owned(),
6093 address,
6094 tags: tags.clone(),
6095 sni: Some("REMOVE.TEST".to_owned()),
6096 alpn: vec!["http/1.1".to_owned(), "h2".to_owned()],
6097 };
6098 let tcp_selector = RequestTcpFrontend {
6099 tags: Default::default(),
6100 sni: Some("remove.test".to_owned()),
6101 alpn: vec!["h2".to_owned(), "http/1.1".to_owned()],
6102 ..tcp.clone()
6103 };
6104
6105 let udp = RequestUdpFrontend {
6106 cluster_id: cluster_id.to_owned(),
6107 address,
6108 tags,
6109 };
6110
6111 let cases = [
6112 (
6113 "http",
6114 RequestType::AddHttpFrontend(http),
6115 RequestType::RemoveHttpFrontend(http_selector),
6116 ),
6117 (
6118 "https",
6119 RequestType::AddHttpsFrontend(https),
6120 RequestType::RemoveHttpsFrontend(https_selector),
6121 ),
6122 (
6123 "tcp",
6124 RequestType::AddTcpFrontend(tcp),
6125 RequestType::RemoveTcpFrontend(tcp_selector),
6126 ),
6127 (
6128 "udp",
6129 RequestType::AddUdpFrontend(udp.clone()),
6130 RequestType::RemoveUdpFrontend(udp),
6131 ),
6132 ];
6133
6134 for (protocol, add, remove) in cases {
6135 let mut server = bare_server();
6136 server
6137 .config_state
6138 .dispatch(
6139 &RequestType::AddCluster(Cluster {
6140 cluster_id: cluster_id.to_owned(),
6141 ..Default::default()
6142 })
6143 .into(),
6144 )
6145 .expect("the setup cluster must enter worker state");
6146 server
6147 .config_state
6148 .dispatch(&add.into())
6149 .unwrap_or_else(|error| {
6150 panic!("the {protocol} setup frontend must be stored: {error}")
6151 });
6152 let before = server
6153 .config_state
6154 .cluster_state(cluster_id)
6155 .expect("the setup cluster must expose its frontend");
6156
6157 let responses = send(&mut server, &format!("remove-{protocol}"), remove);
6158 assert_eq!(
6159 responses
6160 .iter()
6161 .map(|response| response.status)
6162 .collect::<Vec<_>>(),
6163 vec![ResponseStatus::Failure as i32],
6164 "a frontend removal with no runtime listener must be refused: {responses:?}"
6165 );
6166 assert_eq!(
6167 server.config_state.cluster_state(cluster_id),
6168 Some(before),
6169 "a refused {protocol} removal must restore the complete stored frontend"
6170 );
6171 }
6172 }
6173
6174 #[test]
6179 fn a_refused_duplicate_frontend_add_preserves_the_preexisting_frontend() {
6180 let cluster_id = "worker-add-rollback";
6181 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6182 let tags =
6183 std::collections::BTreeMap::from([("owner".to_owned(), "preexisting".to_owned())]);
6184 let mut http = http_front(Some(cluster_id), "duplicate.test", "/api");
6185 http.address = address;
6186 http.method = Some("PATCH".to_owned());
6187 http.position = RulePosition::Pre.into();
6188 http.tags = tags.clone();
6189
6190 let mut https = http.clone();
6191 https.hostname = "secure-duplicate.test".to_owned();
6192 let tcp = RequestTcpFrontend {
6193 cluster_id: cluster_id.to_owned(),
6194 address,
6195 tags: tags.clone(),
6196 sni: Some("duplicate.test".to_owned()),
6197 alpn: vec!["h2".to_owned()],
6198 };
6199 let udp = RequestUdpFrontend {
6200 cluster_id: cluster_id.to_owned(),
6201 address,
6202 tags,
6203 };
6204
6205 let cases = [
6206 ("http", RequestType::AddHttpFrontend(http)),
6207 ("https", RequestType::AddHttpsFrontend(https)),
6208 ("tcp", RequestType::AddTcpFrontend(tcp)),
6209 ("udp", RequestType::AddUdpFrontend(udp)),
6210 ];
6211
6212 for (protocol, add) in cases {
6213 let mut server = bare_server();
6214 server
6215 .config_state
6216 .dispatch(
6217 &RequestType::AddCluster(Cluster {
6218 cluster_id: cluster_id.to_owned(),
6219 ..Default::default()
6220 })
6221 .into(),
6222 )
6223 .expect("the setup cluster must enter worker state");
6224 server
6225 .config_state
6226 .dispatch(&add.clone().into())
6227 .unwrap_or_else(|error| {
6228 panic!("the {protocol} setup frontend must be stored: {error}")
6229 });
6230 let before = server
6231 .config_state
6232 .cluster_state(cluster_id)
6233 .expect("the setup cluster must expose its frontend");
6234
6235 let responses = send(&mut server, &format!("duplicate-{protocol}"), add);
6236 assert_eq!(
6237 responses
6238 .iter()
6239 .map(|response| response.status)
6240 .collect::<Vec<_>>(),
6241 vec![ResponseStatus::Failure as i32],
6242 "a duplicate frontend with no runtime listener must be refused: {responses:?}"
6243 );
6244 assert_eq!(
6245 server.config_state.cluster_state(cluster_id),
6246 Some(before),
6247 "a refused duplicate {protocol} add must preserve the preexisting frontend"
6248 );
6249 }
6250 }
6251
6252 #[test]
6256 fn a_refused_duplicate_listener_add_preserves_the_preexisting_listener() {
6257 let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6258 let listener = ListenerBuilder::new_http(address)
6259 .to_http(None)
6260 .expect("the test HTTP listener configuration must build");
6261 let mut server = bare_server();
6262 server
6263 .config_state
6264 .dispatch(&RequestType::AddHttpListener(listener.clone()).into())
6265 .expect("the setup listener must enter worker state");
6266 let before = server.config_state.http_listeners.clone();
6267
6268 let threshold = server.sessions.borrow().accept_slab_threshold();
6271 while server.sessions.borrow().slab.len() < threshold {
6272 server
6273 .sessions
6274 .borrow_mut()
6275 .slab
6276 .insert(Rc::new(RefCell::new(ListenSession {
6277 protocol: Protocol::HTTPListen,
6278 })));
6279 }
6280 assert!(server.sessions.borrow().at_capacity());
6281
6282 let responses = send(
6283 &mut server,
6284 "duplicate-listener",
6285 RequestType::AddHttpListener(listener),
6286 );
6287 assert_eq!(
6288 responses
6289 .iter()
6290 .map(|response| response.status)
6291 .collect::<Vec<_>>(),
6292 vec![ResponseStatus::Failure as i32],
6293 "the full session slab must refuse the duplicate listener: {responses:?}"
6294 );
6295 assert_eq!(
6296 server.config_state.http_listeners, before,
6297 "a refused duplicate listener add must preserve the preexisting listener"
6298 );
6299 }
6300
6301 fn backend(cluster_id: &str, port: u16) -> AddBackend {
6302 AddBackend {
6303 cluster_id: cluster_id.to_owned(),
6304 backend_id: format!("{cluster_id}-0"),
6305 address: SocketAddress::new_v4(127, 0, 0, 1, port),
6306 load_balancing_parameters: Some(LoadBalancingParams::default()),
6307 ..Default::default()
6308 }
6309 }
6310
6311 #[test]
6322 fn remove_then_readd_same_cluster_id_preserves_and_releases_live_admission_leases() {
6323 let mut server = bare_server();
6324 let cluster_id = ClusterId::from(REMOVED);
6325 let source: IpAddr = "203.0.113.9".parse().expect("test address must parse");
6326 let subnet = server.sessions.borrow().subnet_key(&source);
6327 let old_token = Token(60_001);
6328 let new_token = Token(60_002);
6329
6330 for response in send(
6331 &mut server,
6332 "add-old-incarnation",
6333 RequestType::AddCluster(Cluster {
6334 cluster_id: REMOVED.to_owned(),
6335 max_connections_per_ip: Some(2),
6336 max_connections_per_subnet: Some(2),
6337 ..Default::default()
6338 }),
6339 ) {
6340 assert_eq!(response.status, ResponseStatus::Ok as i32);
6341 }
6342 server.sessions.borrow_mut().track_cluster_connection(
6343 old_token,
6344 cluster_id.clone(),
6345 source,
6346 Some(2),
6347 );
6348
6349 for response in send(
6350 &mut server,
6351 "remove-old-incarnation",
6352 RequestType::RemoveCluster(REMOVED.to_owned()),
6353 ) {
6354 assert_eq!(response.status, ResponseStatus::Ok as i32);
6355 }
6356 for response in send(
6357 &mut server,
6358 "add-new-incarnation",
6359 RequestType::AddCluster(Cluster {
6360 cluster_id: REMOVED.to_owned(),
6361 max_connections_per_ip: Some(2),
6362 max_connections_per_subnet: Some(2),
6363 ..Default::default()
6364 }),
6365 ) {
6366 assert_eq!(response.status, ResponseStatus::Ok as i32);
6367 }
6368
6369 {
6370 let mut sessions = server.sessions.borrow_mut();
6371 assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 1);
6372 assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 1);
6373 sessions.track_cluster_connection(new_token, cluster_id, source, Some(2));
6374 assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 2);
6375 assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 2);
6376
6377 sessions.untrack_all_cluster_ip(old_token);
6378 assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 1);
6379 assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 1);
6380 assert!(sessions.cluster_ip_tracks.contains_key(&new_token));
6381 assert!(sessions.cluster_subnet_tracks.contains_key(&new_token));
6382
6383 sessions.untrack_all_cluster_ip(new_token);
6384 assert!(!sessions.connections_per_cluster_ip.contains_key(REMOVED));
6385 assert!(
6386 !sessions
6387 .connections_per_cluster_subnet
6388 .contains_key(REMOVED)
6389 );
6390 }
6391 }
6392
6393 fn http_route(
6397 server: &Server,
6398 address: SocketAddress,
6399 hostname: &str,
6400 path: &str,
6401 ) -> Option<Option<String>> {
6402 let http = server.http.borrow();
6403 let token = http
6404 .listener_token(&SocketAddr::from(address).into())
6405 .expect("the test HTTP listener must own a token");
6406 let listener = http
6407 .get_listener(&token)
6408 .expect("the test HTTP listener must exist");
6409 listener
6410 .borrow()
6411 .frontend_from_request(hostname, path, &Method::Get)
6412 .ok()
6413 .map(|route| route.cluster_id.map(|id| id.to_string()))
6414 }
6415
6416 #[test]
6423 fn remove_cluster_removes_its_routes_and_backends_from_the_worker() {
6424 let mut server = bare_server();
6425 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6426 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6427
6428 let mut removed_front = http_front(Some(REMOVED), "removed.test", "/");
6429 removed_front.address = http_address;
6430 removed_front.position = RulePosition::Pre.into();
6433 let mut deny_front = http_front(None, "removed.test", "/deny");
6434 deny_front.address = http_address;
6435 let mut kept_front = http_front(Some(KEPT), "kept.test", "/");
6436 kept_front.address = http_address;
6437 let removed_tcp_front = RequestTcpFrontend {
6438 cluster_id: REMOVED.to_owned(),
6439 address: tcp_address,
6440 ..Default::default()
6441 };
6442
6443 let setup = vec![
6444 RequestType::AddHttpListener(
6445 ListenerBuilder::new_http(http_address)
6446 .to_http(None)
6447 .expect("could not build the test HTTP listener config"),
6448 ),
6449 RequestType::AddTcpListener(
6450 ListenerBuilder::new_tcp(tcp_address)
6451 .to_tcp(None)
6452 .expect("could not build the test TCP listener config"),
6453 ),
6454 RequestType::AddCluster(Cluster {
6455 cluster_id: REMOVED.to_owned(),
6456 ..Default::default()
6457 }),
6458 RequestType::AddCluster(Cluster {
6459 cluster_id: KEPT.to_owned(),
6460 ..Default::default()
6461 }),
6462 RequestType::AddHttpFrontend(removed_front.clone()),
6463 RequestType::AddHttpFrontend(deny_front),
6464 RequestType::AddHttpFrontend(kept_front),
6465 RequestType::AddTcpFrontend(removed_tcp_front.clone()),
6466 RequestType::AddBackend(backend(REMOVED, 1026)),
6467 RequestType::AddBackend(backend(KEPT, 1027)),
6468 ];
6469 for (index, request) in setup.into_iter().enumerate() {
6470 for response in send(&mut server, &format!("setup-{index}"), request) {
6471 assert_eq!(
6472 response.status,
6473 ResponseStatus::Ok as i32,
6474 "setup must succeed: {response:?}"
6475 );
6476 }
6477 }
6478 assert_eq!(
6479 http_route(&server, http_address, "removed.test", "/"),
6480 Some(Some(REMOVED.to_owned())),
6481 "premise: the removed cluster's hostname routes to it"
6482 );
6483
6484 let responses = send(
6485 &mut server,
6486 "remove-cluster",
6487 RequestType::RemoveCluster(REMOVED.to_owned()),
6488 );
6489 assert_eq!(
6490 responses.iter().map(|r| r.id.as_str()).collect::<Vec<_>>(),
6491 vec!["remove-cluster"],
6492 "the worker must answer the RemoveCluster exactly once: {responses:?}"
6493 );
6494 assert_eq!(
6495 responses[0].status,
6496 ResponseStatus::Ok as i32,
6497 "RemoveCluster must succeed: {responses:?}"
6498 );
6499
6500 assert_eq!(
6501 http_route(&server, http_address, "removed.test", "/"),
6502 None,
6503 "the removed cluster's hostname must no longer route"
6504 );
6505 assert_eq!(
6506 http_route(&server, http_address, "removed.test", "/deny"),
6507 Some(None),
6508 "a route that names no cluster must survive the removal of a cluster on its hostname"
6509 );
6510 assert_eq!(
6511 http_route(&server, http_address, "kept.test", "/"),
6512 Some(Some(KEPT.to_owned())),
6513 "another cluster's route must stay"
6514 );
6515 let backends = server.backends.borrow();
6516 assert!(
6517 !backends.backends.contains_key(REMOVED),
6518 "the removed cluster's backends must be gone from the BackendMap"
6519 );
6520 assert!(
6521 backends.backends.contains_key(KEPT),
6522 "another cluster's backends must stay"
6523 );
6524 drop(backends);
6525
6526 for (index, request) in [
6527 RequestType::RemoveHttpFrontend(removed_front),
6528 RequestType::RemoveTcpFrontend(removed_tcp_front),
6529 RequestType::RemoveBackend(RemoveBackend {
6530 cluster_id: REMOVED.to_owned(),
6531 backend_id: format!("{REMOVED}-0"),
6532 address: SocketAddress::new_v4(127, 0, 0, 1, 1026),
6533 }),
6534 ]
6535 .into_iter()
6536 .enumerate()
6537 {
6538 let responses = send(&mut server, &format!("follow-up-{index}"), request);
6539 assert!(
6540 !responses.is_empty()
6541 && responses
6542 .iter()
6543 .all(|r| r.status == ResponseStatus::Ok as i32),
6544 "a removal the cascade already performed must answer ok: {responses:?}"
6545 );
6546 }
6547 }
6548
6549 #[test]
6554 fn a_stale_frontend_removal_never_removes_another_clusters_route() {
6555 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6556 let route = |cluster_id: &str| {
6557 let mut front = http_front(Some(cluster_id), "reused.test", "/");
6558 front.address = http_address;
6559 front
6560 };
6561 let cluster = |cluster_id: &str| {
6562 RequestType::AddCluster(Cluster {
6563 cluster_id: cluster_id.to_owned(),
6564 ..Default::default()
6565 })
6566 };
6567 let listener = || {
6568 RequestType::AddHttpListener(
6569 ListenerBuilder::new_http(http_address)
6570 .to_http(None)
6571 .expect("could not build the test HTTP listener config"),
6572 )
6573 };
6574
6575 for (a_is_gone, setup, expected_status) in [
6576 (
6577 true,
6578 vec![
6579 listener(),
6580 cluster("stale_a"),
6581 RequestType::AddHttpFrontend(route("stale_a")),
6582 RequestType::RemoveCluster("stale_a".to_owned()),
6583 cluster("stale_b"),
6584 RequestType::AddHttpFrontend(route("stale_b")),
6585 ],
6586 ResponseStatus::Ok,
6587 ),
6588 (
6589 false,
6590 vec![
6591 listener(),
6592 cluster("stale_a"),
6593 cluster("stale_b"),
6594 RequestType::AddHttpFrontend(route("stale_b")),
6595 ],
6596 ResponseStatus::Failure,
6597 ),
6598 ] {
6599 let mut server = bare_server();
6600 for (index, request) in setup.into_iter().enumerate() {
6601 for response in send(&mut server, &format!("setup-{index}"), request) {
6602 assert_eq!(
6603 response.status,
6604 ResponseStatus::Ok as i32,
6605 "setup must succeed: {response:?}"
6606 );
6607 }
6608 }
6609 assert_eq!(
6610 http_route(&server, http_address, "reused.test", "/"),
6611 Some(Some("stale_b".to_owned())),
6612 "premise: cluster B owns the route"
6613 );
6614
6615 let responses = send(
6616 &mut server,
6617 "stale-removal",
6618 RequestType::RemoveHttpFrontend(route("stale_a")),
6619 );
6620 assert_eq!(
6621 responses.iter().map(|r| r.status).collect::<Vec<_>>(),
6622 vec![expected_status as i32],
6623 "A gone: {a_is_gone}, answers: {responses:?}"
6624 );
6625 assert_eq!(
6626 http_route(&server, http_address, "reused.test", "/"),
6627 Some(Some("stale_b".to_owned())),
6628 "cluster B's route must survive a stale removal naming cluster A (A gone: {a_is_gone})"
6629 );
6630 }
6631 }
6632
6633 #[test]
6639 fn a_stale_tcp_or_udp_frontend_removal_never_unmaps_another_clusters_listener() {
6640 let cluster = |cluster_id: &str| {
6641 RequestType::AddCluster(Cluster {
6642 cluster_id: cluster_id.to_owned(),
6643 ..Default::default()
6644 })
6645 };
6646 for udp in [false, true] {
6647 let x = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6648 let y = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6649 let listener = |address: SocketAddress| {
6650 if udp {
6651 RequestType::AddUdpListener(
6652 ListenerBuilder::new_udp(address)
6653 .to_udp(None)
6654 .expect("could not build the test UDP listener config"),
6655 )
6656 } else {
6657 RequestType::AddTcpListener(
6658 ListenerBuilder::new_tcp(address)
6659 .to_tcp(None)
6660 .expect("could not build the test TCP listener config"),
6661 )
6662 }
6663 };
6664 let front = |cluster_id: &str, address: SocketAddress| {
6665 if udp {
6666 (
6667 RequestType::AddUdpFrontend(RequestUdpFrontend {
6668 cluster_id: cluster_id.to_owned(),
6669 address,
6670 ..Default::default()
6671 }),
6672 RequestType::RemoveUdpFrontend(RequestUdpFrontend {
6673 cluster_id: cluster_id.to_owned(),
6674 address,
6675 ..Default::default()
6676 }),
6677 )
6678 } else {
6679 (
6680 RequestType::AddTcpFrontend(RequestTcpFrontend {
6681 cluster_id: cluster_id.to_owned(),
6682 address,
6683 ..Default::default()
6684 }),
6685 RequestType::RemoveTcpFrontend(RequestTcpFrontend {
6686 cluster_id: cluster_id.to_owned(),
6687 address,
6688 ..Default::default()
6689 }),
6690 )
6691 }
6692 };
6693 let owner = |server: &Server| {
6694 let key = SocketAddr::from(x).into();
6695 if udp {
6696 server.udp.borrow().listener_cluster(&key)
6697 } else {
6698 server.tcp.borrow().listener_cluster(&key)
6699 }
6700 .map(|id| id.to_string())
6701 };
6702
6703 let mut server = bare_server();
6704 for (index, request) in [
6705 listener(x),
6706 listener(y),
6707 cluster("stale_a"),
6708 front("stale_a", x).0,
6709 RequestType::RemoveCluster("stale_a".to_owned()),
6710 cluster("stale_b"),
6711 front("stale_b", x).0,
6712 cluster("stale_a"),
6713 front("stale_a", y).0,
6714 ]
6715 .into_iter()
6716 .enumerate()
6717 {
6718 for response in send(&mut server, &format!("setup-{index}"), request) {
6719 assert_eq!(
6720 response.status,
6721 ResponseStatus::Ok as i32,
6722 "setup must succeed (udp: {udp}): {response:?}"
6723 );
6724 }
6725 }
6726 assert_eq!(
6727 owner(&server).as_deref(),
6728 Some("stale_b"),
6729 "premise: cluster B owns the listener on X (udp: {udp})"
6730 );
6731
6732 let responses = send(&mut server, "stale-removal", front("stale_a", x).1);
6733 assert_eq!(
6734 responses.iter().map(|r| r.status).collect::<Vec<_>>(),
6735 vec![ResponseStatus::Failure as i32],
6736 "the stale removal must be refused (udp: {udp}): {responses:?}"
6737 );
6738 assert_eq!(
6739 owner(&server).as_deref(),
6740 Some("stale_b"),
6741 "cluster B must keep the listener on X after a stale removal naming A (udp: {udp})"
6742 );
6743 }
6744 }
6745}
6746
6747#[cfg(test)]
6748mod scm_listener_handoff_tests {
6749 use std::{
6750 net::{TcpStream as StdTcpStream, UdpSocket as StdUdpSocket},
6751 os::fd::{IntoRawFd, RawFd},
6752 };
6753
6754 use sozu_command::{config::ListenerBuilder, proto::command::SocketAddress};
6755
6756 use super::*;
6757 use crate::{
6758 ProxyError,
6759 socket::{server_bind, udp_bind},
6760 testing::{ServerParts, prebuild_server, provide_port},
6761 };
6762
6763 fn state_with_four_active_listeners(
6766 http_address: SocketAddress,
6767 https_address: SocketAddress,
6768 tcp_address: SocketAddress,
6769 udp_address: SocketAddress,
6770 ) -> ConfigState {
6771 let mut state = ConfigState::new();
6772 let adds: Vec<(Request, SocketAddress, ListenerType)> = vec![
6773 (
6774 RequestType::AddHttpListener(
6775 ListenerBuilder::new_http(http_address)
6776 .to_http(None)
6777 .expect("could not build the test HTTP listener config"),
6778 )
6779 .into(),
6780 http_address,
6781 ListenerType::Http,
6782 ),
6783 (
6784 RequestType::AddHttpsListener(
6785 ListenerBuilder::new_https(https_address)
6786 .to_tls(None)
6787 .expect("could not build the test HTTPS listener config"),
6788 )
6789 .into(),
6790 https_address,
6791 ListenerType::Https,
6792 ),
6793 (
6794 RequestType::AddTcpListener(
6795 ListenerBuilder::new_tcp(tcp_address)
6796 .to_tcp(None)
6797 .expect("could not build the test TCP listener config"),
6798 )
6799 .into(),
6800 tcp_address,
6801 ListenerType::Tcp,
6802 ),
6803 (
6804 RequestType::AddUdpListener(
6805 ListenerBuilder::new_udp(udp_address)
6806 .to_udp(None)
6807 .expect("could not build the test UDP listener config"),
6808 )
6809 .into(),
6810 udp_address,
6811 ListenerType::Udp,
6812 ),
6813 ];
6814 for (add, address, proxy) in adds {
6815 state.dispatch(&add).expect("could not add the listener");
6816 state
6817 .dispatch(
6818 &RequestType::ActivateListener(ActivateListener {
6819 interface: None,
6820 address,
6821 proxy: proxy.into(),
6822 from_scm: false,
6823 })
6824 .into(),
6825 )
6826 .expect("could not mark the listener active");
6827 }
6828 state
6829 }
6830
6831 fn inherited_table<'a>(server: &'a Server, label: &str) -> &'a Vec<(ListenerKey, RawFd)> {
6833 let scm_listeners = server
6834 .scm_listeners
6835 .as_ref()
6836 .expect("scm_listeners must be present");
6837 match label {
6838 "HTTP" => &scm_listeners.http,
6839 "HTTPS" => &scm_listeners.tls,
6840 "TCP" => &scm_listeners.tcp,
6841 "UDP" => &scm_listeners.udp,
6842 other => panic!("unknown protocol label {other}"),
6843 }
6844 }
6845
6846 fn descriptor_is_socket_on_port(fd: RawFd, port: u16) -> bool {
6856 let mut storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
6857 let mut length = std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
6858 let result = unsafe {
6862 libc::getsockname(
6863 fd,
6864 (&raw mut storage).cast::<libc::sockaddr>(),
6865 &raw mut length,
6866 )
6867 };
6868 if result != 0 || storage.ss_family != libc::AF_INET as libc::sa_family_t {
6869 return false;
6870 }
6871 let inet = unsafe { &*(&raw const storage).cast::<libc::sockaddr_in>() };
6874 u16::from_be(inet.sin_port) == port
6875 }
6876
6877 const QUEUED_DATAGRAM: &[u8] = b"queued-before-the-handoff";
6879
6880 #[test]
6910 fn inherited_listener_sockets_are_adopted_by_the_initial_activation() {
6911 let ServerParts {
6912 event_loop,
6913 sessions,
6914 pool,
6915 backends,
6916 client_scm_socket,
6917 server_scm_socket,
6918 server_config,
6919 ..
6920 } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
6921 let (_command_channel, proxy_channel) =
6922 Channel::generate(1000, 10000).expect("could not generate a test channel");
6923
6924 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6925 let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6926 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6927 let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6928
6929 let http_listener = server_bind(http_address.into(), None)
6932 .expect("could not bind the inherited HTTP socket");
6933 let https_listener = server_bind(https_address.into(), None)
6934 .expect("could not bind the inherited HTTPS socket");
6935 let tcp_listener =
6936 server_bind(tcp_address.into(), None).expect("could not bind the inherited TCP socket");
6937 let udp_socket =
6938 udp_bind(udp_address.into(), None).expect("could not bind the inherited UDP socket");
6939
6940 let http_client = StdTcpStream::connect::<SocketAddr>(http_address.into())
6945 .expect("could not queue a connection on the inherited HTTP socket");
6946 let https_client = StdTcpStream::connect::<SocketAddr>(https_address.into())
6947 .expect("could not queue a connection on the inherited HTTPS socket");
6948 let tcp_client = StdTcpStream::connect::<SocketAddr>(tcp_address.into())
6949 .expect("could not queue a connection on the inherited TCP socket");
6950 let datagram_sender =
6951 StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test datagram sender");
6952 datagram_sender
6953 .send_to(QUEUED_DATAGRAM, SocketAddr::from(udp_address))
6954 .expect("could not queue a datagram on the inherited UDP socket");
6955
6956 let listeners = Listeners {
6959 http: vec![(http_address.into(), http_listener.into_raw_fd())],
6960 tls: vec![(https_address.into(), https_listener.into_raw_fd())],
6961 tcp: vec![(tcp_address.into(), tcp_listener.into_raw_fd())],
6962 udp: vec![(udp_address.into(), udp_socket.into_raw_fd())],
6963 };
6964 client_scm_socket
6965 .send_listeners(&listeners)
6966 .expect("could not send the inherited listeners");
6967 listeners.close();
6968
6969 let initial_state =
6975 state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address)
6976 .produce_initial_state();
6977 assert_eq!(
6978 initial_state.requests.len(),
6979 8,
6980 "the generator must emit one Add and one Activate per active listener"
6981 );
6982
6983 let server = Server::new(
6984 event_loop,
6985 proxy_channel,
6986 server_scm_socket,
6987 sessions,
6988 pool,
6989 backends,
6990 None,
6991 None,
6992 None,
6993 server_config,
6994 Some(initial_state),
6995 false,
6996 )
6997 .expect("could not build the test server");
6998
6999 let accepted: Vec<Protocol> = server
7000 .accept_queue
7001 .iter()
7002 .map(|(_, _, protocol, _, _)| *protocol)
7003 .collect();
7004 assert!(
7005 accepted.contains(&Protocol::HTTPListen),
7006 "the connection queued on the inherited HTTP socket must survive the hand-off, got {accepted:?}"
7007 );
7008 assert!(
7009 accepted.contains(&Protocol::HTTPSListen),
7010 "the connection queued on the inherited HTTPS socket must survive the hand-off, got {accepted:?}"
7011 );
7012 assert!(
7013 accepted.contains(&Protocol::TCPListen),
7014 "the connection queued on the inherited TCP socket must survive the hand-off, got {accepted:?}"
7015 );
7016
7017 for (protocol, client) in [
7025 (Protocol::HTTPListen, &http_client),
7026 (Protocol::HTTPSListen, &https_client),
7027 (Protocol::TCPListen, &tcp_client),
7028 ] {
7029 let (sock, _, _, _, peer) = server
7030 .accept_queue
7031 .iter()
7032 .find(|(_, _, queued, _, _)| *queued == protocol)
7033 .expect("the queued connection was asserted above");
7034 assert_eq!(
7035 socket2::SockRef::from(sock).tcp_nodelay().ok(),
7036 Some(true),
7037 "a {protocol:?} connection queued before the hand-off must carry TCP_NODELAY"
7038 );
7039 assert_eq!(
7040 *peer,
7041 client.local_addr().expect("the test client has an address"),
7042 "the {protocol:?} peer address must be the client's"
7043 );
7044 }
7045
7046 let (_, adopted_udp_socket) = server
7047 .udp
7048 .borrow_mut()
7049 .give_back_listeners()
7050 .pop()
7051 .expect("the UDP listener must hold a socket after activation");
7052 let mut buffer = [0u8; 64];
7053 let received = adopted_udp_socket.recv_from(&mut buffer);
7054 match received {
7055 Ok((length, _)) => assert_eq!(
7056 &buffer[..length],
7057 QUEUED_DATAGRAM,
7058 "the datagram queued on the inherited UDP socket must survive the hand-off"
7059 ),
7060 Err(error) => panic!(
7061 "the datagram queued on the inherited UDP socket must survive the hand-off, got {error:?}"
7062 ),
7063 }
7064 }
7065
7066 #[test]
7090 fn a_repeated_activation_closes_the_descriptor_it_cannot_adopt_now() {
7091 let ServerParts {
7092 event_loop,
7093 sessions,
7094 pool,
7095 backends,
7096 server_scm_socket,
7097 server_config,
7098 ..
7099 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7100 let (_command_channel, proxy_channel) =
7101 Channel::generate(1000, 10000).expect("could not generate a test channel");
7102
7103 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7104 let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7105 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7106 let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7107
7108 let initial_state =
7111 state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address)
7112 .produce_initial_state();
7113 let mut server = Server::new(
7114 event_loop,
7115 proxy_channel,
7116 server_scm_socket,
7117 sessions,
7118 pool,
7119 backends,
7120 None,
7121 None,
7122 None,
7123 server_config,
7124 Some(initial_state),
7125 false,
7126 )
7127 .expect("could not build the test server");
7128
7129 let http_fd = server_bind(http_address.into(), None)
7131 .expect("could not bind the spare HTTP socket")
7132 .into_raw_fd();
7133 let https_fd = server_bind(https_address.into(), None)
7134 .expect("could not bind the spare HTTPS socket")
7135 .into_raw_fd();
7136 let tcp_fd = server_bind(tcp_address.into(), None)
7137 .expect("could not bind the spare TCP socket")
7138 .into_raw_fd();
7139 let udp_fd = udp_bind(udp_address.into(), None)
7140 .expect("could not bind the spare UDP socket")
7141 .into_raw_fd();
7142 {
7143 let scm_listeners = server
7144 .scm_listeners
7145 .as_mut()
7146 .expect("Server::new must have populated scm_listeners");
7147 scm_listeners.http.push((http_address.into(), http_fd));
7148 scm_listeners.tls.push((https_address.into(), https_fd));
7149 scm_listeners.tcp.push((tcp_address.into(), tcp_fd));
7150 scm_listeners.udp.push((udp_address.into(), udp_fd));
7151 }
7152
7153 for (address, proxy) in [
7154 (http_address, ListenerType::Http),
7155 (https_address, ListenerType::Https),
7156 (tcp_address, ListenerType::Tcp),
7157 (udp_address, ListenerType::Udp),
7158 ] {
7159 let response = server.notify_activate_listener(
7160 "test-repeat",
7161 &ActivateListener {
7162 interface: None,
7163 address,
7164 proxy: proxy.into(),
7165 from_scm: false,
7166 },
7167 );
7168 assert_eq!(
7169 response.status,
7170 ResponseStatus::Ok as i32,
7171 "a repeated {proxy:?} activation must still answer ok: {response:?}"
7172 );
7173 }
7174
7175 let scm_listeners = server
7176 .scm_listeners
7177 .as_ref()
7178 .expect("scm_listeners must still be present");
7179 for (label, table, fd, port) in [
7180 (
7181 "HTTP",
7182 &scm_listeners.http,
7183 http_fd,
7184 http_address.port as u16,
7185 ),
7186 (
7187 "HTTPS",
7188 &scm_listeners.tls,
7189 https_fd,
7190 https_address.port as u16,
7191 ),
7192 ("TCP", &scm_listeners.tcp, tcp_fd, tcp_address.port as u16),
7193 ("UDP", &scm_listeners.udp, udp_fd, udp_address.port as u16),
7194 ] {
7195 assert!(
7196 !table.iter().any(|(_, entry)| *entry == fd),
7197 "the inherited {label} descriptor this activation cannot adopt must leave the SCM table"
7198 );
7199 assert!(
7200 !descriptor_is_socket_on_port(fd, port),
7201 "the inherited {label} descriptor this activation cannot adopt must be closed, \
7202 not left bound in the address's SO_REUSEPORT group, where it is expected — \
7203 inferred, not measured here — to go on taking a share of new connections"
7204 );
7205 }
7206 }
7207
7208 #[test]
7225 fn an_inherited_descriptor_with_no_listener_yet_is_adopted_by_a_later_activation() {
7226 let ServerParts {
7227 event_loop,
7228 sessions,
7229 pool,
7230 backends,
7231 server_scm_socket,
7232 server_config,
7233 ..
7234 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7235 let (_command_channel, proxy_channel) =
7236 Channel::generate(1000, 10000).expect("could not generate a test channel");
7237
7238 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7239 let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7240 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7241 let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7242
7243 let mut server = Server::new(
7244 event_loop,
7245 proxy_channel,
7246 server_scm_socket,
7247 sessions,
7248 pool,
7249 backends,
7250 None,
7251 None,
7252 None,
7253 server_config,
7254 None,
7255 false,
7256 )
7257 .expect("could not build the test server");
7258
7259 let http_listener = server_bind(http_address.into(), None)
7263 .expect("could not bind the inherited HTTP socket");
7264 let https_listener = server_bind(https_address.into(), None)
7265 .expect("could not bind the inherited HTTPS socket");
7266 let tcp_listener =
7267 server_bind(tcp_address.into(), None).expect("could not bind the inherited TCP socket");
7268 let udp_socket =
7269 udp_bind(udp_address.into(), None).expect("could not bind the inherited UDP socket");
7270 let _http_client = StdTcpStream::connect::<SocketAddr>(http_address.into())
7271 .expect("could not queue a connection on the inherited HTTP socket");
7272 let _https_client = StdTcpStream::connect::<SocketAddr>(https_address.into())
7273 .expect("could not queue a connection on the inherited HTTPS socket");
7274 let _tcp_client = StdTcpStream::connect::<SocketAddr>(tcp_address.into())
7275 .expect("could not queue a connection on the inherited TCP socket");
7276 let (http_fd, https_fd, tcp_fd, udp_fd) = (
7277 http_listener.into_raw_fd(),
7278 https_listener.into_raw_fd(),
7279 tcp_listener.into_raw_fd(),
7280 udp_socket.into_raw_fd(),
7281 );
7282 {
7283 let scm_listeners = server
7284 .scm_listeners
7285 .as_mut()
7286 .expect("Server::new must have populated scm_listeners");
7287 scm_listeners.http.push((http_address.into(), http_fd));
7288 scm_listeners.tls.push((https_address.into(), https_fd));
7289 scm_listeners.tcp.push((tcp_address.into(), tcp_fd));
7290 scm_listeners.udp.push((udp_address.into(), udp_fd));
7291 }
7292
7293 let add_requests =
7294 state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address);
7295 let cases = [
7296 ("HTTP", http_address, ListenerType::Http, http_fd),
7297 ("HTTPS", https_address, ListenerType::Https, https_fd),
7298 ("TCP", tcp_address, ListenerType::Tcp, tcp_fd),
7299 ("UDP", udp_address, ListenerType::Udp, udp_fd),
7300 ];
7301
7302 for (label, address, proxy, fd) in cases {
7305 let response = server.notify_activate_listener(
7306 "test-early",
7307 &ActivateListener {
7308 interface: None,
7309 address,
7310 proxy: proxy.into(),
7311 from_scm: false,
7312 },
7313 );
7314 assert_ne!(
7315 response.status,
7316 ResponseStatus::Ok as i32,
7317 "activating {label} with no listener must fail: {response:?}"
7318 );
7319 assert!(
7320 inherited_table(&server, label)
7321 .iter()
7322 .any(|(_, e)| *e == fd),
7323 "the {label} descriptor must stay in the SCM table while no listener owns \
7324 its address"
7325 );
7326 assert!(
7327 descriptor_is_socket_on_port(fd, address.port as u16),
7328 "the {label} descriptor must stay open while no listener owns its address"
7329 );
7330 }
7331
7332 for request in add_requests.produce_initial_state().requests {
7335 server.notify_proxys(request);
7336 }
7337 for (label, _address, _proxy, fd) in cases {
7338 assert!(
7339 !inherited_table(&server, label)
7340 .iter()
7341 .any(|(_, e)| *e == fd),
7342 "the retained {label} descriptor must be adopted by the later activation"
7343 );
7344 }
7345 let accepted: Vec<Protocol> = server
7346 .accept_queue
7347 .iter()
7348 .map(|(_, _, protocol, _, _)| *protocol)
7349 .collect();
7350 for expected in [
7351 Protocol::HTTPListen,
7352 Protocol::HTTPSListen,
7353 Protocol::TCPListen,
7354 ] {
7355 assert!(
7356 accepted.contains(&expected),
7357 "the connection queued on the retained {expected:?} descriptor must be served \
7358 by the later activation, got {accepted:?}"
7359 );
7360 }
7361 }
7362
7363 #[test]
7388 fn a_socket_parked_by_a_failed_registration_is_not_mistaken_for_a_live_one() {
7389 let ServerParts {
7390 registry,
7391 sessions,
7392 pool,
7393 backends,
7394 ..
7395 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7396
7397 let token_for = |protocol: Protocol| {
7398 let mut manager = sessions.borrow_mut();
7399 let entry = manager.slab.vacant_entry();
7400 let token = Token(entry.key());
7401 entry.insert(Rc::new(RefCell::new(ListenSession { protocol })));
7402 token
7403 };
7404 let pre_registered = |address: SocketAddress| {
7407 let mut socket = server_bind(address.into(), None)
7408 .expect("could not bind the test listening socket");
7409 registry
7410 .register(&mut socket, Token(usize::MAX - 2), Interest::READABLE)
7411 .expect("could not pre-register the test socket");
7412 socket
7413 };
7414
7415 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7416 let mut http_proxy = http::HttpProxy::new(
7417 registry.try_clone().expect("could not clone the registry"),
7418 sessions.clone(),
7419 pool.clone(),
7420 backends.clone(),
7421 );
7422 http_proxy
7423 .add_listener(
7424 ListenerBuilder::new_http(http_address)
7425 .to_http(None)
7426 .expect("could not build the test HTTP listener config"),
7427 token_for(Protocol::HTTPListen),
7428 )
7429 .expect("could not add the test HTTP listener");
7430 assert!(
7431 http_proxy
7432 .activate_listener(&http_address.into(), Some(pre_registered(http_address)))
7433 .is_err(),
7434 "the pre-registered descriptor must make the HTTP registration fail"
7435 );
7436 assert!(
7437 matches!(
7438 http_proxy.give_back_listener(&http_address.into()),
7439 Err(ProxyError::UnactivatedListener)
7440 ),
7441 "an HTTP listener holding only a parked socket must not answer as activated"
7442 );
7443 assert!(
7444 http_proxy.soft_stop().is_ok(),
7445 "a parked socket must not fail an HTTP soft stop"
7446 );
7447
7448 let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7449 let mut https_proxy = https::HttpsProxy::new(
7450 registry.try_clone().expect("could not clone the registry"),
7451 sessions.clone(),
7452 pool.clone(),
7453 backends.clone(),
7454 );
7455 https_proxy
7456 .add_listener(
7457 ListenerBuilder::new_https(https_address)
7458 .to_tls(None)
7459 .expect("could not build the test HTTPS listener config"),
7460 token_for(Protocol::HTTPSListen),
7461 )
7462 .expect("could not add the test HTTPS listener");
7463 assert!(
7464 https_proxy
7465 .activate_listener(&https_address.into(), Some(pre_registered(https_address)))
7466 .is_err(),
7467 "the pre-registered descriptor must make the HTTPS registration fail"
7468 );
7469 assert!(
7470 matches!(
7471 https_proxy.give_back_listener(&https_address.into()),
7472 Err(ProxyError::UnactivatedListener)
7473 ),
7474 "an HTTPS listener holding only a parked socket must not answer as activated"
7475 );
7476 assert!(
7477 https_proxy.hard_stop().is_ok(),
7478 "a parked socket must not fail an HTTPS hard stop"
7479 );
7480
7481 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7482 let mut tcp_proxy = tcp::TcpProxy::new(
7483 registry.try_clone().expect("could not clone the registry"),
7484 sessions.clone(),
7485 pool.clone(),
7486 backends.clone(),
7487 );
7488 tcp_proxy
7489 .add_listener(
7490 ListenerBuilder::new_tcp(tcp_address)
7491 .to_tcp(None)
7492 .expect("could not build the test TCP listener config"),
7493 token_for(Protocol::TCPListen),
7494 )
7495 .expect("could not add the test TCP listener");
7496 assert!(
7497 tcp_proxy
7498 .activate_listener(&tcp_address.into(), Some(pre_registered(tcp_address)))
7499 .is_err(),
7500 "the pre-registered descriptor must make the TCP registration fail"
7501 );
7502 assert!(
7503 matches!(
7504 tcp_proxy.give_back_listener(&tcp_address.into()),
7505 Err(ProxyError::UnactivatedListener)
7506 ),
7507 "a TCP listener holding only a parked socket must not answer as activated"
7508 );
7509
7510 let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7511 let mut udp_proxy = udp::UdpProxy::new(
7512 registry.try_clone().expect("could not clone the registry"),
7513 sessions.clone(),
7514 pool.clone(),
7515 backends.clone(),
7516 16,
7517 16384,
7518 );
7519 udp_proxy
7520 .add_listener(
7521 ListenerBuilder::new_udp(udp_address)
7522 .to_udp(None)
7523 .expect("could not build the test UDP listener config"),
7524 token_for(Protocol::UDP),
7525 )
7526 .expect("could not add the test UDP listener");
7527 let mut udp_socket =
7528 udp_bind(udp_address.into(), None).expect("could not bind the test UDP socket");
7529 registry
7530 .register(&mut udp_socket, Token(usize::MAX - 2), Interest::READABLE)
7531 .expect("could not pre-register the test UDP socket");
7532 assert!(
7533 udp_proxy
7534 .activate_listener(&udp_address.into(), Some(udp_socket))
7535 .is_err(),
7536 "the pre-registered descriptor must make the UDP registration fail"
7537 );
7538 assert!(
7539 matches!(
7540 udp_proxy.give_back_listener(&udp_address.into()),
7541 Err(ProxyError::UnactivatedListener)
7542 ),
7543 "a UDP listener holding only a parked socket must not answer as activated"
7544 );
7545 }
7546
7547 #[test]
7561 fn a_failed_registration_does_not_close_the_socket_it_was_handed() {
7562 let ServerParts {
7563 registry,
7564 sessions,
7565 pool,
7566 backends,
7567 ..
7568 } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7569
7570 let token_for = |protocol: Protocol| {
7571 let mut manager = sessions.borrow_mut();
7572 let entry = manager.slab.vacant_entry();
7573 let token = Token(entry.key());
7574 entry.insert(Rc::new(RefCell::new(ListenSession { protocol })));
7575 token
7576 };
7577
7578 let handed_out = |address: SocketAddress| {
7581 let mut socket = server_bind(address.into(), None)
7582 .expect("could not bind the test listening socket");
7583 registry
7584 .register(&mut socket, Token(usize::MAX - 1), Interest::READABLE)
7585 .expect("could not pre-register the test socket");
7586 let fd = socket.as_raw_fd();
7587 (socket, fd)
7588 };
7589
7590 let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7591 let token = token_for(Protocol::HTTPListen);
7592 let mut http_proxy = http::HttpProxy::new(
7593 registry.try_clone().expect("could not clone the registry"),
7594 sessions.clone(),
7595 pool.clone(),
7596 backends.clone(),
7597 );
7598 http_proxy
7599 .add_listener(
7600 ListenerBuilder::new_http(http_address)
7601 .to_http(None)
7602 .expect("could not build the test HTTP listener config"),
7603 token,
7604 )
7605 .expect("could not add the test HTTP listener");
7606 let (socket, http_fd) = handed_out(http_address);
7607 let result = http_proxy.activate_listener(&http_address.into(), Some(socket));
7608 assert!(
7609 result.is_err(),
7610 "the pre-registered descriptor must make the HTTP registration fail"
7611 );
7612 assert!(
7613 descriptor_is_socket_on_port(http_fd, http_address.port as u16),
7614 "a failed HTTP registration must not close the socket it was handed"
7615 );
7616
7617 let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7618 let token = token_for(Protocol::TCPListen);
7619 let mut tcp_proxy = tcp::TcpProxy::new(
7620 registry.try_clone().expect("could not clone the registry"),
7621 sessions.clone(),
7622 pool.clone(),
7623 backends.clone(),
7624 );
7625 tcp_proxy
7626 .add_listener(
7627 ListenerBuilder::new_tcp(tcp_address)
7628 .to_tcp(None)
7629 .expect("could not build the test TCP listener config"),
7630 token,
7631 )
7632 .expect("could not add the test TCP listener");
7633 let (socket, tcp_fd) = handed_out(tcp_address);
7634 let result = tcp_proxy.activate_listener(&tcp_address.into(), Some(socket));
7635 assert!(
7636 result.is_err(),
7637 "the pre-registered descriptor must make the TCP registration fail"
7638 );
7639 assert!(
7640 descriptor_is_socket_on_port(tcp_fd, tcp_address.port as u16),
7641 "a failed TCP registration must not close the socket it was handed"
7642 );
7643
7644 let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7645 let token = token_for(Protocol::HTTPSListen);
7646 let mut https_proxy = https::HttpsProxy::new(
7647 registry.try_clone().expect("could not clone the registry"),
7648 sessions.clone(),
7649 pool.clone(),
7650 backends.clone(),
7651 );
7652 https_proxy
7653 .add_listener(
7654 ListenerBuilder::new_https(https_address)
7655 .to_tls(None)
7656 .expect("could not build the test HTTPS listener config"),
7657 token,
7658 )
7659 .expect("could not add the test HTTPS listener");
7660 let (socket, https_fd) = handed_out(https_address);
7661 let result = https_proxy.activate_listener(&https_address.into(), Some(socket));
7662 assert!(
7663 result.is_err(),
7664 "the pre-registered descriptor must make the HTTPS registration fail"
7665 );
7666 assert!(
7667 descriptor_is_socket_on_port(https_fd, https_address.port as u16),
7668 "a failed HTTPS registration must not close the socket it was handed"
7669 );
7670
7671 let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7672 let token = token_for(Protocol::UDP);
7673 let mut udp_proxy = udp::UdpProxy::new(
7674 registry.try_clone().expect("could not clone the registry"),
7675 sessions.clone(),
7676 pool.clone(),
7677 backends.clone(),
7678 16,
7679 16384,
7680 );
7681 udp_proxy
7682 .add_listener(
7683 ListenerBuilder::new_udp(udp_address)
7684 .to_udp(None)
7685 .expect("could not build the test UDP listener config"),
7686 token,
7687 )
7688 .expect("could not add the test UDP listener");
7689 let mut socket =
7690 udp_bind(udp_address.into(), None).expect("could not bind the test UDP socket");
7691 registry
7692 .register(&mut socket, Token(usize::MAX - 1), Interest::READABLE)
7693 .expect("could not pre-register the test UDP socket");
7694 let udp_fd = socket.as_raw_fd();
7695 let result = udp_proxy.activate_listener(&udp_address.into(), Some(socket));
7696 assert!(
7697 result.is_err(),
7698 "the pre-registered descriptor must make the UDP registration fail"
7699 );
7700 assert!(
7701 descriptor_is_socket_on_port(udp_fd, udp_address.port as u16),
7702 "a failed UDP registration must not close the socket it was handed"
7703 );
7704 }
7705}
7706
7707#[cfg(test)]
7708mod tests {
7709 use super::{ClusterId, SessionManager, mask_ipv4, mask_ipv6, subnet_key};
7710 use mio::Token;
7711 use slab::Slab;
7712 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7713
7714 #[test]
7721 fn a_cluster_override_dropping_to_zero_after_a_track_is_a_no_op() {
7722 let manager = SessionManager::new(Slab::with_capacity(8), 8, 0, 0, 0, 24, 56);
7723 let token = Token(1);
7724 let cluster = ClusterId::from("api");
7725 let ip: IpAddr = "203.0.113.9".parse().expect("test address must parse");
7726
7727 manager
7729 .borrow_mut()
7730 .track_cluster_subnet(token, cluster.clone(), ip, Some(5));
7731 assert!(
7732 !manager.borrow().subnet_tracking_is_empty(),
7733 "premise: the positive override must have recorded a slot"
7734 );
7735
7736 manager
7740 .borrow_mut()
7741 .track_cluster_subnet(token, cluster.clone(), ip, Some(0));
7742
7743 manager.borrow_mut().untrack_all_cluster_ip(token);
7745 assert!(
7746 manager.borrow().subnet_tracking_is_empty(),
7747 "teardown must drain the slot even though the cap was disabled in between"
7748 );
7749 }
7750
7751 #[test]
7756 fn masking_handles_both_shift_overflowing_prefix_bounds() {
7757 let v4: Ipv4Addr = "203.0.113.45".parse().expect("test address must parse");
7758 assert_eq!(
7759 mask_ipv4(v4, 32),
7760 v4,
7761 "a /32 must be the address itself, not a wrapped-shift artefact"
7762 );
7763 assert_eq!(
7764 mask_ipv4(v4, 0),
7765 Ipv4Addr::UNSPECIFIED,
7766 "a /0 must mask every bit away"
7767 );
7768
7769 let v6: Ipv6Addr = "2001:db8:dead:beef::1"
7770 .parse()
7771 .expect("test address must parse");
7772 assert_eq!(
7773 mask_ipv6(v6, 128),
7774 v6,
7775 "a /128 must be the address itself, not a wrapped-shift artefact"
7776 );
7777 assert_eq!(
7778 mask_ipv6(v6, 0),
7779 Ipv6Addr::UNSPECIFIED,
7780 "a /0 must mask every bit away"
7781 );
7782
7783 assert_eq!(
7786 mask_ipv4(v4, 33),
7787 v4,
7788 "an over-wide IPv4 prefix must clamp to the address, not panic or wrap"
7789 );
7790 assert_eq!(
7791 mask_ipv6(v6, 129),
7792 v6,
7793 "an over-wide IPv6 prefix must clamp to the address, not panic or wrap"
7794 );
7795 }
7796
7797 #[test]
7800 fn masking_groups_addresses_at_the_recommended_prefixes() {
7801 let parse_v4 = |s: &str| -> Ipv4Addr { s.parse().expect("test address must parse") };
7802 assert_eq!(
7803 mask_ipv4(parse_v4("203.0.113.1"), 24),
7804 mask_ipv4(parse_v4("203.0.113.254"), 24),
7805 "two hosts in one /24 must share a subnet key"
7806 );
7807 assert_ne!(
7808 mask_ipv4(parse_v4("203.0.113.1"), 24),
7809 mask_ipv4(parse_v4("203.0.114.1"), 24),
7810 "hosts in different /24s must NOT share a subnet key"
7811 );
7812
7813 let parse_v6 = |s: &str| -> Ipv6Addr { s.parse().expect("test address must parse") };
7814 assert_eq!(
7815 mask_ipv6(parse_v6("2001:db8:abcd:0000::1"), 56),
7816 mask_ipv6(parse_v6("2001:db8:abcd:00ff::9999"), 56),
7817 "two addresses in one /56 must share a subnet key"
7818 );
7819 assert_ne!(
7820 mask_ipv6(parse_v6("2001:db8:abcd:0000::1"), 56),
7821 mask_ipv6(parse_v6("2001:db8:abcd:0100::1"), 56),
7822 "addresses in different /56s must NOT share a subnet key"
7823 );
7824
7825 assert_eq!(
7828 mask_ipv6(parse_v6("2001:db8:abcd:1234::1"), 56),
7829 mask_ipv6(parse_v6("2001:db8:abcd:1234:ffff:ffff:ffff:ffff"), 56),
7830 "every address a /64 holder can mint must collapse to one /56 key — that \
7831 collapse IS the mitigation for sozu-proxy/sozu#1270"
7832 );
7833 }
7834
7835 #[test]
7840 fn an_ipv4_mapped_source_is_charged_to_the_ipv4_prefix() {
7841 let mapped: IpAddr = "::ffff:203.0.113.7".parse().expect("must parse");
7842 let native: IpAddr = "203.0.113.7".parse().expect("must parse");
7843 assert_eq!(
7844 subnet_key(&mapped, 24, 56),
7845 subnet_key(&native, 24, 56),
7846 "a mapped IPv4 source must key identically to the same address arriving natively"
7847 );
7848 assert!(
7849 subnet_key(&mapped, 24, 56).is_ipv4(),
7850 "the key for a mapped source must be an IPv4 key"
7851 );
7852
7853 let other: IpAddr = "::ffff:198.51.100.7".parse().expect("must parse");
7857 assert_ne!(
7858 subnet_key(&mapped, 24, 56),
7859 subnet_key(&other, 24, 56),
7860 "mapped sources from different IPv4 /24s must NOT share a subnet key — if they \
7861 do, every IPv4 client on a dual-stack listener shares one bucket"
7862 );
7863
7864 let genuine: IpAddr = "2001:db8::1".parse().expect("must parse");
7866 assert!(
7867 subnet_key(&genuine, 24, 56).is_ipv6(),
7868 "a genuine IPv6 source must keep an IPv6 subnet key"
7869 );
7870 }
7871}