Skip to main content

sozu_lib/
server.rs

1//! event loop management
2use 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
55/// Number of bounded buckets for the per-source connect-rate counter.
56///
57/// `incr!` requires a `&'static str`, so per-IP labelling would either need
58/// runtime `Box::leak` per unique source (unbounded under SYN flood — direct
59/// OWASP A05 / NIST SP 800-92 cardinality-blow-up risk) or a fixed bucket
60/// table. We pick the bucket table: 256 static labels precomputed at startup,
61/// each masked subnet hashes into one of them.
62///
63/// Bucket-noise vs per-IP fidelity is a deliberate trade. Operators wanting
64/// per-IP attribution should pair these counters with structured access logs
65/// or a downstream rate-limiter; the metric here is for "is some /24 spamming
66/// us right now?", not "which IP exactly". 256 buckets keep the memory + UDP
67/// statsd cost flat regardless of attacker effort.
68pub const PER_SOURCE_BUCKETS: usize = 256;
69
70/// Pre-leaked `&'static str` table for per-source bucket counters.
71/// `incr!` requires `&'static str`; we leak once at first access (LazyLock)
72/// for `PER_SOURCE_BUCKETS` keys, totalling ~10 KB heap. The leak is bounded
73/// by `PER_SOURCE_BUCKETS` and never grows with traffic.
74static 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        // e.g. "client.connect.per_source.bucket_042"
78        let owned = format!("client.connect.per_source.bucket_{i:03}");
79        *slot = Box::leak(owned.into_boxed_str());
80    }
81    keys
82});
83
84/// Mask an IP address to its bounded prefix (/24 for IPv4, /48 for IPv6) and
85/// hash it into one of `PER_SOURCE_BUCKETS` slots. The hash is `DefaultHasher`,
86/// which is deterministic within a process but salted across runs — fine for
87/// telemetry, not suitable for cross-host correlation.
88fn 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            // /24 mask: keep first three octets, zero the host portion.
94            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            // /48 mask: keep first 6 bytes, zero the rest.
100            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
109/// Period between two `accept_queue.saturated_seconds` ticks. The counter is
110/// incremented once per period while [`SessionManager::can_accept`] is `false`,
111/// distinguishing "queue spent N seconds at max" from "queue briefly hit max"
112/// — the binary `accept_queue.backpressure` gauge collapses that duration.
113const 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
142/// Whether `request` removes a frontend, of any protocol.
143fn 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
155/// Build the `WorkerMetricDetailStatus` content payload returned in
156/// every successful `SetMetricDetail` worker response. The master
157/// collects these across the fan-out and assembles them into
158/// `MetricDetailStatus.workers[<worker_id>]` so the TUI sees each
159/// worker's actual aggregator state instead of the master's view.
160fn 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
176/// Build a `METRIC_DETAIL_CHANGED` event carrying the worker-local
177/// transition payload (previous/effective levels + transition kind).
178/// `client_id` is `Some(_)` for explicit apply/clear, `None` for the
179/// polled janitor's bulk expiry. The master folds this Event into the
180/// audit log alongside operator-initiated transitions emitted at the
181/// dispatch site in `bin/src/command/requests.rs::worker_request`.
182fn 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    // No-op when nothing actually changed. Defence-in-depth — every
190    // caller already gates on `previous != effective`, but
191    // double-checking here means future call sites can't accidentally
192    // emit a "ghost" transition.
193    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
210/// Polled lease-expiry janitor: `SetMetricDetail` leases self-expire after
211/// their TTL so a crashed `sozu top` cannot permanently elevate metrics
212/// cardinality. Called once per event-loop iteration from [`Server::run`],
213/// which wakes at least once per `poll_timeout` (1 s) even with no traffic
214/// and no command, and from [`Server::notify`]. `lease_tick_due` gates the
215/// HashMap walk to once per `LEASE_TICK_INTERVAL` (5 s), so an abandoned
216/// lease is retired within TTL + 5 s + one poll timeout (#1831).
217fn tick_metric_detail_leases(now: Instant) {
218    // Capture (previous, effective) before releasing the borrow so we can
219    // emit an Event afterwards. Holding `METRICS.borrow_mut` across
220    // `push_event` would re-enter the same thread-local from inside
221    // `QUEUE.with` (safe but conceptually noisy); the two-step split keeps
222    // the borrow scopes minimal. Single-threaded worker, so `borrow_mut` is
223    // safe here.
224    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        // The janitor retired one or more leases AND the effective level
235        // moved. Surface the worker-local transition as an Event so the
236        // master folds it into the audit log. `client_id` is `None` because
237        // the janitor may have retired multiple leases at once.
238        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
271/// Mask `address` down to the network named by `prefix` bits.
272///
273/// `prefix` is validated at config-load time
274/// (`ConfigError::InvalidSubnetPrefix` in `command/src/config.rs`), but
275/// this runs on the per-request hot path and does not assume that
276/// happened: a prefix at or above the address width returns the address
277/// unmasked.
278///
279/// Both ends are handled by an explicit branch rather than by the shift.
280/// `u32::MAX << 32` is an overflowing shift — it panics under
281/// `debug_assertions` and silently wraps to a no-op shift in release —
282/// so a full-width prefix must never reach the shift. A zero prefix is
283/// branched from the other end for the same reason.
284fn 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    // Only a strictly-interior prefix reaches the shift, which is what
292    // makes `u32::BITS - prefix` a legal shift amount in 1..32.
293    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    // Pair assertion, positive then negative space: the result keeps
300    // every masked-in bit of the input, and holds none of the host bits
301    // the prefix is supposed to erase.
302    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
315/// IPv6 twin of [`mask_ipv4`]. The same two shift-overflowing ends are
316/// branched explicitly: `u128::MAX << 128` is as ill-defined as its
317/// 32-bit counterpart.
318fn 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
344/// Derive the per-(cluster, source-SUBNET) counter key for `address`.
345///
346/// An IPv4-mapped IPv6 address is canonicalised to its IPv4 form and
347/// charged to `ipv4_prefix`. This matters: a dual-stack `[::]` listener
348/// hands every IPv4 client back as `::ffff:a.b.c.d`, and masking those
349/// with an IPv6 prefix would collapse the entire IPv4 internet into the
350/// single `::/56` bucket — the subnet cap would then fire on unrelated
351/// clients the moment it was switched on. The per-IP counter is NOT
352/// canonicalised: it keeps charging the address exactly as it arrived,
353/// which is part of what leaves its behaviour untouched by this feature.
354pub 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        // Unreachable: `canonical_v4` is `Some` for every `IpAddr::V4`.
363        (None, IpAddr::V4(v4)) => IpAddr::V4(mask_ipv4(*v4, ipv4_prefix)),
364    };
365    // Pair assertion: the key stays in the family it will be compared
366    // against — a mapped source yields a V4 key (positive space), and a
367    // genuine IPv6 source never yields one (negative space).
368    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    /// Default per-(cluster, source-IP) connection limit. `0` disables
387    /// the feature; cluster-level overrides take precedence at check
388    /// time.
389    pub max_connections_per_ip: u64,
390    /// Default `Retry-After` header value (seconds) for HTTP 429
391    /// responses emitted on per-(cluster, source-IP) limit hit. `0`
392    /// omits the header.
393    pub retry_after: u32,
394    /// Active **frontend connections** per `(cluster_id, source_ip)`.
395    /// Each frontend session contributes AT MOST 1 to the count for any
396    /// given `(cluster, ip)` pair, regardless of how many streams it
397    /// multiplexes to that cluster (an H2 connection serving 100
398    /// streams to cluster X from IP 1.2.3.4 still counts as 1). The
399    /// counter is incremented the first time a session's
400    /// `Router::plan_connect` resolves to a fresh `(cluster, ip)` pair, and
401    /// decremented when the session closes. Empty when the feature is
402    /// unused.
403    ///
404    /// ── Why nested maps instead of `HashMap<(String, IpAddr), usize>` ──
405    ///
406    /// The per-request hot path (`cluster_ip_at_limit`, called from
407    /// `mux/router::plan_connect` for every cluster-resolving request) used
408    /// to allocate a `String` to build the compound key on every
409    /// lookup. Splitting the storage so the outer key is a [`ClusterId`]
410    /// lets the lookup take `&str` — `HashMap::get(cluster_id)` on a
411    /// `HashMap<ClusterId, _>` accepts a borrow via the `Borrow<str>`
412    /// impl. The hot path no longer allocates, and `track_cluster_ip`
413    /// keys the maps with the caller's reference-counted handle, so no
414    /// key is ever a copy of the id. Memory footprint is unchanged
415    /// in steady state — entries are still reaped to zero on session
416    /// close.
417    connections_per_cluster_ip: HashMap<ClusterId, HashMap<IpAddr, usize>>,
418    /// Reverse index: per-token map of `cluster_id` → set of source IPs
419    /// already counted against `connections_per_cluster_ip`. Used to
420    /// make `track_cluster_ip` idempotent within a session (so H2
421    /// streams to the same cluster from the same client only consume
422    /// one slot in the limit) and to drain a session's contributions on
423    /// close. Same nesting rationale as above.
424    cluster_ip_tracks: HashMap<Token, HashMap<ClusterId, HashSet<IpAddr>>>,
425    /// Default per-(cluster, source-SUBNET) connection limit. `0`
426    /// disables the subnet limiter, which is the default; cluster-level
427    /// overrides take precedence at check time.
428    ///
429    /// This is a SECOND, INDEPENDENT counter standing beside
430    /// `max_connections_per_ip`, not a replacement for it. The per-IP
431    /// cap serves the API-gateway case — one abusive client, one
432    /// address. The per-subnet cap serves the denial-of-service case,
433    /// where an attacker holding a routed prefix (an ordinary
434    /// residential IPv6 `/64`) defeats a per-address counter outright
435    /// by taking a fresh address for every connection
436    /// (sozu-proxy/sozu#1270). Both gates are consulted and BOTH must
437    /// admit, so "10 per IP AND 100 per /64" is expressible.
438    pub max_connections_per_subnet: u64,
439    /// IPv4 prefix length, in bits, defining a "subnet" for the counter
440    /// above. Boot-time only: changing a mask would re-key every live
441    /// counter, so there is no runtime setter for it. 32 masks nothing.
442    pub subnet_ipv4_prefix: u32,
443    /// IPv6 prefix length, in bits. Boot-time only, as above. 128 masks
444    /// nothing.
445    pub subnet_ipv6_prefix: u32,
446    /// Active frontend connections per `(cluster_id, masked_source_ip)`.
447    /// Exact mirror of `connections_per_cluster_ip`, keyed on the subnet
448    /// key from `subnet_key` instead of the raw address; the same
449    /// nesting rationale applies verbatim.
450    ///
451    /// Stays EMPTY while the subnet limiter is disabled: unlike the
452    /// per-IP index, which is populated unconditionally, this one is
453    /// only written when a positive limit resolves for the cluster (see
454    /// `SessionManager::track_cluster_subnet`). That is what makes the
455    /// default genuinely free rather than merely inert — no second
456    /// allocation, no second hash, no second entry per session for an
457    /// operator who never turns the feature on.
458    connections_per_cluster_subnet: HashMap<ClusterId, HashMap<IpAddr, usize>>,
459    /// Reverse index for `connections_per_cluster_subnet`, mirroring
460    /// `cluster_ip_tracks`. Also empty while the limiter is disabled.
461    cluster_subnet_tracks: HashMap<Token, HashMap<ClusterId, HashSet<IpAddr>>>,
462    /// Default backend connection attempt budget of a request, the first
463    /// attempt included: `ServerConfig::max_connection_attempts`, changed at
464    /// runtime by `SetMaxConnectionAttempts`. A cluster's own
465    /// `max_connection_attempts` takes precedence
466    /// ([`Self::effective_max_connection_attempts`]). Validated, so between
467    /// 1 and 255.
468    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    /// Resolve the backend connection attempt budget of a request routed to
500    /// a cluster whose own setting is `override_value`: the cluster's value
501    /// when it sets one, the global default otherwise (sozu-proxy/sozu#1800).
502    pub fn effective_max_connection_attempts(&self, override_value: Option<u32>) -> u32 {
503        override_value.unwrap_or(self.max_connection_attempts)
504    }
505
506    /// Resolve the effective per-(cluster, source-IP) limit. `override_value`
507    /// is the cluster-level setting from the proto `Cluster` message:
508    /// `None` inherits the global default, `Some(0)` is explicit
509    /// "unlimited", `Some(n > 0)` overrides.
510    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    /// Resolve the effective `Retry-After` header value. `Some(0)` (or
515    /// the global default of 0) signals "omit the header" — caller
516    /// must skip emission rather than render `Retry-After: 0`.
517    pub fn effective_retry_after(&self, override_value: Option<u32>) -> u32 {
518        override_value.unwrap_or(self.retry_after)
519    }
520
521    /// Resolve the effective per-(cluster, source-SUBNET) limit. Same
522    /// three-state contract as `effective_max_connections_per_ip`:
523    /// `None` inherits the global default, `Some(0)` is explicit
524    /// "unlimited", `Some(n > 0)` overrides.
525    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    /// Derive the subnet counter key for `ip` under this manager's
530    /// configured prefixes. Allocation-free — `IpAddr` is `Copy`.
531    pub fn subnet_key(&self, ip: &IpAddr) -> IpAddr {
532        subnet_key(ip, self.subnet_ipv4_prefix, self.subnet_ipv6_prefix)
533    }
534
535    /// Returns `true` when admitting `token` to one more connection for
536    /// `(cluster, subnet_of(ip))` would exceed the resolved subnet
537    /// limit. Exact mirror of `cluster_ip_at_limit` on the subnet
538    /// counter: `0` is unlimited, and a token already holding a slot
539    /// for this `(cluster, subnet)` is never at the limit.
540    ///
541    /// The subnet key is derived only AFTER the `limit == 0` early
542    /// return, so a deployment that never enables this feature pays no
543    /// masking work on the hot path at all.
544    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    /// THE gate. Returns `true` when EITHER the per-IP cap or the
582    /// per-subnet cap would be exceeded by admitting `token` to one more
583    /// connection for `cluster_id` from `ip`.
584    ///
585    /// Both enforcement points — the H1+H2 mux router and raw TCP — call
586    /// this rather than the two halves, so the conjunction "admit only
587    /// if BOTH caps allow it" is stated once and cannot drift between
588    /// protocols. The per-IP half is evaluated first and is byte-for-byte
589    /// the pre-existing check; the subnet half is the single line this
590    /// feature adds to the admission path.
591    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    /// Returns `true` when admitting `token` to one more connection for
604    /// `(cluster, ip)` would exceed the resolved limit. `0` is treated
605    /// as unlimited. A token that already holds a slot for this
606    /// `(cluster, ip)` is NEVER at the limit — H2 sessions multiplex
607    /// many streams to the same cluster on a single connection, and
608    /// the limit governs distinct frontend connections, not streams.
609    ///
610    /// Hot-path: called for every cluster-resolving request from
611    /// `mux/router::plan_connect`. The nested-map storage lets both lookups
612    /// borrow `cluster_id` and `ip`; no per-call allocation runs here
613    /// in steady state.
614    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        // Pure query: the limit==0 branch already returned, so any work below
626        // runs only with a positive cap.
627        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            // Reverse-index/forward-count coherence: if this token already
638            // holds a slot for (cluster, ip), the forward count must be > 0
639            // (decrement-to-zero reaps the inner entry in untrack_all).
640            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    /// Account `token`'s active connection against `(cluster, ip)`.
656    /// Idempotent within a token: a second call for the same
657    /// `(cluster, ip)` is a no-op so H2 retries / multi-stream opens
658    /// to the same cluster do not double-count.
659    ///
660    /// Copies no cluster id: `entry(cluster_id.clone())` only bumps the
661    /// [`ClusterId`] count. A first `(token, cluster)` observation
662    /// allocates the new slot's IP set; later IPs under it reuse the
663    /// set, and a repeat call allocates nothing.
664    ///
665    /// ── A FAILED backend dial keeps the slot, and that is deliberate ──
666    ///
667    /// `Router::plan_connect` (`lib/src/protocol/mux/router.rs`) claims
668    /// here, after the `Self::cluster_ip_at_limit` gate but BEFORE
669    /// backend selection and the dial. Six fallible exits follow the
670    /// claim and none of them releases. Since sozu-proxy/sozu#1519 they
671    /// sit in two functions rather than one: `Router::plan_connect`
672    /// keeps the two pool-reuse refusals, and `Mux::dial_backend` with the
673    /// `Mux::attach_dialed` it calls (`lib/src/protocol/mux/mod.rs`) own the
674    /// four on the fresh-dial
675    /// path — backend selection, buffer-pool exhaustion, the new
676    /// connection refusing the stream, and the `L7Proxy::register_socket`
677    /// rollback, which restores its gauges and slab entry but not the
678    /// slot. The split moved no ordering: the claim still precedes all
679    /// six. `Self::untrack_all_cluster_ip` is the only release, and
680    /// session teardown is its only caller — the
681    /// `L7Proxy::remove_session` impls on `HttpProxy` / `HttpsProxy` for
682    /// the mux, `TcpSession::close` for raw TCP.
683    /// An unpaired claim reads like an oversight; it is not one, and
684    /// sozu-proxy/sozu#1521 is where the reasoning was measured rather
685    /// than assumed. Do not pair a release with those exits as part of a
686    /// refactor — it would LOOSEN a connection limit under exactly the
687    /// conditions a client can induce.
688    ///
689    /// What makes the unpaired claim cheap on H1 is that a failed dial
690    /// ENDS the frontend connection, so the slot returns with it:
691    ///
692    /// - `Mux::ready_inner` joins the plan and the dial into a single
693    ///   `Result` with `and_then`, so every post-claim exit from either
694    ///   function reaches the same `BackendConnectionError` arm of its
695    ///   pending-link loop, which answers the stream from the template
696    ///   registry instead of retrying. No arm there returns
697    ///   `SessionResult::Close` of its own.
698    /// - Every built-in template in
699    ///   `lib/src/protocol/kawa_h1/answers.rs` carries
700    ///   `Connection: close`, so `set_default_answer_with_retry_after`
701    ///   (`lib/src/protocol/mux/answers.rs`) clears
702    ///   `HttpContext::keep_alive_frontend`. On H1 that one bit is what
703    ///   closes the connection; H2 does not read it.
704    /// - H1: once the answer flushes, the response-complete branch of
705    ///   `ConnectionH1::writable` (`lib/src/protocol/mux/h1.rs`) takes
706    ///   `ConnectionH1::defer_close_for_tls_flush` instead of the
707    ///   keep-alive reset, so the session closes after that one flush.
708    /// - H2: the answer ends its own stream only (RFC 9113 §8.2.2:
709    ///   `Connection` is not an H2 field, sozu-proxy/sozu#1740). The
710    ///   connection stays open, so the slot is held until the client
711    ///   closes it or it times out.
712    /// - TCP never reaches an answer at all: any error out of
713    ///   `TcpSession::connect_to_backend` (`lib/src/tcp.rs`) becomes
714    ///   `SessionResult::Close` in the same event-loop pass.
715    /// - A registered, `Normal` backend that is down may not fail on the
716    ///   dial at all. The dial — `Mux::dial_backend`
717    ///   (`lib/src/protocol/mux/mod.rs`) for HTTP, `Backend::try_connect`
718    ///   (`lib/src/backends.rs`) for TCP — is non-blocking: a refusal the
719    ///   kernel reports synchronously surfaces as
720    ///   `BackendError::ConnectionFailures`, which the mux re-queues for
721    ///   another backend, while an `EINPROGRESS` connect returns `Ok` and
722    ///   its refusal (or its `connect_timeout`) arrives later as a backend
723    ///   failure, so the stream retries through `EndStreamAction::Reconnect`.
724    ///   The re-track is the no-op above, so one slot — not one per
725    ///   attempt — is held for at most `max_connection_attempts` attempts
726    ///   (sozu-proxy/sozu#1800).
727    ///
728    /// The window IS the connection's whole lifetime in two cases: any
729    /// H2 connection, and an H1 connection answered by an operator
730    /// template that omits `Connection: close` (the opt-out
731    /// `doc/rate-limit-design.md` records for the 429 template). Either
732    /// holds its one slot per (connection, cluster) until the client goes
733    /// away, as a connection whose dial succeeded does.
734    pub fn track_cluster_ip(&mut self, token: Token, cluster_id: ClusterId, ip: IpAddr) {
735        // Snapshot the forward count for this (cluster, ip) before the insert
736        // so we can pair-assert the delta. Ungated `let`: read only inside the
737        // debug_assert! below → optimised out in release (no E0425).
738        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        // Postconditions: the reverse index now records this (token, cluster,
760        // ip), and the forward count advanced by exactly `inserted as usize`
761        // (idempotent: a repeat call for the same triple is a no-op on both).
762        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    /// Account `token`'s active connection against
783    /// `(cluster, subnet_of(ip))`. Mirror of `track_cluster_ip` on the
784    /// subnet counter, with one deliberate difference: it is a NO-OP
785    /// while the resolved subnet limit is `0`.
786    ///
787    /// That conditional is what keeps the default genuinely free. The
788    /// per-IP index is populated unconditionally, so an operator who
789    /// never enables the subnet cap would otherwise pay a second hash,
790    /// a second allocation and a second per-session entry forever for a
791    /// feature they do not use. The cost is that enabling the cap at
792    /// runtime does not retroactively count sessions already in flight;
793    /// the counter converges as those sessions close. The per-IP path
794    /// has the same shape of gap after `clear_cluster_ip_tracking`.
795    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            // No assertion that nothing is recorded for this token: that
804            // is NOT an invariant. A cluster override can drop to
805            // `Some(0)` after a session was tracked under a positive
806            // limit — `AddCluster` is an upsert and clears no counters,
807            // and only the global `SetMaxConnectionsPerSubnet(0)` drains
808            // the bookkeeping. The stale slot is harmless (a `0` limit
809            // never refuses) and is released normally on session close.
810            return;
811        }
812        let subnet = self.subnet_key(&ip);
813        // Snapshot before the insert so the delta can be pair-asserted.
814        // Ungated `let`: read only inside the debug_assert! below, so it
815        // is optimised out in release (no E0425).
816        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    /// Account `token`'s active connection against BOTH counters. The
858    /// companion of `cluster_connection_at_limit`: the two enforcement
859    /// points take the gate and the slot through one call each, so the
860    /// per-IP and per-subnet bookkeeping can never fall out of step
861    /// between the mux and raw TCP.
862    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    /// Drain every slot held by `token` — per-IP AND per-subnet — and
874    /// apply the matching decrements. Called on session teardown only —
875    /// there is no per-stream untrack because the limit is
876    /// per-connection, not per-stream. Removes empty inner maps so the
877    /// outer `connections_per_cluster_ip` does not retain
878    /// `(cluster_id, empty_map)` orphans across cluster lifetimes.
879    ///
880    /// The name is historical: this is the SINGLE release point every
881    /// protocol already calls on close (`HttpSession`, `HttpsSession`,
882    /// `TcpSession`), so the subnet counter is drained from here rather
883    /// than from a second call each of those three sites would have to
884    /// remember. A missed release leaks a slot until worker restart,
885    /// which is exactly the failure a second entry point would invite.
886    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        // The reverse index for this token was just drained by `remove`; no
892        // other code path re-inserts it within this call.
893        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        // No orphan bookkeeping survives: the forward map must not retain a
916        // cluster with an empty inner ip-map, nor an ip whose count is zero.
917        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    /// Drain every `(cluster, subnet)` slot held by `token`. Mirror of
928    /// the per-IP drain in `untrack_all_cluster_ip`, which is its only
929    /// caller. A no-op when the subnet limiter was never enabled, since
930    /// nothing was tracked in the first place.
931    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    /// Whether both halves of the subnet accounting are empty.
966    ///
967    /// Exists so a test can assert that a DISABLED subnet limiter leaves
968    /// no bookkeeping behind at all — "inert" and "free" are different
969    /// claims, and only the second one is checkable from outside.
970    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    /// Wipe every per-(cluster, source-SUBNET) accounting bucket.
975    /// Called by the runtime `SetMaxConnectionsPerSubnet(0)` path.
976    /// Deliberately does NOT touch the per-IP buckets: the two limiters
977    /// are independent, and disabling one must not silently reset the
978    /// other's live counters.
979    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    /// Wipe every per-(cluster, source-IP) accounting bucket. Called by
991    /// the runtime `SetMaxConnectionsPerIp(0)` path so disabling the
992    /// feature does not leave dead bookkeeping behind that a future
993    /// re-enable would consult.
994    pub fn clear_cluster_ip_tracking(&mut self) {
995        self.cluster_ip_tracks.clear();
996        self.connections_per_cluster_ip.clear();
997        // Both halves of the per-(cluster, ip) accounting are now empty; a
998        // future re-enable starts from a clean slate.
999        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    /// The slab is considered at capacity if it contains more sessions than twice max_connections
1008    pub fn at_capacity(&self) -> bool {
1009        self.slab.len() >= self.accept_slab_threshold()
1010    }
1011
1012    /// The slab fill level at which `at_capacity` flips to true and the
1013    /// accept queue is flushed. Reported as `slab.accept_threshold` so the
1014    /// per-iteration `slab.accept_threshold_percent` gauge in the run loop
1015    /// can chart proximity to this gate, distinct from raw slab usage.
1016    ///
1017    /// The constant `10 + 2 * max_connections` is the historical pre-knob
1018    /// budget; configured slab capacity is
1019    /// `10 + slab_entries_per_connection * max_connections` (see
1020    /// `command/src/config.rs`) and can be larger, so `slab.usage_percent`
1021    /// (against `slab.capacity()`) and `slab.accept_threshold_percent`
1022    /// (against this gate) are emitted as independent gauges.
1023    pub fn accept_slab_threshold(&self) -> usize {
1024        let threshold = 10 + 2 * self.max_connections;
1025        // The gate must leave headroom above the connection cap so listener /
1026        // system slots (Channel, Metrics, Timer, listeners) are never starved
1027        // by frontend connections alone.
1028        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    /// Check the number of connections against max_connections, and the slab capacity.
1036    /// Returns false if limits are reached.
1037    pub fn check_limits(&mut self) -> bool {
1038        // Live-count invariant: the accounted connection count never exceeds
1039        // the configured cap (incr() enforces this, decr() never underflows).
1040        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            // A negative result must have closed the accept gate.
1049            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        // A positive result means there is room under both gates.
1073        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        // The counter advances by exactly one per accepted session.
1089        debug_assert_eq!(
1090            self.nb_connections,
1091            before + 1,
1092            "incr must raise nb_connections by exactly one"
1093        );
1094        // `client.connections_max` and `client.connections_percent` are
1095        // emitted from the run loop alongside `process.uptime_seconds` /
1096        // `server.live` so all proxy gauges advance in lock-step. Keeping
1097        // `client.connections` per-event preserves the high-resolution
1098        // signal scrapers expect.
1099        gauge!(names::client::CONNECTIONS, self.nb_connections);
1100    }
1101
1102    /// Decrements the number of sessions, start accepting new connections
1103    /// if the capacity limit of 90% has not been reached.
1104    pub fn decr(&mut self) {
1105        assert!(self.nb_connections != 0);
1106        let before = self.nb_connections;
1107        self.nb_connections -= 1;
1108        // Mirror of incr: exactly one connection is released, no underflow.
1109        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        // do not be ready to accept right away, wait until we get back to 10% capacity
1117        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    /// Full cross-field invariant sweep for the session manager. Called as a
1128    /// `debug_assert!`-guarded postcondition from the mutating cluster-IP
1129    /// tracking methods. Asserts logic-bug conditions only — every clause
1130    /// holds on any well-formed manager regardless of traffic.
1131    #[cfg(debug_assertions)]
1132    fn check_invariants(&self) {
1133        // 1. Live connection count never exceeds the configured cap.
1134        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        // 2. The forward count map never retains an empty inner ip-map or a
1141        //    zero count (both are reaped on the last untrack).
1142        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        // 3. Reverse-index → forward-count coherence: every (token, cluster,
1149        //    ip) recorded in the reverse index must have a positive forward
1150        //    count. The forward count is the sum of contributing tokens, so a
1151        //    tracked slot can never point at a missing/zero count.
1152        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        // 4. The reverse index never retains an empty inner structure.
1166        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        // 5-7. The same three structural clauses, on the subnet mirror.
1173        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
1216/// `Server` handles the event loop, the listeners, the sessions and
1217/// communication with the configuration channel.
1218///
1219/// A listener wraps a listen socket, the associated proxying protocols
1220/// (HTTP, HTTPS and TCP) and the routing configuration for clusters.
1221/// Listeners handle creating sessions from accepted sockets.
1222///
1223/// A session manages a "front" socket for a connected client, and all
1224/// of the associated data (back socket, protocol state machine, buffers,
1225/// metrics...).
1226///
1227/// `Server` gets configuration updates from the channel (domIN/path routes,
1228/// backend server address...).
1229///
1230/// Listeners and sessions are all stored in a slab structure to index them
1231/// by a [Token], they all have to implement the [ProxySession] trait.
1232pub struct Server {
1233    accept_queue_timeout: Duration,
1234    /// Tuple layout: `(socket, listen token, protocol, accept time, peer
1235    /// address)`. The peer is the address `accept(2)` returned with the
1236    /// socket: it attributes the `client.connect.per_source.*` counter at
1237    /// accept time and is handed to `create_session`, so no one has to read it
1238    /// back with `getpeername(2)` (sozu-proxy/sozu#1586). Unlike that lookup,
1239    /// which fails once the peer has reset, `accept(2)` always reports it.
1240    accept_queue: VecDeque<(TcpStream, ListenToken, Protocol, Instant, SocketAddr)>,
1241    /// When the accept queue saturates and `check_limits` refuses, evict the
1242    /// oldest non-listener sessions to make room. Default off — see
1243    /// `command::config::DEFAULT_EVICT_ON_QUEUE_FULL` for the rationale.
1244    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    /// Wall-clock anchor for the `process.uptime_seconds` gauge. Captured once
1259    /// in [`Server::new`]; never reset on hot upgrades (the new worker that
1260    /// inherits FDs is a fresh process and starts its own counter).
1261    started_at: Instant,
1262    /// Last time the 1Hz `accept_queue.saturated_seconds` ticker fired. The
1263    /// counter is incremented once per [`ACCEPT_SATURATION_TICK`] while
1264    /// `SessionManager::can_accept` is `false`, so dashboards can plot the
1265    /// time spent saturated rather than just whether saturation occurred.
1266    last_saturation_tick: Instant,
1267    max_poll_errors: i32, // TODO: make this configurable? this defaults to 10000 for now
1268    /// Shared reference to the buffer pool the protocol stacks check buffers
1269    /// in and out of. Held here so the run loop can sample
1270    /// `buffer.in_use` / `buffer.capacity` / `buffer.usage_percent` once per
1271    /// iteration without requiring each protocol module to expose its own
1272    /// snapshot.
1273    pool: Rc<RefCell<Pool>>,
1274    pub poll: Poll,
1275    poll_timeout: Option<Duration>, // TODO: make this configurable? this defaults to 1000 milliseconds for now
1276    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        // Commit the operator-configured Basic-auth credential cap (or
1296        // keep the built-in default) once per worker process. The
1297        // `OnceLock` rejects any later attempt to change the value, so
1298        // calling here — before any L7 listener has accepted a request
1299        // — guarantees the cap is in force the first time `mux::auth`
1300        // runs.
1301        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        // Same set-once-per-worker-boot pattern for the splice kernel-pipe
1305        // capacity. The setter no-ops on `0` so an explicit zero in config
1306        // does not collapse the pipe to PAGE_SIZE; the kernel still applies
1307        // page-rounding and `/proc/sys/fs/pipe-max-size` clamping at
1308        // SplicePipe::new time. Cfg-gated because the splice module only
1309        // exists on Linux + `splice` feature.
1310        #[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        // Note: slab_capacity uses 4x multiplier (up from 2x) to account for H2
1322        // multiplexing where each session can have multiple backend connections.
1323        // Newer `optional` proto fields fall through to the
1324        // command-lib defaults when an older worker manager omits them.
1325        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        // Newer field, so an older main may omit it. The main validates it at
1345        // config load; a value that still breaks the bounds keeps the default
1346        // rather than disabling retries or overflowing the attempt counters.
1347        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            // initializing feature flags
1421        });
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        // UDP proxy is constructed internally (no constructor parameter) so the
1478        // public `Server::new` / `try_new_from_config` signatures stay
1479        // unchanged — bin/ is not forced to change for construction.
1480        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, // to be reset on server run
1512            last_shutting_down_message: None,
1513            last_zombie_check: Instant::now(), // to be reset on server run
1514            loop_start: Instant::now(),        // to be reset on server run
1515            started_at: Instant::now(),        // captured once, never reset
1516            last_saturation_tick: Instant::now(), // 1Hz saturation ticker anchor
1517            max_poll_errors: 10000,            // TODO: make it configurable?
1518            pool,
1519            poll_timeout: Some(Duration::from_millis(1000)), // TODO: make it configurable?
1520            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        // Take the retiring worker's listening sockets BEFORE applying the
1534        // initial state, because that state is what adopts them.
1535        //
1536        // `fork_main_into_worker` (`bin/src/worker.rs`) sends the descriptors
1537        // over the SCM socket right after the fork — before the main process
1538        // sends the initial `Status` request — and
1539        // `ConfigState::produce_initial_state` carries one `ActivateListener`
1540        // per active listener. Receiving them afterwards left `scm_listeners`
1541        // empty for the whole initial state, so every `ActivateListener` took
1542        // the `server_bind` / `udp_bind` branch (which succeeds only because
1543        // of `SO_REUSEPORT`), the listener came up `active`, and the inherited
1544        // descriptor was then dropped unused by the next, short-circuiting
1545        // `activate()` — discarding the accept backlog (TCP/HTTP/HTTPS) and
1546        // the receive buffer (UDP) the hand-off exists to preserve (sozu#1342).
1547        //
1548        // Blocking here is the same blocking read as before, only earlier: the
1549        // sole production caller of `fork_main_into_worker`,
1550        // `CommandServer::launch_new_worker`, always passes `Some(listeners)`,
1551        // so a fresh start receives an empty manifest immediately and every
1552        // listener binds its own socket exactly as it always did.
1553        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        // initialize the worker with the state we got from a file
1580        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            // do not send back answers to the initialization messages
1587            QUEUE.with(|queue| {
1588                (*queue.borrow_mut()).clear();
1589            });
1590        }
1591
1592        server.report_unclaimed_inherited_sockets();
1593
1594        if expects_initial_status {
1595            // the main process sends a Status message, so we can notify it
1596            // when the initial state is loaded
1597            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    /// The server runs in a loop until a shutdown is ordered
1624    pub fn run(&mut self) {
1625        let mut events = Events::with_capacity(1024); // TODO: make event capacity configurable?
1626        // The session owning each event's token when `epoll_wait` returned,
1627        // by event position; see `Server::ready_if_still_owned`. Reused
1628        // across turns so the loop allocates it once.
1629        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            // Before `send_queue`, so an expiry event leaves on this turn.
1658            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                    // this is the command channel
1666                    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 here because iterations has borrow issues
1679                        loop {
1680                            QUEUE.with(|queue| {
1681                                if !(*queue.borrow()).is_empty() {
1682                                    self.channel.interest.insert(Ready::WRITABLE);
1683                                }
1684                            });
1685
1686                            //trace!("WORKER[{}] channel readiness={:?}, interest={:?}, queue={} elements",
1687                            //  line!(), self.channel.readiness, self.channel.interest, self.queue.len());
1688                            if self.channel.readiness() == Ready::EMPTY {
1689                                break;
1690                            }
1691
1692                            // exit the big loop if the message is HardStop
1693                            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                    // timer tick
1707                    Token(1) => {
1708                        while let Some(t) = TIMER.with(|timer| timer.borrow_mut().poll()) {
1709                            self.timeout(t);
1710                        }
1711                    }
1712                    // metrics socket is writable
1713                    Token(2) => METRICS.with(|metrics| {
1714                        (*metrics.borrow_mut()).writable();
1715                    }),
1716                    // ListenToken: 1 listener <=> 1 token
1717                    // ProtocolToken (HTTP/HTTPS/TCP): 1 connection <=> 1 token
1718                    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                    //info!("polled for timeout: {:?}", t);
1733                    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            // Drive the UDP endpoint health prober (TCP-probe + hysteresis +
1745            // fail-open). Non-blocking; no-op when no UDP cluster has health
1746            // configured.
1747            self.udp.borrow_mut().health_poll();
1748
1749            // Frontend session gauges. `client.connections` keeps the
1750            // per-event signal from `SessionManager::incr/decr`; the rest
1751            // follow the once-per-iteration batching contract this run loop
1752            // uses for `process.uptime_seconds` / `server.live`.
1753            //
1754            // `slab.usage_percent` charts pure slab utilisation against
1755            // `slab.capacity()`. `slab.accept_threshold_percent` charts how
1756            // close the slab is to the `at_capacity()` accept gate
1757            // (`10 + 2 * max_connections`, see
1758            // `SessionManager::accept_slab_threshold`). Configured slab
1759            // capacity can be larger than that gate (`slab_entries_per_connection`
1760            // > 2), so the two gauges are kept independent on purpose.
1761            {
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            // Buffer pool gauges. `buffer.in_use` replaces the older
1785            // `buffer.number` (renamed in `lib/src/pool.rs` for naming
1786            // consistency with the surrounding `buffer.*` keys).
1787            // `buffer.usage_percent` is computed against the configured
1788            // `buffer.capacity` so dashboards can chart pool pressure.
1789            {
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            // 1Hz tick for `accept_queue.saturated_seconds`. Increments once
1800            // per `ACCEPT_SATURATION_TICK` while `SessionManager::can_accept`
1801            // is `false`. Distinguishes "queue spent N seconds at max" from
1802            // "queue briefly hit max" — the binary `accept_queue.backpressure`
1803            // gauge collapses that duration. Sampled here rather than via a
1804            // dedicated mio timer because the run loop ticks at least once
1805            // per `poll_timeout` (1s by default), which is granular enough.
1806            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            // Process / runtime gauges sampled once per loop iteration. Same
1814            // batch as `client.connections` so dashboards see them update in
1815            // lock-step.
1816            gauge!(
1817                names::process::UPTIME_SECONDS,
1818                self.started_at.elapsed().as_secs() as usize
1819            );
1820            // `server.live` flips to 0 once a graceful shutdown is requested,
1821            // matching Envoy's `server.live` semantics. L4 health checks
1822            // (HAProxy / cloud LBs) can poll this gauge to drain a worker
1823            // before the OS-level termination signal lands.
1824            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    /// Returns true if hardstop
1892    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                // Not an error per se, occurs when there is nothing to read
1935                Err(_) => {
1936                    // if the message was too large, we grow the buffer and retry to read if possible
1937                    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    /// Scans all sessions that have been inactive for longer than the configured interval
1951    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        // `now` is sampled this iteration and we only get here past the
1958        // interval gate, so the check timestamp advances monotonically.
1959        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        // find the zombie sessions
1968        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        // Listen/system sessions report `Instant::now()` as their last event,
1983        // so `now - last_event` is ~0 and never exceeds the interval: a
1984        // listener can never be collected as a zombie. Assert the set is free
1985        // of listen protocols before we reap it.
1986        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    /// Calls close on targeted sessions, yields the number of entries in the slab
2009    /// that were not properly removed
2010    fn shut_down_sessions_by_frontend_tokens(&self, tokens: HashSet<Token>) -> usize {
2011        if tokens.is_empty() {
2012            return 0;
2013        }
2014
2015        // close the sessions associated with the tokens
2016        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                // The removed token is truly gone afterwards. The slab may shrink
2023                // by MORE than one: `close()` also frees the session's backend
2024                // slab slot(s) (the multi-token pattern), so assert it shrank by
2025                // at least the frontend slot we just removed, not exactly one.
2026                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        // find the entries of closed sessions in the session manager (they should not be there)
2038        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        // remove these from the session manager
2046        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        // Postcondition: no surviving slab entry still references any of the
2055        // closed frontend tokens — both the direct remove and the dangling
2056        // sweep together leave the slab clean of these sessions.
2057        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    /// Order sessions to shut down, check that they are all down
2070    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            // self.block_channel();
2110            let id = self
2111                .shutting_down
2112                .take()
2113                .expect("should have shut down correctly"); // panicking here makes sense actually
2114
2115            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        // Run the lease janitor here too, not only from the run loop: the
2177        // same `now` reading is threaded into `lease_apply` below
2178        // (`SetMetricDetail` arm) so the whole lease lifecycle for this
2179        // `notify` call is anchored to one clock read, and a query never
2180        // reports a lease the janitor is already due to retire.
2181        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            // Runtime cardinality lease verb — apply, renew, or clear a lease
2212            // on this worker's `Aggregator`. The lease bumps `effective` to
2213            // `max(configured, max(active leases))`; expiry runs on the polled
2214            // janitor, `tick_metric_detail_leases`. Master-side aggregation
2215            // into `MetricDetailStatus` lands in a follow-up; for now the
2216            // worker acks with a bare OK so the existing `worker_request`
2217            // fan-out path can collect.
2218            Some(RequestType::SetMetricDetail(req)) => {
2219                // Master populates the peer binding from the connecting
2220                // `ClientSession` before fan-out (`bin/src/command/
2221                // requests.rs::worker_request`). A pre-binding caller or a
2222                // platform without `SO_PEERCRED` yields `PeerBinding::default()`
2223                // — clears against that lease are accepted from anyone, per
2224                // the proto contract on `SetMetricDetail.peer_pid`.
2225                let presented_binding = crate::metrics::PeerBinding {
2226                    pid: req.peer_pid,
2227                    // Master sends Crockford-base32 ULIDs (`Ulid::to_string`);
2228                    // accept those, with a fallback hex parse for callers that
2229                    // happen to send `0x…` form. A failed parse degrades to
2230                    // `None` — the lease store treats that as "binding
2231                    // unknown" per the proto contract.
2232                    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                    // Defense-in-depth: the master pre-validates `client_id`
2241                    // length at the dispatch site, but worker IPC is not
2242                    // master-only — fuzz harnesses, serial_test-flagged
2243                    // integration tests, and future internal callers can
2244                    // issue an oversized clear directly. Mirror the apply
2245                    // path's `ClientIdTooLong` arm so an unbounded HashMap
2246                    // lookup is never driven by an operator-supplied string
2247                    // here either. The reason string echoes the byte length
2248                    // but not the operator bytes themselves (symmetric with
2249                    // the audit-column-smuggling guard on the apply path).
2250                    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                    // Capture transition fields + post-clear snapshot
2261                    // before releasing the borrow so we can emit an
2262                    // Event after AND build the WorkerMetricDetailStatus
2263                    // payload that the master folds into
2264                    // `MetricDetailStatus.workers[<worker_id>]`. Without
2265                    // this payload the master used its own view as a
2266                    // stand-in for the worker's per-aggregator state.
2267                    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                            // Silent no-op: no lease existed for that
2298                            // id. The worker's state is unchanged so
2299                            // previous_effective == effective.
2300                            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                            // Do NOT echo `req.client_id` here: the operator-
2312                            // supplied bytes flow back through the master's
2313                            // worker→reason aggregation into the audit line's
2314                            // `reason=` column, which is sanitised for control
2315                            // bytes only. The dedicated `lease_id=` audit
2316                            // column already carries the operator string
2317                            // through `sanitize_for_audit_kv`, so re-embedding
2318                            // it here would let a value containing `,` or `=`
2319                            // forge a sibling KV pair against SIEM consumers
2320                            // that split on `, key=value`.
2321                            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                        // Operator-supplied `client_id` is intentionally
2334                        // omitted from the reason string: the dedicated
2335                        // `lease_id=` audit column carries it through the
2336                        // strict KV sanitiser. See the matching comment on
2337                        // the `Unauthorized` arm above for the column-
2338                        // smuggling rationale.
2339                        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                // Bound the worst case BEFORE we touch the aggregator: the
2357                // proto contract on `SetMetricDetail.ttl_seconds` says the
2358                // worker rejects values larger than `LEASE_TTL_MAX` so a
2359                // stuck operator-side renewer (or a buggy third-party client)
2360                // cannot lock the worker into elevated cardinality. The
2361                // `Aggregator::lease_apply` clamp is still in place as a
2362                // defence-in-depth net for code paths that bypass this
2363                // dispatch (proto fuzzing, future internal callers).
2364                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                    // The default fits in a u32 by construction
2377                    // (LEASE_TTL_DEFAULT = 60 s); the lossy `as u32` cast
2378                    // is replaced with a checked conversion so any
2379                    // future tweak past `u32::MAX` seconds (≈ 136 years)
2380                    // can't silently truncate. Falls through to 60 s on
2381                    // the theoretical overflow path.
2382                    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                        // Same audit-column-smuggling guard as the
2423                        // `Unauthorized` and missing-detail arms: the
2424                        // operator-supplied `client_id` is already rendered
2425                        // safely through the strict KV sanitiser in the
2426                        // audit envelope's `lease_id=` column, so we keep
2427                        // it out of the reason string.
2428                        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                        // Unreachable in the normal flow: the dispatch-time
2439                        // gate above already rejected ttl > LEASE_TTL_MAX.
2440                        // Surface explicitly so any future bypass (proto
2441                        // fuzzing, internal callers) fails loud rather
2442                        // than silently capping the lessor's intent.
2443                        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                        // A renewal arrived against an existing lease whose
2453                        // apply-time peer binding does not match the
2454                        // presented one. The `client_id` is intentionally
2455                        // omitted from the error string — the audit-log
2456                        // row already carries it in the dedicated
2457                        // `lease_id` column, and echoing it here would
2458                        // route operator-controlled bytes through the
2459                        // freeform reason field.
2460                        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                // there should not be any errors as it was already parsed by the main process
2475                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                // Disabling the feature on the fly should not leave
2514                // stale `(cluster, ip)` entries behind: drain the
2515                // bookkeeping so a re-enable starts from a clean slate
2516                // and `cluster_ip_at_limit` does not consult dead state.
2517                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                // Disabling the subnet limiter on the fly should not
2543                // leave stale `(cluster, subnet)` entries behind: drain
2544                // its bookkeeping so a re-enable starts from a clean
2545                // slate. The per-IP buckets are deliberately untouched —
2546                // the two limiters are independent.
2547                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                // The prefixes ride along because they are boot-time
2578                // only: this reply is the sole runtime surface that can
2579                // tell an operator what the live cap is a cap ON.
2580                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            // A filter carrying a fingerprint is answered from this worker's
2612            // own `ConfigState` rather than from its resolver. Note that
2613            // `get_certificates` tests its domain arm FIRST, so a request
2614            // carrying BOTH filters is answered by exact SAN equality on the
2615            // domain and the fingerprint is ignored — the one path where
2616            // `--domain` is not the trie lookup `query_certificate_for_domain`
2617            // and `bin`'s `certificates_serving_domain` both perform
2618            // (sozu#1383). `QueryCertificatesFilters` documents that the two
2619            // filters do not compound; `doc/configure_cli.md` says to pass one
2620            // at a time.
2621            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            // if all certificates are queried, or filtered by domain name,
2643            // the request will be handled by the https proxy
2644            _other_request => {}
2645        }
2646        self.notify_proxys(message);
2647    }
2648
2649    pub fn notify_proxys(&mut self, request: WorkerRequest) {
2650        // Read before the dispatch below: `ConfigState::remove_cluster`
2651        // (`command/src/state.rs`) removes these objects from the state, and
2652        // the `RemoveCluster` arm removes them from the proxies.
2653        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        if let Err(e) = self.config_state.dispatch(&request.content) {
2661            if self
2662                .config_state
2663                .removed_with_its_cluster(&request.content, &e)
2664            {
2665                // The object went with its cluster (`RemoveCluster` cascades),
2666                // for instance when a main process that predates the cascade
2667                // follows its `RemoveCluster` with the removal of each object.
2668                // There is nothing left to remove: answer ok rather than fail
2669                // on a missing route.
2670                debug!("{} already removed with its cluster: {}", request.id, e);
2671                push_queue(WorkerResponse::ok(request.id));
2672                return;
2673            }
2674            if is_frontend_removal(&request.content)
2675                && matches!(e, StateError::NotFound { .. } | StateError::NoChange)
2676            {
2677                // The state holds no frontend of the named cluster under
2678                // this identity: `NotFound` when it holds none of that
2679                // cluster at all (or the route key is another cluster's),
2680                // `NoChange` when the TCP or UDP frontends it holds for that
2681                // cluster do not include this one. The proxies look a
2682                // frontend up by address, hostname and path, or by listener,
2683                // whatever cluster owns it: applying the removal to them
2684                // could evict another cluster's route or listener mapping.
2685                // Refuse it without touching them.
2686                push_queue(WorkerResponse::error(
2687                    request.id,
2688                    format!("Could not execute order on config state: {e}"),
2689                ));
2690                return;
2691            }
2692            error!("Could not execute order on config state: {}", e);
2693        }
2694
2695        let req_id = request.id.clone();
2696
2697        match request.content.request_type {
2698            Some(RequestType::AddCluster(ref cluster)) => {
2699                // Mirror the master-side ConfigState::add_cluster check so
2700                // off-channel paths (TOML reload, SaveState/LoadState, direct
2701                // API) that smuggle a malformed `cluster.health_check` cannot
2702                // arm the worker's BackendList::set_health_check_config with
2703                // a CRLF/NUL/C0 URI or zero thresholds. The SetHealthCheck
2704                // handler below already runs the same validation; this is the
2705                // AddCluster mirror.
2706                if let Some(hc) = cluster.health_check.as_ref()
2707                    && let Err(reason) = sozu_command::config::validate_health_check_config(hc)
2708                {
2709                    push_queue(worker_response_error(req_id, reason));
2710                    return;
2711                }
2712                // Same mirror for the shuffle-sharding knobs (#524): a worker
2713                // never arms a shard the master would have refused.
2714                if let Err(reason) = sozu_command::config::validate_shuffle_sharding(
2715                    cluster.shard_percent,
2716                    cluster.shard_min_backends,
2717                    cluster.shard_mode,
2718                ) {
2719                    push_queue(worker_response_error(req_id, reason));
2720                    return;
2721                }
2722                if let Some(Err(reason)) = cluster
2723                    .max_connection_attempts
2724                    .map(sozu_command::config::validate_max_connection_attempts)
2725                {
2726                    push_queue(worker_response_error(req_id, reason));
2727                    return;
2728                }
2729                self.add_cluster(cluster);
2730                // An upsert without a health check clears it like
2731                // `RemoveHealthCheck`: `add_cluster` already reset the
2732                // backends, drop the probes still in flight too (#1811).
2733                if cluster.health_check.is_none() {
2734                    self.health_checker.remove_cluster(&cluster.cluster_id);
2735                }
2736                // Re-arm the metric drain tombstone in case this cluster id
2737                // was previously removed — without this the drain would
2738                // continue dropping every emission for the resurrected
2739                // cluster. Idempotent on a fresh id.
2740                METRICS.with(|metrics| {
2741                    (*metrics.borrow_mut()).add_cluster(&cluster.cluster_id);
2742                });
2743                //not returning because the message must still be handled by each proxy
2744            }
2745            Some(RequestType::RemoveCluster(ref cluster_id)) => {
2746                self.remove_cluster_objects(&req_id, cluster_id, cluster_objects);
2747                self.remove_health_check_state(cluster_id);
2748                METRICS.with(|metrics| {
2749                    (*metrics.borrow_mut()).remove_cluster(cluster_id);
2750                });
2751                //not returning because the message must still be handled by each proxy
2752            }
2753            Some(RequestType::SetHealthCheck(ref set)) => {
2754                if let Err(reason) = sozu_command::config::validate_health_check_config(&set.config)
2755                {
2756                    push_queue(worker_response_error(req_id, reason));
2757                    return;
2758                }
2759                self.backends
2760                    .borrow_mut()
2761                    .set_health_check_config(&set.cluster_id, Some(set.config.to_owned()));
2762                push_queue(WorkerResponse::ok(req_id));
2763                return;
2764            }
2765            Some(RequestType::RemoveHealthCheck(ref cluster_id)) => {
2766                self.remove_health_check_state(cluster_id);
2767                push_queue(WorkerResponse::ok(req_id));
2768                return;
2769            }
2770            Some(RequestType::AddBackend(ref backend)) => {
2771                push_queue(self.add_backend(&req_id, backend));
2772                return;
2773            }
2774            Some(RequestType::RemoveBackend(ref remove_backend)) => {
2775                push_queue(self.remove_backend(&req_id, remove_backend));
2776                return;
2777            }
2778            _ => {}
2779        };
2780
2781        let proxy_destinations = request.content.get_destinations();
2782        let mut notify_response = None;
2783        if proxy_destinations.to_http_proxy {
2784            notify_response = Some(self.http.borrow_mut().notify(request.clone()));
2785        }
2786        if proxy_destinations.to_https_proxy {
2787            let http_proxy_response = self.https.borrow_mut().notify(request.clone());
2788            if http_proxy_response.is_failure() || notify_response.is_none() {
2789                notify_response = Some(http_proxy_response);
2790            }
2791        }
2792        if proxy_destinations.to_tcp_proxy {
2793            let tcp_proxy_response = self.tcp.borrow_mut().notify(request.clone());
2794            if tcp_proxy_response.is_failure() || notify_response.is_none() {
2795                notify_response = Some(tcp_proxy_response);
2796            }
2797        }
2798        if proxy_destinations.to_udp_proxy {
2799            let udp_proxy_response = self.udp.borrow_mut().notify(request.clone());
2800            if udp_proxy_response.is_failure() || notify_response.is_none() {
2801                notify_response = Some(udp_proxy_response);
2802            }
2803        }
2804        if let Some(response) = notify_response {
2805            push_queue(response);
2806        }
2807
2808        match request.content.request_type {
2809            // special case for adding listeners, because we need to register a listener
2810            Some(RequestType::AddHttpListener(listener)) => {
2811                push_queue(self.notify_add_http_listener(&req_id, listener));
2812            }
2813            Some(
2814                RequestType::AddHttpsListener(listener)
2815                | RequestType::AddHttpsListenerWithClientAuth(listener),
2816            ) => {
2817                push_queue(self.notify_add_https_listener(&req_id, listener));
2818            }
2819            Some(RequestType::AddTcpListener(listener)) => {
2820                push_queue(self.notify_add_tcp_listener(&req_id, listener));
2821            }
2822            Some(RequestType::AddUdpListener(listener)) => {
2823                push_queue(self.notify_add_udp_listener(&req_id, listener));
2824            }
2825            Some(RequestType::UpdateHttpListener(patch)) => {
2826                push_queue(self.notify_update_http_listener(&req_id, patch));
2827            }
2828            Some(
2829                RequestType::UpdateHttpsListener(patch)
2830                | RequestType::UpdateHttpsListenerWithClientAuth(patch),
2831            ) => {
2832                push_queue(self.notify_update_https_listener(&req_id, patch));
2833            }
2834            Some(RequestType::UpdateTcpListener(patch)) => {
2835                push_queue(self.notify_update_tcp_listener(&req_id, patch));
2836            }
2837            Some(RequestType::UpdateUdpListener(patch)) => {
2838                push_queue(self.notify_update_udp_listener(&req_id, patch));
2839            }
2840            Some(RequestType::RemoveListener(ref remove)) => {
2841                debug!("{} remove {:?} listener {:?}", req_id, remove.proxy, remove);
2842                // We only remove a listener that was previously added, so the
2843                // base count is at least 1 — the subtraction cannot underflow.
2844                debug_assert!(
2845                    self.base_sessions_count > 0,
2846                    "removing a listener with base_sessions_count == 0 would underflow"
2847                );
2848                self.base_sessions_count -= 1;
2849                let listener_type = ListenerType::try_from(remove.proxy);
2850                // `RemoveListener` is the sole release point of the slab slot
2851                // `AddListener` reserved (see `reserve_listen_token`): no
2852                // proxy's `remove_listener` touches the session slab, so
2853                // without this the slot leaks one slab key per add/remove
2854                // cycle — and `base_sessions_count`, decremented just above,
2855                // drifts below the number of reserved slots. Read the token
2856                // BEFORE the proxy drops the listener that holds it.
2857                let address = remove.listener_key();
2858                let listen_token = match listener_type {
2859                    Ok(ListenerType::Http) => self.http.borrow().listener_token(&address),
2860                    Ok(ListenerType::Https) => self.https.borrow().listener_token(&address),
2861                    Ok(ListenerType::Tcp) => self.tcp.borrow().listener_token(&address),
2862                    Ok(ListenerType::Udp) => self.udp.borrow().listener_token(&address),
2863                    Err(_) => None,
2864                };
2865                let response = match listener_type {
2866                    Ok(ListenerType::Http) => self.http.borrow_mut().notify(request),
2867                    Ok(ListenerType::Https) => self.https.borrow_mut().notify(request),
2868                    Ok(ListenerType::Tcp) => self.tcp.borrow_mut().notify(request),
2869                    Ok(ListenerType::Udp) => self.udp.borrow_mut().notify(request),
2870                    Err(_) => WorkerResponse::error(req_id, "Wrong variant ListenerType"),
2871                };
2872                if let Some(token) = listen_token {
2873                    self.sessions.borrow_mut().slab.try_remove(token.0);
2874                    // Same rule as the deactivate arms: a listen token with no
2875                    // slab slot must not stay queued for a deferred accept.
2876                    self.accept_ready.remove(&ListenToken(token.0));
2877                    info!("released listen token {:?}", token);
2878                }
2879                push_queue(response);
2880            }
2881            Some(RequestType::ActivateListener(ref activate)) => {
2882                push_queue(self.notify_activate_listener(&req_id, activate));
2883            }
2884            Some(RequestType::DeactivateListener(ref deactivate)) => {
2885                push_queue(self.notify_deactivate_listener(&req_id, deactivate));
2886            }
2887            _other_request => {}
2888        };
2889    }
2890
2891    fn add_cluster(&mut self, cluster: &Cluster) {
2892        let mut backends = self.backends.borrow_mut();
2893        backends.set_load_balancing_policy_for_cluster(
2894            &cluster.cluster_id,
2895            LoadBalancingAlgorithms::try_from(cluster.load_balancing).unwrap_or_default(),
2896            cluster
2897                .load_metric
2898                .and_then(|n| LoadMetric::try_from(n).ok()),
2899        );
2900        backends.set_shuffle_sharding_for_cluster(
2901            &cluster.cluster_id,
2902            ShuffleSharding::from_cluster(
2903                cluster.shard_percent,
2904                cluster.shard_min_backends,
2905                cluster.shard_mode,
2906            ),
2907        );
2908        backends.set_health_check_config(&cluster.cluster_id, cluster.health_check.to_owned());
2909        backends.set_cluster_http2(&cluster.cluster_id, cluster.http2.unwrap_or(false));
2910    }
2911
2912    fn add_backend(&mut self, req_id: &str, add_backend: &AddBackend) -> WorkerResponse {
2913        let new_backend = Backend::new(
2914            &add_backend.backend_id,
2915            add_backend.address.into(),
2916            add_backend.sticky_id.clone(),
2917            add_backend.load_balancing_parameters,
2918            add_backend.backup,
2919        );
2920        self.backends
2921            .borrow_mut()
2922            .add_backend(&add_backend.cluster_id, new_backend);
2923
2924        WorkerResponse::ok(req_id)
2925    }
2926
2927    /// Remove from the proxies and the `BackendMap` the frontends and backends
2928    /// of a cluster being removed, `requests` being the removal orders
2929    /// `ConfigState::cluster_removal_requests` (`command/src/state.rs`)
2930    /// returned for it. Each object goes through the removal its own order
2931    /// takes, so the router, the tags, the TCP and UDP listener to cluster
2932    /// mappings and the per-backend metrics are cleaned the same way.
2933    ///
2934    /// A session already established keeps the backend connection it holds
2935    /// and drains on it; only new requests and connections stop routing. A
2936    /// failure is logged and does not stop the others: the `RemoveCluster`
2937    /// answer stays the single response the main process waits for.
2938    fn remove_cluster_objects(&mut self, req_id: &str, cluster_id: &str, requests: Vec<Request>) {
2939        for request in requests {
2940            let result = match request.request_type {
2941                Some(RequestType::RemoveHttpFrontend(front)) => {
2942                    self.http.borrow_mut().remove_http_frontend(front)
2943                }
2944                Some(RequestType::RemoveHttpsFrontend(front)) => self
2945                    .https
2946                    .borrow_mut()
2947                    .remove_https_frontend(front)
2948                    .map(|_| ()),
2949                Some(RequestType::RemoveTcpFrontend(front)) => {
2950                    self.tcp.borrow_mut().remove_tcp_front(front)
2951                }
2952                Some(RequestType::RemoveUdpFrontend(front)) => {
2953                    self.udp.borrow_mut().remove_udp_front(front)
2954                }
2955                Some(RequestType::RemoveBackend(backend)) => {
2956                    let response = self.remove_backend(req_id, &backend);
2957                    debug_assert_eq!(
2958                        response.status,
2959                        ResponseStatus::Ok as i32,
2960                        "Server::remove_backend always answers ok"
2961                    );
2962                    Ok(())
2963                }
2964                other => {
2965                    debug_assert!(
2966                        false,
2967                        "cluster_removal_requests only returns frontend and backend removals, got {other:?}"
2968                    );
2969                    Ok(())
2970                }
2971            };
2972            if let Err(error) = result {
2973                warn!(
2974                    "{} removing cluster {}: could not remove one of its objects: {}",
2975                    req_id, cluster_id, error
2976                );
2977            }
2978        }
2979        self.backends.borrow_mut().remove_cluster(cluster_id);
2980        debug_assert!(
2981            !self.backends.borrow().backends.contains_key(cluster_id),
2982            "a removed cluster must leave no backend list behind"
2983        );
2984    }
2985
2986    /// Stop health checking `cluster_id`: drop its in-flight probes, then
2987    /// forget its configuration and reset every backend it marked DOWN, since
2988    /// no probe will ever mark them UP again (#1811). Dropping the probes first
2989    /// keeps one already in flight from marking a backend DOWN after the reset.
2990    fn remove_health_check_state(&mut self, cluster_id: &str) {
2991        self.health_checker.remove_cluster(cluster_id);
2992        self.backends
2993            .borrow_mut()
2994            .set_health_check_config(cluster_id, None);
2995    }
2996
2997    fn remove_backend(&mut self, req_id: &str, backend: &RemoveBackend) -> WorkerResponse {
2998        let address = backend.address.into();
2999        // Runtime removal keys on `(backend_id, address)`, as `ConfigState`
3000        // does: another id at the same address stays (#1821). The metrics
3001        // layer is id-keyed, so one row aggregates every entry carrying the
3002        // id: it is dropped only once no entry of that id remains at any
3003        // address, and is still tidied when the backend was already gone,
3004        // clearing any orphan row.
3005        let id_still_live = {
3006            let mut backends = self.backends.borrow_mut();
3007            backends.remove_backend(&backend.cluster_id, &backend.backend_id, &address);
3008            backends.has_backend_id(&backend.cluster_id, &backend.backend_id)
3009        };
3010        if !id_still_live {
3011            METRICS.with(|metrics| {
3012                (*metrics.borrow_mut()).remove_backend(&backend.cluster_id, &backend.backend_id);
3013            });
3014        }
3015
3016        WorkerResponse::ok(req_id)
3017    }
3018
3019    fn notify_add_http_listener(
3020        &mut self,
3021        req_id: &str,
3022        listener: HttpListenerConfig,
3023    ) -> WorkerResponse {
3024        debug!("{} add http listener {:?}", req_id, listener);
3025
3026        if self.sessions.borrow().at_capacity() {
3027            return worker_response_error(req_id, "session list is full, cannot add a listener");
3028        }
3029
3030        let mut session_manager = self.sessions.borrow_mut();
3031        // The vacant entry's key is free now and becomes the listener's token.
3032        let slab_before = session_manager.slab.len();
3033        debug_assert!(
3034            !session_manager
3035                .slab
3036                .contains(session_manager.slab.vacant_key()),
3037            "the next vacant slab key must be free before insertion"
3038        );
3039        let entry = session_manager.slab.vacant_entry();
3040        let token = Token(entry.key());
3041
3042        match self.http.borrow_mut().add_listener(listener, token) {
3043            Ok(_token) => {
3044                entry.insert(Rc::new(RefCell::new(ListenSession {
3045                    protocol: Protocol::HTTPListen,
3046                })));
3047                // The listener session occupies exactly the token's slab key,
3048                // and the slab grew by exactly one slot.
3049                debug_assert!(
3050                    session_manager.slab.contains(token.0),
3051                    "listener insert must occupy the token's slab key"
3052                );
3053                debug_assert_eq!(
3054                    session_manager.slab.len(),
3055                    slab_before + 1,
3056                    "adding a listener must occupy exactly one slab slot"
3057                );
3058                self.base_sessions_count += 1;
3059                WorkerResponse::ok(req_id)
3060            }
3061            Err(e) => worker_response_error(req_id, format!("Could not add HTTP listener: {e}")),
3062        }
3063    }
3064
3065    fn notify_add_https_listener(
3066        &mut self,
3067        req_id: &str,
3068        listener: HttpsListenerConfig,
3069    ) -> WorkerResponse {
3070        debug!("{} add https 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();
3078        debug_assert!(
3079            !session_manager
3080                .slab
3081                .contains(session_manager.slab.vacant_key()),
3082            "the next vacant slab key must be free before insertion"
3083        );
3084        let entry = session_manager.slab.vacant_entry();
3085        let token = Token(entry.key());
3086
3087        match self
3088            .https
3089            .borrow_mut()
3090            .add_listener(listener.clone(), token)
3091        {
3092            Ok(_token) => {
3093                entry.insert(Rc::new(RefCell::new(ListenSession {
3094                    protocol: Protocol::HTTPSListen,
3095                })));
3096                debug_assert!(
3097                    session_manager.slab.contains(token.0),
3098                    "listener insert must occupy the token's slab key"
3099                );
3100                debug_assert_eq!(
3101                    session_manager.slab.len(),
3102                    slab_before + 1,
3103                    "adding a listener must occupy exactly one slab slot"
3104                );
3105                self.base_sessions_count += 1;
3106                WorkerResponse::ok(req_id)
3107            }
3108            Err(e) => worker_response_error(req_id, format!("Could not add HTTPS listener: {e}")),
3109        }
3110    }
3111
3112    fn notify_add_tcp_listener(
3113        &mut self,
3114        req_id: &str,
3115        listener: CommandTcpListener,
3116    ) -> WorkerResponse {
3117        debug!("{} add tcp listener {:?}", req_id, listener);
3118
3119        if self.sessions.borrow().at_capacity() {
3120            return worker_response_error(req_id, "session list is full, cannot add a listener");
3121        }
3122
3123        let mut session_manager = self.sessions.borrow_mut();
3124        let slab_before = session_manager.slab.len();
3125        debug_assert!(
3126            !session_manager
3127                .slab
3128                .contains(session_manager.slab.vacant_key()),
3129            "the next vacant slab key must be free before insertion"
3130        );
3131        let entry = session_manager.slab.vacant_entry();
3132        let token = Token(entry.key());
3133
3134        match self.tcp.borrow_mut().add_listener(listener, token) {
3135            Ok(_token) => {
3136                entry.insert(Rc::new(RefCell::new(ListenSession {
3137                    protocol: Protocol::TCPListen,
3138                })));
3139                debug_assert!(
3140                    session_manager.slab.contains(token.0),
3141                    "listener insert must occupy the token's slab key"
3142                );
3143                debug_assert_eq!(
3144                    session_manager.slab.len(),
3145                    slab_before + 1,
3146                    "adding a listener must occupy exactly one slab slot"
3147                );
3148                self.base_sessions_count += 1;
3149                WorkerResponse::ok(req_id)
3150            }
3151            Err(e) => worker_response_error(req_id, format!("Could not add TCP listener: {e}")),
3152        }
3153    }
3154
3155    fn notify_add_udp_listener(
3156        &mut self,
3157        req_id: &str,
3158        listener: CommandUdpListener,
3159    ) -> WorkerResponse {
3160        debug!("{} add udp listener {:?}", req_id, listener);
3161
3162        if self.sessions.borrow().at_capacity() {
3163            return worker_response_error(req_id, "session list is full, cannot add a listener");
3164        }
3165
3166        let mut session_manager = self.sessions.borrow_mut();
3167        let entry = session_manager.slab.vacant_entry();
3168        let token = Token(entry.key());
3169
3170        match self.udp.borrow_mut().add_listener(listener, token) {
3171            Ok(_token) => {
3172                entry.insert(Rc::new(RefCell::new(ListenSession {
3173                    protocol: Protocol::UDPListen,
3174                })));
3175                self.base_sessions_count += 1;
3176                WorkerResponse::ok(req_id)
3177            }
3178            Err(e) => worker_response_error(req_id, format!("Could not add UDP listener: {e}")),
3179        }
3180    }
3181
3182    fn notify_update_udp_listener(
3183        &mut self,
3184        req_id: &str,
3185        patch: UpdateUdpListenerConfig,
3186    ) -> WorkerResponse {
3187        debug!("{} update udp listener {:?}", req_id, patch.address);
3188        match self.udp.borrow_mut().update_listener(patch) {
3189            Ok(()) => WorkerResponse::ok(req_id),
3190            Err(e) => worker_response_error(req_id, format!("Could not update UDP listener: {e}")),
3191        }
3192    }
3193
3194    fn notify_update_http_listener(
3195        &mut self,
3196        req_id: &str,
3197        patch: UpdateHttpListenerConfig,
3198    ) -> WorkerResponse {
3199        debug!("{} update http listener {:?}", req_id, patch.address);
3200        match self.http.borrow_mut().update_listener(patch) {
3201            Ok(()) => WorkerResponse::ok(req_id),
3202            Err(e) => worker_response_error(req_id, format!("Could not update HTTP listener: {e}")),
3203        }
3204    }
3205
3206    fn notify_update_https_listener(
3207        &mut self,
3208        req_id: &str,
3209        patch: UpdateHttpsListenerConfig,
3210    ) -> WorkerResponse {
3211        debug!("{} update https listener {:?}", req_id, patch.address);
3212        match self.https.borrow_mut().update_listener(patch) {
3213            Ok(()) => WorkerResponse::ok(req_id),
3214            Err(e) => {
3215                worker_response_error(req_id, format!("Could not update HTTPS listener: {e}"))
3216            }
3217        }
3218    }
3219
3220    fn notify_update_tcp_listener(
3221        &mut self,
3222        req_id: &str,
3223        patch: UpdateTcpListenerConfig,
3224    ) -> WorkerResponse {
3225        debug!("{} update tcp listener {:?}", req_id, patch.address);
3226        match self.tcp.borrow_mut().update_listener(patch) {
3227            Ok(()) => WorkerResponse::ok(req_id),
3228            Err(e) => worker_response_error(req_id, format!("Could not update TCP listener: {e}")),
3229        }
3230    }
3231
3232    /// Re-arm a deactivated listener's slab slot with the inert
3233    /// [`ListenSession`] placeholder that `AddListener` first installed there.
3234    ///
3235    /// DESIGN (2026-09-18) — a listener owns exactly one slab slot for its
3236    /// whole `AddListener` -> `RemoveListener` lifetime, activated or not.
3237    /// The alternative was to stop retaining the token across a deactivate and
3238    /// allocate a fresh slab key on every activate; it was rejected because
3239    /// the token is the *identity* of a listener in all four proxies — their
3240    /// `listeners` maps (plus UDP's `managers`, `cluster_for_listener` and
3241    /// `listener_sessions`) are keyed by it, `give_back_listener` hands the
3242    /// same token back and `activate()` returns it again — so a fresh key
3243    /// would mean rekeying five maps across four proxies on every activate,
3244    /// far more machinery than the defect warrants.
3245    ///
3246    /// Reserving the slot is what makes the retained token honest, and it
3247    /// closes both halves of the defect at once:
3248    ///
3249    /// * the slot is never vacant, so `Server::ready` keeps dispatching the
3250    ///   listen token after a deactivate/activate cycle. Freeing it left the
3251    ///   socket re-registered under a token with no slab entry — `ready()`
3252    ///   silently drops the event, and the reactivated listener was deaf
3253    ///   while `ActivateListener` had reported success;
3254    /// * the slab can never hand that key to another session, so the UDP
3255    ///   activate path can no longer overwrite a live session with its
3256    ///   listener session. Mode 2 is gone by construction, not by check.
3257    ///
3258    /// `RemoveListener` is the sole release point (see `notify_proxys`). No
3259    /// proxy's `remove_listener` touches the session slab, so before this
3260    /// change the deactivate arms were the ONLY code that ever freed a
3261    /// listener's slot — removing a listener that had not been deactivated
3262    /// first already stranded its slab key (and, for UDP, the last
3263    /// `UdpListenerSession` reference, hence the listener's open `UdpSocket`).
3264    /// Reserving the slot here makes that release mandatory rather than
3265    /// incidental.
3266    fn reserve_listen_token(&mut self, token: Token, protocol: Protocol) {
3267        let mut sessions = self.sessions.borrow_mut();
3268        match sessions.slab.get_mut(token.0) {
3269            Some(slot) => {
3270                *slot = Rc::new(RefCell::new(ListenSession { protocol }));
3271                info!("reserved listen token {:?} for {:?}", token, protocol);
3272            }
3273            // Unreachable while the lifetime above holds: `AddListener`
3274            // inserted the slot and only `RemoveListener` frees it. Loud
3275            // rather than silent, because the listener is deaf if it happens.
3276            None => error!(
3277                "listen token {:?} ({:?}) has no slab slot to reserve; the listener would not be reachable if reactivated",
3278                token, protocol
3279            ),
3280        }
3281    }
3282
3283    /// Say what the initial state left unclaimed in the SCM table.
3284    ///
3285    /// A descriptor is kept whenever no listener exists at its address yet,
3286    /// because an `AddListener` + `ActivateListener` pair can still arrive and
3287    /// adopt it — `UpgradeWorkerTask` (`bin/src/command/upgrade.rs`) scatters
3288    /// `generate_activate_requests()` only after `Server::new` has returned, so
3289    /// there is no point during startup at which a leftover is provably
3290    /// unclaimable. Retention is therefore open-ended, and the one thing this
3291    /// worker can do is make it visible.
3292    ///
3293    /// An address that already has a listener is the expected shape (an initial
3294    /// state carrying the listeners inactive, activated straight afterwards) and
3295    /// is reported at `info`. An address with no listener at all is the one an
3296    /// operator needs to see: nothing in this worker's state mentions it, so the
3297    /// descriptor is the retiring worker's listening socket, still bound with
3298    /// `SO_REUSEPORT` and registered with no event loop, which is expected to
3299    /// keep taking a share of new connections that nothing accepts. (That
3300    /// expectation follows from `SO_REUSEPORT` load-balancing semantics; nothing
3301    /// here measures it.) That is reported at `warn`.
3302    fn report_unclaimed_inherited_sockets(&self) {
3303        let Some(scm_listeners) = self.scm_listeners.as_ref() else {
3304            return;
3305        };
3306        let leftovers: [(&str, &Vec<(ListenerKey, RawFd)>); 4] = [
3307            ("HTTP", &scm_listeners.http),
3308            ("HTTPS", &scm_listeners.tls),
3309            ("TCP", &scm_listeners.tcp),
3310            ("UDP", &scm_listeners.udp),
3311        ];
3312        for (label, table) in leftovers {
3313            for (address, fd) in table {
3314                let has_listener = match label {
3315                    "HTTP" => self.http.borrow().listener_token(address).is_some(),
3316                    "HTTPS" => self.https.borrow().listener_token(address).is_some(),
3317                    "TCP" => self.tcp.borrow().listener_token(address).is_some(),
3318                    _ => self.udp.borrow().listener_token(address).is_some(),
3319                };
3320                if has_listener {
3321                    info!(
3322                        "inherited {} listening socket for {} (fd {}) is held for an \
3323                         ActivateListener this worker has not processed yet",
3324                        label, address, fd
3325                    );
3326                } else {
3327                    warn!(
3328                        "inherited {} listening socket for {} (fd {}) is held with no listener \
3329                         at that address: until an AddListener plus ActivateListener for {} \
3330                         adopts it, it is expected to keep taking a share of new connections \
3331                         that nothing accepts (inferred from SO_REUSEPORT semantics, not \
3332                         measured here)",
3333                        label, address, fd, address
3334                    );
3335                }
3336            }
3337        }
3338    }
3339
3340    fn notify_activate_listener(
3341        &mut self,
3342        req_id: &str,
3343        activate: &ActivateListener,
3344    ) -> WorkerResponse {
3345        use crate::InheritedSocketFate;
3346
3347        debug!(
3348            "{} activate {:?} listener {:?}",
3349            req_id, activate.proxy, activate
3350        );
3351
3352        let address = activate.listener_key();
3353
3354        match ListenerType::try_from(activate.proxy) {
3355            Ok(ListenerType::Http) => {
3356                let listener = match self.http.borrow().inherited_socket_fate(&address) {
3357                    InheritedSocketFate::Adopted => self
3358                        .scm_listeners
3359                        .as_mut()
3360                        .and_then(|listeners| listeners.get_http(&address))
3361                        // SAFETY: `fd` was just received from the supervisor via SCM_RIGHTS
3362                        // (see `command/src/scm_socket.rs`) and is not owned elsewhere — the
3363                        // `Listeners` table removes it on `get_http`. Ownership transfers to
3364                        // the mio wrapper, whose `Drop` closes the descriptor.
3365                        .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3366                    InheritedSocketFate::Refused => {
3367                        discard_inherited_socket::<MioTcpListener>(
3368                            self.scm_listeners
3369                                .as_mut()
3370                                .and_then(|listeners| listeners.get_http(&address)),
3371                            &address,
3372                            "HTTP",
3373                        );
3374                        None
3375                    }
3376                    // Left in the SCM table on purpose: a later `AddListener` +
3377                    // `ActivateListener` for this address can still adopt it.
3378                    InheritedSocketFate::Unclaimed => None,
3379                };
3380
3381                let activated_token = self.http.borrow_mut().activate_listener(&address, listener);
3382                match activated_token {
3383                    Ok(token) => {
3384                        self.accept(ListenToken(token.0), Protocol::HTTPListen);
3385                        WorkerResponse::ok(req_id)
3386                    }
3387                    Err(activate_error) => worker_response_error(
3388                        req_id,
3389                        format!("Could not activate HTTP listener: {activate_error}"),
3390                    ),
3391                }
3392            }
3393            Ok(ListenerType::Https) => {
3394                let listener = match self.https.borrow().inherited_socket_fate(&address) {
3395                    InheritedSocketFate::Adopted => self
3396                        .scm_listeners
3397                        .as_mut()
3398                        .and_then(|listeners| listeners.get_https(&address))
3399                        // SAFETY: `fd` was just received from the supervisor via SCM_RIGHTS
3400                        // (see `command/src/scm_socket.rs`) and is not owned elsewhere — the
3401                        // `Listeners` table removes it on `get_https`. Ownership transfers to
3402                        // the mio wrapper, whose `Drop` closes the descriptor.
3403                        .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3404                    InheritedSocketFate::Refused => {
3405                        discard_inherited_socket::<MioTcpListener>(
3406                            self.scm_listeners
3407                                .as_mut()
3408                                .and_then(|listeners| listeners.get_https(&address)),
3409                            &address,
3410                            "HTTPS",
3411                        );
3412                        None
3413                    }
3414                    // Left in the SCM table on purpose: a later `AddListener` +
3415                    // `ActivateListener` for this address can still adopt it.
3416                    InheritedSocketFate::Unclaimed => None,
3417                };
3418
3419                let activated_token = self
3420                    .https
3421                    .borrow_mut()
3422                    .activate_listener(&address, listener);
3423                match activated_token {
3424                    Ok(token) => {
3425                        self.accept(ListenToken(token.0), Protocol::HTTPSListen);
3426                        WorkerResponse::ok(req_id)
3427                    }
3428                    Err(activate_error) => worker_response_error(
3429                        req_id,
3430                        format!("Could not activate HTTPS listener: {activate_error}"),
3431                    ),
3432                }
3433            }
3434            Ok(ListenerType::Tcp) => {
3435                let listener = match self.tcp.borrow().inherited_socket_fate(&address) {
3436                    InheritedSocketFate::Adopted => self
3437                        .scm_listeners
3438                        .as_mut()
3439                        .and_then(|listeners| listeners.get_tcp(&address))
3440                        // SAFETY: `fd` was just received from the supervisor via SCM_RIGHTS
3441                        // (see `command/src/scm_socket.rs`) and is not owned elsewhere — the
3442                        // `Listeners` table removes it on `get_tcp`. Ownership transfers to
3443                        // the mio wrapper, whose `Drop` closes the descriptor.
3444                        .map(|fd| unsafe { MioTcpListener::from_raw_fd(fd) }),
3445                    InheritedSocketFate::Refused => {
3446                        discard_inherited_socket::<MioTcpListener>(
3447                            self.scm_listeners
3448                                .as_mut()
3449                                .and_then(|listeners| listeners.get_tcp(&address)),
3450                            &address,
3451                            "TCP",
3452                        );
3453                        None
3454                    }
3455                    // Left in the SCM table on purpose: a later `AddListener` +
3456                    // `ActivateListener` for this address can still adopt it.
3457                    InheritedSocketFate::Unclaimed => None,
3458                };
3459
3460                let listener_token = self.tcp.borrow_mut().activate_listener(&address, listener);
3461                match listener_token {
3462                    Ok(token) => {
3463                        self.accept(ListenToken(token.0), Protocol::TCPListen);
3464                        WorkerResponse::ok(req_id)
3465                    }
3466                    Err(activate_error) => worker_response_error(
3467                        req_id,
3468                        format!("Could not activate TCP listener: {activate_error}"),
3469                    ),
3470                }
3471            }
3472            Ok(ListenerType::Udp) => {
3473                let socket = match self.udp.borrow().inherited_socket_fate(&address) {
3474                    InheritedSocketFate::Adopted => self
3475                        .scm_listeners
3476                        .as_mut()
3477                        .and_then(|listeners| listeners.get_udp(&address))
3478                        // SAFETY: `fd` was just received from the supervisor via
3479                        // SCM_RIGHTS (see `command/src/scm_socket.rs`) and is not
3480                        // owned elsewhere — `Listeners::get_udp` removes it from
3481                        // the table. Ownership transfers to the mio `UdpSocket`
3482                        // wrapper, whose `Drop` closes the descriptor. `O_NONBLOCK`
3483                        // + `SO_REUSE*` are file-description flags preserved across
3484                        // SCM + exec.
3485                        .map(|fd| unsafe { MioUdpSocket::from_raw_fd(fd) }),
3486                    InheritedSocketFate::Refused => {
3487                        discard_inherited_socket::<MioUdpSocket>(
3488                            self.scm_listeners
3489                                .as_mut()
3490                                .and_then(|listeners| listeners.get_udp(&address)),
3491                            &address,
3492                            "UDP",
3493                        );
3494                        None
3495                    }
3496                    // Left in the SCM table on purpose: a later `AddListener` +
3497                    // `ActivateListener` for this address can still adopt it.
3498                    InheritedSocketFate::Unclaimed => None,
3499                };
3500
3501                let activated_token = self.udp.borrow_mut().activate_listener(&address, socket);
3502                match activated_token {
3503                    Ok(token) => {
3504                        // UDP never uses accept()/create_session: replace the
3505                        // `ListenSession` placeholder at the listener token with
3506                        // the real `UdpListenerSession` so the READABLE
3507                        // registration drives `Server::ready`'s generic path
3508                        // into `UdpListenerSession::update_readiness`.
3509                        //
3510                        // Install it once per ACTIVATION, not once per request.
3511                        // `Ok(token)` does not mean this call activated
3512                        // anything: `UdpListener::activate` short-circuits on
3513                        // its own `active` flag and answers `Ok(self.token)` for
3514                        // a listener that is already up. Nothing upstream
3515                        // filters that repeat out either —
3516                        // `ConfigState::activate_listener` sets `active = true`
3517                        // and answers `Ok(())` however often it is asked, so the
3518                        // main process forwards a second
3519                        // `sozu listener udp activate`, and
3520                        // `ConfigState::generate_requests` re-emits
3521                        // `ActivateListener` for every active listener on each
3522                        // state replay.
3523                        //
3524                        // Rebuilding on a repeat would therefore overwrite a
3525                        // LIVE session: the proxy keeps the shared `UdpManager`
3526                        // and its flow table, but the replacement session starts
3527                        // with empty `upstream_sockets` / `upstream_to_flow` /
3528                        // `flow_to_upstream`, so every in-flight flow stops
3529                        // forwarding and its upstream slab slot can never be
3530                        // released (`on_close_flow` reaches it only through
3531                        // `flow_to_upstream`). `close()` is a `ProxySession`
3532                        // method and not `Drop`, so dropping the displaced
3533                        // session runs no teardown on the way out.
3534                        //
3535                        // `has_listener_session` is the precise "already
3536                        // installed" test — `build_session` is its only
3537                        // producer, and deactivate / remove / soft+hard stop are
3538                        // its only consumers — paired with the reserved slab
3539                        // slot still being present, so a slot that vanished
3540                        // still falls through to the invariant-break report
3541                        // below. It deliberately tracks the INSTALLED session
3542                        // and not the listener's `active` flag: the SCM
3543                        // hand-off's `give_back_listeners` clears `active` and
3544                        // takes the socket without removing the session, and
3545                        // skipping the rebuild there is right — the retained
3546                        // session reaches the freshly bound socket through the
3547                        // shared `Rc<RefCell<UdpListener>>`, so its flows
3548                        // survive the re-exec instead of being orphaned. A repeat then answers `ok` having touched
3549                        // nothing: the listener is active and its session is in
3550                        // the slab, which is exactly what the caller asked for,
3551                        // and it matches what the HTTP/HTTPS/TCP arms already
3552                        // answer for their own repeats.
3553                        let session_installed = self.udp.borrow().has_listener_session(token)
3554                            && self.sessions.borrow().slab.contains(token.0);
3555                        if !session_installed {
3556                            // The slot is reserved for the listener's whole
3557                            // lifetime (`reserve_listen_token`), so both failures
3558                            // below are invariant breaks; report them instead of
3559                            // returning `ok` for a listener whose socket is
3560                            // registered under a token `Server::ready` would
3561                            // ignore.
3562                            let session = match self.udp.borrow_mut().build_session(token) {
3563                                Some(session) => session,
3564                                None => {
3565                                    return worker_response_error(
3566                                        req_id,
3567                                        format!(
3568                                            "Could not build the UDP listener session for {address}"
3569                                        ),
3570                                    );
3571                                }
3572                            };
3573                            let mut sessions = self.sessions.borrow_mut();
3574                            match sessions.slab.get_mut(token.0) {
3575                                Some(slot) => *slot = session,
3576                                None => {
3577                                    return worker_response_error(
3578                                        req_id,
3579                                        format!(
3580                                            "UDP listener {address} has no session slot at {token:?}"
3581                                        ),
3582                                    );
3583                                }
3584                            }
3585                        }
3586                        WorkerResponse::ok(req_id)
3587                    }
3588                    Err(activate_error) => worker_response_error(
3589                        req_id,
3590                        format!("Could not activate UDP listener: {activate_error}"),
3591                    ),
3592                }
3593            }
3594            Err(_) => worker_response_error(req_id, "Wrong variant for ListenerType on request"),
3595        }
3596    }
3597
3598    fn notify_deactivate_listener(
3599        &mut self,
3600        req_id: &str,
3601        deactivate: &DeactivateListener,
3602    ) -> WorkerResponse {
3603        debug!(
3604            "{} deactivate {:?} listener {:?}",
3605            req_id, deactivate.proxy, deactivate
3606        );
3607
3608        let address = deactivate.listener_key();
3609
3610        match ListenerType::try_from(deactivate.proxy) {
3611            Ok(ListenerType::Http) => {
3612                let (token, mut listener) =
3613                    match self.http.borrow_mut().give_back_listener(&address) {
3614                        Ok((token, listener)) => (token, listener),
3615                        Err(e) => {
3616                            return worker_response_error(
3617                                req_id,
3618                                format!("Couldn't deactivate HTTP listener at {address}: {e}"),
3619                            );
3620                        }
3621                    };
3622
3623                if let Err(e) = self.poll.registry().deregister(&mut listener) {
3624                    error!(
3625                        "error deregistering HTTP listen socket({:?}): {:?}",
3626                        deactivate, e
3627                    );
3628                }
3629
3630                // The slot stays RESERVED for the deactivated listener — see
3631                // `reserve_listen_token`. It carries the same inert placeholder
3632                // `AddListener` installed, so a later `ActivateListener` finds
3633                // its token still valid and no other session can take the key.
3634                self.reserve_listen_token(token, Protocol::HTTPListen);
3635                // The listen token may still be queued for a deferred accept
3636                // (`ready()` enqueues it whenever the listener is readable but
3637                // `can_accept` is false). The listener has just given its
3638                // socket back, so `handle_remaining_readiness` must not call
3639                // `accept()` on it.
3640                self.accept_ready.remove(&ListenToken(token.0));
3641
3642                if deactivate.to_scm {
3643                    self.unblock_scm_socket();
3644                    let listeners = Listeners {
3645                        http: vec![(address.clone(), listener.as_raw_fd())],
3646                        tls: vec![],
3647                        tcp: vec![],
3648                        udp: vec![],
3649                    };
3650                    info!("sending HTTP listener: {:?}", listeners);
3651                    let res = self.scm.send_listeners(&listeners);
3652
3653                    self.block_scm_socket();
3654
3655                    info!("sent HTTP listener: {:?}", res);
3656                }
3657                WorkerResponse::ok(req_id)
3658            }
3659            Ok(ListenerType::Https) => {
3660                let (token, mut listener) =
3661                    match self.https.borrow_mut().give_back_listener(&address) {
3662                        Ok((token, listener)) => (token, listener),
3663                        Err(e) => {
3664                            return worker_response_error(
3665                                req_id,
3666                                format!("Couldn't deactivate HTTPS listener at {address}: {e}",),
3667                            );
3668                        }
3669                    };
3670                if let Err(e) = self.poll.registry().deregister(&mut listener) {
3671                    error!(
3672                        "error deregistering HTTPS listen socket({:?}): {:?}",
3673                        deactivate, e
3674                    );
3675                }
3676                // See the HTTP arm: the slot stays reserved, and a
3677                // socket-less listener must not stay queued for an accept.
3678                self.reserve_listen_token(token, Protocol::HTTPSListen);
3679                self.accept_ready.remove(&ListenToken(token.0));
3680
3681                if deactivate.to_scm {
3682                    self.unblock_scm_socket();
3683                    let listeners = Listeners {
3684                        http: vec![],
3685                        tls: vec![(address.clone(), listener.as_raw_fd())],
3686                        tcp: vec![],
3687                        udp: vec![],
3688                    };
3689                    info!("sending HTTPS listener: {:?}", listeners);
3690                    let res = self.scm.send_listeners(&listeners);
3691
3692                    self.block_scm_socket();
3693
3694                    info!("sent HTTPS listener: {:?}", res);
3695                }
3696                WorkerResponse::ok(req_id)
3697            }
3698            Ok(ListenerType::Tcp) => {
3699                let (token, mut listener) = match self.tcp.borrow_mut().give_back_listener(&address)
3700                {
3701                    Ok((token, listener)) => (token, listener),
3702                    Err(e) => {
3703                        return worker_response_error(
3704                            req_id,
3705                            format!("Could not deactivate TCP listener at {address}: {e}"),
3706                        );
3707                    }
3708                };
3709
3710                if let Err(e) = self.poll.registry().deregister(&mut listener) {
3711                    error!(
3712                        "error deregistering TCP listen socket({:?}): {:?}",
3713                        deactivate, e
3714                    );
3715                }
3716                // See the HTTP arm: the slot stays reserved, and a
3717                // socket-less listener must not stay queued for an accept.
3718                self.reserve_listen_token(token, Protocol::TCPListen);
3719                self.accept_ready.remove(&ListenToken(token.0));
3720
3721                if deactivate.to_scm {
3722                    self.unblock_scm_socket();
3723                    let listeners = Listeners {
3724                        http: vec![],
3725                        tls: vec![],
3726                        tcp: vec![(address.clone(), listener.as_raw_fd())],
3727                        udp: vec![],
3728                    };
3729                    info!("sending TCP listener: {:?}", listeners);
3730                    let res = self.scm.send_listeners(&listeners);
3731
3732                    self.block_scm_socket();
3733
3734                    info!("sent TCP listener: {:?}", res);
3735                }
3736                WorkerResponse::ok(req_id)
3737            }
3738            Ok(ListenerType::Udp) => {
3739                let (token, mut listener) = match self.udp.borrow_mut().give_back_listener(&address)
3740                {
3741                    Ok((token, listener)) => (token, listener),
3742                    Err(e) => {
3743                        return worker_response_error(
3744                            req_id,
3745                            format!("Could not deactivate UDP listener at {address}: {e}"),
3746                        );
3747                    }
3748                };
3749
3750                if let Err(e) = self.poll.registry().deregister(&mut listener) {
3751                    error!(
3752                        "error deregistering UDP listen socket({:?}): {:?}",
3753                        deactivate, e
3754                    );
3755                }
3756                // See the HTTP arm. For UDP this ALSO drops the live
3757                // `UdpListenerSession` the activate path installed here,
3758                // putting the slot back to the placeholder — the flows it owned
3759                // were already torn down by `give_back_listener`.
3760                self.reserve_listen_token(token, Protocol::UDPListen);
3761                // A UDP listen token never reaches `accept_ready` (`ready()`
3762                // only enqueues the three accept-driven listen protocols), but
3763                // the removal keeps the rule uniform across the four arms.
3764                self.accept_ready.remove(&ListenToken(token.0));
3765
3766                if deactivate.to_scm {
3767                    self.unblock_scm_socket();
3768                    let listeners = Listeners {
3769                        http: vec![],
3770                        tls: vec![],
3771                        tcp: vec![],
3772                        udp: vec![(address.clone(), listener.as_raw_fd())],
3773                    };
3774                    info!("sending UDP listener: {:?}", listeners);
3775                    let res = self.scm.send_listeners(&listeners);
3776
3777                    self.block_scm_socket();
3778
3779                    info!("sent UDP listener: {:?}", res);
3780                }
3781                WorkerResponse::ok(req_id)
3782            }
3783            Err(_) => worker_response_error(req_id, "Wrong variant for ListenerType on request"),
3784        }
3785    }
3786
3787    /// Send all socket addresses and file descriptors of all proxies, via the scm socket
3788    pub fn return_listen_sockets(&mut self) -> Result<(), ScmSocketError> {
3789        self.unblock_scm_socket();
3790
3791        let mut http_listeners = self.http.borrow_mut().give_back_listeners();
3792        for &mut (_, ref mut sock) in http_listeners.iter_mut() {
3793            if let Err(e) = self.poll.registry().deregister(sock) {
3794                error!(
3795                    "error deregistering HTTP listen socket({:?}): {:?}",
3796                    sock, e
3797                );
3798            }
3799        }
3800
3801        let mut https_listeners = self.https.borrow_mut().give_back_listeners();
3802        for &mut (_, ref mut sock) in https_listeners.iter_mut() {
3803            if let Err(e) = self.poll.registry().deregister(sock) {
3804                error!(
3805                    "error deregistering HTTPS listen socket({:?}): {:?}",
3806                    sock, e
3807                );
3808            }
3809        }
3810
3811        let mut tcp_listeners = self.tcp.borrow_mut().give_back_listeners();
3812        for &mut (_, ref mut sock) in tcp_listeners.iter_mut() {
3813            if let Err(e) = self.poll.registry().deregister(sock) {
3814                error!("error deregistering TCP listen socket({:?}): {:?}", sock, e);
3815            }
3816        }
3817
3818        let mut udp_listeners = self.udp.borrow_mut().give_back_listeners();
3819        for &mut (_, ref mut sock) in udp_listeners.iter_mut() {
3820            if let Err(e) = self.poll.registry().deregister(sock) {
3821                error!("error deregistering UDP listen socket({:?}): {:?}", sock, e);
3822            }
3823        }
3824
3825        // use as_raw_fd because the listeners should be dropped after sending them
3826        let listeners = Listeners {
3827            http: http_listeners
3828                .iter()
3829                .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3830                .collect(),
3831            tls: https_listeners
3832                .iter()
3833                .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3834                .collect(),
3835            tcp: tcp_listeners
3836                .iter()
3837                .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3838                .collect(),
3839            udp: udp_listeners
3840                .iter()
3841                .map(|(key, listener)| (key.clone(), listener.as_raw_fd()))
3842                .collect(),
3843        };
3844        // Each handed-back listener is collected exactly once: the assembled
3845        // fd lists mirror the give_back lists one-to-one (the maps above are
3846        // straight `.iter().map().collect()` with no filtering or dedup).
3847        debug_assert_eq!(
3848            listeners.http.len(),
3849            http_listeners.len(),
3850            "every HTTP listener must be collected exactly once"
3851        );
3852        debug_assert_eq!(
3853            listeners.tls.len(),
3854            https_listeners.len(),
3855            "every HTTPS listener must be collected exactly once"
3856        );
3857        debug_assert_eq!(
3858            listeners.tcp.len(),
3859            tcp_listeners.len(),
3860            "every TCP listener must be collected exactly once"
3861        );
3862        info!("sending default listeners: {:?}", listeners);
3863        let res = self.scm.send_listeners(&listeners);
3864
3865        self.block_scm_socket();
3866
3867        info!("sent default listeners: {:?}", res);
3868        res
3869    }
3870
3871    fn block_scm_socket(&mut self) {
3872        if let Err(e) = self.scm.set_blocking(true) {
3873            error!("Could not block scm socket: {}", e);
3874        }
3875    }
3876
3877    fn unblock_scm_socket(&mut self) {
3878        if let Err(e) = self.scm.set_blocking(false) {
3879            error!("Could not unblock scm socket: {}", e);
3880        }
3881    }
3882
3883    pub fn to_session(&self, token: Token) -> SessionToken {
3884        SessionToken(token.0)
3885    }
3886
3887    pub fn from_session(&self, token: SessionToken) -> Token {
3888        Token(token.0)
3889    }
3890
3891    pub fn accept(&mut self, token: ListenToken, protocol: Protocol) {
3892        // Per-protocol counter key. Keeping the namespace static (3 keys +
3893        // aggregate) is a deliberate cardinality cap: per-listener-address
3894        // labelling would require runtime `Box::leak` because `incr!` takes
3895        // `&'static str`, and listener addresses can be reconfigured at
3896        // runtime by the control plane. Operators wanting per-listener
3897        // attribution should correlate with the listener-protocol breakdown
3898        // below.
3899        //
3900        // Non-listen protocols reach this code only on an invariant break
3901        // upstream (`ready()` dispatched a non-listen `Protocol` to
3902        // `accept()`). Log and return rather than panicking — defense in
3903        // depth on the accept path, which is process-fatal if it aborts.
3904        let (proto_key, accepted_protocol) = match protocol {
3905            Protocol::TCPListen => ("listener.accepted.tcp", Protocol::TCPListen),
3906            Protocol::HTTPListen => ("listener.accepted.http", Protocol::HTTPListen),
3907            Protocol::HTTPSListen => ("listener.accepted.https", Protocol::HTTPSListen),
3908            other => {
3909                warn!(
3910                    "accept() called with non-listen protocol {:?} on token {:?}; skipping",
3911                    other, token
3912                );
3913                return;
3914            }
3915        };
3916
3917        // Past the guard, `accepted_protocol` is one of the three listen
3918        // variants — the inner dispatch's `unreachable!` arm relies on this.
3919        debug_assert!(
3920            matches!(
3921                accepted_protocol,
3922                Protocol::TCPListen | Protocol::HTTPListen | Protocol::HTTPSListen
3923            ),
3924            "accept dispatch must run with a listen protocol only"
3925        );
3926
3927        loop {
3928            let result = match accepted_protocol {
3929                Protocol::TCPListen => self.tcp.borrow_mut().accept(token),
3930                Protocol::HTTPListen => self.http.borrow_mut().accept(token),
3931                Protocol::HTTPSListen => self.https.borrow_mut().accept(token),
3932                // The outer match populates `accepted_protocol` only with the
3933                // three listen variants and returns early otherwise — this
3934                // arm is structurally unreachable.
3935                other => unreachable!(
3936                    "accept dispatch reached non-listen protocol {:?} after outer guard",
3937                    other
3938                ),
3939            };
3940            match result {
3941                Ok((sock, peer)) => {
3942                    // `peer` is the address `accept(2)` returned: the counter
3943                    // and the session below both use it, and neither costs a
3944                    // `getpeername(2)`.
3945                    incr!(names::listener::ACCEPTED_TOTAL);
3946                    incr!(proto_key);
3947                    incr!(per_source_bucket(&peer));
3948                    let queue_before = self.accept_queue.len();
3949                    self.accept_queue.push_back((
3950                        sock,
3951                        token,
3952                        accepted_protocol,
3953                        Instant::now(),
3954                        peer,
3955                    ));
3956                    // One accepted socket enqueues exactly one entry.
3957                    debug_assert_eq!(
3958                        self.accept_queue.len(),
3959                        queue_before + 1,
3960                        "each accepted socket must enqueue exactly one entry"
3961                    );
3962                }
3963                Err(AcceptError::WouldBlock) => {
3964                    self.accept_ready.remove(&token);
3965                    break;
3966                }
3967                Err(other) => {
3968                    error!(
3969                        "error accepting {:?} sockets: {:?}",
3970                        accepted_protocol, other
3971                    );
3972                    self.accept_ready.remove(&token);
3973                    break;
3974                }
3975            }
3976        }
3977
3978        gauge!(names::accept_queue::CONNECTIONS, self.accept_queue.len());
3979    }
3980
3981    pub fn create_sessions(&mut self) {
3982        while let Some((sock, token, protocol, timestamp, peer)) = self.accept_queue.pop_back() {
3983            let wait_time = Instant::now() - timestamp;
3984            time!(names::accept_queue::WAIT_TIME, wait_time.as_millis());
3985            if wait_time > self.accept_queue_timeout {
3986                incr!(names::accept_queue::TIMEOUT);
3987                continue;
3988            }
3989
3990            if !self.sessions.borrow_mut().check_limits() {
3991                // Every `break` below drops the socket we just popped,
3992                // unserved, while the remaining queued sockets wait for the
3993                // next pass or time out. `listener.connection_capped` counts
3994                // exactly that dropped socket, once, at each `break`: a socket
3995                // served after eviction made room is not a refusal.
3996                if !self.evict_on_queue_full {
3997                    incr!(names::listener::CONNECTION_CAPPED);
3998                    break;
3999                }
4000
4001                // Skip eviction during graceful shutdown — defeats the
4002                // shutting_down semantics and is wasted work since the
4003                // worker is winding down anyway.
4004                if self.shutting_down.is_some() {
4005                    incr!(names::listener::CONNECTION_CAPPED);
4006                    break;
4007                }
4008
4009                // Evict 1% of `max_connections` per iteration. Conservative
4010                // ratio: large enough to make meaningful progress clearing
4011                // the accept queue, small enough to limit collateral damage
4012                // to active sessions. The cap loop re-checks limits after
4013                // each eviction round, so multiple rounds can run if the
4014                // queue has many pending connections. Decoupled from
4015                // `slab_entries_per_connection` (which only sizes the slab,
4016                // not `max_connections`).
4017                let to_evict = (self.sessions.borrow().max_connections / 100).max(1);
4018                let evicted = self.evict_least_active_sessions(to_evict);
4019                if evicted == 0 {
4020                    // Informational, not an invariant break: the worker may
4021                    // be at boot, or every active session is a system
4022                    // protocol (Channel/Metrics/Timer/listeners) and is
4023                    // ineligible. Stay at warn so operators see it in info-
4024                    // level production logs.
4025                    warn!("evict_on_queue_full enabled but no candidate sessions to evict");
4026                    incr!(names::listener::CONNECTION_CAPPED);
4027                    break;
4028                }
4029
4030                count!(names::sessions::EVICTED, evicted as i64);
4031                warn!(
4032                    "evicted {} least recently active sessions to make room",
4033                    evicted
4034                );
4035
4036                if !self.sessions.borrow_mut().check_limits() {
4037                    incr!(names::listener::CONNECTION_CAPPED);
4038                    break;
4039                }
4040            }
4041
4042            //FIXME: check the timestamp
4043            //TODO: create_session should return the session and
4044            // the server should insert it in the the SessionManager
4045            // The accept path only ever enqueues listen protocols, so the
4046            // `_ => panic!` arm below is genuinely unreachable. Assert the set
4047            // here so a future enqueue regression trips in debug, not prod.
4048            debug_assert!(
4049                matches!(
4050                    protocol,
4051                    Protocol::TCPListen | Protocol::HTTPListen | Protocol::HTTPSListen
4052                ),
4053                "accept queue must only hold listen protocols, got {protocol:?}"
4054            );
4055            match protocol {
4056                Protocol::TCPListen => {
4057                    let proxy = self.tcp.clone();
4058                    if self
4059                        .tcp
4060                        .borrow_mut()
4061                        .create_session(sock, peer, token, wait_time, proxy)
4062                        .is_err()
4063                    {
4064                        break;
4065                    }
4066                }
4067                Protocol::HTTPListen => {
4068                    let proxy = self.http.clone();
4069                    if self
4070                        .http
4071                        .borrow_mut()
4072                        .create_session(sock, peer, token, wait_time, proxy)
4073                        .is_err()
4074                    {
4075                        break;
4076                    }
4077                }
4078                Protocol::HTTPSListen => {
4079                    if self
4080                        .https
4081                        .borrow_mut()
4082                        .create_session(sock, peer, token, wait_time, self.https.clone())
4083                        .is_err()
4084                    {
4085                        break;
4086                    }
4087                }
4088                _ => panic!("should not call accept() on a HTTP, HTTPS or TCP session"),
4089            };
4090            let nb_before = self.sessions.borrow().nb_connections;
4091            self.sessions.borrow_mut().incr();
4092            // A successfully created session bumps the live count by one.
4093            debug_assert_eq!(
4094                self.sessions.borrow().nb_connections,
4095                nb_before + 1,
4096                "create_sessions must account exactly one new connection per created session"
4097            );
4098        }
4099
4100        gauge!(names::accept_queue::CONNECTIONS, self.accept_queue.len());
4101    }
4102
4103    /// Record, for each token of one `epoll_wait` result, the session its
4104    /// slab slot holds right now, before any of the result is dispatched.
4105    fn snapshot_event_owners(
4106        &self,
4107        tokens: impl Iterator<Item = Token>,
4108        owners: &mut Vec<Option<Rc<RefCell<dyn ProxySession>>>>,
4109    ) {
4110        debug_assert!(
4111            owners.is_empty(),
4112            "the previous turn must have consumed every event owner"
4113        );
4114        let sessions = self.sessions.borrow();
4115        owners.extend(tokens.map(|token| sessions.slab.get(token.0).cloned()));
4116    }
4117
4118    /// Dispatch `events` through `Server::ready` only if `token` still
4119    /// belongs to `owner`, the session `Server::snapshot_event_owners`
4120    /// recorded for it when `epoll_wait` returned.
4121    ///
4122    /// A session that closes while the loop walks one `epoll_wait` result
4123    /// frees its slab slots at once, and the slab hands the most recently
4124    /// freed key to the next allocation. A session dispatched later in the
4125    /// same result can therefore take that key for its new backend
4126    /// (`TcpSession::connect_to_backend` in `lib/src/tcp.rs`, or the mux's
4127    /// backend dial through `L7Proxy::add_session`, `lib/src/lib.rs`) while the
4128    /// result still holds an event, typically a HUP, from the closed
4129    /// session's own socket. Delivered under the reused token, that event
4130    /// reads as the new backend hanging up, and the new session closes
4131    /// (sozu-proxy/sozu#1877). Comparing with the recorded owner drops it.
4132    /// The recorded `Rc` stays alive until this call, so no new session can
4133    /// sit at its address and pass the comparison by accident.
4134    ///
4135    /// The guard compares sessions, not sockets: a session that closes one of
4136    /// its own backends and dials another in the same result, on a different
4137    /// descriptor's event, gets the freed key back under the same `Rc`, and a
4138    /// queued event of the old socket still reaches it.
4139    fn ready_if_still_owned(
4140        &mut self,
4141        token: Token,
4142        events: Ready,
4143        owner: Option<Rc<RefCell<dyn ProxySession>>>,
4144    ) {
4145        let still_owned = owner.is_some_and(|owner| {
4146            self.sessions
4147                .borrow()
4148                .slab
4149                .get(token.0)
4150                .is_some_and(|current| Rc::ptr_eq(current, &owner))
4151        });
4152        if !still_owned {
4153            trace!(
4154                "PROXY\t{:?} dropping events {:?} from a socket whose session closed earlier in this epoll result",
4155                token, events
4156            );
4157            return;
4158        }
4159        self.ready(token, events);
4160    }
4161
4162    pub fn ready(&mut self, token: Token, events: Ready) {
4163        trace!("PROXY\t{:?} got events: {:?}", token, events);
4164
4165        let session_token = token.0;
4166        if self.sessions.borrow().slab.contains(session_token) {
4167            //info!("sessions contains {:?}", session_token);
4168            let protocol = self.sessions.borrow().slab[session_token]
4169                .borrow()
4170                .protocol();
4171            // NOTE: `token` is NOT necessarily the session's frontend token. A
4172            // session is registered under BOTH its frontend and its backend slab
4173            // slots (the multi-token pattern: `connect_to_backend` inserts the
4174            // same session Rc under a second vacant key), so `ready()` is also
4175            // dispatched with the backend token, where `frontend_token() !=
4176            // session_token`. There is therefore no `token == frontend_token`
4177            // identity to assert here.
4178            //info!("protocol: {:?}", protocol);
4179            match protocol {
4180                Protocol::HTTPListen | Protocol::HTTPSListen | Protocol::TCPListen => {
4181                    //info!("PROTOCOL IS LISTEN");
4182                    if events.is_readable() {
4183                        self.accept_ready.insert(ListenToken(token.0));
4184                        if self.sessions.borrow().can_accept {
4185                            self.accept(ListenToken(token.0), protocol);
4186                        }
4187                        return;
4188                    }
4189
4190                    if events.is_writable() {
4191                        error!(
4192                            "received writable for listener {:?}, this should not happen",
4193                            token
4194                        );
4195                        return;
4196                    }
4197
4198                    if events.is_hup() {
4199                        error!("should not happen: server {:?} closed", token);
4200                        return;
4201                    }
4202
4203                    unreachable!();
4204                }
4205                _ => {}
4206            }
4207
4208            let session = self.sessions.borrow_mut().slab[session_token].clone();
4209            session.borrow_mut().update_readiness(token, events);
4210            if session.borrow_mut().ready(session.clone()) {
4211                debug!(
4212                    "Server killing session from ready: token={:?}, protocol={:?}, events={:?}",
4213                    token, protocol, events
4214                );
4215                self.kill_session(session);
4216            }
4217        }
4218    }
4219
4220    pub fn timeout(&mut self, token: Token) {
4221        trace!("PROXY\t{:?} got timeout", token);
4222
4223        let session_token = token.0;
4224        if self.sessions.borrow().slab.contains(session_token) {
4225            let session = self.sessions.borrow_mut().slab[session_token].clone();
4226            if session.borrow_mut().timeout(token) {
4227                debug!(
4228                    "Server killing session from timeout: token={:?}, protocol={:?}",
4229                    token,
4230                    session.borrow().protocol()
4231                );
4232                self.kill_session(session);
4233            } else if session.borrow().needs_ready_pass() {
4234                // The timeout left work only `ready` can do, and no socket
4235                // event will come to trigger it: a backend whose connect timed
4236                // out must be counted as failed and its requests sent to
4237                // another backend (sozu-proxy/sozu#1800).
4238                if session.borrow_mut().ready(session.clone()) {
4239                    debug!(
4240                        "Server killing session from ready after timeout: token={:?}, protocol={:?}",
4241                        token,
4242                        session.borrow().protocol()
4243                    );
4244                    self.kill_session(session);
4245                }
4246            }
4247        }
4248    }
4249
4250    pub fn handle_remaining_readiness(&mut self) {
4251        // try to accept again after handling all session events,
4252        // since we might have released a few session slots
4253        if self.sessions.borrow().can_accept && !self.accept_ready.is_empty() {
4254            while let Some(token) = self
4255                .accept_ready
4256                .iter()
4257                .next()
4258                .map(|token| ListenToken(token.0))
4259            {
4260                // A listen token queued here while `can_accept` was false can
4261                // outlive its slab entry. `notify_deactivate_listener` no
4262                // longer frees that entry — it RESERVES the slot, re-arming it
4263                // with the inert `ListenSession` placeholder (see
4264                // `reserve_listen_token`) — so after a deactivate the lookup
4265                // below still succeeds and it is the `accept_ready` purge in
4266                // those arms that keeps a socket-less listener out of
4267                // `accept()`. `RemoveListener` is the sole release point of the
4268                // slot (`notify_proxys`), and it purges the token too, so a
4269                // token reaching this loop with no slab entry means those
4270                // purges were bypassed. Indexing the slab with it panicked the
4271                // worker, hence the fallible `get`. Purging it HERE as well as
4272                // at those sites is what keeps the loop finite: `accept()`
4273                // below only removes the token on `WouldBlock`/error, so a
4274                // `continue` without the removal would hand `iter().next()` the
4275                // same stale token forever.
4276                let protocol = self
4277                    .sessions
4278                    .borrow()
4279                    .slab
4280                    .get(token.0)
4281                    .map(|session| session.borrow().protocol());
4282                let Some(protocol) = protocol else {
4283                    error!(
4284                        "accept_ready holds listen token {:?}, which no longer has a session; dropping it",
4285                        token
4286                    );
4287                    self.accept_ready.remove(&token);
4288                    continue;
4289                };
4290                self.accept(token, protocol);
4291                if !self.sessions.borrow().can_accept || self.accept_ready.is_empty() {
4292                    break;
4293                }
4294            }
4295        }
4296    }
4297    fn block_channel(&mut self) {
4298        if let Err(e) = self.channel.blocking() {
4299            error!("Could not block channel: {}", e);
4300        }
4301    }
4302    fn unblock_channel(&mut self) {
4303        if let Err(e) = self.channel.nonblocking() {
4304            error!("Could not block channel: {}", e);
4305        }
4306    }
4307
4308    /// Evict the `count` least-recently-active non-listener sessions and
4309    /// return how many tokens were enqueued for shutdown. Used by
4310    /// `create_sessions` when the accept queue is saturated and the
4311    /// `evict_on_queue_full` knob is set.
4312    ///
4313    /// Uses `select_nth_unstable_by_key` (introselect, O(n) average) to
4314    /// partition the oldest `count` sessions in-place rather than a full
4315    /// O(n log n) sort. The candidate `Vec` is unavoidable because of
4316    /// `RefCell` borrow rules — the immutable borrow on `self.sessions`
4317    /// must drop before `shut_down_sessions_by_frontend_tokens` can take
4318    /// its mutable borrow.
4319    fn evict_least_active_sessions(&self, count: usize) -> usize {
4320        if count == 0 {
4321            return 0;
4322        }
4323
4324        let tokens = {
4325            let sessions = self.sessions.borrow();
4326            let mut candidates: Vec<(Token, Instant)> = sessions
4327                .slab
4328                .iter()
4329                .filter(|(_, session)| {
4330                    !matches!(
4331                        session.borrow().protocol(),
4332                        Protocol::HTTPListen
4333                            | Protocol::HTTPSListen
4334                            | Protocol::TCPListen
4335                            | Protocol::UDPListen
4336                            | Protocol::Channel
4337                            | Protocol::Metrics
4338                            | Protocol::Timer
4339                    )
4340                })
4341                .map(|(_, session)| {
4342                    let s = session.borrow();
4343                    (s.frontend_token(), s.last_event())
4344                })
4345                .collect();
4346
4347            // Early return is load-bearing: the `pivot` computation below
4348            // does `count.min(len) - 1`, which underflows on empty input.
4349            if candidates.is_empty() {
4350                return 0;
4351            }
4352
4353            let pivot = count.min(candidates.len()) - 1;
4354            candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4355
4356            candidates[..=pivot]
4357                .iter()
4358                .map(|&(token, _)| token)
4359                .collect::<HashSet<Token>>()
4360        };
4361
4362        let evicted = tokens.len();
4363        self.shut_down_sessions_by_frontend_tokens(tokens);
4364        evicted
4365    }
4366}
4367
4368/// Close an inherited listening socket this worker is not going to use, and
4369/// say so.
4370///
4371/// Reached only for [`crate::InheritedSocketFate::Refused`]: a listener is
4372/// already up at `address` on a socket of its own, so `activate()`
4373/// short-circuits and nothing consumes this descriptor. That is a statement
4374/// about now, not about the lifetime — `DeactivateListener` then
4375/// `ActivateListener` would bring the address back to `Adopted`, and because
4376/// `scm_listeners` is filled once and never refilled, closing here means such a
4377/// reactivation binds a fresh socket. See [`crate::InheritedSocketFate::Refused`]
4378/// for why that trade is taken: a queued backlog decays, whereas an unaccepted
4379/// socket in the address's `SO_REUSEPORT` group is expected to go on taking a
4380/// share of new connections for as long as it stays open. That expectation
4381/// follows from `SO_REUSEPORT` load-balancing semantics; nothing here measures
4382/// it.
4383///
4384/// The close is explicit rather than a dropped wrapper falling out of scope: it
4385/// is the same syscall either way, but this one is deliberate, attributable and
4386/// logged, which is precisely what sozu#1342 was missing.
4387fn discard_inherited_socket<T: FromRawFd>(
4388    fd: Option<RawFd>,
4389    address: &ListenerKey,
4390    protocol: &str,
4391) {
4392    let Some(fd) = fd else {
4393        return;
4394    };
4395    warn!(
4396        "closing the inherited {} listening socket for {}: this worker already has an active \
4397         listener there, so nothing will adopt this descriptor unless that listener is \
4398         deactivated and reactivated, and until then it is expected to keep taking a share of \
4399         new connections that nothing accepts (inferred from SO_REUSEPORT semantics, not \
4400         measured here)",
4401        protocol, address
4402    );
4403    // SAFETY: `fd` was just removed from the SCM table by `Listeners::get_*`,
4404    // which hands over ownership, so no other owner survives. The wrapper is
4405    // built only to be dropped: `Drop` is what performs the close.
4406    drop(unsafe { T::from_raw_fd(fd) });
4407}
4408
4409/// log the error together with the request id
4410/// create a WorkerResponse
4411fn worker_response_error<S: ToString, T: ToString>(request_id: S, error: T) -> WorkerResponse {
4412    error!(
4413        "error on request {}, {}",
4414        request_id.to_string(),
4415        error.to_string()
4416    );
4417    WorkerResponse::error(request_id, error)
4418}
4419
4420pub struct ListenSession {
4421    pub protocol: Protocol,
4422}
4423
4424impl ProxySession for ListenSession {
4425    fn last_event(&self) -> Instant {
4426        Instant::now()
4427    }
4428
4429    fn print_session(&self) {}
4430
4431    fn frontend_token(&self) -> Token {
4432        Token(0)
4433    }
4434
4435    fn protocol(&self) -> Protocol {
4436        self.protocol
4437    }
4438
4439    fn ready(&mut self, _session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4440        false
4441    }
4442
4443    fn shutting_down(&mut self) -> SessionIsToBeClosed {
4444        false
4445    }
4446
4447    fn update_readiness(&mut self, _token: Token, _events: Ready) {}
4448
4449    fn close(&mut self) {}
4450
4451    fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4452        error!(
4453            "called ProxySession::timeout(token={:?}, time) on ListenSession {{ protocol: {:?} }}",
4454            _token, self.protocol
4455        );
4456        false
4457    }
4458}
4459
4460#[cfg(test)]
4461mod accept_telemetry_tests {
4462    use super::*;
4463
4464    /// Two IPv4 addresses sharing the same /24 must hash to the same bucket;
4465    /// the masking logic guarantees this regardless of the host octet.
4466    #[test]
4467    fn per_source_bucket_collapses_ipv4_slash24() {
4468        let a: SocketAddr = "203.0.113.5:1234".parse().unwrap();
4469        let b: SocketAddr = "203.0.113.250:9999".parse().unwrap();
4470        assert_eq!(
4471            per_source_bucket(&a),
4472            per_source_bucket(&b),
4473            "addresses in the same /24 must land in the same bucket"
4474        );
4475    }
4476
4477    /// Two IPv6 addresses sharing the same /48 must hash to the same bucket.
4478    #[test]
4479    fn per_source_bucket_collapses_ipv6_slash48() {
4480        let a: SocketAddr = "[2001:db8:1234::1]:443".parse().unwrap();
4481        let b: SocketAddr = "[2001:db8:1234:abcd::ffff]:8443".parse().unwrap();
4482        assert_eq!(
4483            per_source_bucket(&a),
4484            per_source_bucket(&b),
4485            "addresses in the same /48 must land in the same bucket"
4486        );
4487    }
4488
4489    /// Every bucket label must be one of `PER_SOURCE_BUCKETS` precomputed
4490    /// statics — the cardinality cap is the load-bearing property.
4491    #[test]
4492    fn per_source_bucket_keys_are_bounded() {
4493        assert_eq!(PER_SOURCE_BUCKET_KEYS.len(), PER_SOURCE_BUCKETS);
4494        for (i, key) in PER_SOURCE_BUCKET_KEYS.iter().enumerate() {
4495            let expected = format!("client.connect.per_source.bucket_{i:03}");
4496            assert_eq!(*key, expected.as_str());
4497        }
4498    }
4499
4500    /// A modest sample across distinct /24 prefixes should hit a healthy
4501    /// number of distinct buckets — guards against the hash collapsing.
4502    #[test]
4503    fn per_source_bucket_distributes_distinct_subnets() {
4504        let mut hits = std::collections::HashSet::new();
4505        for i in 0..200u8 {
4506            let addr: SocketAddr = format!("10.0.{i}.42:80").parse().unwrap();
4507            hits.insert(per_source_bucket(&addr));
4508        }
4509        // With 200 distinct /24 prefixes hashed into 256 buckets we expect
4510        // many distinct labels — assert a conservative lower bound that
4511        // tolerates birthday collisions.
4512        assert!(
4513            hits.len() >= 100,
4514            "expected at least 100 distinct buckets across 200 /24s, got {}",
4515            hits.len()
4516        );
4517    }
4518}
4519
4520#[cfg(test)]
4521mod state_error_log_tests {
4522    use sozu_command::state::StateError;
4523
4524    #[test]
4525    fn worker_state_dispatch_error_sink_bounds_the_reason() {
4526        const REASON_SECRET: &str = "WORKER_STATE_ERROR_REASON_SECRET_SENTINEL";
4527
4528        let reason = format!("{REASON_SECRET}{}", "x".repeat(4096));
4529        let reason_len = reason.len();
4530        let output = crate::capture_test_logs(move || {
4531            let error = StateError::InvalidTcpFrontend {
4532                address: "127.0.0.1:443"
4533                    .parse()
4534                    .expect("test TCP frontend address must parse"),
4535                reason,
4536            };
4537            error!("Could not execute order on config state: {}", error);
4538        });
4539
4540        assert!(
4541            !output.contains(REASON_SECRET),
4542            "worker state-error sink leaked the reason: {output}"
4543        );
4544        assert!(
4545            output.contains(&format!("reason_bytes={reason_len}")),
4546            "worker state-error sink omitted bounded reason metadata: {output}"
4547        );
4548        assert!(
4549            output.len() <= 512,
4550            "worker state-error sink is not bounded: {} bytes",
4551            output.len()
4552        );
4553    }
4554}
4555
4556#[cfg(test)]
4557mod eviction_tests {
4558    use std::collections::HashSet;
4559    use std::time::{Duration, Instant};
4560
4561    use mio::Token;
4562
4563    /// `select_nth_unstable_by_key` partitions in O(n) so that the first
4564    /// `pivot + 1` entries are the `pivot + 1` smallest by key. This guards
4565    /// against a future refactor swapping the comparator orientation.
4566    #[test]
4567    fn select_nth_finds_oldest_sessions() {
4568        let now = Instant::now();
4569        let mut candidates = [
4570            (Token(1), now - Duration::from_secs(10)), // 10s old
4571            (Token(2), now - Duration::from_secs(50)), // 50s old (oldest)
4572            (Token(3), now - Duration::from_secs(5)),  // 5s old (newest)
4573            (Token(4), now - Duration::from_secs(30)), // 30s old
4574            (Token(5), now - Duration::from_secs(20)), // 20s old
4575        ];
4576
4577        let count = 2;
4578        let pivot = count.min(candidates.len()) - 1;
4579        candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4580
4581        let selected: HashSet<Token> = candidates[..=pivot]
4582            .iter()
4583            .map(|&(token, _)| token)
4584            .collect();
4585
4586        assert_eq!(selected.len(), 2);
4587        assert!(
4588            selected.contains(&Token(2)),
4589            "should contain 50s-old session"
4590        );
4591        assert!(
4592            selected.contains(&Token(4)),
4593            "should contain 30s-old session"
4594        );
4595    }
4596
4597    /// When `count` exceeds available candidates, the pivot collapses to
4598    /// `len - 1` so we evict everything; this test pins that behaviour
4599    /// against a future refactor that might silently truncate.
4600    #[test]
4601    fn select_nth_with_count_exceeding_candidates() {
4602        let now = Instant::now();
4603        let mut candidates = [(Token(1), now - Duration::from_secs(10))];
4604
4605        let count = 5;
4606        let pivot = count.min(candidates.len()) - 1;
4607        candidates.select_nth_unstable_by_key(pivot, |&(_, last_event)| last_event);
4608
4609        let selected: HashSet<Token> = candidates[..=pivot]
4610            .iter()
4611            .map(|&(token, _)| token)
4612            .collect();
4613
4614        assert_eq!(selected.len(), 1);
4615        assert!(selected.contains(&Token(1)));
4616    }
4617}
4618
4619#[cfg(test)]
4620mod accept_ready_tests {
4621    use sozu_command::{
4622        config::ListenerBuilder,
4623        proto::command::{DeactivateListener, ListenerType, SocketAddress},
4624    };
4625
4626    use super::*;
4627    use crate::testing::{ServerParts, prebuild_server, provide_port};
4628
4629    /// A worker holding one activated TCP listener, reachable through the very
4630    /// same `Server` surface the event loop drives. The listen token carries a
4631    /// `ListenSession` placeholder in the slab, exactly like
4632    /// `tcp::testing::start_tcp_worker` installs it.
4633    pub(super) fn server_with_tcp_listener() -> (Server, Token, SocketAddress) {
4634        let ServerParts {
4635            event_loop,
4636            registry,
4637            sessions,
4638            pool,
4639            backends,
4640            server_scm_socket,
4641            server_config,
4642            ..
4643        // `send_scm = true`: `Server::new` makes a BLOCKING
4644        // `receive_listeners()` call before it applies the initial state, so
4645        // the client side must have queued its (empty) listener set first --
4646        // exactly what `tcp::testing::start_tcp_worker` does.
4647        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
4648        let (_command_channel, proxy_channel) =
4649            Channel::generate(1000, 10000).expect("could not generate a test channel");
4650
4651        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
4652        let listener_config = ListenerBuilder::new_tcp(address)
4653            .to_tcp(None)
4654            .expect("could not build a TcpListenerConfig for the test");
4655
4656        let listen_token = {
4657            let mut session_manager = sessions.borrow_mut();
4658            let entry = session_manager.slab.vacant_entry();
4659            let token = Token(entry.key());
4660            entry.insert(Rc::new(RefCell::new(ListenSession {
4661                protocol: Protocol::TCPListen,
4662            })));
4663            token
4664        };
4665
4666        let mut tcp_proxy =
4667            tcp::TcpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
4668        tcp_proxy
4669            .add_listener(listener_config, listen_token)
4670            .expect("could not add the test TCP listener");
4671        tcp_proxy
4672            .activate_listener(&address.into(), None)
4673            .expect("could not activate the test TCP listener");
4674
4675        let server = Server::new(
4676            event_loop,
4677            proxy_channel,
4678            server_scm_socket,
4679            sessions,
4680            pool,
4681            backends,
4682            None,
4683            None,
4684            Some(tcp_proxy),
4685            server_config,
4686            None,
4687            false,
4688        )
4689        .expect("could not build the test server");
4690
4691        // `_command_channel` is dropped here on purpose: nothing in these
4692        // tests reads the worker's side of the channel.
4693        (server, listen_token, address)
4694    }
4695
4696    /// `ready()` queues a listen token in `accept_ready` whenever its listener
4697    /// is readable but the worker cannot accept (buffer-pool backpressure), and
4698    /// only `accept()` — which never runs while `can_accept` is false — takes
4699    /// it back out. Deactivating that listener hands its socket back, so
4700    /// without this purge the deferred accept pass called `accept()` on a
4701    /// socket-less listener (and, before the slot was reserved for the
4702    /// listener's whole lifetime, indexed a vacant slab key).
4703    ///
4704    /// To SEE THIS RED: drop `self.accept_ready.remove(&ListenToken(token.0))`
4705    /// from `notify_deactivate_listener`'s TCP arm — the last assertion below
4706    /// fails.
4707    #[test]
4708    fn deactivating_a_listener_drops_its_pending_accept_token() {
4709        let (mut server, listen_token, address) = server_with_tcp_listener();
4710
4711        // The backpressure state: readable listener, worker at capacity.
4712        server.accept_ready.insert(ListenToken(listen_token.0));
4713
4714        let response = server.notify_deactivate_listener(
4715            "test-deactivate",
4716            &DeactivateListener {
4717                interface: None,
4718                address,
4719                proxy: ListenerType::Tcp as i32,
4720                to_scm: false,
4721            },
4722        );
4723        assert_eq!(
4724            response.status,
4725            ResponseStatus::Ok as i32,
4726            "deactivating an activated TCP listener must succeed: {response:?}"
4727        );
4728        assert!(
4729            server.sessions.borrow().slab.contains(listen_token.0),
4730            "deactivating a listener must KEEP its slab slot reserved — see \
4731             `reserve_listen_token`; the accept purge below is what stops the \
4732             deferred pass from accepting on a socket-less listener"
4733        );
4734        assert!(
4735            !server.accept_ready.contains(&ListenToken(listen_token.0)),
4736            "a deactivated listener must not stay queued for a deferred accept"
4737        );
4738
4739        // The pass that used to panic.
4740        server.handle_remaining_readiness();
4741    }
4742
4743    /// Structural safety for the deferred-accept pass itself: whatever else
4744    /// ever drops a listen token's slab entry, indexing the slab with a token
4745    /// that is no longer there must not take the worker down. Dropping the
4746    /// stale token is also what keeps THIS branch finite: `accept()` only
4747    /// removes a token on `WouldBlock`/error, so re-entering the loop without
4748    /// the removal would spin on the same vacant slot forever.
4749    ///
4750    /// To SEE THIS RED: restore `self.sessions.borrow().slab[token.0]` in
4751    /// `handle_remaining_readiness` — this test then panics with
4752    /// "invalid key".
4753    #[test]
4754    fn a_stale_accept_token_is_dropped_instead_of_panicking() {
4755        let (mut server, listen_token, _address) = server_with_tcp_listener();
4756
4757        server.sessions.borrow_mut().slab.remove(listen_token.0);
4758        server.accept_ready.insert(ListenToken(listen_token.0));
4759
4760        server.handle_remaining_readiness();
4761
4762        assert!(
4763            server.accept_ready.is_empty(),
4764            "a listen token with no session must be dropped from accept_ready"
4765        );
4766    }
4767}
4768/// One `epoll_wait` result can hold events for a session that closes while
4769/// the loop walks it, after another session took over one of its slab keys.
4770/// `Server::ready_if_still_owned` must deliver such an event to nobody
4771/// (sozu-proxy/sozu#1877).
4772#[cfg(test)]
4773mod event_owner_tests {
4774    use super::*;
4775
4776    /// A session that closes on its first `ready` and frees its backend slot
4777    /// from `close`, as `TcpSession::close_backend` (`lib/src/tcp.rs`) does.
4778    struct ClosingSession {
4779        sessions: Rc<RefCell<SessionManager>>,
4780        frontend: Token,
4781        backend: Token,
4782    }
4783
4784    impl ProxySession for ClosingSession {
4785        fn protocol(&self) -> Protocol {
4786            Protocol::TCP
4787        }
4788        fn ready(&mut self, _session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4789            true
4790        }
4791        fn shutting_down(&mut self) -> SessionIsToBeClosed {
4792            true
4793        }
4794        fn update_readiness(&mut self, _token: Token, _events: Ready) {}
4795        fn close(&mut self) {
4796            self.sessions.borrow_mut().slab.try_remove(self.backend.0);
4797        }
4798        fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4799            false
4800        }
4801        fn last_event(&self) -> Instant {
4802            Instant::now()
4803        }
4804        fn print_session(&self) {}
4805        fn frontend_token(&self) -> Token {
4806            self.frontend
4807        }
4808    }
4809
4810    /// A session whose first `ready` dials a backend, taking the slab's next
4811    /// vacant key as `TcpSession::connect_to_backend` (`lib/src/tcp.rs`) does,
4812    /// and that records every token it is handed readiness for.
4813    struct DialingSession {
4814        sessions: Rc<RefCell<SessionManager>>,
4815        frontend: Token,
4816        backend: Option<Token>,
4817        delivered: Vec<Token>,
4818    }
4819
4820    impl ProxySession for DialingSession {
4821        fn protocol(&self) -> Protocol {
4822            Protocol::TCP
4823        }
4824        fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
4825            if self.backend.is_none() {
4826                self.backend = Some(Token(self.sessions.borrow_mut().slab.insert(session)));
4827            }
4828            false
4829        }
4830        fn shutting_down(&mut self) -> SessionIsToBeClosed {
4831            true
4832        }
4833        fn update_readiness(&mut self, token: Token, _events: Ready) {
4834            self.delivered.push(token);
4835        }
4836        fn close(&mut self) {}
4837        fn timeout(&mut self, _token: Token) -> SessionIsToBeClosed {
4838            false
4839        }
4840        fn last_event(&self) -> Instant {
4841            Instant::now()
4842        }
4843        fn print_session(&self) {}
4844        fn frontend_token(&self) -> Token {
4845            self.frontend
4846        }
4847    }
4848
4849    /// The batch of sozu-proxy/sozu#1877: the client's FIN on the first
4850    /// session's frontend, the first readiness of the second session, then
4851    /// the HUP of the first session's backend socket, in that order.
4852    ///
4853    /// To SEE THIS RED: make `Server::ready_if_still_owned` call
4854    /// `self.ready(token, events)` unconditionally — the dialing session then
4855    /// receives the closed backend's HUP under its own backend token.
4856    #[test]
4857    fn an_event_of_a_closed_session_is_not_delivered_to_the_session_reusing_its_token() {
4858        let (mut server, _listen_token, _address) =
4859            super::accept_ready_tests::server_with_tcp_listener();
4860        let sessions = server.sessions.clone();
4861
4862        let closing = Rc::new(RefCell::new(ClosingSession {
4863            sessions: sessions.clone(),
4864            frontend: Token(0),
4865            backend: Token(0),
4866        }));
4867        let dialing = Rc::new(RefCell::new(DialingSession {
4868            sessions: sessions.clone(),
4869            frontend: Token(0),
4870            backend: None,
4871            delivered: Vec::new(),
4872        }));
4873        {
4874            let mut sessions = sessions.borrow_mut();
4875            let closing_session: Rc<RefCell<dyn ProxySession>> = closing.clone();
4876            let frontend = Token(sessions.slab.insert(closing_session.clone()));
4877            let backend = Token(sessions.slab.insert(closing_session));
4878            let mut closing = closing.borrow_mut();
4879            closing.frontend = frontend;
4880            closing.backend = backend;
4881            let dialing_session: Rc<RefCell<dyn ProxySession>> = dialing.clone();
4882            dialing.borrow_mut().frontend = Token(sessions.slab.insert(dialing_session));
4883            sessions.incr();
4884            sessions.incr();
4885        }
4886        let closed_frontend = closing.borrow().frontend;
4887        let closed_backend = closing.borrow().backend;
4888        let dialing_frontend = dialing.borrow().frontend;
4889
4890        let batch = [
4891            (closed_frontend, Ready::READABLE | Ready::HUP),
4892            (dialing_frontend, Ready::READABLE | Ready::WRITABLE),
4893            (
4894                closed_backend,
4895                Ready::READABLE | Ready::WRITABLE | Ready::HUP,
4896            ),
4897        ];
4898        let mut owners = Vec::new();
4899        server.snapshot_event_owners(batch.iter().map(|(token, _)| *token), &mut owners);
4900        for ((token, events), owner) in batch.into_iter().zip(owners.drain(..)) {
4901            server.ready_if_still_owned(token, events, owner);
4902        }
4903
4904        assert_eq!(
4905            dialing.borrow().backend,
4906            Some(closed_backend),
4907            "precondition: the dial must reuse the key the closed session freed in this batch"
4908        );
4909        assert!(
4910            !sessions.borrow().slab.contains(closed_frontend.0),
4911            "the closed session must have left the slab"
4912        );
4913        assert_eq!(
4914            dialing.borrow().delivered,
4915            vec![dialing_frontend],
4916            "the closed backend's HUP must not reach the session that reused its token"
4917        );
4918    }
4919}
4920/// The listener slab slot is reserved for a listener's whole `AddListener` ->
4921/// `RemoveListener` lifetime. These tests pin both ends of that lifetime and
4922/// the deactivate/reactivate cycle in between; see `reserve_listen_token` for
4923/// the design and the alternative it rejects.
4924#[cfg(test)]
4925mod listener_lifecycle_tests {
4926    use std::net::UdpSocket as StdUdpSocket;
4927
4928    use sozu_command::{
4929        config::ListenerBuilder,
4930        proto::command::{
4931            ActivateListener, DeactivateListener, ListenerType, LoadBalancingParams,
4932            RemoveListener, RequestUdpFrontend, SocketAddress, filtered_metrics,
4933        },
4934    };
4935
4936    use super::accept_ready_tests::server_with_tcp_listener;
4937    use super::*;
4938    use crate::{
4939        metrics::{METRICS, names},
4940        testing::{ServerParts, prebuild_server, provide_port},
4941    };
4942
4943    /// A worker with no listener at all, driven through `Server`'s own request
4944    /// surface. Same shape as `accept_ready_tests::server_with_tcp_listener`,
4945    /// minus the pre-installed listener — `Server::new` builds its own UDP
4946    /// proxy, which is the one these tests exercise.
4947    pub(super) fn bare_server() -> Server {
4948        let ServerParts {
4949            event_loop,
4950            sessions,
4951            pool,
4952            backends,
4953            server_scm_socket,
4954            server_config,
4955            ..
4956        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
4957        let (_command_channel, proxy_channel) =
4958            Channel::generate(1000, 10000).expect("could not generate a test channel");
4959
4960        Server::new(
4961            event_loop,
4962            proxy_channel,
4963            server_scm_socket,
4964            sessions,
4965            pool,
4966            backends,
4967            None,
4968            None,
4969            None,
4970            server_config,
4971            None,
4972            false,
4973        )
4974        .expect("could not build the test server")
4975    }
4976
4977    fn add_udp_listener(server: &mut Server, address: SocketAddress) -> Token {
4978        let listener_config = ListenerBuilder::new_udp(address)
4979            .to_udp(None)
4980            .expect("could not build a UdpListenerConfig for the test");
4981        let response = server.notify_add_udp_listener("test-add-udp", listener_config);
4982        assert_eq!(
4983            response.status,
4984            ResponseStatus::Ok as i32,
4985            "adding a UDP listener must succeed: {response:?}"
4986        );
4987        server
4988            .udp
4989            .borrow()
4990            .listener_token(&address.into())
4991            .expect("the added UDP listener must own a token")
4992    }
4993
4994    fn activate(server: &mut Server, address: SocketAddress, proxy: ListenerType) {
4995        let response = server.notify_activate_listener(
4996            "test-activate",
4997            &ActivateListener {
4998                interface: None,
4999                address,
5000                proxy: proxy as i32,
5001                from_scm: false,
5002            },
5003        );
5004        assert_eq!(
5005            response.status,
5006            ResponseStatus::Ok as i32,
5007            "activating a {proxy:?} listener must succeed: {response:?}"
5008        );
5009    }
5010
5011    fn deactivate(server: &mut Server, address: SocketAddress, proxy: ListenerType) {
5012        let response = server.notify_deactivate_listener(
5013            "test-deactivate",
5014            &DeactivateListener {
5015                interface: None,
5016                address,
5017                proxy: proxy as i32,
5018                to_scm: false,
5019            },
5020        );
5021        assert_eq!(
5022            response.status,
5023            ResponseStatus::Ok as i32,
5024            "deactivating a {proxy:?} listener must succeed: {response:?}"
5025        );
5026    }
5027
5028    /// The protocol recorded in the slab slot at `token`, and whether that slot
5029    /// holds the inert `ListenSession` placeholder. `ListenSession` reports
5030    /// `Token(0)` as its frontend token while a real `UdpListenerSession`
5031    /// reports its own listen token — the only way to tell the two apart, since
5032    /// both answer `Protocol::UDPListen`.
5033    fn slot_state(server: &Server, token: Token) -> Option<(Protocol, bool)> {
5034        let sessions = server.sessions.borrow();
5035        let session = sessions.slab.get(token.0)?;
5036        let session = session.borrow();
5037        Some((session.protocol(), session.frontend_token() == Token(0)))
5038    }
5039
5040    /// FAILURE MODE 1, the whole point of this change: a deactivated listener
5041    /// that is activated again must be reachable from the event loop.
5042    ///
5043    /// The proxies retain the listener's token across a deactivate
5044    /// (`give_back_listener` and `activate()` both hand back the same one), and
5045    /// `Server::ready` ignores any token the slab does not contain. Freeing the
5046    /// slot on deactivate therefore left `ActivateListener` re-registering the
5047    /// socket under a dead token: the request answered `ok` and the listener
5048    /// never saw another readiness event.
5049    ///
5050    /// `can_accept` is forced to `false` so `ready()` parks the token in
5051    /// `accept_ready` instead of immediately draining it in `accept()` —
5052    /// membership there is the observable proof that the event reached the
5053    /// listener's dispatch path at all.
5054    ///
5055    /// To SEE THIS RED: in `notify_deactivate_listener`'s TCP arm, replace
5056    /// `self.reserve_listen_token(token, Protocol::TCPListen);` with
5057    /// `self.sessions.borrow_mut().slab.remove(token.0);` — the reactivated
5058    /// listener's readiness event is then dropped, and the assertion fails
5059    /// with "a reactivated listener must still be dispatched by `ready()`".
5060    #[test]
5061    fn a_reactivated_tcp_listener_is_reachable_from_the_event_loop() {
5062        let (mut server, listen_token, address) = server_with_tcp_listener();
5063
5064        deactivate(&mut server, address, ListenerType::Tcp);
5065        activate(&mut server, address, ListenerType::Tcp);
5066
5067        server.sessions.borrow_mut().can_accept = false;
5068        server.ready(listen_token, Ready::READABLE);
5069
5070        assert!(
5071            server.accept_ready.contains(&ListenToken(listen_token.0)),
5072            "a reactivated listener must still be dispatched by `ready()`"
5073        );
5074    }
5075
5076    /// The UDP half of failure mode 1. UDP never accepts, so its activate path
5077    /// installs a real `UdpListenerSession` at the listen token instead — which
5078    /// it skipped outright when the slot had been freed, leaving the socket
5079    /// registered against the placeholder-less token and every datagram
5080    /// unread, while the request still answered `ok`.
5081    ///
5082    /// To SEE THIS RED: in `notify_deactivate_listener`'s UDP arm, replace
5083    /// `self.reserve_listen_token(token, Protocol::UDPListen);` with
5084    /// `self.sessions.borrow_mut().slab.remove(token.0);` — the activate arm
5085    /// then returns an error response ("has no session slot"), so `activate()`
5086    /// panics on the status assertion.
5087    #[test]
5088    fn a_reactivated_udp_listener_gets_its_session_back() {
5089        let mut server = bare_server();
5090        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5091        let listen_token = add_udp_listener(&mut server, address);
5092
5093        assert_eq!(
5094            slot_state(&server, listen_token),
5095            Some((Protocol::UDPListen, true)),
5096            "an added, not-yet-activated UDP listener holds the placeholder"
5097        );
5098
5099        activate(&mut server, address, ListenerType::Udp);
5100        assert_eq!(
5101            slot_state(&server, listen_token),
5102            Some((Protocol::UDPListen, false)),
5103            "activating must install the real UdpListenerSession"
5104        );
5105
5106        deactivate(&mut server, address, ListenerType::Udp);
5107        activate(&mut server, address, ListenerType::Udp);
5108
5109        assert_eq!(
5110            slot_state(&server, listen_token),
5111            Some((Protocol::UDPListen, false)),
5112            "reactivating must reinstall the real UdpListenerSession"
5113        );
5114    }
5115
5116    /// FAILURE MODE 2, now impossible by construction: while the slot was
5117    /// freed on deactivate, the slab was free to hand that key to the next
5118    /// session, and the UDP activate path — which found `contains` true and
5119    /// assigned — overwrote that live session with its listener session.
5120    ///
5121    /// The reserved slot is never vacant, so `vacant_entry()` can no longer
5122    /// return it. This test pins that, and ONLY that: the session created while
5123    /// the listener is deactivated must land on a different key and survive the
5124    /// reactivate. It says nothing about what the activate arm writes into the
5125    /// listener's OWN key, because the reactivate here follows a deactivate and
5126    /// so takes the full activation path. The repeat with no deactivate in
5127    /// between — where the arm's answer is `Ok(token)` from
5128    /// `UdpListener::activate`'s `active` short-circuit and the listener's own
5129    /// key holds a LIVE `UdpListenerSession` — is FAILURE MODE 3, pinned by
5130    /// `a_duplicate_udp_activate_keeps_the_live_listener_session` and the three
5131    /// tests beside it. Naming this one for "a live session" hid that gap: it
5132    /// stayed green throughout.
5133    ///
5134    /// To SEE THIS RED: in `notify_deactivate_listener`'s UDP arm, replace
5135    /// `self.reserve_listen_token(token, Protocol::UDPListen);` with
5136    /// `self.sessions.borrow_mut().slab.remove(token.0);` — the interloper
5137    /// then takes the listener's key and the `assert_ne!` below fails with
5138    /// "a deactivated listener's key must not be handed to another session".
5139    #[test]
5140    fn a_deactivated_udp_listeners_key_is_not_handed_to_another_session() {
5141        let mut server = bare_server();
5142        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5143        let listen_token = add_udp_listener(&mut server, address);
5144        activate(&mut server, address, ListenerType::Udp);
5145        deactivate(&mut server, address, ListenerType::Udp);
5146
5147        // Whatever the worker admits next takes the slab's next vacant key.
5148        // `Protocol::Channel` is just a marker no listener ever reports.
5149        let interloper = {
5150            let mut sessions = server.sessions.borrow_mut();
5151            let entry = sessions.slab.vacant_entry();
5152            let key = entry.key();
5153            entry.insert(Rc::new(RefCell::new(ListenSession {
5154                protocol: Protocol::Channel,
5155            })));
5156            Token(key)
5157        };
5158        assert_ne!(
5159            interloper, listen_token,
5160            "a deactivated listener's key must not be handed to another session"
5161        );
5162
5163        activate(&mut server, address, ListenerType::Udp);
5164
5165        assert_eq!(
5166            slot_state(&server, interloper),
5167            Some((Protocol::Channel, true)),
5168            "reactivating a listener must not overwrite another session's slot"
5169        );
5170    }
5171
5172    /// The cluster every UDP data-path test in this module routes through.
5173    const UDP_FLOW_CLUSTER: &str = "udp-duplicate-activate";
5174
5175    /// The session Rc currently occupying `token`. Identity (`Rc::ptr_eq`)
5176    /// across an operation is the direct statement that the operation did not
5177    /// replace the live session — `UdpListenerSession`'s per-flow maps are
5178    /// private to `udp.rs`, so identity plus an end-to-end datagram are what
5179    /// this module can observe.
5180    fn slab_session(server: &Server, token: Token) -> Rc<RefCell<dyn ProxySession>> {
5181        server
5182            .sessions
5183            .borrow()
5184            .slab
5185            .get(token.0)
5186            .expect("the listener must own a slab slot")
5187            .clone()
5188    }
5189
5190    /// The current value of the `udp.active_flows` gauge for this test thread.
5191    /// `METRICS` is a `thread_local!` and libtest gives each test its own
5192    /// thread, so this reads only what the test itself emitted.
5193    fn active_flows_gauge() -> i64 {
5194        METRICS.with(|metrics| {
5195            metrics
5196                .borrow_mut()
5197                .dump_local_proxy_metrics()
5198                .get(names::udp::ACTIVE_FLOWS)
5199                .and_then(|metric| match metric.inner {
5200                    Some(filtered_metrics::Inner::Gauge(value)) => Some(value as i64),
5201                    _ => None,
5202                })
5203                .unwrap_or(0)
5204        })
5205    }
5206
5207    /// A worker holding one activated UDP listener, a frontend and one backend,
5208    /// plus the real client and backend sockets. The UDP data path is driven for
5209    /// real on loopback because the orphaning these tests pin — the shell's
5210    /// `upstream_to_flow` / `flow_to_upstream` / `upstream_sockets` maps going
5211    /// empty under a live `UdpManager` — is only observable end to end.
5212    fn udp_worker_with_backend(
5213        server: &mut Server,
5214        address: SocketAddress,
5215    ) -> (StdUdpSocket, StdUdpSocket) {
5216        server.notify_proxys(WorkerRequest {
5217            id: "test-add-udp-front".to_owned(),
5218            content: RequestType::AddUdpFrontend(RequestUdpFrontend {
5219                cluster_id: UDP_FLOW_CLUSTER.to_owned(),
5220                address,
5221                tags: Default::default(),
5222            })
5223            .into(),
5224        });
5225
5226        let backend_socket =
5227            StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test UDP backend");
5228        backend_socket
5229            .set_read_timeout(Some(Duration::from_millis(500)))
5230            .expect("could not set the backend read timeout");
5231        let backend_address = backend_socket
5232            .local_addr()
5233            .expect("the test UDP backend must have a local address");
5234
5235        server.notify_proxys(WorkerRequest {
5236            id: "test-add-udp-backend".to_owned(),
5237            content: RequestType::AddBackend(AddBackend {
5238                cluster_id: UDP_FLOW_CLUSTER.to_owned(),
5239                backend_id: format!("{UDP_FLOW_CLUSTER}-0"),
5240                address: backend_address.into(),
5241                load_balancing_parameters: Some(LoadBalancingParams::default()),
5242                sticky_id: None,
5243                backup: None,
5244            })
5245            .into(),
5246        });
5247
5248        let client_socket =
5249            StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test UDP client");
5250        (client_socket, backend_socket)
5251    }
5252
5253    /// Send one datagram from `client` to the listener and drive a single
5254    /// READABLE event through `Server::ready`, exactly as the event loop does.
5255    /// Returns what reached the backend within its read timeout, or `None` when
5256    /// nothing was forwarded. Loopback `send_to` has already queued the datagram
5257    /// on the listener socket by the time it returns, so one pass is enough.
5258    fn forward_one(
5259        server: &mut Server,
5260        listen_token: Token,
5261        listen_address: SocketAddress,
5262        client: &StdUdpSocket,
5263        backend: &StdUdpSocket,
5264        payload: &[u8],
5265    ) -> Option<Vec<u8>> {
5266        let target: std::net::SocketAddr = listen_address.into();
5267        client
5268            .send_to(payload, target)
5269            .expect("the test client must reach the UDP listener");
5270        server.ready(listen_token, Ready::READABLE);
5271
5272        let mut received = [0u8; 64];
5273        backend
5274            .recv_from(&mut received)
5275            .ok()
5276            .map(|(len, _)| received[..len].to_vec())
5277    }
5278
5279    /// FAILURE MODE 3, the residual of the listener-slot change: a SECOND
5280    /// `ActivateListener` for a listener that is already active must not rebuild
5281    /// its session.
5282    ///
5283    /// `UdpListener::activate` short-circuits on its own `active` flag and
5284    /// answers `Ok(self.token)` without doing any work, so `Ok(token)` does not
5285    /// mean this call activated anything — and the arm used to run
5286    /// `build_session` + `*slot = session` on that answer regardless.
5287    ///
5288    /// To SEE THIS RED: in `notify_activate_listener`'s UDP arm, drop the
5289    /// `if !session_installed` guard so `build_session` and the slab write run
5290    /// unconditionally again — the second activate then installs a fresh
5291    /// session and the `Rc::ptr_eq` assertion fails with "a repeated
5292    /// ActivateListener must not replace the live UDP listener session".
5293    #[test]
5294    fn a_duplicate_udp_activate_keeps_the_live_listener_session() {
5295        let mut server = bare_server();
5296        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5297        let listen_token = add_udp_listener(&mut server, address);
5298        activate(&mut server, address, ListenerType::Udp);
5299
5300        let installed = slab_session(&server, listen_token);
5301
5302        let response = server.notify_activate_listener(
5303            "test-activate-twice",
5304            &ActivateListener {
5305                interface: None,
5306                address,
5307                proxy: ListenerType::Udp as i32,
5308                from_scm: false,
5309            },
5310        );
5311        // The response an already-active listener must get. `ok` and not an
5312        // error: `ConfigState::activate_listener` answers `Ok(())` for the same
5313        // repeat, `generate_activate_requests` re-emits one per active listener
5314        // on every state replay, and the HTTP/HTTPS/TCP arms all answer `ok`
5315        // for their own repeats. Erroring here would fail an ordinary replay and
5316        // make the worker disagree with the state the main process persisted.
5317        assert_eq!(
5318            response.status,
5319            ResponseStatus::Ok as i32,
5320            "a repeated ActivateListener on an active UDP listener must answer ok: {response:?}"
5321        );
5322        assert_eq!(
5323            slot_state(&server, listen_token),
5324            Some((Protocol::UDPListen, false)),
5325            "the real UdpListenerSession must still be the one in the slab"
5326        );
5327        assert!(
5328            Rc::ptr_eq(&installed, &slab_session(&server, listen_token)),
5329            "a repeated ActivateListener must not replace the live UDP listener session"
5330        );
5331    }
5332
5333    /// The consequence of that rebuild, end to end. The displaced session took
5334    /// the whole shell side of every flow with it — `upstream_sockets`,
5335    /// `upstream_to_flow`, `flow_to_upstream` — while the proxy kept the shared
5336    /// `UdpManager` and its flow table. `close()` is a `ProxySession` method and
5337    /// not `Drop`, so nothing tore those flows down; they simply stopped
5338    /// forwarding while `udp.active_flows` went on counting them.
5339    ///
5340    /// To SEE THIS RED: in `notify_activate_listener`'s UDP arm, drop the
5341    /// `if !session_installed` guard — the datagram sent after the duplicate
5342    /// activate then never reaches the backend and the assertion fails with
5343    /// "an in-flight UDP flow must survive a repeated ActivateListener".
5344    #[test]
5345    fn in_flight_udp_flows_survive_a_duplicate_udp_activate() {
5346        let mut server = bare_server();
5347        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5348        let listen_token = add_udp_listener(&mut server, address);
5349        activate(&mut server, address, ListenerType::Udp);
5350        let (client, backend) = udp_worker_with_backend(&mut server, address);
5351
5352        assert_eq!(
5353            forward_one(
5354                &mut server,
5355                listen_token,
5356                address,
5357                &client,
5358                &backend,
5359                b"before"
5360            )
5361            .as_deref(),
5362            Some(&b"before"[..]),
5363            "the flow must be established before the duplicate activate"
5364        );
5365        assert_eq!(
5366            active_flows_gauge(),
5367            1,
5368            "one established flow must be on the gauge"
5369        );
5370
5371        activate(&mut server, address, ListenerType::Udp);
5372
5373        // The gauge counts what the manager holds, and the manager is retained
5374        // across the rebuild — so the gauge alone cannot tell a live flow from
5375        // an orphaned one. Assert both together: the flow the gauge claims must
5376        // still be a flow the data path can serve.
5377        assert_eq!(
5378            active_flows_gauge(),
5379            1,
5380            "a repeated ActivateListener must neither open nor evict a flow"
5381        );
5382        assert_eq!(
5383            forward_one(
5384                &mut server,
5385                listen_token,
5386                address,
5387                &client,
5388                &backend,
5389                b"after"
5390            )
5391            .as_deref(),
5392            Some(&b"after"[..]),
5393            "an in-flight UDP flow must survive a repeated ActivateListener"
5394        );
5395    }
5396
5397    /// The resource the rebuild strands for good. Every flow holds one extra
5398    /// slab slot for its upstream socket, and `on_close_flow` reaches that slot
5399    /// only through `flow_to_upstream`. A replacement session's map is empty, so
5400    /// the eventual manager-driven teardown frees the flow, balances the gauge —
5401    /// and leaves the upstream slot allocated for the worker's whole life.
5402    ///
5403    /// To SEE THIS RED: in `notify_activate_listener`'s UDP arm, drop the
5404    /// `if !session_installed` guard — the deactivate then leaves the upstream
5405    /// slot behind and the assertion fails with "tearing the listener down must
5406    /// release every per-flow slab slot".
5407    #[test]
5408    fn a_duplicate_udp_activate_strands_no_upstream_slab_slot() {
5409        let mut server = bare_server();
5410        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5411        let listen_token = add_udp_listener(&mut server, address);
5412        activate(&mut server, address, ListenerType::Udp);
5413        let (client, backend) = udp_worker_with_backend(&mut server, address);
5414
5415        // Baseline AFTER the listener exists: its own reserved slot is part of
5416        // `base_sessions_count` and is not what this test measures.
5417        let baseline = server.sessions.borrow().slab.len();
5418
5419        assert!(
5420            forward_one(
5421                &mut server,
5422                listen_token,
5423                address,
5424                &client,
5425                &backend,
5426                b"flow"
5427            )
5428            .is_some(),
5429            "the flow must be established before the duplicate activate"
5430        );
5431        assert_eq!(
5432            server.sessions.borrow().slab.len(),
5433            baseline + 1,
5434            "an established flow owns exactly one upstream slab slot"
5435        );
5436
5437        activate(&mut server, address, ListenerType::Udp);
5438        deactivate(&mut server, address, ListenerType::Udp);
5439
5440        assert_eq!(
5441            server.sessions.borrow().slab.len(),
5442            baseline,
5443            "tearing the listener down must release every per-flow slab slot"
5444        );
5445        assert_eq!(
5446            active_flows_gauge(),
5447            0,
5448            "tearing the listener down must return the active-flows gauge to zero"
5449        );
5450    }
5451
5452    /// The path that reaches this arm without anyone typing a command:
5453    /// `load_state` replays the state the main process persisted, and
5454    /// `ConfigState::generate_activate_requests` emits one `ActivateListener`
5455    /// per ACTIVE listener. For a worker whose listener is already up, that
5456    /// replay is a duplicate activate by construction — the ordinary case, not
5457    /// an operator mistake, which is why this arm answers `ok` instead of
5458    /// surfacing the repeat as an error.
5459    ///
5460    /// Driven through the real `ConfigState` rather than a hand-written
5461    /// `ActivateListener`, so the test breaks if that emission ever changes.
5462    ///
5463    /// To SEE THIS RED: in `notify_activate_listener`'s UDP arm, drop the
5464    /// `if !session_installed` guard — the replay then rebuilds the session and
5465    /// the `Rc::ptr_eq` assertion fails with "replaying ActivateListener must
5466    /// leave the live UDP listener session in place".
5467    #[test]
5468    fn replaying_activate_listener_is_a_clean_no_op_for_an_active_udp_listener() {
5469        let mut server = bare_server();
5470        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5471        let listener_config = ListenerBuilder::new_udp(address)
5472            .to_udp(None)
5473            .expect("could not build a UdpListenerConfig for the test");
5474
5475        // Through `notify_proxys` so the worker's own `ConfigState` records the
5476        // listener and its activation, exactly as a live worker's does.
5477        server.notify_proxys(WorkerRequest {
5478            id: "test-add-udp".to_owned(),
5479            content: RequestType::AddUdpListener(listener_config).into(),
5480        });
5481        server.notify_proxys(WorkerRequest {
5482            id: "test-activate".to_owned(),
5483            content: RequestType::ActivateListener(ActivateListener {
5484                interface: None,
5485                address,
5486                proxy: ListenerType::Udp as i32,
5487                from_scm: false,
5488            })
5489            .into(),
5490        });
5491        let listen_token = server
5492            .udp
5493            .borrow()
5494            .listener_token(&address.into())
5495            .expect("the added UDP listener must own a token");
5496        let installed = slab_session(&server, listen_token);
5497
5498        let replayed: Vec<ActivateListener> = server
5499            .config_state
5500            .generate_activate_requests()
5501            .into_iter()
5502            .filter_map(|request| match request.request_type {
5503                Some(RequestType::ActivateListener(activate)) => Some(activate),
5504                _ => None,
5505            })
5506            .collect();
5507        assert_eq!(
5508            replayed.len(),
5509            1,
5510            "an active UDP listener must be replayed exactly once: {replayed:?}"
5511        );
5512
5513        for activate in &replayed {
5514            let response = server.notify_activate_listener("test-replay", activate);
5515            assert_eq!(
5516                response.status,
5517                ResponseStatus::Ok as i32,
5518                "a replayed ActivateListener must answer ok: {response:?}"
5519            );
5520        }
5521
5522        assert_eq!(
5523            slot_state(&server, listen_token),
5524            Some((Protocol::UDPListen, false)),
5525            "the replay must leave the real UdpListenerSession in the slab"
5526        );
5527        assert!(
5528            Rc::ptr_eq(&installed, &slab_session(&server, listen_token)),
5529            "replaying ActivateListener must leave the live UDP listener session in place"
5530        );
5531    }
5532
5533    /// The other end of the lifetime. Reserving the slot across a deactivate is
5534    /// only sound if something releases it: no proxy's `remove_listener`
5535    /// touches the session slab, so `RemoveListener` is the sole release point.
5536    /// Without it each add/remove cycle would strand one slab key — and
5537    /// `base_sessions_count`, decremented on every `RemoveListener`, would
5538    /// drift below the number of occupied slots, so a soft stop would wait for
5539    /// sessions that do not exist.
5540    ///
5541    /// Three full cycles, because a leak is only visible as growth, and every
5542    /// other one removes the listener while it is still ACTIVATED: that path
5543    /// drops a live `UdpListenerSession` out of the slab — the Rc that, left
5544    /// there, also pinned the listener's `UdpSocket` open and kept `ready()`
5545    /// dispatching to a removed listener.
5546    ///
5547    /// To SEE THIS RED: drop the `if let Some(token) = listen_token` block from
5548    /// `notify_proxys`'s `RemoveListener` arm — the slab then grows by one slot
5549    /// per cycle and the assertion fails with "cycle 0: removing a listener
5550    /// must release its reserved slot".
5551    #[test]
5552    fn removing_a_listener_releases_its_reserved_slab_slot() {
5553        let mut server = bare_server();
5554        let baseline = server.sessions.borrow().slab.len();
5555        let base_sessions_count = server.base_sessions_count;
5556
5557        for cycle in 0..3 {
5558            let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5559            let listen_token = add_udp_listener(&mut server, address);
5560            activate(&mut server, address, ListenerType::Udp);
5561            if cycle % 2 == 0 {
5562                deactivate(&mut server, address, ListenerType::Udp);
5563            }
5564
5565            server.notify_proxys(WorkerRequest {
5566                id: "test-remove".to_owned(),
5567                content: RequestType::RemoveListener(RemoveListener {
5568                    interface: None,
5569                    address,
5570                    proxy: ListenerType::Udp as i32,
5571                })
5572                .into(),
5573            });
5574
5575            assert!(
5576                !server.sessions.borrow().slab.contains(listen_token.0),
5577                "cycle {cycle}: removing a listener must release its reserved slot"
5578            );
5579            assert_eq!(
5580                server.sessions.borrow().slab.len(),
5581                baseline,
5582                "cycle {cycle}: the slab must return to its baseline occupancy"
5583            );
5584            assert_eq!(
5585                server.base_sessions_count, base_sessions_count,
5586                "cycle {cycle}: base_sessions_count must track the reserved slots"
5587            );
5588        }
5589    }
5590
5591    /// The UDP per-source flow limit is opt-in per cluster: a global
5592    /// `max_connections_per_ip` / `max_connections_per_subnet` — here set at
5593    /// runtime, as an operator would for HTTP/TCP — never reaches a UDP
5594    /// listener, while the cluster's own `max_connections_per_ip` does, and
5595    /// sheds a new port of an address that holds its limit.
5596    ///
5597    /// To SEE THIS RED: in `UdpProxy::cluster_config_for`, replace
5598    /// `per_ip.unwrap_or(0)` with
5599    /// `per_ip.unwrap_or(self.sessions.borrow().max_connections_per_ip)`, so
5600    /// UDP inherits the global default; the first assertion then reads `1`.
5601    #[test]
5602    fn udp_flow_limits_come_from_the_cluster_not_the_global_defaults() {
5603        use std::{net::SocketAddr, time::Instant};
5604
5605        use crate::protocol::udp::{
5606            BackendId, BackendSource, ManagerInput, MetricEvent, Output, UdpManager,
5607        };
5608
5609        struct OneBackend;
5610        impl BackendSource for OneBackend {
5611            fn select(
5612                &mut self,
5613                _cluster: &str,
5614                _key: Option<u64>,
5615                _now: Instant,
5616            ) -> Option<(BackendId, SocketAddr)> {
5617                Some(("b".to_owned(), SocketAddr::from(([127, 0, 0, 1], 5353))))
5618            }
5619        }
5620        // Feed one datagram from `port` of one address; `true` when it opened
5621        // a flow, `false` when it was shed at the per-source limit.
5622        fn admit(manager: &RefCell<UdpManager>, port: u16) -> bool {
5623            let mut manager = manager.borrow_mut();
5624            manager.handle_input(
5625                ManagerInput::ClientDatagram {
5626                    src: SocketAddr::from(([192, 0, 2, 1], port)),
5627                    payload: b"q",
5628                    backends: &mut OneBackend,
5629                },
5630                Instant::now(),
5631            );
5632            let mut opened = false;
5633            let mut shed = false;
5634            while let Some(out) = manager.poll_output() {
5635                opened |= matches!(out, Output::OpenUpstream { .. });
5636                shed |= matches!(out, Output::Metric(MetricEvent::FlowShedSourceLimit));
5637            }
5638            assert_ne!(opened, shed, "a datagram is either admitted or shed");
5639            opened
5640        }
5641        fn add_cluster(server: &mut Server, max_connections_per_ip: Option<u64>) {
5642            server.notify_proxys(WorkerRequest {
5643                id: "test-add-cluster".to_owned(),
5644                content: RequestType::AddCluster(Cluster {
5645                    cluster_id: "udp-limits".to_owned(),
5646                    max_connections_per_ip,
5647                    ..Default::default()
5648                })
5649                .into(),
5650            });
5651        }
5652
5653        let mut server = bare_server();
5654        let address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
5655        let token = add_udp_listener(&mut server, address);
5656        server.notify(WorkerRequest {
5657            id: "test-set-per-ip".to_owned(),
5658            content: RequestType::SetMaxConnectionsPerIp(1).into(),
5659        });
5660        server.notify(WorkerRequest {
5661            id: "test-set-per-subnet".to_owned(),
5662            content: RequestType::SetMaxConnectionsPerSubnet(1).into(),
5663        });
5664        add_cluster(&mut server, None);
5665        server.notify_proxys(WorkerRequest {
5666            id: "test-add-udp-front".to_owned(),
5667            content: RequestType::AddUdpFrontend(RequestUdpFrontend {
5668                cluster_id: "udp-limits".to_owned(),
5669                address,
5670                tags: Default::default(),
5671            })
5672            .into(),
5673        });
5674        let manager = server
5675            .udp
5676            .borrow()
5677            .manager(token)
5678            .expect("the UDP listener must own a flow manager");
5679
5680        {
5681            let manager = manager.borrow();
5682            let cfg = manager.cluster_config();
5683            assert_eq!(
5684                cfg.max_flows_per_ip, 0,
5685                "the global per-IP default must not apply to UDP"
5686            );
5687            assert_eq!(
5688                cfg.max_flows_per_subnet, 0,
5689                "the global per-subnet default must not apply to UDP"
5690            );
5691        }
5692        assert!(admit(&manager, 1000));
5693        assert!(
5694            admit(&manager, 1001),
5695            "a global limit of 1 must not shed a second UDP flow"
5696        );
5697
5698        add_cluster(&mut server, Some(2));
5699        assert_eq!(manager.borrow().cluster_config().max_flows_per_ip, 2);
5700        assert!(
5701            !admit(&manager, 1002),
5702            "the cluster's own limit of 2 counts the two open flows and sheds a third"
5703        );
5704    }
5705}
5706
5707#[cfg(test)]
5708mod add_cluster_validation_tests {
5709    use sozu_command::proto::command::{LoadBalancingParams, ShardMode};
5710
5711    use super::listener_lifecycle_tests::bare_server;
5712    use super::*;
5713    use crate::{backends::ShardOutcome, load_balancing::affinity_key_from_ip};
5714
5715    const CLUSTER: &str = "sharded";
5716    const ADD_CLUSTER_ID: &str = "test-add-invalid-shard";
5717
5718    /// The worker mirrors `validate_shuffle_sharding` on `AddCluster`
5719    /// (#524): an `AddCluster` that reaches the worker without the master's
5720    /// check, as a TOML reload or a `LoadState` can, with `shard_percent`
5721    /// outside `1..=100` is answered with an error and leaves the cluster
5722    /// unsharded.
5723    ///
5724    /// TO SEE THIS RED: delete the `validate_shuffle_sharding` check from the
5725    /// `AddCluster` arm of `Server::notify_proxys`; the worker then answers
5726    /// `Ok` and arms a shard of two over the four backends.
5727    #[test]
5728    fn an_add_cluster_with_an_invalid_shard_percent_is_refused_and_unsharded() {
5729        let mut server = bare_server();
5730        server.notify_proxys(WorkerRequest {
5731            id: ADD_CLUSTER_ID.to_owned(),
5732            content: RequestType::AddCluster(Cluster {
5733                cluster_id: CLUSTER.to_owned(),
5734                load_balancing: LoadBalancingAlgorithms::RoundRobin as i32,
5735                // The only invalid knob: `shard_min_backends` and
5736                // `shard_mode` are valid on their own.
5737                shard_percent: Some(0),
5738                shard_min_backends: Some(2),
5739                shard_mode: Some(ShardMode::Strict as i32),
5740                ..Default::default()
5741            })
5742            .into(),
5743        });
5744        let response = QUEUE
5745            .with(|queue| {
5746                queue
5747                    .borrow()
5748                    .iter()
5749                    .find(|response| response.id == ADD_CLUSTER_ID)
5750                    .cloned()
5751            })
5752            .expect("the worker must answer the AddCluster");
5753        assert_eq!(
5754            response.status,
5755            ResponseStatus::Failure as i32,
5756            "an invalid shard_percent must be refused: {response:?}"
5757        );
5758        assert_eq!(response.message, "shard_percent must lie in 1..=100");
5759
5760        for index in 0..4u16 {
5761            server.notify_proxys(WorkerRequest {
5762                id: format!("test-add-backend-{index}"),
5763                content: RequestType::AddBackend(AddBackend {
5764                    cluster_id: CLUSTER.to_owned(),
5765                    backend_id: format!("{CLUSTER}-{index}"),
5766                    address: SocketAddr::from(([127, 0, 0, 1], 23_000 + index)).into(),
5767                    load_balancing_parameters: Some(LoadBalancingParams::default()),
5768                    sticky_id: None,
5769                    backup: None,
5770                })
5771                .into(),
5772            });
5773        }
5774        let key = affinity_key_from_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
5775        let mut backends = server.backends.borrow_mut();
5776        let list = backends
5777            .backends
5778            .get_mut(CLUSTER)
5779            .expect("the backends must have created the cluster's list");
5780        assert_eq!(
5781            list.select_with_key(Some(key), Instant::now()).1,
5782            ShardOutcome::Unsharded,
5783            "a refused AddCluster must not arm a shard"
5784        );
5785    }
5786}
5787
5788/// Clearing a cluster's health check resets the backends a probe marked DOWN
5789/// (#1811): no probe will ever mark them UP again.
5790#[cfg(test)]
5791mod remove_health_check_tests {
5792    use sozu_command::proto::command::{HealthCheckConfig, LoadBalancingParams};
5793
5794    use super::listener_lifecycle_tests::bare_server;
5795    use super::*;
5796
5797    const CLUSTER: &str = "health-checked";
5798
5799    fn health_check() -> HealthCheckConfig {
5800        HealthCheckConfig {
5801            uri: "/livez".to_owned(),
5802            interval: 1,
5803            timeout: 1,
5804            healthy_threshold: 1,
5805            unhealthy_threshold: 1,
5806            ..Default::default()
5807        }
5808    }
5809
5810    fn add_cluster(server: &mut Server, health_check: Option<HealthCheckConfig>) {
5811        server.notify_proxys(WorkerRequest {
5812            id: "test-add-cluster".to_owned(),
5813            content: RequestType::AddCluster(Cluster {
5814                cluster_id: CLUSTER.to_owned(),
5815                health_check,
5816                ..Default::default()
5817            })
5818            .into(),
5819        });
5820    }
5821
5822    /// A server whose cluster carries a health check and one backend a probe
5823    /// marked DOWN.
5824    fn server_with_down_backend() -> Server {
5825        let mut server = bare_server();
5826        add_cluster(&mut server, Some(health_check()));
5827        server.notify_proxys(WorkerRequest {
5828            id: "test-add-backend".to_owned(),
5829            content: RequestType::AddBackend(AddBackend {
5830                cluster_id: CLUSTER.to_owned(),
5831                backend_id: format!("{CLUSTER}-0"),
5832                address: SocketAddr::from(([127, 0, 0, 1], 23_100)).into(),
5833                load_balancing_parameters: Some(LoadBalancingParams::default()),
5834                sticky_id: None,
5835                backup: None,
5836            })
5837            .into(),
5838        });
5839        server.backends.borrow().backends[CLUSTER].backends[0]
5840            .borrow_mut()
5841            .health
5842            .record_failure(1);
5843        assert!(!backend_is_healthy(&server), "test setup: backend DOWN");
5844        server
5845    }
5846
5847    fn backend_is_healthy(server: &Server) -> bool {
5848        server.backends.borrow().backends[CLUSTER].backends[0]
5849            .borrow()
5850            .health
5851            .is_healthy()
5852    }
5853
5854    /// TO SEE THIS RED: make `Server::remove_health_check_state` remove the
5855    /// cluster from `health_check_configs` directly instead of calling
5856    /// `set_health_check_config(cluster_id, None)`; the backend stays DOWN.
5857    #[test]
5858    fn remove_health_check_resets_a_backend_marked_down() {
5859        let mut server = server_with_down_backend();
5860        server.notify_proxys(WorkerRequest {
5861            id: "test-remove-health-check".to_owned(),
5862            content: RequestType::RemoveHealthCheck(CLUSTER.to_owned()).into(),
5863        });
5864        assert!(
5865            !server
5866                .backends
5867                .borrow()
5868                .health_check_configs
5869                .contains_key(CLUSTER),
5870            "RemoveHealthCheck must forget the cluster's configuration"
5871        );
5872        assert!(
5873            backend_is_healthy(&server),
5874            "RemoveHealthCheck must reset a backend a probe marked DOWN"
5875        );
5876    }
5877
5878    /// An `AddCluster` upsert without a health check clears it the same way.
5879    ///
5880    /// TO SEE THIS RED: delete the `set_health_check_config` call from
5881    /// `Server::add_cluster`; the backend stays DOWN. The in-flight probes the
5882    /// `AddCluster` arm drops are not covered: that needs a live probe socket.
5883    #[test]
5884    fn add_cluster_without_health_check_resets_a_backend_marked_down() {
5885        let mut server = server_with_down_backend();
5886        add_cluster(&mut server, None);
5887        assert!(
5888            !server
5889                .backends
5890                .borrow()
5891                .health_check_configs
5892                .contains_key(CLUSTER),
5893            "an AddCluster without a health check must forget the configuration"
5894        );
5895        assert!(
5896            backend_is_healthy(&server),
5897            "an AddCluster without a health check must reset a backend marked DOWN"
5898        );
5899    }
5900}
5901
5902/// The SCM hand-off a worker upgrade performs: the retiring worker's listening
5903/// sockets travel to the new worker over the SCM socket, and the new worker
5904/// must ADOPT them instead of binding fresh ones.
5905/// `RemoveCluster` removes the cluster's frontends and backends from the
5906/// worker's proxies, the way `ConfigState::remove_cluster`
5907/// (`command/src/state.rs`) removes them from the state.
5908#[cfg(test)]
5909mod remove_cluster_cascade_tests {
5910    use sozu_command::{
5911        config::ListenerBuilder,
5912        proto::command::{
5913            LoadBalancingParams, PathRule, RequestHttpFrontend, RequestTcpFrontend,
5914            RequestUdpFrontend, RulePosition, SocketAddress,
5915        },
5916    };
5917
5918    use super::listener_lifecycle_tests::bare_server;
5919    use super::*;
5920    use crate::{L7ListenerHandler, protocol::http::parser::Method, testing::provide_port};
5921
5922    const REMOVED: &str = "cascade_removed";
5923    const KEPT: &str = "cascade_kept";
5924
5925    /// The answers the worker queues for `request`. Events it queues along
5926    /// the way (a removed backend's, for instance) are not answers and are
5927    /// left out.
5928    fn send(server: &mut Server, id: &str, request: RequestType) -> Vec<WorkerResponse> {
5929        server.notify_proxys(WorkerRequest {
5930            id: id.to_owned(),
5931            content: request.into(),
5932        });
5933        QUEUE.with(|queue| {
5934            queue
5935                .borrow_mut()
5936                .drain(..)
5937                .filter(|response| response.id != "EVENT")
5938                .collect()
5939        })
5940    }
5941
5942    fn http_front(cluster_id: Option<&str>, hostname: &str, path: &str) -> RequestHttpFrontend {
5943        RequestHttpFrontend {
5944            cluster_id: cluster_id.map(str::to_owned),
5945            hostname: hostname.to_owned(),
5946            path: PathRule::prefix(path.to_owned()),
5947            address: SocketAddress::new_v4(127, 0, 0, 1, 0),
5948            ..Default::default()
5949        }
5950    }
5951
5952    fn backend(cluster_id: &str, port: u16) -> AddBackend {
5953        AddBackend {
5954            cluster_id: cluster_id.to_owned(),
5955            backend_id: format!("{cluster_id}-0"),
5956            address: SocketAddress::new_v4(127, 0, 0, 1, port),
5957            load_balancing_parameters: Some(LoadBalancingParams::default()),
5958            ..Default::default()
5959        }
5960    }
5961
5962    /// Admission leases belong to live frontend-session tokens, not to the
5963    /// current cluster configuration object. Removing a cluster therefore
5964    /// leaves the old session's contribution in both counters; re-adding the
5965    /// same id lets a new session contribute alongside it; closing either
5966    /// session releases exactly that token's contribution.
5967    ///
5968    /// To SEE THIS RED: change `untrack_all_cluster_ip`'s per-IP decrement from
5969    /// `saturating_sub(1)` to `saturating_sub(0)`. The old lease then remains at
5970    /// two and the post-close assertion fails, proving this observes release
5971    /// rather than only reconfiguration.
5972    #[test]
5973    fn remove_then_readd_same_cluster_id_preserves_and_releases_live_admission_leases() {
5974        let mut server = bare_server();
5975        let cluster_id = ClusterId::from(REMOVED);
5976        let source: IpAddr = "203.0.113.9".parse().expect("test address must parse");
5977        let subnet = server.sessions.borrow().subnet_key(&source);
5978        let old_token = Token(60_001);
5979        let new_token = Token(60_002);
5980
5981        for response in send(
5982            &mut server,
5983            "add-old-incarnation",
5984            RequestType::AddCluster(Cluster {
5985                cluster_id: REMOVED.to_owned(),
5986                max_connections_per_ip: Some(2),
5987                max_connections_per_subnet: Some(2),
5988                ..Default::default()
5989            }),
5990        ) {
5991            assert_eq!(response.status, ResponseStatus::Ok as i32);
5992        }
5993        server.sessions.borrow_mut().track_cluster_connection(
5994            old_token,
5995            cluster_id.clone(),
5996            source,
5997            Some(2),
5998        );
5999
6000        for response in send(
6001            &mut server,
6002            "remove-old-incarnation",
6003            RequestType::RemoveCluster(REMOVED.to_owned()),
6004        ) {
6005            assert_eq!(response.status, ResponseStatus::Ok as i32);
6006        }
6007        for response in send(
6008            &mut server,
6009            "add-new-incarnation",
6010            RequestType::AddCluster(Cluster {
6011                cluster_id: REMOVED.to_owned(),
6012                max_connections_per_ip: Some(2),
6013                max_connections_per_subnet: Some(2),
6014                ..Default::default()
6015            }),
6016        ) {
6017            assert_eq!(response.status, ResponseStatus::Ok as i32);
6018        }
6019
6020        {
6021            let mut sessions = server.sessions.borrow_mut();
6022            assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 1);
6023            assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 1);
6024            sessions.track_cluster_connection(new_token, cluster_id, source, Some(2));
6025            assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 2);
6026            assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 2);
6027
6028            sessions.untrack_all_cluster_ip(old_token);
6029            assert_eq!(sessions.connections_per_cluster_ip[REMOVED][&source], 1);
6030            assert_eq!(sessions.connections_per_cluster_subnet[REMOVED][&subnet], 1);
6031            assert!(sessions.cluster_ip_tracks.contains_key(&new_token));
6032            assert!(sessions.cluster_subnet_tracks.contains_key(&new_token));
6033
6034            sessions.untrack_all_cluster_ip(new_token);
6035            assert!(!sessions.connections_per_cluster_ip.contains_key(REMOVED));
6036            assert!(
6037                !sessions
6038                    .connections_per_cluster_subnet
6039                    .contains_key(REMOVED)
6040            );
6041        }
6042    }
6043
6044    /// The route an HTTP request to `hostname` + `path` resolves to on the
6045    /// worker's only HTTP listener, as a cluster id (`Some(None)` for a route
6046    /// that names no cluster), or `None` when nothing routes it.
6047    fn http_route(
6048        server: &Server,
6049        address: SocketAddress,
6050        hostname: &str,
6051        path: &str,
6052    ) -> Option<Option<String>> {
6053        let http = server.http.borrow();
6054        let token = http
6055            .listener_token(&SocketAddr::from(address).into())
6056            .expect("the test HTTP listener must own a token");
6057        let listener = http
6058            .get_listener(&token)
6059            .expect("the test HTTP listener must exist");
6060        listener
6061            .borrow()
6062            .frontend_from_request(hostname, path, &Method::Get)
6063            .ok()
6064            .map(|route| route.cluster_id.map(|id| id.to_string()))
6065    }
6066
6067    /// A removed cluster's HTTP routes and backends are gone from the worker,
6068    /// while another cluster's and a route that names no cluster stay. The
6069    /// worker answers the `RemoveCluster` once, and a removal of one of the
6070    /// cluster's objects sent afterwards (what a main process that predates
6071    /// the cascade still sends) answers ok instead of failing on a route the
6072    /// cascade already removed.
6073    #[test]
6074    fn remove_cluster_removes_its_routes_and_backends_from_the_worker() {
6075        let mut server = bare_server();
6076        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6077        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6078
6079        let mut removed_front = http_front(Some(REMOVED), "removed.test", "/");
6080        removed_front.address = http_address;
6081        // A pre-rule: unlike a tree rule, removing it twice is an error in
6082        // `Router::remove_pre_rule` (`lib/src/router/mod.rs`).
6083        removed_front.position = RulePosition::Pre.into();
6084        let mut deny_front = http_front(None, "removed.test", "/deny");
6085        deny_front.address = http_address;
6086        let mut kept_front = http_front(Some(KEPT), "kept.test", "/");
6087        kept_front.address = http_address;
6088        let removed_tcp_front = RequestTcpFrontend {
6089            cluster_id: REMOVED.to_owned(),
6090            address: tcp_address,
6091            ..Default::default()
6092        };
6093
6094        let setup = vec![
6095            RequestType::AddHttpListener(
6096                ListenerBuilder::new_http(http_address)
6097                    .to_http(None)
6098                    .expect("could not build the test HTTP listener config"),
6099            ),
6100            RequestType::AddTcpListener(
6101                ListenerBuilder::new_tcp(tcp_address)
6102                    .to_tcp(None)
6103                    .expect("could not build the test TCP listener config"),
6104            ),
6105            RequestType::AddCluster(Cluster {
6106                cluster_id: REMOVED.to_owned(),
6107                ..Default::default()
6108            }),
6109            RequestType::AddCluster(Cluster {
6110                cluster_id: KEPT.to_owned(),
6111                ..Default::default()
6112            }),
6113            RequestType::AddHttpFrontend(removed_front.clone()),
6114            RequestType::AddHttpFrontend(deny_front),
6115            RequestType::AddHttpFrontend(kept_front),
6116            RequestType::AddTcpFrontend(removed_tcp_front.clone()),
6117            RequestType::AddBackend(backend(REMOVED, 1026)),
6118            RequestType::AddBackend(backend(KEPT, 1027)),
6119        ];
6120        for (index, request) in setup.into_iter().enumerate() {
6121            for response in send(&mut server, &format!("setup-{index}"), request) {
6122                assert_eq!(
6123                    response.status,
6124                    ResponseStatus::Ok as i32,
6125                    "setup must succeed: {response:?}"
6126                );
6127            }
6128        }
6129        assert_eq!(
6130            http_route(&server, http_address, "removed.test", "/"),
6131            Some(Some(REMOVED.to_owned())),
6132            "premise: the removed cluster's hostname routes to it"
6133        );
6134
6135        let responses = send(
6136            &mut server,
6137            "remove-cluster",
6138            RequestType::RemoveCluster(REMOVED.to_owned()),
6139        );
6140        assert_eq!(
6141            responses.iter().map(|r| r.id.as_str()).collect::<Vec<_>>(),
6142            vec!["remove-cluster"],
6143            "the worker must answer the RemoveCluster exactly once: {responses:?}"
6144        );
6145        assert_eq!(
6146            responses[0].status,
6147            ResponseStatus::Ok as i32,
6148            "RemoveCluster must succeed: {responses:?}"
6149        );
6150
6151        assert_eq!(
6152            http_route(&server, http_address, "removed.test", "/"),
6153            None,
6154            "the removed cluster's hostname must no longer route"
6155        );
6156        assert_eq!(
6157            http_route(&server, http_address, "removed.test", "/deny"),
6158            Some(None),
6159            "a route that names no cluster must survive the removal of a cluster on its hostname"
6160        );
6161        assert_eq!(
6162            http_route(&server, http_address, "kept.test", "/"),
6163            Some(Some(KEPT.to_owned())),
6164            "another cluster's route must stay"
6165        );
6166        let backends = server.backends.borrow();
6167        assert!(
6168            !backends.backends.contains_key(REMOVED),
6169            "the removed cluster's backends must be gone from the BackendMap"
6170        );
6171        assert!(
6172            backends.backends.contains_key(KEPT),
6173            "another cluster's backends must stay"
6174        );
6175        drop(backends);
6176
6177        for (index, request) in [
6178            RequestType::RemoveHttpFrontend(removed_front),
6179            RequestType::RemoveTcpFrontend(removed_tcp_front),
6180            RequestType::RemoveBackend(RemoveBackend {
6181                cluster_id: REMOVED.to_owned(),
6182                backend_id: format!("{REMOVED}-0"),
6183                address: SocketAddress::new_v4(127, 0, 0, 1, 1026),
6184            }),
6185        ]
6186        .into_iter()
6187        .enumerate()
6188        {
6189            let responses = send(&mut server, &format!("follow-up-{index}"), request);
6190            assert!(
6191                !responses.is_empty()
6192                    && responses
6193                        .iter()
6194                        .all(|r| r.status == ResponseStatus::Ok as i32),
6195                "a removal the cascade already performed must answer ok: {responses:?}"
6196            );
6197        }
6198    }
6199
6200    /// The route key names no cluster, so cluster B can take the key cluster
6201    /// A's frontend used. A removal of A's frontend arriving afterwards must
6202    /// leave B's route in the worker's router, whether A is gone (the removal
6203    /// answers ok: A's frontend went with A) or still there (refused).
6204    #[test]
6205    fn a_stale_frontend_removal_never_removes_another_clusters_route() {
6206        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6207        let route = |cluster_id: &str| {
6208            let mut front = http_front(Some(cluster_id), "reused.test", "/");
6209            front.address = http_address;
6210            front
6211        };
6212        let cluster = |cluster_id: &str| {
6213            RequestType::AddCluster(Cluster {
6214                cluster_id: cluster_id.to_owned(),
6215                ..Default::default()
6216            })
6217        };
6218        let listener = || {
6219            RequestType::AddHttpListener(
6220                ListenerBuilder::new_http(http_address)
6221                    .to_http(None)
6222                    .expect("could not build the test HTTP listener config"),
6223            )
6224        };
6225
6226        for (a_is_gone, setup, expected_status) in [
6227            (
6228                true,
6229                vec![
6230                    listener(),
6231                    cluster("stale_a"),
6232                    RequestType::AddHttpFrontend(route("stale_a")),
6233                    RequestType::RemoveCluster("stale_a".to_owned()),
6234                    cluster("stale_b"),
6235                    RequestType::AddHttpFrontend(route("stale_b")),
6236                ],
6237                ResponseStatus::Ok,
6238            ),
6239            (
6240                false,
6241                vec![
6242                    listener(),
6243                    cluster("stale_a"),
6244                    cluster("stale_b"),
6245                    RequestType::AddHttpFrontend(route("stale_b")),
6246                ],
6247                ResponseStatus::Failure,
6248            ),
6249        ] {
6250            let mut server = bare_server();
6251            for (index, request) in setup.into_iter().enumerate() {
6252                for response in send(&mut server, &format!("setup-{index}"), request) {
6253                    assert_eq!(
6254                        response.status,
6255                        ResponseStatus::Ok as i32,
6256                        "setup must succeed: {response:?}"
6257                    );
6258                }
6259            }
6260            assert_eq!(
6261                http_route(&server, http_address, "reused.test", "/"),
6262                Some(Some("stale_b".to_owned())),
6263                "premise: cluster B owns the route"
6264            );
6265
6266            let responses = send(
6267                &mut server,
6268                "stale-removal",
6269                RequestType::RemoveHttpFrontend(route("stale_a")),
6270            );
6271            assert_eq!(
6272                responses.iter().map(|r| r.status).collect::<Vec<_>>(),
6273                vec![expected_status as i32],
6274                "A gone: {a_is_gone}, answers: {responses:?}"
6275            );
6276            assert_eq!(
6277                http_route(&server, http_address, "reused.test", "/"),
6278                Some(Some("stale_b".to_owned())),
6279                "cluster B's route must survive a stale removal naming cluster A (A gone: {a_is_gone})"
6280            );
6281        }
6282    }
6283
6284    /// TCP and UDP frontends are removed from their listener by address,
6285    /// whatever cluster owns it. Cluster A frees address X, B takes X, and A
6286    /// comes back with a frontend on Y. A stale removal of A's frontend on X
6287    /// then finds A's frontend list but no match in it (`NoChange`, not
6288    /// `NotFound`), and must still leave B's listener on X alone.
6289    #[test]
6290    fn a_stale_tcp_or_udp_frontend_removal_never_unmaps_another_clusters_listener() {
6291        let cluster = |cluster_id: &str| {
6292            RequestType::AddCluster(Cluster {
6293                cluster_id: cluster_id.to_owned(),
6294                ..Default::default()
6295            })
6296        };
6297        for udp in [false, true] {
6298            let x = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6299            let y = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6300            let listener = |address: SocketAddress| {
6301                if udp {
6302                    RequestType::AddUdpListener(
6303                        ListenerBuilder::new_udp(address)
6304                            .to_udp(None)
6305                            .expect("could not build the test UDP listener config"),
6306                    )
6307                } else {
6308                    RequestType::AddTcpListener(
6309                        ListenerBuilder::new_tcp(address)
6310                            .to_tcp(None)
6311                            .expect("could not build the test TCP listener config"),
6312                    )
6313                }
6314            };
6315            let front = |cluster_id: &str, address: SocketAddress| {
6316                if udp {
6317                    (
6318                        RequestType::AddUdpFrontend(RequestUdpFrontend {
6319                            cluster_id: cluster_id.to_owned(),
6320                            address,
6321                            ..Default::default()
6322                        }),
6323                        RequestType::RemoveUdpFrontend(RequestUdpFrontend {
6324                            cluster_id: cluster_id.to_owned(),
6325                            address,
6326                            ..Default::default()
6327                        }),
6328                    )
6329                } else {
6330                    (
6331                        RequestType::AddTcpFrontend(RequestTcpFrontend {
6332                            cluster_id: cluster_id.to_owned(),
6333                            address,
6334                            ..Default::default()
6335                        }),
6336                        RequestType::RemoveTcpFrontend(RequestTcpFrontend {
6337                            cluster_id: cluster_id.to_owned(),
6338                            address,
6339                            ..Default::default()
6340                        }),
6341                    )
6342                }
6343            };
6344            let owner = |server: &Server| {
6345                let key = SocketAddr::from(x).into();
6346                if udp {
6347                    server.udp.borrow().listener_cluster(&key)
6348                } else {
6349                    server.tcp.borrow().listener_cluster(&key)
6350                }
6351                .map(|id| id.to_string())
6352            };
6353
6354            let mut server = bare_server();
6355            for (index, request) in [
6356                listener(x),
6357                listener(y),
6358                cluster("stale_a"),
6359                front("stale_a", x).0,
6360                RequestType::RemoveCluster("stale_a".to_owned()),
6361                cluster("stale_b"),
6362                front("stale_b", x).0,
6363                cluster("stale_a"),
6364                front("stale_a", y).0,
6365            ]
6366            .into_iter()
6367            .enumerate()
6368            {
6369                for response in send(&mut server, &format!("setup-{index}"), request) {
6370                    assert_eq!(
6371                        response.status,
6372                        ResponseStatus::Ok as i32,
6373                        "setup must succeed (udp: {udp}): {response:?}"
6374                    );
6375                }
6376            }
6377            assert_eq!(
6378                owner(&server).as_deref(),
6379                Some("stale_b"),
6380                "premise: cluster B owns the listener on X (udp: {udp})"
6381            );
6382
6383            let responses = send(&mut server, "stale-removal", front("stale_a", x).1);
6384            assert_eq!(
6385                responses.iter().map(|r| r.status).collect::<Vec<_>>(),
6386                vec![ResponseStatus::Failure as i32],
6387                "the stale removal must be refused (udp: {udp}): {responses:?}"
6388            );
6389            assert_eq!(
6390                owner(&server).as_deref(),
6391                Some("stale_b"),
6392                "cluster B must keep the listener on X after a stale removal naming A (udp: {udp})"
6393            );
6394        }
6395    }
6396}
6397
6398#[cfg(test)]
6399mod scm_listener_handoff_tests {
6400    use std::{
6401        net::{TcpStream as StdTcpStream, UdpSocket as StdUdpSocket},
6402        os::fd::{IntoRawFd, RawFd},
6403    };
6404
6405    use sozu_command::{config::ListenerBuilder, proto::command::SocketAddress};
6406
6407    use super::*;
6408    use crate::{
6409        ProxyError,
6410        socket::{server_bind, udp_bind},
6411        testing::{ServerParts, prebuild_server, provide_port},
6412    };
6413
6414    /// A `ConfigState` holding one ACTIVE listener per protocol, i.e. what the
6415    /// main process carries into `produce_initial_state` for a worker upgrade.
6416    fn state_with_four_active_listeners(
6417        http_address: SocketAddress,
6418        https_address: SocketAddress,
6419        tcp_address: SocketAddress,
6420        udp_address: SocketAddress,
6421    ) -> ConfigState {
6422        let mut state = ConfigState::new();
6423        let adds: Vec<(Request, SocketAddress, ListenerType)> = vec![
6424            (
6425                RequestType::AddHttpListener(
6426                    ListenerBuilder::new_http(http_address)
6427                        .to_http(None)
6428                        .expect("could not build the test HTTP listener config"),
6429                )
6430                .into(),
6431                http_address,
6432                ListenerType::Http,
6433            ),
6434            (
6435                RequestType::AddHttpsListener(
6436                    ListenerBuilder::new_https(https_address)
6437                        .to_tls(None)
6438                        .expect("could not build the test HTTPS listener config"),
6439                )
6440                .into(),
6441                https_address,
6442                ListenerType::Https,
6443            ),
6444            (
6445                RequestType::AddTcpListener(
6446                    ListenerBuilder::new_tcp(tcp_address)
6447                        .to_tcp(None)
6448                        .expect("could not build the test TCP listener config"),
6449                )
6450                .into(),
6451                tcp_address,
6452                ListenerType::Tcp,
6453            ),
6454            (
6455                RequestType::AddUdpListener(
6456                    ListenerBuilder::new_udp(udp_address)
6457                        .to_udp(None)
6458                        .expect("could not build the test UDP listener config"),
6459                )
6460                .into(),
6461                udp_address,
6462                ListenerType::Udp,
6463            ),
6464        ];
6465        for (add, address, proxy) in adds {
6466            state.dispatch(&add).expect("could not add the listener");
6467            state
6468                .dispatch(
6469                    &RequestType::ActivateListener(ActivateListener {
6470                        interface: None,
6471                        address,
6472                        proxy: proxy.into(),
6473                        from_scm: false,
6474                    })
6475                    .into(),
6476                )
6477                .expect("could not mark the listener active");
6478        }
6479        state
6480    }
6481
6482    /// The SCM table for `label`, so a test can assert on retention per protocol.
6483    fn inherited_table<'a>(server: &'a Server, label: &str) -> &'a Vec<(ListenerKey, RawFd)> {
6484        let scm_listeners = server
6485            .scm_listeners
6486            .as_ref()
6487            .expect("scm_listeners must be present");
6488        match label {
6489            "HTTP" => &scm_listeners.http,
6490            "HTTPS" => &scm_listeners.tls,
6491            "TCP" => &scm_listeners.tcp,
6492            "UDP" => &scm_listeners.udp,
6493            other => panic!("unknown protocol label {other}"),
6494        }
6495    }
6496
6497    /// Whether `fd` is still the listening socket bound to `port`.
6498    ///
6499    /// Deliberately identity-aware rather than a bare "is it open" check: the
6500    /// test binary runs its tests in parallel, so a descriptor closed here can
6501    /// be handed straight back out to an unrelated file opened by another test
6502    /// and a liveness-only probe would call that "still open". Every port in
6503    /// these tests comes from `provide_port()` and is therefore unique to one
6504    /// test, so `getsockname` answering with that exact port is proof the
6505    /// descriptor is still the same socket.
6506    fn descriptor_is_socket_on_port(fd: RawFd, port: u16) -> bool {
6507        let mut storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
6508        let mut length = std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
6509        // SAFETY: `getsockname` writes at most `length` bytes into `storage`
6510        // and updates `length`; both outlive the call. A closed, reused or
6511        // non-socket descriptor answers -1, which is the answer under test.
6512        let result = unsafe {
6513            libc::getsockname(
6514                fd,
6515                (&raw mut storage).cast::<libc::sockaddr>(),
6516                &raw mut length,
6517            )
6518        };
6519        if result != 0 || storage.ss_family != libc::AF_INET as libc::sa_family_t {
6520            return false;
6521        }
6522        // SAFETY: the family check above proves the kernel filled a
6523        // `sockaddr_in`, which `sockaddr_storage` is aligned and sized for.
6524        let inet = unsafe { &*(&raw const storage).cast::<libc::sockaddr_in>() };
6525        u16::from_be(inet.sin_port) == port
6526    }
6527
6528    /// The datagram queued on the inherited UDP socket before the hand-off.
6529    const QUEUED_DATAGRAM: &[u8] = b"queued-before-the-handoff";
6530
6531    /// A worker that inherits its predecessor's listening sockets must adopt
6532    /// them, not bind its own and leave the inherited descriptors unused.
6533    ///
6534    /// `fork_main_into_worker` (`bin/src/worker.rs`) sends the retiring
6535    /// worker's descriptors over the SCM socket immediately after the fork,
6536    /// before the main process sends anything else, and
6537    /// `ConfigState::produce_initial_state` carries one `ActivateListener` per
6538    /// active listener. `Server::new` must therefore have received those
6539    /// descriptors BEFORE it applies the initial state: otherwise every
6540    /// `ActivateListener` takes the `server_bind` / `udp_bind` branch — which
6541    /// succeeds only because of `SO_REUSEPORT` — and the inherited descriptor
6542    /// is dropped unused, discarding the accept backlog (TCP/HTTP/HTTPS) and
6543    /// the receive buffer (UDP) the hand-off exists to preserve.
6544    ///
6545    /// The three TCP-family assertions read `accept_queue`, which
6546    /// `notify_activate_listener` fills through `Server::accept` right after a
6547    /// successful activation; the UDP assertion reads the listener's own
6548    /// socket, since UDP has no accept path.
6549    ///
6550    /// To SEE THIS RED: in `Server::new`, move the `receive_listeners()` block
6551    /// back below the `if let Some(state) = initial_state` block. All four
6552    /// assertions fail — each listener binds its own socket, and the queued
6553    /// connections and datagram stay on the abandoned descriptors.
6554    ///
6555    /// The `TCP_NODELAY` assertions guard the backlog drain of
6556    /// sozu-proxy/sozu#1586. To SEE THEM RED: delete the
6557    /// `self.nodelay_backlog = true;` line from `HttpListener::activate`. The
6558    /// listener still gets the flag, but the connection queued before it did
6559    /// is accepted without it.
6560    #[test]
6561    fn inherited_listener_sockets_are_adopted_by_the_initial_activation() {
6562        let ServerParts {
6563            event_loop,
6564            sessions,
6565            pool,
6566            backends,
6567            client_scm_socket,
6568            server_scm_socket,
6569            server_config,
6570            ..
6571        } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
6572        let (_command_channel, proxy_channel) =
6573            Channel::generate(1000, 10000).expect("could not generate a test channel");
6574
6575        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6576        let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6577        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6578        let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6579
6580        // The retiring worker's sockets, bound exactly the way a live worker
6581        // binds them.
6582        let http_listener = server_bind(http_address.into(), None)
6583            .expect("could not bind the inherited HTTP socket");
6584        let https_listener = server_bind(https_address.into(), None)
6585            .expect("could not bind the inherited HTTPS socket");
6586        let tcp_listener =
6587            server_bind(tcp_address.into(), None).expect("could not bind the inherited TCP socket");
6588        let udp_socket =
6589            udp_bind(udp_address.into(), None).expect("could not bind the inherited UDP socket");
6590
6591        // Queue on each inherited socket exactly what the hand-off exists to
6592        // preserve: a completed connection in the accept backlog, and a
6593        // datagram in the receive buffer. The clients stay alive for the whole
6594        // test so the connections are not closed from under the backlog.
6595        let http_client = StdTcpStream::connect::<SocketAddr>(http_address.into())
6596            .expect("could not queue a connection on the inherited HTTP socket");
6597        let https_client = StdTcpStream::connect::<SocketAddr>(https_address.into())
6598            .expect("could not queue a connection on the inherited HTTPS socket");
6599        let tcp_client = StdTcpStream::connect::<SocketAddr>(tcp_address.into())
6600            .expect("could not queue a connection on the inherited TCP socket");
6601        let datagram_sender =
6602            StdUdpSocket::bind("127.0.0.1:0").expect("could not bind the test datagram sender");
6603        datagram_sender
6604            .send_to(QUEUED_DATAGRAM, SocketAddr::from(udp_address))
6605            .expect("could not queue a datagram on the inherited UDP socket");
6606
6607        // The main process's side of the hand-off: send the descriptors, then
6608        // close its own copies, exactly as `fork_main_into_worker` does.
6609        let listeners = Listeners {
6610            http: vec![(http_address.into(), http_listener.into_raw_fd())],
6611            tls: vec![(https_address.into(), https_listener.into_raw_fd())],
6612            tcp: vec![(tcp_address.into(), tcp_listener.into_raw_fd())],
6613            udp: vec![(udp_address.into(), udp_socket.into_raw_fd())],
6614        };
6615        client_scm_socket
6616            .send_listeners(&listeners)
6617            .expect("could not send the inherited listeners");
6618        listeners.close();
6619
6620        // The state the main process writes for the new worker. Built through
6621        // `ConfigState` and `produce_initial_state` rather than hand-rolled, so
6622        // the emission order under test is the real generator's: this breaks if
6623        // `generate_requests` ever stops pairing each `AddXxxListener` with the
6624        // `ActivateListener` that follows it.
6625        let initial_state =
6626            state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address)
6627                .produce_initial_state();
6628        assert_eq!(
6629            initial_state.requests.len(),
6630            8,
6631            "the generator must emit one Add and one Activate per active listener"
6632        );
6633
6634        let server = Server::new(
6635            event_loop,
6636            proxy_channel,
6637            server_scm_socket,
6638            sessions,
6639            pool,
6640            backends,
6641            None,
6642            None,
6643            None,
6644            server_config,
6645            Some(initial_state),
6646            false,
6647        )
6648        .expect("could not build the test server");
6649
6650        let accepted: Vec<Protocol> = server
6651            .accept_queue
6652            .iter()
6653            .map(|(_, _, protocol, _, _)| *protocol)
6654            .collect();
6655        assert!(
6656            accepted.contains(&Protocol::HTTPListen),
6657            "the connection queued on the inherited HTTP socket must survive the hand-off, got {accepted:?}"
6658        );
6659        assert!(
6660            accepted.contains(&Protocol::HTTPSListen),
6661            "the connection queued on the inherited HTTPS socket must survive the hand-off, got {accepted:?}"
6662        );
6663        assert!(
6664            accepted.contains(&Protocol::TCPListen),
6665            "the connection queued on the inherited TCP socket must survive the hand-off, got {accepted:?}"
6666        );
6667
6668        // What those queued connections are handed to the sessions with. They
6669        // completed their handshake BEFORE the new worker set `TCP_NODELAY` on
6670        // the inherited socket, and a socket inherits the flag when its
6671        // handshake completes, not when it is accepted, so the listener alone
6672        // cannot have given it to them: the first accept drain after an
6673        // activation must. And the peer address queued with each one is the
6674        // one `accept(2)` returned, which is the client's own address.
6675        for (protocol, client) in [
6676            (Protocol::HTTPListen, &http_client),
6677            (Protocol::HTTPSListen, &https_client),
6678            (Protocol::TCPListen, &tcp_client),
6679        ] {
6680            let (sock, _, _, _, peer) = server
6681                .accept_queue
6682                .iter()
6683                .find(|(_, _, queued, _, _)| *queued == protocol)
6684                .expect("the queued connection was asserted above");
6685            assert_eq!(
6686                socket2::SockRef::from(sock).tcp_nodelay().ok(),
6687                Some(true),
6688                "a {protocol:?} connection queued before the hand-off must carry TCP_NODELAY"
6689            );
6690            assert_eq!(
6691                *peer,
6692                client.local_addr().expect("the test client has an address"),
6693                "the {protocol:?} peer address must be the client's"
6694            );
6695        }
6696
6697        let (_, adopted_udp_socket) = server
6698            .udp
6699            .borrow_mut()
6700            .give_back_listeners()
6701            .pop()
6702            .expect("the UDP listener must hold a socket after activation");
6703        let mut buffer = [0u8; 64];
6704        let received = adopted_udp_socket.recv_from(&mut buffer);
6705        match received {
6706            Ok((length, _)) => assert_eq!(
6707                &buffer[..length],
6708                QUEUED_DATAGRAM,
6709                "the datagram queued on the inherited UDP socket must survive the hand-off"
6710            ),
6711            Err(error) => panic!(
6712                "the datagram queued on the inherited UDP socket must survive the hand-off, got {error:?}"
6713            ),
6714        }
6715    }
6716
6717    /// An `ActivateListener` for a listener that is ALREADY active must dispose
6718    /// of the inherited descriptor deliberately, not leave it in the table.
6719    ///
6720    /// `activate()` short-circuits on `if self.active`, so nothing adopts that
6721    /// descriptor while the listener holds the address — a deactivate plus
6722    /// reactivate would, which is why `InheritedSocketFate::Refused` documents
6723    /// the close as a trade rather than an impossibility. Leaving it in the SCM
6724    /// table is not neutral either: it is the retiring worker's listening
6725    /// socket, still bound with `SO_REUSEPORT` and registered with no event
6726    /// loop, which is expected to keep taking a share of new connections that
6727    /// nothing accepts. Nothing here measures that expectation — the assertion
6728    /// below checks only that the descriptor is gone from the table and closed.
6729    ///
6730    /// Receiving the SCM listeners before the initial state means the ordinary
6731    /// upgrade no longer reaches this state, so the test constructs it directly.
6732    ///
6733    /// To SEE THIS RED: make any arm's `InheritedSocketFate::Refused` branch
6734    /// return `None` without discarding — that protocol's descriptor stays in the
6735    /// table and open, and both assertions below fail. This guards the
6736    /// retain-everything regression; the ORIGINAL defect closed the descriptor
6737    /// here too, by dropping the wrapper, and is guarded by
6738    /// `inherited_listener_sockets_are_adopted_by_the_initial_activation`
6739    /// instead.
6740    #[test]
6741    fn a_repeated_activation_closes_the_descriptor_it_cannot_adopt_now() {
6742        let ServerParts {
6743            event_loop,
6744            sessions,
6745            pool,
6746            backends,
6747            server_scm_socket,
6748            server_config,
6749            ..
6750        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
6751        let (_command_channel, proxy_channel) =
6752            Channel::generate(1000, 10000).expect("could not generate a test channel");
6753
6754        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6755        let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6756        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6757        let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6758
6759        // A worker whose four listeners are up on their OWN sockets: the initial
6760        // state activates them and `scm_listeners` is empty, every ordinary start.
6761        let initial_state =
6762            state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address)
6763                .produce_initial_state();
6764        let mut server = Server::new(
6765            event_loop,
6766            proxy_channel,
6767            server_scm_socket,
6768            sessions,
6769            pool,
6770            backends,
6771            None,
6772            None,
6773            None,
6774            server_config,
6775            Some(initial_state),
6776            false,
6777        )
6778        .expect("could not build the test server");
6779
6780        // Now hand that already-active worker a descriptor per address.
6781        let http_fd = server_bind(http_address.into(), None)
6782            .expect("could not bind the spare HTTP socket")
6783            .into_raw_fd();
6784        let https_fd = server_bind(https_address.into(), None)
6785            .expect("could not bind the spare HTTPS socket")
6786            .into_raw_fd();
6787        let tcp_fd = server_bind(tcp_address.into(), None)
6788            .expect("could not bind the spare TCP socket")
6789            .into_raw_fd();
6790        let udp_fd = udp_bind(udp_address.into(), None)
6791            .expect("could not bind the spare UDP socket")
6792            .into_raw_fd();
6793        {
6794            let scm_listeners = server
6795                .scm_listeners
6796                .as_mut()
6797                .expect("Server::new must have populated scm_listeners");
6798            scm_listeners.http.push((http_address.into(), http_fd));
6799            scm_listeners.tls.push((https_address.into(), https_fd));
6800            scm_listeners.tcp.push((tcp_address.into(), tcp_fd));
6801            scm_listeners.udp.push((udp_address.into(), udp_fd));
6802        }
6803
6804        for (address, proxy) in [
6805            (http_address, ListenerType::Http),
6806            (https_address, ListenerType::Https),
6807            (tcp_address, ListenerType::Tcp),
6808            (udp_address, ListenerType::Udp),
6809        ] {
6810            let response = server.notify_activate_listener(
6811                "test-repeat",
6812                &ActivateListener {
6813                    interface: None,
6814                    address,
6815                    proxy: proxy.into(),
6816                    from_scm: false,
6817                },
6818            );
6819            assert_eq!(
6820                response.status,
6821                ResponseStatus::Ok as i32,
6822                "a repeated {proxy:?} activation must still answer ok: {response:?}"
6823            );
6824        }
6825
6826        let scm_listeners = server
6827            .scm_listeners
6828            .as_ref()
6829            .expect("scm_listeners must still be present");
6830        for (label, table, fd, port) in [
6831            (
6832                "HTTP",
6833                &scm_listeners.http,
6834                http_fd,
6835                http_address.port as u16,
6836            ),
6837            (
6838                "HTTPS",
6839                &scm_listeners.tls,
6840                https_fd,
6841                https_address.port as u16,
6842            ),
6843            ("TCP", &scm_listeners.tcp, tcp_fd, tcp_address.port as u16),
6844            ("UDP", &scm_listeners.udp, udp_fd, udp_address.port as u16),
6845        ] {
6846            assert!(
6847                !table.iter().any(|(_, entry)| *entry == fd),
6848                "the inherited {label} descriptor this activation cannot adopt must leave the SCM table"
6849            );
6850            assert!(
6851                !descriptor_is_socket_on_port(fd, port),
6852                "the inherited {label} descriptor this activation cannot adopt must be closed, \
6853                 not left bound in the address's SO_REUSEPORT group, where it is expected — \
6854                 inferred, not measured here — to go on taking a share of new connections"
6855            );
6856        }
6857    }
6858
6859    /// An inherited descriptor whose address has NO listener yet must stay in
6860    /// the SCM table, and a later `AddListener` + `ActivateListener` must adopt
6861    /// it — backlog and all.
6862    ///
6863    /// This is a real upgrade shape, not a hypothetical: `Worker::upgrade`
6864    /// (`e2e/src/sozu/worker.rs`) hands the new worker a state whose listeners
6865    /// are inactive and sends `generate_activate_requests()` afterwards, and
6866    /// `UpgradeWorkerTask` (`bin/src/command/upgrade.rs`) scatters those
6867    /// requests only once `Server::new` has returned. Closing leftovers at the
6868    /// end of the initial state would destroy exactly this case, which is why
6869    /// `InheritedSocketFate::Unclaimed` keeps them.
6870    ///
6871    /// To SEE THIS RED: make any arm's `InheritedSocketFate::Unclaimed` branch
6872    /// take the descriptor out of the table like the other two — the first
6873    /// activation consumes and closes it, so the listener added afterwards binds
6874    /// its own socket and that protocol's assertions fail.
6875    #[test]
6876    fn an_inherited_descriptor_with_no_listener_yet_is_adopted_by_a_later_activation() {
6877        let ServerParts {
6878            event_loop,
6879            sessions,
6880            pool,
6881            backends,
6882            server_scm_socket,
6883            server_config,
6884            ..
6885        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
6886        let (_command_channel, proxy_channel) =
6887            Channel::generate(1000, 10000).expect("could not generate a test channel");
6888
6889        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6890        let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6891        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6892        let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
6893
6894        let mut server = Server::new(
6895            event_loop,
6896            proxy_channel,
6897            server_scm_socket,
6898            sessions,
6899            pool,
6900            backends,
6901            None,
6902            None,
6903            None,
6904            server_config,
6905            None,
6906            false,
6907        )
6908        .expect("could not build the test server");
6909
6910        // Four inherited descriptors and no listener anywhere. The TCP-family
6911        // ones each carry a completed connection so the adoption can be shown to
6912        // preserve the backlog, not merely the descriptor.
6913        let http_listener = server_bind(http_address.into(), None)
6914            .expect("could not bind the inherited HTTP socket");
6915        let https_listener = server_bind(https_address.into(), None)
6916            .expect("could not bind the inherited HTTPS socket");
6917        let tcp_listener =
6918            server_bind(tcp_address.into(), None).expect("could not bind the inherited TCP socket");
6919        let udp_socket =
6920            udp_bind(udp_address.into(), None).expect("could not bind the inherited UDP socket");
6921        let _http_client = StdTcpStream::connect::<SocketAddr>(http_address.into())
6922            .expect("could not queue a connection on the inherited HTTP socket");
6923        let _https_client = StdTcpStream::connect::<SocketAddr>(https_address.into())
6924            .expect("could not queue a connection on the inherited HTTPS socket");
6925        let _tcp_client = StdTcpStream::connect::<SocketAddr>(tcp_address.into())
6926            .expect("could not queue a connection on the inherited TCP socket");
6927        let (http_fd, https_fd, tcp_fd, udp_fd) = (
6928            http_listener.into_raw_fd(),
6929            https_listener.into_raw_fd(),
6930            tcp_listener.into_raw_fd(),
6931            udp_socket.into_raw_fd(),
6932        );
6933        {
6934            let scm_listeners = server
6935                .scm_listeners
6936                .as_mut()
6937                .expect("Server::new must have populated scm_listeners");
6938            scm_listeners.http.push((http_address.into(), http_fd));
6939            scm_listeners.tls.push((https_address.into(), https_fd));
6940            scm_listeners.tcp.push((tcp_address.into(), tcp_fd));
6941            scm_listeners.udp.push((udp_address.into(), udp_fd));
6942        }
6943
6944        let add_requests =
6945            state_with_four_active_listeners(http_address, https_address, tcp_address, udp_address);
6946        let cases = [
6947            ("HTTP", http_address, ListenerType::Http, http_fd),
6948            ("HTTPS", https_address, ListenerType::Https, https_fd),
6949            ("TCP", tcp_address, ListenerType::Tcp, tcp_fd),
6950            ("UDP", udp_address, ListenerType::Udp, udp_fd),
6951        ];
6952
6953        // No listener at any of the addresses: every activation fails, and every
6954        // descriptor must survive it untouched.
6955        for (label, address, proxy, fd) in cases {
6956            let response = server.notify_activate_listener(
6957                "test-early",
6958                &ActivateListener {
6959                    interface: None,
6960                    address,
6961                    proxy: proxy.into(),
6962                    from_scm: false,
6963                },
6964            );
6965            assert_ne!(
6966                response.status,
6967                ResponseStatus::Ok as i32,
6968                "activating {label} with no listener must fail: {response:?}"
6969            );
6970            assert!(
6971                inherited_table(&server, label)
6972                    .iter()
6973                    .any(|(_, e)| *e == fd),
6974                "the {label} descriptor must stay in the SCM table while no listener owns \
6975                 its address"
6976            );
6977            assert!(
6978                descriptor_is_socket_on_port(fd, address.port as u16),
6979                "the {label} descriptor must stay open while no listener owns its address"
6980            );
6981        }
6982
6983        // The listeners arrive, then their activations: every descriptor is
6984        // adopted, so nothing is left in the table and nothing was closed unused.
6985        for request in add_requests.produce_initial_state().requests {
6986            server.notify_proxys(request);
6987        }
6988        for (label, _address, _proxy, fd) in cases {
6989            assert!(
6990                !inherited_table(&server, label)
6991                    .iter()
6992                    .any(|(_, e)| *e == fd),
6993                "the retained {label} descriptor must be adopted by the later activation"
6994            );
6995        }
6996        let accepted: Vec<Protocol> = server
6997            .accept_queue
6998            .iter()
6999            .map(|(_, _, protocol, _, _)| *protocol)
7000            .collect();
7001        for expected in [
7002            Protocol::HTTPListen,
7003            Protocol::HTTPSListen,
7004            Protocol::TCPListen,
7005        ] {
7006            assert!(
7007                accepted.contains(&expected),
7008                "the connection queued on the retained {expected:?} descriptor must be served \
7009                 by the later activation, got {accepted:?}"
7010            );
7011        }
7012    }
7013
7014    /// A socket parked by a failed registration must not be mistaken for a
7015    /// live, registered one.
7016    ///
7017    /// Every consumer of the listener's socket field reads its `Some` as
7018    /// "registered and live": `give_back_listener` hands it back as an
7019    /// activated socket and answers `Ok`, `soft_stop` / `hard_stop` deregister
7020    /// it and fold a failure into `ProxyError::SoftStop` / `HardStop`, `accept`
7021    /// accepts on it. Parking there made a listener that had simply never come
7022    /// up look activated — and, for a registration failure that leaves the
7023    /// socket unregistered, made `deregister` answer `ENOENT` and report a
7024    /// failed shutdown. The park therefore lives in its own `parked_listener` /
7025    /// `parked_socket` field, which nothing but `activate()` reads.
7026    ///
7027    /// `give_back_listener` is the assertion that falsifies the mutation:
7028    /// `ProxyError::UnactivatedListener` keys on the live field being `None`,
7029    /// so parking into it turns the answer into `Ok`. The stop assertions ride
7030    /// along to pin the shutdown behaviour, but they cannot redden on this
7031    /// repro — the EEXIST it uses means the descriptor IS registered (under
7032    /// another token), so `deregister` succeeds either way.
7033    ///
7034    /// To SEE THIS RED: in any `activate()`, replace
7035    /// `self.parked_listener = Some(listener)` (or `self.parked_socket = Some(socket)`)
7036    /// with `self.listener = Some(listener)` / `self.socket = Some(socket)` —
7037    /// that protocol's `give_back_listener` assertion fails.
7038    #[test]
7039    fn a_socket_parked_by_a_failed_registration_is_not_mistaken_for_a_live_one() {
7040        let ServerParts {
7041            registry,
7042            sessions,
7043            pool,
7044            backends,
7045            ..
7046        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7047
7048        let token_for = |protocol: Protocol| {
7049            let mut manager = sessions.borrow_mut();
7050            let entry = manager.slab.vacant_entry();
7051            let token = Token(entry.key());
7052            entry.insert(Rc::new(RefCell::new(ListenSession { protocol })));
7053            token
7054        };
7055        // A socket whose descriptor is already registered on the same epoll
7056        // instance, so `activate()`'s own `register` answers EEXIST and parks.
7057        let pre_registered = |address: SocketAddress| {
7058            let mut socket = server_bind(address.into(), None)
7059                .expect("could not bind the test listening socket");
7060            registry
7061                .register(&mut socket, Token(usize::MAX - 2), Interest::READABLE)
7062                .expect("could not pre-register the test socket");
7063            socket
7064        };
7065
7066        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7067        let mut http_proxy = http::HttpProxy::new(
7068            registry.try_clone().expect("could not clone the registry"),
7069            sessions.clone(),
7070            pool.clone(),
7071            backends.clone(),
7072        );
7073        http_proxy
7074            .add_listener(
7075                ListenerBuilder::new_http(http_address)
7076                    .to_http(None)
7077                    .expect("could not build the test HTTP listener config"),
7078                token_for(Protocol::HTTPListen),
7079            )
7080            .expect("could not add the test HTTP listener");
7081        assert!(
7082            http_proxy
7083                .activate_listener(&http_address.into(), Some(pre_registered(http_address)))
7084                .is_err(),
7085            "the pre-registered descriptor must make the HTTP registration fail"
7086        );
7087        assert!(
7088            matches!(
7089                http_proxy.give_back_listener(&http_address.into()),
7090                Err(ProxyError::UnactivatedListener)
7091            ),
7092            "an HTTP listener holding only a parked socket must not answer as activated"
7093        );
7094        assert!(
7095            http_proxy.soft_stop().is_ok(),
7096            "a parked socket must not fail an HTTP soft stop"
7097        );
7098
7099        let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7100        let mut https_proxy = https::HttpsProxy::new(
7101            registry.try_clone().expect("could not clone the registry"),
7102            sessions.clone(),
7103            pool.clone(),
7104            backends.clone(),
7105        );
7106        https_proxy
7107            .add_listener(
7108                ListenerBuilder::new_https(https_address)
7109                    .to_tls(None)
7110                    .expect("could not build the test HTTPS listener config"),
7111                token_for(Protocol::HTTPSListen),
7112            )
7113            .expect("could not add the test HTTPS listener");
7114        assert!(
7115            https_proxy
7116                .activate_listener(&https_address.into(), Some(pre_registered(https_address)))
7117                .is_err(),
7118            "the pre-registered descriptor must make the HTTPS registration fail"
7119        );
7120        assert!(
7121            matches!(
7122                https_proxy.give_back_listener(&https_address.into()),
7123                Err(ProxyError::UnactivatedListener)
7124            ),
7125            "an HTTPS listener holding only a parked socket must not answer as activated"
7126        );
7127        assert!(
7128            https_proxy.hard_stop().is_ok(),
7129            "a parked socket must not fail an HTTPS hard stop"
7130        );
7131
7132        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7133        let mut tcp_proxy = tcp::TcpProxy::new(
7134            registry.try_clone().expect("could not clone the registry"),
7135            sessions.clone(),
7136            pool.clone(),
7137            backends.clone(),
7138        );
7139        tcp_proxy
7140            .add_listener(
7141                ListenerBuilder::new_tcp(tcp_address)
7142                    .to_tcp(None)
7143                    .expect("could not build the test TCP listener config"),
7144                token_for(Protocol::TCPListen),
7145            )
7146            .expect("could not add the test TCP listener");
7147        assert!(
7148            tcp_proxy
7149                .activate_listener(&tcp_address.into(), Some(pre_registered(tcp_address)))
7150                .is_err(),
7151            "the pre-registered descriptor must make the TCP registration fail"
7152        );
7153        assert!(
7154            matches!(
7155                tcp_proxy.give_back_listener(&tcp_address.into()),
7156                Err(ProxyError::UnactivatedListener)
7157            ),
7158            "a TCP listener holding only a parked socket must not answer as activated"
7159        );
7160
7161        let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7162        let mut udp_proxy = udp::UdpProxy::new(
7163            registry.try_clone().expect("could not clone the registry"),
7164            sessions.clone(),
7165            pool.clone(),
7166            backends.clone(),
7167            16,
7168            16384,
7169        );
7170        udp_proxy
7171            .add_listener(
7172                ListenerBuilder::new_udp(udp_address)
7173                    .to_udp(None)
7174                    .expect("could not build the test UDP listener config"),
7175                token_for(Protocol::UDP),
7176            )
7177            .expect("could not add the test UDP listener");
7178        let mut udp_socket =
7179            udp_bind(udp_address.into(), None).expect("could not bind the test UDP socket");
7180        registry
7181            .register(&mut udp_socket, Token(usize::MAX - 2), Interest::READABLE)
7182            .expect("could not pre-register the test UDP socket");
7183        assert!(
7184            udp_proxy
7185                .activate_listener(&udp_address.into(), Some(udp_socket))
7186                .is_err(),
7187            "the pre-registered descriptor must make the UDP registration fail"
7188        );
7189        assert!(
7190            matches!(
7191                udp_proxy.give_back_listener(&udp_address.into()),
7192                Err(ProxyError::UnactivatedListener)
7193            ),
7194            "a UDP listener holding only a parked socket must not answer as activated"
7195        );
7196    }
7197
7198    /// A failed mio registration must not close the socket it was handed.
7199    ///
7200    /// `activate()` moves the socket into a local to register it. Propagating a
7201    /// registration error straight from that local drops it — and closes a
7202    /// descriptor `Listeners::get_*` has already removed from the SCM table,
7203    /// which is the sozu#1342 close in a second place. `Registry::register`
7204    /// answers `EEXIST` for a descriptor already registered on the same epoll
7205    /// instance, which is what the test arranges and what a `deactivate` whose
7206    /// `deregister` only logged leaves behind.
7207    ///
7208    /// To SEE THIS RED: in any `activate()`, go back to
7209    /// `registry.register(..).map_err(..)?` followed by `self.listener = Some(listener)`
7210    /// — the `?` drops the local and that protocol's assertion fails.
7211    #[test]
7212    fn a_failed_registration_does_not_close_the_socket_it_was_handed() {
7213        let ServerParts {
7214            registry,
7215            sessions,
7216            pool,
7217            backends,
7218            ..
7219        } = prebuild_server(16, 16384, true).expect("could not prebuild a test server");
7220
7221        let token_for = |protocol: Protocol| {
7222            let mut manager = sessions.borrow_mut();
7223            let entry = manager.slab.vacant_entry();
7224            let token = Token(entry.key());
7225            entry.insert(Rc::new(RefCell::new(ListenSession { protocol })));
7226            token
7227        };
7228
7229        // Hand each proxy a socket whose descriptor is ALREADY registered on the
7230        // same epoll instance, so `activate()`'s own `register` answers EEXIST.
7231        let handed_out = |address: SocketAddress| {
7232            let mut socket = server_bind(address.into(), None)
7233                .expect("could not bind the test listening socket");
7234            registry
7235                .register(&mut socket, Token(usize::MAX - 1), Interest::READABLE)
7236                .expect("could not pre-register the test socket");
7237            let fd = socket.as_raw_fd();
7238            (socket, fd)
7239        };
7240
7241        let http_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7242        let token = token_for(Protocol::HTTPListen);
7243        let mut http_proxy = http::HttpProxy::new(
7244            registry.try_clone().expect("could not clone the registry"),
7245            sessions.clone(),
7246            pool.clone(),
7247            backends.clone(),
7248        );
7249        http_proxy
7250            .add_listener(
7251                ListenerBuilder::new_http(http_address)
7252                    .to_http(None)
7253                    .expect("could not build the test HTTP listener config"),
7254                token,
7255            )
7256            .expect("could not add the test HTTP listener");
7257        let (socket, http_fd) = handed_out(http_address);
7258        let result = http_proxy.activate_listener(&http_address.into(), Some(socket));
7259        assert!(
7260            result.is_err(),
7261            "the pre-registered descriptor must make the HTTP registration fail"
7262        );
7263        assert!(
7264            descriptor_is_socket_on_port(http_fd, http_address.port as u16),
7265            "a failed HTTP registration must not close the socket it was handed"
7266        );
7267
7268        let tcp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7269        let token = token_for(Protocol::TCPListen);
7270        let mut tcp_proxy = tcp::TcpProxy::new(
7271            registry.try_clone().expect("could not clone the registry"),
7272            sessions.clone(),
7273            pool.clone(),
7274            backends.clone(),
7275        );
7276        tcp_proxy
7277            .add_listener(
7278                ListenerBuilder::new_tcp(tcp_address)
7279                    .to_tcp(None)
7280                    .expect("could not build the test TCP listener config"),
7281                token,
7282            )
7283            .expect("could not add the test TCP listener");
7284        let (socket, tcp_fd) = handed_out(tcp_address);
7285        let result = tcp_proxy.activate_listener(&tcp_address.into(), Some(socket));
7286        assert!(
7287            result.is_err(),
7288            "the pre-registered descriptor must make the TCP registration fail"
7289        );
7290        assert!(
7291            descriptor_is_socket_on_port(tcp_fd, tcp_address.port as u16),
7292            "a failed TCP registration must not close the socket it was handed"
7293        );
7294
7295        let https_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7296        let token = token_for(Protocol::HTTPSListen);
7297        let mut https_proxy = https::HttpsProxy::new(
7298            registry.try_clone().expect("could not clone the registry"),
7299            sessions.clone(),
7300            pool.clone(),
7301            backends.clone(),
7302        );
7303        https_proxy
7304            .add_listener(
7305                ListenerBuilder::new_https(https_address)
7306                    .to_tls(None)
7307                    .expect("could not build the test HTTPS listener config"),
7308                token,
7309            )
7310            .expect("could not add the test HTTPS listener");
7311        let (socket, https_fd) = handed_out(https_address);
7312        let result = https_proxy.activate_listener(&https_address.into(), Some(socket));
7313        assert!(
7314            result.is_err(),
7315            "the pre-registered descriptor must make the HTTPS registration fail"
7316        );
7317        assert!(
7318            descriptor_is_socket_on_port(https_fd, https_address.port as u16),
7319            "a failed HTTPS registration must not close the socket it was handed"
7320        );
7321
7322        let udp_address = SocketAddress::new_v4(127, 0, 0, 1, provide_port());
7323        let token = token_for(Protocol::UDP);
7324        let mut udp_proxy = udp::UdpProxy::new(
7325            registry.try_clone().expect("could not clone the registry"),
7326            sessions.clone(),
7327            pool.clone(),
7328            backends.clone(),
7329            16,
7330            16384,
7331        );
7332        udp_proxy
7333            .add_listener(
7334                ListenerBuilder::new_udp(udp_address)
7335                    .to_udp(None)
7336                    .expect("could not build the test UDP listener config"),
7337                token,
7338            )
7339            .expect("could not add the test UDP listener");
7340        let mut socket =
7341            udp_bind(udp_address.into(), None).expect("could not bind the test UDP socket");
7342        registry
7343            .register(&mut socket, Token(usize::MAX - 1), Interest::READABLE)
7344            .expect("could not pre-register the test UDP socket");
7345        let udp_fd = socket.as_raw_fd();
7346        let result = udp_proxy.activate_listener(&udp_address.into(), Some(socket));
7347        assert!(
7348            result.is_err(),
7349            "the pre-registered descriptor must make the UDP registration fail"
7350        );
7351        assert!(
7352            descriptor_is_socket_on_port(udp_fd, udp_address.port as u16),
7353            "a failed UDP registration must not close the socket it was handed"
7354        );
7355    }
7356}
7357
7358#[cfg(test)]
7359mod tests {
7360    use super::{ClusterId, SessionManager, mask_ipv4, mask_ipv6, subnet_key};
7361    use mio::Token;
7362    use slab::Slab;
7363    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7364
7365    /// A cluster override can drop to `Some(0)` AFTER a session was
7366    /// tracked under a positive limit: `AddCluster` is an upsert and
7367    /// clears no counters, and only the GLOBAL `SetMaxConnectionsPerSubnet(0)`
7368    /// drains the bookkeeping. The next track for that still-open session
7369    /// then takes the disabled-limiter early return with its slot already
7370    /// recorded, which must be an ordinary no-op rather than a panic.
7371    #[test]
7372    fn a_cluster_override_dropping_to_zero_after_a_track_is_a_no_op() {
7373        let manager = SessionManager::new(Slab::with_capacity(8), 8, 0, 0, 0, 24, 56);
7374        let token = Token(1);
7375        let cluster = ClusterId::from("api");
7376        let ip: IpAddr = "203.0.113.9".parse().expect("test address must parse");
7377
7378        // Tracked under a positive per-cluster override.
7379        manager
7380            .borrow_mut()
7381            .track_cluster_subnet(token, cluster.clone(), ip, Some(5));
7382        assert!(
7383            !manager.borrow().subnet_tracking_is_empty(),
7384            "premise: the positive override must have recorded a slot"
7385        );
7386
7387        // The operator upserts the cluster with the cap disabled. The same
7388        // session opens another stream: the early return must not trip on
7389        // the slot it legitimately left behind.
7390        manager
7391            .borrow_mut()
7392            .track_cluster_subnet(token, cluster.clone(), ip, Some(0));
7393
7394        // And the stale slot is still released normally on close.
7395        manager.borrow_mut().untrack_all_cluster_ip(token);
7396        assert!(
7397            manager.borrow().subnet_tracking_is_empty(),
7398            "teardown must drain the slot even though the cap was disabled in between"
7399        );
7400    }
7401
7402    /// The two ends of the prefix range are the ones that break: a
7403    /// full-width prefix would shift by the whole word, and a zero prefix
7404    /// would shift by it from the other side. Both are handled by an
7405    /// explicit branch, and this pins that neither reaches the shift.
7406    #[test]
7407    fn masking_handles_both_shift_overflowing_prefix_bounds() {
7408        let v4: Ipv4Addr = "203.0.113.45".parse().expect("test address must parse");
7409        assert_eq!(
7410            mask_ipv4(v4, 32),
7411            v4,
7412            "a /32 must be the address itself, not a wrapped-shift artefact"
7413        );
7414        assert_eq!(
7415            mask_ipv4(v4, 0),
7416            Ipv4Addr::UNSPECIFIED,
7417            "a /0 must mask every bit away"
7418        );
7419
7420        let v6: Ipv6Addr = "2001:db8:dead:beef::1"
7421            .parse()
7422            .expect("test address must parse");
7423        assert_eq!(
7424            mask_ipv6(v6, 128),
7425            v6,
7426            "a /128 must be the address itself, not a wrapped-shift artefact"
7427        );
7428        assert_eq!(
7429            mask_ipv6(v6, 0),
7430            Ipv6Addr::UNSPECIFIED,
7431            "a /0 must mask every bit away"
7432        );
7433
7434        // Out-of-range prefixes cannot reach the shift either. Config load
7435        // rejects them, but the hot path does not rely on that having run.
7436        assert_eq!(
7437            mask_ipv4(v4, 33),
7438            v4,
7439            "an over-wide IPv4 prefix must clamp to the address, not panic or wrap"
7440        );
7441        assert_eq!(
7442            mask_ipv6(v6, 129),
7443            v6,
7444            "an over-wide IPv6 prefix must clamp to the address, not panic or wrap"
7445        );
7446    }
7447
7448    /// The recommended prefixes from sozu-proxy/sozu#1270 — /24 and /56 —
7449    /// must group the addresses an operator expects them to.
7450    #[test]
7451    fn masking_groups_addresses_at_the_recommended_prefixes() {
7452        let parse_v4 = |s: &str| -> Ipv4Addr { s.parse().expect("test address must parse") };
7453        assert_eq!(
7454            mask_ipv4(parse_v4("203.0.113.1"), 24),
7455            mask_ipv4(parse_v4("203.0.113.254"), 24),
7456            "two hosts in one /24 must share a subnet key"
7457        );
7458        assert_ne!(
7459            mask_ipv4(parse_v4("203.0.113.1"), 24),
7460            mask_ipv4(parse_v4("203.0.114.1"), 24),
7461            "hosts in different /24s must NOT share a subnet key"
7462        );
7463
7464        let parse_v6 = |s: &str| -> Ipv6Addr { s.parse().expect("test address must parse") };
7465        assert_eq!(
7466            mask_ipv6(parse_v6("2001:db8:abcd:0000::1"), 56),
7467            mask_ipv6(parse_v6("2001:db8:abcd:00ff::9999"), 56),
7468            "two addresses in one /56 must share a subnet key"
7469        );
7470        assert_ne!(
7471            mask_ipv6(parse_v6("2001:db8:abcd:0000::1"), 56),
7472            mask_ipv6(parse_v6("2001:db8:abcd:0100::1"), 56),
7473            "addresses in different /56s must NOT share a subnet key"
7474        );
7475
7476        // The issue's own scenario: a /64 holder taking a fresh address
7477        // per connection lands in one bucket at /56.
7478        assert_eq!(
7479            mask_ipv6(parse_v6("2001:db8:abcd:1234::1"), 56),
7480            mask_ipv6(parse_v6("2001:db8:abcd:1234:ffff:ffff:ffff:ffff"), 56),
7481            "every address a /64 holder can mint must collapse to one /56 key — that \
7482             collapse IS the mitigation for sozu-proxy/sozu#1270"
7483        );
7484    }
7485
7486    /// A dual-stack `[::]` listener hands IPv4 clients back as
7487    /// `::ffff:a.b.c.d`. Masking those with an IPv6 prefix would collapse
7488    /// the entire IPv4 internet into one `::/56` bucket, so the mapped
7489    /// form is canonicalised to IPv4 and charged to the IPv4 prefix.
7490    #[test]
7491    fn an_ipv4_mapped_source_is_charged_to_the_ipv4_prefix() {
7492        let mapped: IpAddr = "::ffff:203.0.113.7".parse().expect("must parse");
7493        let native: IpAddr = "203.0.113.7".parse().expect("must parse");
7494        assert_eq!(
7495            subnet_key(&mapped, 24, 56),
7496            subnet_key(&native, 24, 56),
7497            "a mapped IPv4 source must key identically to the same address arriving natively"
7498        );
7499        assert!(
7500            subnet_key(&mapped, 24, 56).is_ipv4(),
7501            "the key for a mapped source must be an IPv4 key"
7502        );
7503
7504        // Negative space: two mapped addresses from DIFFERENT /24s must
7505        // not collide. Under the bug this guards against, both would mask
7506        // to `::/56` and share one counter.
7507        let other: IpAddr = "::ffff:198.51.100.7".parse().expect("must parse");
7508        assert_ne!(
7509            subnet_key(&mapped, 24, 56),
7510            subnet_key(&other, 24, 56),
7511            "mapped sources from different IPv4 /24s must NOT share a subnet key — if they \
7512             do, every IPv4 client on a dual-stack listener shares one bucket"
7513        );
7514
7515        // A genuine IPv6 source is still keyed as IPv6.
7516        let genuine: IpAddr = "2001:db8::1".parse().expect("must parse");
7517        assert!(
7518            subnet_key(&genuine, 24, 56).is_ipv6(),
7519            "a genuine IPv6 source must keep an IPv6 subnet key"
7520        );
7521    }
7522}