Skip to main content

sozu_lib/
http.rs

1use std::{
2    cell::RefCell,
3    collections::{BTreeMap, HashMap, hash_map::Entry},
4    io::ErrorKind,
5    net::SocketAddr,
6    rc::{Rc, Weak},
7    str::from_utf8_unchecked,
8    time::{Duration, Instant},
9};
10
11use mio::{
12    Interest, Registry, Token,
13    net::{TcpListener as MioTcpListener, TcpStream},
14};
15use rusty_ulid::Ulid;
16use socket2::SockRef;
17use sozu_command::{
18    listener_key::ListenerKey,
19    logging::CachedTags,
20    proto::command::{
21        Cluster, ForwardedHeaders, HttpListenerConfig, ListenerType, RemoveListener,
22        RequestHttpFrontend, UpdateHttpListenerConfig, WorkerRequest, WorkerResponse,
23        request::RequestType,
24    },
25    ready::Ready,
26    response::HttpFrontend,
27    state::{
28        ClusterId, validate_forwarded_headers, validate_h2_flood_knobs_http,
29        validate_sozu_id_header,
30    },
31};
32
33use crate::metrics::names;
34use crate::{
35    AcceptError, FrontendFromRequestError, L7ListenerHandler, L7Proxy, ListenerError,
36    ListenerHandler, Protocol, ProxyConfiguration, ProxyError, ProxySession, SessionIsToBeClosed,
37    SessionMetrics, SessionResult, StateMachineBuilder, StateResult,
38    backends::BackendMap,
39    pool::Pool,
40    protocol::{
41        Pipe, SessionState,
42        http::{
43            answers::HttpAnswers,
44            parser::{Method, hostname_and_port},
45        },
46        mux::{self, Mux, MuxClear},
47        proxy_protocol::expect::ExpectProxyProtocol,
48    },
49    router::{RequestHeaders, RouteResult, Router},
50    server::{ListenToken, SessionManager},
51    socket::server_bind,
52    timer::TimeoutContainer,
53};
54
55#[derive(PartialEq, Eq)]
56pub enum SessionStatus {
57    Normal,
58    DefaultAnswer,
59}
60
61StateMachineBuilder! {
62    /// The various Stages of an HTTP connection:
63    ///
64    /// 1. optional (ExpectProxyProtocol)
65    /// 2. HTTP (via Mux in H1 mode)
66    /// 3. WebSocket (passthrough)
67    enum HttpStateMachine impl SessionState {
68        Expect(ExpectProxyProtocol<TcpStream>),
69        Mux(MuxClear),
70        WebSocket(Pipe<crate::socket::SessionTcpStream, HttpListener>),
71    }
72}
73
74/// Module-level prefix for log lines emitted from this file when no session
75/// is in scope. Produces a bold bright-white `HTTP` label in colored mode.
76/// Used by [`HttpProxy`] / [`HttpListener`] callbacks (notify, add_cluster,
77/// add_*_frontend, accept, soft_stop, hard_stop, etc.) which own a token map
78/// keyed by listener and have no `frontend_token` of their own.
79macro_rules! log_module_context {
80    () => {{
81        let (open, reset, _, _, _) = sozu_command::logging::ansi_palette();
82        format!("{open}HTTP{reset}\t >>>", open = open, reset = reset)
83    }};
84}
85
86/// Per-session prefix for log lines emitted with an [`HttpSession`] in
87/// scope. Renders the canonical `\tHTTP\tSession(...)\t >>>` envelope from
88/// the session's `frontend_token` (mirrors the bracket convention used by
89/// `MUX-*`, `RUSTLS`, `PIPE`). Operators can grep-correlate against the
90/// token id across log lines for the same H1 connection.
91macro_rules! log_context {
92    ($self:expr) => {{
93        let (open, reset, grey, gray, white) = sozu_command::logging::ansi_palette();
94        format!(
95            "{open}HTTP{reset}\t{grey}Session{reset}({gray}frontend{reset}={white}{frontend}{reset})\t >>>",
96            open = open,
97            reset = reset,
98            grey = grey,
99            gray = gray,
100            white = white,
101            frontend = $self.frontend_token.0,
102        )
103    }};
104}
105
106/// HTTP Session to insert in the SessionManager
107///
108/// 1 session <=> 1 HTTP connection (client to sozu)
109pub struct HttpSession {
110    configured_backend_timeout: Duration,
111    configured_connect_timeout: Duration,
112    configured_frontend_timeout: Duration,
113    frontend_token: Token,
114    last_event: Instant,
115    listener: Rc<RefCell<HttpListener>>,
116    metrics: SessionMetrics,
117    pool: Weak<RefCell<Pool>>,
118    proxy: Rc<RefCell<HttpProxy>>,
119    state: HttpStateMachine,
120    has_been_closed: bool,
121}
122
123impl HttpSession {
124    #[allow(clippy::too_many_arguments)]
125    pub fn new(
126        configured_backend_timeout: Duration,
127        configured_connect_timeout: Duration,
128        configured_frontend_timeout: Duration,
129        configured_request_timeout: Duration,
130        expect_proxy: bool,
131        listener: Rc<RefCell<HttpListener>>,
132        pool: Weak<RefCell<Pool>>,
133        proxy: Rc<RefCell<HttpProxy>>,
134        backends: Rc<RefCell<BackendMap>>,
135        public_address: SocketAddr,
136        sock: TcpStream,
137        peer: SocketAddr,
138        token: Token,
139        wait_time: Duration,
140    ) -> Result<Self, AcceptError> {
141        let request_id = Ulid::generate();
142        let container_frontend_timeout = TimeoutContainer::new(configured_request_timeout, token);
143
144        let state = if expect_proxy {
145            trace!("{} starting in expect proxy state", log_module_context!());
146            gauge_add!(names::protocol::PROXY_EXPECT, 1);
147
148            HttpStateMachine::Expect(ExpectProxyProtocol::new(
149                container_frontend_timeout,
150                sock,
151                token,
152                request_id,
153            ))
154        } else {
155            gauge_add!(names::protocol::HTTP, 1);
156            // The address `accept(2)` returned. On an expect-proxy listener
157            // the other branch ignores it: the session address comes from the
158            // PROXY header at `upgrade_expect`.
159            let session_address = Some(peer);
160            let session_ulid = rusty_ulid::Ulid::generate();
161            let sock = crate::socket::SessionTcpStream::new(sock, session_ulid, session_address);
162
163            let frontend = mux::Connection::new_h1_server(
164                session_ulid,
165                sock,
166                container_frontend_timeout.duration(),
167            );
168            let router = mux::Router::new(configured_backend_timeout, configured_connect_timeout);
169            let mut context = mux::Context::new(
170                session_ulid,
171                pool.clone(),
172                listener.clone(),
173                session_address,
174                public_address,
175            );
176            context
177                .create_stream(request_id, 1 << 16)
178                .ok_or(AcceptError::BufferCapacityReached)?;
179            HttpStateMachine::Mux(Mux {
180                configured_frontend_timeout,
181                frontend_token: token,
182                frontend,
183                router,
184                context,
185                session_ulid,
186                // Carry the already-armed handshake entry into the adapter
187                // rather than dropping it and arming a fresh one: the session's
188                // request timeout started when the socket was accepted.
189                timeouts: mux::MuxTimeouts::new(container_frontend_timeout),
190                backend_registry: mux::BackendRegistry::default(),
191                backends,
192            })
193        };
194
195        // Invariant: `create_session` allocates `token` from the session slab
196        // and registers the frontend socket under it before constructing us.
197        // A session whose frontend token aliased the listen-socket token (0)
198        // would route listener readiness into a session — a state-machine bug.
199        // (`Token(0)` is reserved in real workers; the test harness in this
200        // file builds listeners directly without going through `new`.)
201        debug_assert_eq!(
202            state.marker() as u8,
203            if expect_proxy {
204                StateMarker::Expect as u8
205            } else {
206                StateMarker::Mux as u8
207            },
208            "constructed state must match the expect_proxy branch"
209        );
210        // The freshly-built state can never be a FailedUpgrade: that marker
211        // only appears after a real upgrade attempt drains the state via
212        // `take()`. Catch a future refactor that constructs one by accident.
213        debug_assert!(
214            !state.failed(),
215            "a newly created session must not start in FailedUpgrade"
216        );
217
218        let metrics = SessionMetrics::new(Some(wait_time));
219        let session = HttpSession {
220            configured_backend_timeout,
221            configured_connect_timeout,
222            configured_frontend_timeout,
223            frontend_token: token,
224            has_been_closed: false,
225            last_event: Instant::now(),
226            listener,
227            metrics,
228            pool,
229            proxy,
230            state,
231        };
232        debug_assert_eq!(
233            session.frontend_token, token,
234            "frontend token must be the slab token used for registration"
235        );
236        #[cfg(debug_assertions)]
237        session.check_invariants();
238        Ok(session)
239    }
240
241    /// Full cross-field invariant sweep for the session state machine, used as
242    /// a run-to-completion postcondition. Compiled out in release.
243    ///
244    /// Strong invariants asserted here:
245    /// - the frontend token is always present (slab key is never sentinel);
246    /// - once `close()` has run the state has been drained / cancelled, so the
247    ///   session is terminal and must not be re-driven (callers gate on the
248    ///   returned `SessionIsToBeClosed`);
249    /// - the live state marker is one of the three legal H1 stages — the
250    ///   `FailedUpgrade` marker is only transient (between `take()` and the
251    ///   `close()` that immediately follows a failed upgrade), so it is the
252    ///   single tolerated exception on a still-open session.
253    #[cfg(debug_assertions)]
254    fn check_invariants(&self) {
255        let marker = self.state.marker();
256        debug_assert!(
257            matches!(
258                marker,
259                StateMarker::Expect | StateMarker::Mux | StateMarker::WebSocket
260            ),
261            "session marker must be a legal H1 stage (Expect/Mux/WebSocket), got {marker:?}"
262        );
263        // A failed state is only ever observed transiently between a failed
264        // upgrade and the close() that reaps it; if it survives to a postcondition
265        // sweep on a not-yet-closed session, a close path was skipped.
266        debug_assert!(
267            !self.state.failed() || self.has_been_closed,
268            "FailedUpgrade state must be reaped by close(), never left live"
269        );
270    }
271
272    pub fn upgrade(&mut self) -> SessionIsToBeClosed {
273        debug!("{} upgrade", log_context!(self));
274        // Record the stage we are leaving so we can assert the transition is
275        // legal (no stage-skipping) after the handoff resolves. `take()`
276        // installs a `FailedUpgrade(marker)` placeholder carrying this same
277        // marker, so the session is never left in a half-moved state if an
278        // upgrade_* helper bails out.
279        let from_marker = self.state.marker();
280        let new_state = match self.state.take() {
281            HttpStateMachine::Mux(mux) => self.upgrade_mux(mux),
282            HttpStateMachine::Expect(expect) => self.upgrade_expect(expect),
283            HttpStateMachine::WebSocket(ws) => self.upgrade_websocket(ws),
284            HttpStateMachine::FailedUpgrade(_) => {
285                // Reaching this arm means a prior upgrade already returned
286                // `None` and the session should have been closed. Fall back
287                // to closing cleanly instead of panicking the worker.
288                error!(
289                    "{} upgrade called on FailedUpgrade state; closing session",
290                    log_context!(self)
291                );
292                None
293            }
294        };
295
296        match new_state {
297            Some(state) => {
298                // Legal transitions only: Expect→Mux, Mux→WebSocket, or the
299                // WebSocket self-loop (upgrade_websocket is a no-op guard). Any
300                // other pair means a stage was skipped or reversed.
301                debug_assert!(
302                    matches!(
303                        (from_marker, state.marker()),
304                        (StateMarker::Expect, StateMarker::Mux)
305                            | (StateMarker::Mux, StateMarker::WebSocket)
306                            | (StateMarker::WebSocket, StateMarker::WebSocket)
307                    ),
308                    "illegal protocol-upgrade transition {from_marker:?} -> {:?}",
309                    state.marker()
310                );
311                debug_assert!(
312                    !state.failed(),
313                    "a successful upgrade must not install a FailedUpgrade state"
314                );
315                self.state = state;
316                #[cfg(debug_assertions)]
317                self.check_invariants();
318                false
319            }
320            // The state stays FailedUpgrade, but the Session should be closed right after
321            None => {
322                // On failure `take()` left a FailedUpgrade placeholder behind;
323                // the caller (`ready`) must close the session on the returned
324                // `true`, so we do not run the still-open invariant sweep here.
325                debug_assert!(
326                    self.state.failed(),
327                    "a failed upgrade must leave the session in FailedUpgrade"
328                );
329                true
330            }
331        }
332    }
333
334    fn upgrade_expect(
335        &mut self,
336        expect: ExpectProxyProtocol<TcpStream>,
337    ) -> Option<HttpStateMachine> {
338        debug!("{} switching to HTTP", log_context!(self));
339        match expect
340            .addresses
341            .as_ref()
342            .map(|add| (add.destination(), add.source()))
343        {
344            Some((Some(public_address), Some(session_address))) => {
345                let session_ulid = rusty_ulid::Ulid::generate();
346                let frontend = mux::Connection::new_h1_server(
347                    session_ulid,
348                    crate::socket::SessionTcpStream::new(
349                        expect.frontend,
350                        session_ulid,
351                        Some(session_address),
352                    ),
353                    expect.container_frontend_timeout.duration(),
354                );
355                let router = mux::Router::new(
356                    self.configured_backend_timeout,
357                    self.configured_connect_timeout,
358                );
359                let mut context = mux::Context::new(
360                    session_ulid,
361                    self.pool.clone(),
362                    self.listener.clone(),
363                    Some(session_address),
364                    public_address,
365                );
366                if context.create_stream(expect.request_id, 1 << 16).is_none() {
367                    error!(
368                        "{} expect upgrade failed: could not create stream",
369                        log_context!(self)
370                    );
371                    return None;
372                }
373                let mut mux = Mux {
374                    configured_frontend_timeout: self.configured_frontend_timeout,
375                    frontend_token: self.frontend_token,
376                    frontend,
377                    router,
378                    context,
379                    session_ulid,
380                    timeouts: mux::MuxTimeouts::new(expect.container_frontend_timeout),
381                    backend_registry: mux::BackendRegistry::default(),
382                    // Read once, at the upgrade: the proxy is not borrowed
383                    // while a session runs, and `HttpProxy::backends` is never
384                    // reassigned, so this is the map the session would have
385                    // reached on every dial.
386                    backends: Rc::clone(&self.proxy.borrow().backends),
387                };
388                mux.frontend.readiness_mut().event = expect.frontend_readiness.event;
389
390                // The Expect→Mux handoff must carry the session's frontend
391                // token across unchanged: the slab slot and epoll registration
392                // are keyed by it, so a mismatch would misroute readiness.
393                debug_assert_eq!(
394                    mux.frontend_token, self.frontend_token,
395                    "expect upgrade must preserve the frontend token"
396                );
397                // Fresh Mux: exactly one stream was just created above, and no
398                // backend is connected yet (back token is set iff linked).
399                debug_assert_eq!(
400                    mux.context.streams.len(),
401                    1,
402                    "a freshly upgraded Mux owns exactly the request stream"
403                );
404
405                // Gauge pairing: this connection leaves PROXY_EXPECT and enters
406                // HTTP. The two adjustments must stay together so the live-state
407                // gauges sum to the session count (no double-count, no leak).
408                gauge_add!(names::protocol::PROXY_EXPECT, -1);
409                gauge_add!(names::protocol::HTTP, 1);
410                Some(HttpStateMachine::Mux(mux))
411            }
412            _ => {
413                debug!(
414                    "{} expect upgrade failed: bad header {:?}",
415                    log_context!(self),
416                    expect.addresses
417                );
418                None
419            }
420        }
421    }
422
423    fn upgrade_mux(&mut self, mut mux: MuxClear) -> Option<HttpStateMachine> {
424        debug!("{} mux switching to ws", log_context!(self));
425        let Some(stream) = mux.context.streams.pop() else {
426            error!(
427                "{} upgrade_mux: no stream attached to the mux session, closing",
428                log_context!(self)
429            );
430            return None;
431        };
432        // http.active_requests was already decremented by generate_access_log()
433        // in h1.rs before MuxResult::Upgrade was returned to us.
434
435        // The cores no longer own a wheel handle; the `Mux` adapter does. Move
436        // the frontend's out of the adapter before dismantling the
437        // connection, so the WebSocket `Pipe` inherits the same live entry it
438        // used to inherit from `ConnectionH1`.
439        let mut container_frontend_timeout = mux.timeouts.frontend;
440        let (frontend_readiness, frontend_socket) = match mux.frontend {
441            mux::Connection::H1(mux::ConnectionH1 {
442                readiness, socket, ..
443            }) => (readiness, socket),
444            mux::Connection::H2(_) => {
445                error!(
446                    "{} only h1<->h1 connections can upgrade to websocket",
447                    log_context!(self)
448                );
449                return None;
450            }
451        };
452
453        let mux::StreamState::Linked(back_token) = stream.state else {
454            error!(
455                "{} upgrading stream should be linked to a backend",
456                log_context!(self)
457            );
458            return None;
459        };
460        // Invariant (back token set iff backend connected): a Linked stream
461        // names a backend token that must exist in the router's backend map.
462        // We assert the map is non-empty here, then prove membership by the
463        // `remove` below — a Linked token absent from the map would be a
464        // book-keeping desync between stream state and the backend map.
465        debug_assert!(
466            mux.router.backends.contains_key(&back_token),
467            "a Linked stream's back token must index a connected backend"
468        );
469        let backends_before = mux.router.backends.len();
470        let Some(backend) = mux.router.backends.remove(&back_token) else {
471            error!(
472                "{} upgrade_mux: backend for token {:?} is missing (already disconnected?), closing",
473                log_context!(self),
474                back_token
475            );
476            return None;
477        };
478        let mut container_backend_timeout = mux
479            .timeouts
480            .backends
481            .remove(&back_token)
482            .unwrap_or_else(|| TimeoutContainer::new_empty(mux.router.configured_backend_timeout));
483        let (cluster_id, backend_id, backend_readiness, backend_socket) = match backend {
484            mux::Connection::H1(mux::ConnectionH1 {
485                position: mux::Position::Client(cluster_id, backend, mux::BackendStatus::Connected),
486                readiness,
487                socket,
488                ..
489            }) => (cluster_id, backend, readiness, socket),
490            mux::Connection::H1(_) => {
491                error!(
492                    "{} the backend disconnected just after upgrade, abort",
493                    log_context!(self)
494                );
495                return None;
496            }
497            mux::Connection::H2(_) => {
498                error!(
499                    "{} only h1<->h1 connections can upgrade to websocket",
500                    log_context!(self)
501                );
502                return None;
503            }
504        };
505
506        // The mux core carries an opaque `BackendId` (#1340, Q12); the
507        // WebSocket `Pipe` that takes this backend over still owns the
508        // registry handle, so resolve it here, on the embedder's side of that
509        // perimeter, through the one accessor there is.
510        let Some(backend) = mux.backend_registry.handle(&backend_id).cloned() else {
511            error!(
512                "{} upgrade_mux: backend {:?} names a slot this session never minted, closing",
513                log_context!(self),
514                backend_id.backend_id
515            );
516            return None;
517        };
518        let metric_incarnation = backend_id.metric_incarnation();
519        let backend_id = backend_id.backend_id.to_string();
520
521        // Post-removal book-keeping: the backend is gone from the map and the
522        // count dropped by exactly one (the `remove` matched a present key).
523        debug_assert!(
524            !mux.router.backends.contains_key(&back_token),
525            "the upgraded backend must be evicted from the router map"
526        );
527        debug_assert_eq!(
528            mux.router.backends.len(),
529            backends_before - 1,
530            "removing the backend must drop the backend count by exactly one"
531        );
532
533        let ws_context = stream.context.websocket_context();
534
535        container_frontend_timeout.reset();
536        container_backend_timeout.reset();
537
538        // `Pipe::backend_socket` is typed `Option<TcpStream>` (raw, pre-mux).
539        // The mux wraps every backend TCP socket in `SessionTcpStream` so
540        // SOCKET-layer errors carry the session ULID; unwrap back to the
541        // plain `TcpStream` here to feed Pipe's legacy shape.
542        let backend_socket = backend_socket.stream;
543        let mut pipe = Pipe::new(
544            stream.back.storage.buffer,
545            Some(backend_id),
546            Some(backend_socket),
547            Some(backend),
548            Some(container_backend_timeout),
549            Some(container_frontend_timeout),
550            Some(cluster_id),
551            stream.front.storage.buffer,
552            self.frontend_token,
553            frontend_socket,
554            self.listener.clone(),
555            Protocol::HTTP,
556            stream.context.session_id,
557            stream.context.id,
558            stream.context.session_address,
559            ws_context,
560        );
561        pipe.set_cluster_metrics_incarnation(metric_incarnation);
562
563        pipe.restore_readiness_events(frontend_readiness.event, backend_readiness.event);
564        // The WebSocket pipe inherits the live backend connection, so its back
565        // token must be set (back token present iff a backend is connected).
566        pipe.set_back_token(back_token);
567        debug_assert_eq!(
568            pipe.back_token(),
569            vec![back_token],
570            "websocket pipe must carry exactly the upgraded backend token"
571        );
572
573        // http.active_requests was already decremented by generate_access_log()
574        // in h1.rs when the 101 response was written (before MuxResult::Upgrade).
575        //
576        // Gauge pairing (Mux→WebSocket): leave HTTP, enter WS, and start
577        // accounting one active websocket request. These three must move
578        // together so the protocol gauges stay consistent with the session set.
579        gauge_add!(names::protocol::HTTP, -1);
580        gauge_add!(names::protocol::WS, 1);
581        gauge_add!(names::websocket::ACTIVE_REQUESTS, 1);
582        Some(HttpStateMachine::WebSocket(pipe))
583    }
584
585    fn upgrade_websocket(
586        &self,
587        ws: Pipe<crate::socket::SessionTcpStream, HttpListener>,
588    ) -> Option<HttpStateMachine> {
589        // what do we do here?
590        error!(
591            "{} upgrade called on WS, this should not happen",
592            log_context!(self)
593        );
594        Some(HttpStateMachine::WebSocket(ws))
595    }
596}
597
598impl ProxySession for HttpSession {
599    fn close(&mut self) {
600        if self.has_been_closed {
601            return;
602        }
603        // Reaching here means we are about to do the one-and-only teardown.
604        debug_assert!(
605            !self.has_been_closed,
606            "close past the guard must run on a not-yet-closed session"
607        );
608
609        trace!("{} closing HTTP session", log_context!(self));
610        self.metrics.service_stop();
611
612        // Restore gauges
613        match self.state.marker() {
614            StateMarker::Expect => gauge_add!(names::protocol::PROXY_EXPECT, -1),
615            StateMarker::Mux => gauge_add!(names::protocol::HTTP, -1),
616            StateMarker::WebSocket => {
617                gauge_add!(names::protocol::WS, -1);
618                gauge_add!(names::websocket::ACTIVE_REQUESTS, -1);
619            }
620        }
621
622        if self.state.failed() {
623            match self.state.marker() {
624                StateMarker::Expect => incr!(names::http::UPGRADE_EXPECT_FAILED),
625                StateMarker::Mux => incr!(names::http::UPGRADE_MUX_FAILED),
626                StateMarker::WebSocket => incr!(names::http::UPGRADE_WS_FAILED),
627            }
628            // FailedUpgrade means the socket was consumed by a failed upgrade
629            // attempt, so we can only close the state (no-op) and remove the
630            // session — cancel_timeouts / front_socket are unreachable.
631            self.state.close(self.proxy.clone(), &mut self.metrics);
632            self.proxy.borrow().remove_session(self.frontend_token);
633            self.has_been_closed = true;
634            debug_assert!(
635                self.has_been_closed,
636                "failed-upgrade close path must mark the session closed"
637            );
638            return;
639        }
640
641        self.state.cancel_timeouts();
642        // defer backend closing to the state
643        self.state.close(self.proxy.clone(), &mut self.metrics);
644
645        // Skipped once the client has closed its side: the close that follows
646        // sends the same FIN or RST on its own (`mux::shutdown_write`).
647        let peer_closed =
648            matches!(&self.state, HttpStateMachine::Mux(mux) if mux.frontend_peer_closed());
649        let front_socket = self.state.front_socket();
650        // invariant: write-only shutdown — Shutdown::Both on a TLS frontend
651        // discards the receive buffer and elicits TCP RST, truncating the
652        // already-queued response. Canonical write-up: the `Shutdown::Write`
653        // block of `HttpsSession::close` (`lib/src/https.rs`). Backend sockets
654        // follow the same discipline for symmetry.
655        if let Err(e) = mux::shutdown_write(front_socket, peer_closed) {
656            // error 107 NotConnected can happen when was never fully connected, or was already disconnected due to error
657            if e.kind() != ErrorKind::NotConnected {
658                error!(
659                    "{} error shutting down front socket({:?}): {:?}",
660                    log_context!(self),
661                    front_socket,
662                    e
663                )
664            }
665        }
666
667        // No `EPOLL_CTL_DEL` for the front socket: its only descriptor closes
668        // when this session drops, which `shut_down_sessions_by_frontend_tokens`
669        // (`lib/src/server.rs`) does before the event loop's next `epoll_wait`,
670        // and Linux removes a file from every epoll set on its last close.
671        // That holds because nothing duplicates a session socket: no `dup` or
672        // `try_clone`, no fork from a worker, and SCM_RIGHTS only carries
673        // listeners. The explicit deregister was one syscall per connection
674        // that bought nothing; see §11 of `doc/lifetime_of_a_session.md`.
675        let proxy = self.proxy.borrow();
676        proxy.remove_session(self.frontend_token);
677
678        self.has_been_closed = true;
679        debug_assert!(
680            self.has_been_closed,
681            "close must leave the session marked closed (idempotency latch)"
682        );
683    }
684
685    fn timeout(&mut self, token: Token) -> SessionIsToBeClosed {
686        let state_result = self.state.timeout(token, &mut self.metrics);
687        state_result == StateResult::CloseSession
688    }
689
690    fn needs_ready_pass(&self) -> bool {
691        matches!(&self.state, HttpStateMachine::Mux(mux) if mux.has_pending_backend_failure())
692    }
693
694    fn protocol(&self) -> Protocol {
695        Protocol::HTTP
696    }
697
698    fn update_readiness(&mut self, token: Token, events: Ready) {
699        trace!(
700            "{} token {:?} got event {}",
701            log_context!(self),
702            token,
703            super::ready_to_string(events)
704        );
705        self.last_event = Instant::now();
706        self.metrics.wait_start();
707        self.state.update_readiness(token, events);
708    }
709
710    fn ready(&mut self, session: Rc<RefCell<dyn ProxySession>>) -> SessionIsToBeClosed {
711        self.metrics.service_start();
712
713        let session_result =
714            self.state
715                .ready(session.clone(), self.proxy.clone(), &mut self.metrics);
716
717        let to_be_closed = match session_result {
718            SessionResult::Close => true,
719            SessionResult::Continue => false,
720            SessionResult::Upgrade => match self.upgrade() {
721                false => self.ready(session),
722                true => true,
723            },
724        };
725
726        self.metrics.service_stop();
727        // Run-to-completion postcondition: a session that is being kept alive
728        // must still satisfy its cross-field invariants. When `to_be_closed`
729        // is true the state may be a transient FailedUpgrade or already torn
730        // down, so the sweep only applies to the still-open path.
731        #[cfg(debug_assertions)]
732        if !to_be_closed {
733            self.check_invariants();
734        }
735        to_be_closed
736    }
737
738    fn shutting_down(&mut self) -> SessionIsToBeClosed {
739        self.state.shutting_down()
740    }
741
742    fn targets(&self, target: &crate::SessionTarget) -> bool {
743        self.state.targets(target)
744    }
745
746    fn last_event(&self) -> Instant {
747        self.last_event
748    }
749
750    fn print_session(&self) {
751        self.state.print_state("HTTP");
752        error!("{} Metrics: {:?}", log_context!(self), self.metrics);
753    }
754
755    fn frontend_token(&self) -> Token {
756        self.frontend_token
757    }
758}
759
760pub type Hostname = String;
761
762/// Cleartext HTTP/1.x listener.
763///
764/// # HTTP/2 over cleartext (h2c) is NOT supported
765///
766/// RFC 7540 §3.2 specified an `Upgrade: h2c` mechanism to negotiate HTTP/2
767/// over a cleartext TCP connection, with a companion prior-knowledge
768/// variant in §3.4. Both paths are intentionally absent from this listener:
769///
770/// - No `Upgrade: h2c` handler: the HTTP/1.1 state machine forwards
771///   `Upgrade` headers to the backend but never responds `101 Switching
772///   Protocols` with an HTTP/2 connection preface.
773/// - No prior-knowledge detection: the listener does not sniff the
774///   24-byte `PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n` magic string; a client
775///   that opens a TCP connection and immediately sends the preface will
776///   be interpreted as a malformed HTTP/1 request and rejected with 400.
777///
778/// RFC 9113 (the current HTTP/2 RFC, obsoleting 7540) formally deprecates
779/// the `Upgrade: h2c` mechanism. Clients that want HTTP/2 MUST use the
780/// TLS ALPN path (`HttpsListener`, selector `h2`) instead. This is
781/// consistent with the industry consensus (nginx, envoy, cloudflare) and
782/// removes an entire class of cleartext-preface smuggling primitives.
783pub struct HttpListener {
784    active: bool,
785    address: SocketAddr,
786    answers: Rc<RefCell<HttpAnswers>>,
787    config: HttpListenerConfig,
788    fronts: Router,
789    listener: Option<MioTcpListener>,
790    /// A socket this listener holds but has NOT registered.
791    ///
792    /// `activate()` parks an inherited socket here when `Registry::register`
793    /// fails, instead of letting the `?` drop and close a descriptor
794    /// `Listeners::get_*` has already removed from the SCM table (sozu#1342).
795    /// The next activation reuses it rather than binding a second one.
796    ///
797    /// Deliberately NOT `listener`: every other consumer reads that field's
798    /// `Some` as "registered and live" — `soft_stop` and `hard_stop`
799    /// deregister it and report the failure, `give_back_listener` hands it
800    /// back as an activated socket, `accept` accepts on it. A parked socket is
801    /// none of those things, and putting it there made a failed registration
802    /// answer `ENOENT` from `deregister` and fail the whole soft stop.
803    parked_listener: Option<MioTcpListener>,
804    /// Set by `activate()`, cleared by the first `accept()` that answers
805    /// `WouldBlock`: while set, `accept()` sets `TCP_NODELAY` on each socket
806    /// it returns.
807    ///
808    /// `activate()` sets `TCP_NODELAY` on the listening socket, and the kernel
809    /// copies it to every socket whose handshake completes afterwards, so a
810    /// steady-state accept needs no `setsockopt(2)` of its own
811    /// (sozu-proxy/sozu#1586). A connection that completed its handshake
812    /// BEFORE that call does not get it: the flag is copied when the
813    /// handshake completes, not when the socket is accepted. That is the
814    /// backlog of a socket inherited from a worker that never set the flag,
815    /// and whatever a fresh socket queued between `listen(2)` and
816    /// `activate()`. The first accept drain takes exactly that backlog.
817    nodelay_backlog: bool,
818    tags: BTreeMap<String, CachedTags>,
819    token: Token,
820}
821
822impl ListenerHandler for HttpListener {
823    fn get_addr(&self) -> &SocketAddr {
824        &self.address
825    }
826
827    fn get_tags(&self, key: &str) -> Option<&CachedTags> {
828        self.tags.get(key)
829    }
830
831    fn set_tags(&mut self, key: String, tags: Option<BTreeMap<String, String>>) {
832        match tags {
833            Some(tags) => self.tags.insert(key, CachedTags::new(tags)),
834            None => self.tags.remove(&key),
835        };
836    }
837
838    fn protocol(&self) -> Protocol {
839        Protocol::HTTP
840    }
841
842    fn public_address(&self) -> SocketAddr {
843        self.config
844            .public_address
845            .map(|addr| addr.into())
846            .unwrap_or(self.address)
847    }
848}
849
850impl L7ListenerHandler for HttpListener {
851    fn get_sticky_name(&self) -> &str {
852        &self.config.sticky_name
853    }
854
855    fn get_sozu_id_header(&self) -> &str {
856        self.config
857            .sozu_id_header
858            .as_deref()
859            .filter(|s| !s.is_empty())
860            .unwrap_or("Sozu-Id")
861    }
862
863    fn get_connect_timeout(&self) -> u32 {
864        self.config.connect_timeout
865    }
866
867    // redundant, already called once in extract_route
868    fn frontend_from_request(
869        &self,
870        host: &str,
871        uri: &str,
872        method: &Method,
873        headers: &dyn RequestHeaders,
874    ) -> Result<RouteResult, FrontendFromRequestError> {
875        let start = Instant::now();
876        let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
877            Ok(tuple) => tuple,
878            Err(parse_error) => {
879                // parse_error contains a slice of given_host, which should NOT escape this scope
880                return Err(FrontendFromRequestError::HostParse {
881                    host: host.to_owned(),
882                    error: parse_error.to_string(),
883                });
884            }
885        };
886        if remaining_input != &b""[..] {
887            return Err(FrontendFromRequestError::InvalidCharsAfterHost(
888                host.to_owned(),
889            ));
890        }
891
892        /*if port == Some(&b"80"[..]) {
893        // it is alright to call from_utf8_unchecked,
894        // we already verified that there are only ascii
895        // chars in there
896          unsafe { from_utf8_unchecked(hostname) }
897        } else {
898          host
899        }
900        */
901        // SAFETY: `hostname` was just produced by `hostname_and_port`
902        // (`lib/src/protocol/kawa_h1/parser.rs`), which only accepts
903        // bytes matching `is_hostname_char` (alphanumeric, `-`, `.`, plus
904        // `_` under the tolerant-http1-parser feature). All accepted
905        // bytes are ASCII (≤ 0x7F), so the slice is valid single-byte UTF-8.
906        let host = unsafe { from_utf8_unchecked(hostname) };
907
908        let route = self
909            .fronts
910            .lookup_with_headers(host, uri, method, headers)
911            .map_err(|e| {
912                incr!(names::http::FAILED_BACKEND_MATCHING);
913                FrontendFromRequestError::NoClusterFound(e)
914            })?;
915
916        let now = Instant::now();
917
918        if let Some(cluster) = route.cluster_id.as_deref() {
919            time!(
920                names::event_loop::FRONTEND_MATCHING_TIME,
921                cluster,
922                (now - start).as_millis()
923            );
924        }
925
926        Ok(route)
927    }
928
929    fn get_answers(&self) -> &Rc<RefCell<HttpAnswers>> {
930        &self.answers
931    }
932
933    fn get_h2_flood_config(&self) -> crate::protocol::mux::H2FloodConfig {
934        // `H2FloodConfig`'s fields are private and `from_optional` applies the
935        // `.max(1)` clamp `new` carries (sozu-proxy/sozu#1418). This used to be
936        // a raw struct literal, which skipped that clamp on the only path that
937        // carries operator-set thresholds: config load rejects an out-of-range
938        // knob (`ConfigError::H2ThresholdBelowMinimum`), and the clamp is the
939        // structural backstop for a raw protobuf `AddHttpListener` or a
940        // state file saved before that gate existed.
941        crate::protocol::mux::H2FloodConfig::from_optional(
942            self.config.h2_max_rst_stream_per_window,
943            self.config.h2_max_ping_per_window,
944            self.config.h2_max_settings_per_window,
945            self.config.h2_max_empty_data_per_window,
946            self.config.h2_max_window_update_stream0_per_window,
947            self.config.h2_max_continuation_frames,
948            self.config.h2_max_glitch_count,
949            self.config.h2_max_rst_stream_lifetime,
950            self.config.h2_max_rst_stream_abusive_lifetime,
951            self.config.h2_max_rst_stream_emitted_lifetime,
952            self.config.h2_max_header_list_size,
953            self.config.h2_max_header_table_size,
954            self.config.h2_max_header_fields,
955            self.config.h2_stream_refusal_percent,
956        )
957    }
958
959    fn get_h2_connection_config(&self) -> crate::protocol::mux::H2ConnectionConfig {
960        crate::protocol::mux::H2ConnectionConfig::from_optional(
961            self.config.h2_initial_connection_window,
962            self.config.h2_max_concurrent_streams,
963            self.config.h2_stream_shrink_ratio,
964        )
965    }
966
967    fn get_h2_stream_idle_timeout(&self) -> std::time::Duration {
968        // Inherit `back_timeout` when the knob is unset so listeners tuned for
969        // long-running backends do not cancel streams at the 30 s security
970        // floor. The `max(30, …)` keeps the baseline slow-multiplex mitigation
971        // when `back_timeout` is shorter than 30 s. Explicit values (including
972        // ones below 30 s) win — operators under a slow-multiplex attack can
973        // lower the per-stream deadline to cap buffer pinning.
974        let seconds = self
975            .config
976            .h2_stream_idle_timeout_seconds
977            .map(|s| u64::from(s.max(1)))
978            .unwrap_or_else(|| u64::from(self.config.back_timeout).max(30));
979        std::time::Duration::from_secs(seconds)
980    }
981
982    fn get_h2_graceful_shutdown_deadline(&self) -> Option<std::time::Duration> {
983        match self.config.h2_graceful_shutdown_deadline_seconds {
984            None => Some(std::time::Duration::from_secs(5)),
985            Some(0) => None,
986            Some(s) => Some(std::time::Duration::from_secs(u64::from(s))),
987        }
988    }
989
990    fn get_elide_x_real_ip(&self) -> bool {
991        self.config.elide_x_real_ip.unwrap_or(false)
992    }
993
994    fn get_send_x_real_ip(&self) -> bool {
995        self.config.send_x_real_ip.unwrap_or(false)
996    }
997
998    fn get_forwarded_headers(&self) -> ForwardedHeaders {
999        // An unknown wire value reads as the default, like prost's own
1000        // getter; `update_config` and the main-process state refuse one.
1001        self.config.forwarded_headers()
1002    }
1003}
1004
1005pub struct HttpProxy {
1006    backends: Rc<RefCell<BackendMap>>,
1007    clusters: HashMap<ClusterId, Cluster>,
1008    listeners: HashMap<Token, Rc<RefCell<HttpListener>>>,
1009    pool: Rc<RefCell<Pool>>,
1010    registry: Registry,
1011    sessions: Rc<RefCell<SessionManager>>,
1012}
1013
1014impl HttpProxy {
1015    pub fn new(
1016        registry: Registry,
1017        sessions: Rc<RefCell<SessionManager>>,
1018        pool: Rc<RefCell<Pool>>,
1019        backends: Rc<RefCell<BackendMap>>,
1020    ) -> HttpProxy {
1021        HttpProxy {
1022            backends,
1023            clusters: HashMap::new(),
1024            listeners: HashMap::new(),
1025            pool,
1026            registry,
1027            sessions,
1028        }
1029    }
1030
1031    pub fn add_listener(
1032        &mut self,
1033        config: HttpListenerConfig,
1034        token: Token,
1035    ) -> Result<Token, ProxyError> {
1036        if self.listeners.contains_key(&token) {
1037            return Err(ProxyError::ListenerAlreadyPresent);
1038        }
1039        let mut http_listener =
1040            HttpListener::new(config, token).map_err(ProxyError::AddListener)?;
1041        // Frontends name their listener by address alone, so a listener added
1042        // next to one already on its address (on another interface) takes
1043        // over the routes that one serves: every listener on an address holds
1044        // the same routes, whenever it was added (sozu-proxy/sozu#719).
1045        if let Some(sibling) = self
1046            .listeners_at(http_listener.address)
1047            .ok()
1048            .and_then(|l| l.into_iter().next())
1049        {
1050            let sibling = sibling.borrow();
1051            http_listener.fronts = sibling.fronts.clone();
1052            http_listener.tags = sibling.tags.clone();
1053        }
1054        match self.listeners.entry(token) {
1055            Entry::Vacant(entry) => {
1056                entry.insert(Rc::new(RefCell::new(http_listener)));
1057                Ok(token)
1058            }
1059            _ => Err(ProxyError::ListenerAlreadyPresent),
1060        }
1061    }
1062
1063    /// Add one listener and restore routes retained in the worker state when
1064    /// no live sibling can provide them.
1065    ///
1066    /// A sibling already carries the address-wide routes and `add_listener`
1067    /// clones them. When the last listener on an address was removed, the
1068    /// control-plane state deliberately kept its frontends; replay them onto
1069    /// the new listener before the server commits its session slot. Any
1070    /// failure drops only that new listener.
1071    pub(crate) fn add_listener_with_retained_frontends(
1072        &mut self,
1073        config: HttpListenerConfig,
1074        token: Token,
1075        retained_frontends: Vec<RequestHttpFrontend>,
1076    ) -> Result<Token, ProxyError> {
1077        let address = config.address.into();
1078        let had_sibling = self.listeners_at(address).is_ok();
1079        self.add_listener(config, token)?;
1080
1081        if !had_sibling {
1082            for frontend in retained_frontends {
1083                if let Err(error) = self.add_http_frontend(frontend) {
1084                    self.listeners.remove(&token);
1085                    return Err(error);
1086                }
1087            }
1088        }
1089
1090        Ok(token)
1091    }
1092
1093    pub fn get_listener(&self, token: &Token) -> Option<Rc<RefCell<HttpListener>>> {
1094        self.listeners.get(token).cloned()
1095    }
1096
1097    pub fn remove_listener(&mut self, remove: RemoveListener) -> Result<(), ProxyError> {
1098        let len = self.listeners.len();
1099        let remove_address = remove.listener_key();
1100        self.listeners
1101            .retain(|_, l| !l.borrow().is(&remove_address));
1102
1103        if !self.listeners.len() < len {
1104            info!(
1105                "{} no HTTP listener to remove at {}",
1106                log_module_context!(),
1107                remove_address
1108            );
1109        }
1110        Ok(())
1111    }
1112
1113    /// What this proxy would do with a socket handed to
1114    /// [`activate_listener`](Self::activate_listener) for `addr`. See
1115    /// [`crate::InheritedSocketFate`].
1116    ///
1117    /// The event loop is single-threaded, so nothing can change the listener's
1118    /// `active` flag between this answer and the `activate_listener` call that
1119    /// acts on it.
1120    pub fn inherited_socket_fate(&self, addr: &ListenerKey) -> crate::InheritedSocketFate {
1121        use crate::InheritedSocketFate;
1122        match self
1123            .listeners
1124            .values()
1125            .find(|listener| listener.borrow().is(addr))
1126        {
1127            None => InheritedSocketFate::Unclaimed,
1128            Some(listener) if listener.borrow().active => InheritedSocketFate::Refused,
1129            Some(_) => InheritedSocketFate::Adopted,
1130        }
1131    }
1132
1133    pub fn activate_listener(
1134        &self,
1135        addr: &ListenerKey,
1136        tcp_listener: Option<MioTcpListener>,
1137    ) -> Result<Token, ProxyError> {
1138        let listener = self
1139            .listeners
1140            .values()
1141            .find(|listener| listener.borrow().is(addr))
1142            .ok_or(ProxyError::NoListenerFound(addr.clone()))?;
1143
1144        listener
1145            .borrow_mut()
1146            .activate(&self.registry, tcp_listener)
1147            .map_err(|listener_error| ProxyError::ListenerActivation {
1148                address: addr.address,
1149                listener_error,
1150            })
1151    }
1152
1153    /// The slab token reserved for the listener at `address`, if this proxy
1154    /// holds one.
1155    ///
1156    /// A listener owns exactly one slab slot for its whole `AddListener` ->
1157    /// `RemoveListener` lifetime, and no `remove_listener` implementation
1158    /// touches the session slab. `Server` therefore reads the token here
1159    /// BEFORE dropping the listener, so that reserved slot is released exactly
1160    /// once, at the end of the lifetime.
1161    pub fn listener_token(&self, address: &ListenerKey) -> Option<Token> {
1162        self.listeners
1163            .iter()
1164            .find(|(_, listener)| listener.borrow().is(address))
1165            .map(|(token, _)| *token)
1166    }
1167
1168    pub fn give_back_listeners(&mut self) -> Vec<(ListenerKey, MioTcpListener)> {
1169        self.listeners
1170            .values()
1171            .filter_map(|listener| {
1172                let mut owned = listener.borrow_mut();
1173                if let Some(listener) = owned.listener.take() {
1174                    // Reset `active` so a subsequent `activate()` re-binds
1175                    // instead of short-circuiting on the stale flag.
1176                    owned.active = false;
1177                    return Some((owned.key(), listener));
1178                }
1179
1180                None
1181            })
1182            .collect()
1183    }
1184
1185    pub fn give_back_listener(
1186        &mut self,
1187        address: &ListenerKey,
1188    ) -> Result<(Token, MioTcpListener), ProxyError> {
1189        let listener = self
1190            .listeners
1191            .values()
1192            .find(|listener| listener.borrow().is(address))
1193            .ok_or(ProxyError::NoListenerFound(address.clone()))?;
1194
1195        let mut owned = listener.borrow_mut();
1196
1197        let taken_listener = owned
1198            .listener
1199            .take()
1200            .ok_or(ProxyError::UnactivatedListener)?;
1201
1202        // Reset `active` so a subsequent `activate()` re-binds instead of
1203        // short-circuiting on the stale flag.
1204        owned.active = false;
1205
1206        Ok((owned.token, taken_listener))
1207    }
1208
1209    /// Every listener on `address`, whatever its interface. Frontends and
1210    /// certificates name their listener by address alone, so they apply to
1211    /// all the listeners sharing that address on different interfaces.
1212    fn listeners_at(
1213        &self,
1214        address: SocketAddr,
1215    ) -> Result<Vec<Rc<RefCell<HttpListener>>>, ProxyError> {
1216        let mut listeners: Vec<_> = self
1217            .listeners
1218            .iter()
1219            .filter(|(_, listener)| listener.borrow().address == address)
1220            .map(|(token, listener)| (*token, listener.clone()))
1221            .collect();
1222        // Token order, so a failure is reported the same way on every run.
1223        listeners.sort_by_key(|(token, _)| *token);
1224        let listeners: Vec<_> = listeners
1225            .into_iter()
1226            .map(|(_, listener)| listener)
1227            .collect();
1228        if listeners.is_empty() {
1229            return Err(ProxyError::NoListenerFound(address.into()));
1230        }
1231        Ok(listeners)
1232    }
1233
1234    /// Apply a partial-update patch to the identified HTTP listener.
1235    pub fn update_listener(&mut self, patch: UpdateHttpListenerConfig) -> Result<(), ProxyError> {
1236        let address: std::net::SocketAddr = patch.address.into();
1237        let listener = self
1238            .listeners
1239            .values()
1240            .find(|l| l.borrow().address == address)
1241            .ok_or(ProxyError::NoListenerFound(address.into()))?;
1242        listener
1243            .borrow_mut()
1244            .update_config(&patch)
1245            .map_err(|listener_error| ProxyError::ListenerActivation {
1246                address,
1247                listener_error,
1248            })
1249    }
1250
1251    pub fn add_cluster(&mut self, mut cluster: Cluster) -> Result<(), ProxyError> {
1252        // Reconcile the legacy single-status `answer_503` field with the
1253        // new map. The new map wins on collision.
1254        let mut overrides = cluster.answers.clone();
1255        if let Some(answer_503) = cluster.answer_503.take() {
1256            overrides.entry("503".to_owned()).or_insert(answer_503);
1257        }
1258        if !overrides.is_empty() {
1259            for listener in self.listeners.values() {
1260                listener
1261                    .borrow()
1262                    .answers
1263                    .borrow_mut()
1264                    .add_cluster_answers(&cluster.cluster_id, &overrides)
1265                    .map_err(|(name, error)| {
1266                        ProxyError::AddCluster(ListenerError::TemplateParse(name, error))
1267                    })?;
1268            }
1269        }
1270        self.clusters
1271            .insert(cluster.cluster_id.as_str().into(), cluster);
1272        Ok(())
1273    }
1274
1275    pub fn remove_cluster(&mut self, cluster_id: &str) -> Result<(), ProxyError> {
1276        self.clusters.remove(cluster_id);
1277
1278        for listener in self.listeners.values() {
1279            listener
1280                .borrow()
1281                .answers
1282                .borrow_mut()
1283                .remove_cluster_answers(cluster_id);
1284        }
1285        Ok(())
1286    }
1287
1288    pub fn add_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1289        // RFC 6797 §7.2: `Strict-Transport-Security` MUST NOT be sent over
1290        // plaintext HTTP. Reject any AddHttpFrontend that ships an enabled
1291        // HSTS policy before it ever touches the routing trie. This is
1292        // defense in depth on top of the TOML config-load reject in
1293        // `command/src/config.rs`; sites that build a `RequestHttpFrontend`
1294        // outside the TOML path (`sozu frontend http add`, programmatic
1295        // IPC senders) are caught here.
1296        // Reject ANY hsts field on a plain-HTTP frontend, not just
1297        // `enabled = true`. There is no listener-default HSTS to inherit
1298        // on an HTTP listener (the field doesn't exist on
1299        // `HttpListenerConfig`), so the explicit-disable signal
1300        // (`enabled = false`) has nothing to suppress on this surface.
1301        // Carrying any `hsts` field here is a misconfiguration rather
1302        // than a deliberate choice.
1303        if front.hsts.is_some() {
1304            incr!(names::http::HSTS_SUPPRESSED_PLAINTEXT);
1305            return Err(ProxyError::HstsOnPlainHttp(front.address.into()));
1306        }
1307
1308        let front = front.clone().to_frontend().map_err(|request_error| {
1309            ProxyError::WrongInputFrontend {
1310                front: Box::new(front),
1311                error: request_error.to_string(),
1312            }
1313        })?;
1314
1315        for listener in self.listeners_at(front.address)? {
1316            let mut listener = listener.borrow_mut();
1317            let hostname = front.hostname.to_owned();
1318            let tags = front.tags.to_owned();
1319
1320            listener
1321                .add_http_front(front.clone())
1322                .map_err(ProxyError::AddFrontend)?;
1323            listener.set_tags(hostname, tags);
1324        }
1325        Ok(())
1326    }
1327
1328    pub fn remove_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1329        let front = front.clone().to_frontend().map_err(|request_error| {
1330            ProxyError::WrongInputFrontend {
1331                front: Box::new(front),
1332                error: request_error.to_string(),
1333            }
1334        })?;
1335
1336        // Every listener on the address drops the route, even when another
1337        // one fails: a sibling must never keep a route the others removed.
1338        // The first failure, in token order, is the one reported.
1339        let mut result = Ok(());
1340        for listener in self.listeners_at(front.address)? {
1341            let mut listener = listener.borrow_mut();
1342            let hostname = front.hostname.to_owned();
1343
1344            let removed = listener
1345                .remove_http_front(front.clone())
1346                .map_err(ProxyError::RemoveFrontend);
1347            if result.is_ok() {
1348                result = removed;
1349            }
1350
1351            if !listener.fronts.has_hostname(&hostname) {
1352                listener.set_tags(hostname, None);
1353            }
1354        }
1355        result
1356    }
1357
1358    pub fn soft_stop(&mut self) -> Result<(), ProxyError> {
1359        let listeners = std::mem::take(&mut self.listeners);
1360        let mut socket_errors = vec![];
1361        for l in listeners.values() {
1362            if let Some(mut sock) = l.borrow_mut().listener.take() {
1363                debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1364                if let Err(e) = self.registry.deregister(&mut sock) {
1365                    let error = format!("socket {sock:?}: {e:?}");
1366                    socket_errors.push(error);
1367                }
1368            }
1369        }
1370
1371        if !socket_errors.is_empty() {
1372            return Err(ProxyError::SoftStop {
1373                proxy_protocol: "HTTP".to_string(),
1374                error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1375            });
1376        }
1377
1378        Ok(())
1379    }
1380
1381    pub fn hard_stop(&mut self) -> Result<(), ProxyError> {
1382        let mut listeners = std::mem::take(&mut self.listeners);
1383        let mut socket_errors = vec![];
1384        for (_, l) in listeners.drain() {
1385            if let Some(mut sock) = l.borrow_mut().listener.take() {
1386                debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1387                if let Err(e) = self.registry.deregister(&mut sock) {
1388                    let error = format!("socket {sock:?}: {e:?}");
1389                    socket_errors.push(error);
1390                }
1391            }
1392        }
1393
1394        if !socket_errors.is_empty() {
1395            return Err(ProxyError::HardStop {
1396                proxy_protocol: "HTTP".to_string(),
1397                error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1398            });
1399        }
1400
1401        Ok(())
1402    }
1403}
1404
1405impl HttpListener {
1406    /// This listener's identity: its address, plus the network interface its
1407    /// socket is bound to when it has one.
1408    pub fn key(&self) -> ListenerKey {
1409        ListenerKey::new(self.address, self.config.interface.as_deref())
1410    }
1411
1412    /// Whether this listener is the one `key` names.
1413    pub fn is(&self, key: &ListenerKey) -> bool {
1414        key.matches(&self.address, self.config.interface.as_deref())
1415    }
1416
1417    pub fn new(config: HttpListenerConfig, token: Token) -> Result<HttpListener, ListenerError> {
1418        // Defense in depth, as `update_config`: the main-process state
1419        // refuses an unknown mode, a raw protobuf client may not.
1420        if let Some(v) = config.forwarded_headers {
1421            validate_forwarded_headers(v)?;
1422        }
1423        let answers = Self::build_answers(&config)?;
1424        Ok(HttpListener {
1425            active: false,
1426            address: config.address.into(),
1427            answers: Rc::new(RefCell::new(answers)),
1428            config,
1429            fronts: Router::new(),
1430            listener: None,
1431            parked_listener: None,
1432            nodelay_backlog: false,
1433            tags: BTreeMap::new(),
1434            token,
1435        })
1436    }
1437
1438    /// Build the listener's HTTP answer templates from its config, reconciling
1439    /// the legacy `http_answers` per-status fields with the new `answers`
1440    /// template map (the new map wins on collision; legacy fields fill any
1441    /// status not yet migrated). Shared by [`Self::new`] and
1442    /// [`Self::validate_config`] so the master-side pre-commit check cannot
1443    /// drift from worker construction.
1444    fn build_answers(config: &HttpListenerConfig) -> Result<HttpAnswers, ListenerError> {
1445        let mut answers_map = config.answers.clone();
1446        if let Some(ref legacy) = config.http_answers {
1447            crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1448        }
1449        HttpAnswers::new(&answers_map)
1450            .map_err(|(name, error)| ListenerError::TemplateParse(name, error))
1451    }
1452
1453    /// Validate that a worker can build this HTTP listener configuration (its
1454    /// answer templates) WITHOUT constructing the full listener or binding a
1455    /// socket. The main process calls this before committing an
1456    /// `AddHttpListener` to its `ConfigState` and fanning it out, so an invalid
1457    /// listener never reserves its address and blocks a corrected reload
1458    /// (sozu#1301).
1459    ///
1460    /// Buildability only. The H2 knob floors are deliberately NOT checked
1461    /// here (sozu#1418): a worker *can* build a listener whose threshold is
1462    /// out of range — `H2FloodConfig::from_optional` clamps it — so refusing
1463    /// it is a policy the command socket applies to what an operator is
1464    /// authoring, not a statement about what a worker can construct. That
1465    /// gate lives beside its audience, in
1466    /// `bin/src/command/requests.rs::validate_h2_knob_floors`.
1467    pub fn validate_config(config: &HttpListenerConfig) -> Result<(), ListenerError> {
1468        Self::build_answers(config)?;
1469        Ok(())
1470    }
1471
1472    pub fn activate(
1473        &mut self,
1474        registry: &Registry,
1475        tcp_listener: Option<MioTcpListener>,
1476    ) -> Result<Token, ListenerError> {
1477        if self.active {
1478            return Ok(self.token);
1479        }
1480        let address: SocketAddr = self.config.address.into();
1481
1482        // Reuse a socket a previous attempt parked here after a failed
1483        // registration, rather than binding a second one. Taking it
1484        // unconditionally is what keeps the two from coexisting: if the caller
1485        // ALSO handed one over, the parked socket is disposed of deliberately
1486        // below instead of being silently overwritten.
1487        let parked = self.parked_listener.take();
1488        let mut listener = match (tcp_listener, parked) {
1489            (Some(inherited), Some(parked)) => {
1490                // Not reachable while `Listeners::get_*` removes the entry it
1491                // hands over and `scm_listeners` is written once: a parked
1492                // socket means this address's entry was already consumed, so
1493                // the caller cannot have another. Neither of those is enforced
1494                // here, so dispose of the loser explicitly and loudly rather
1495                // than letting an assignment close it — that silent close is
1496                // sozu#1342 itself.
1497                warn!(
1498                    "{} closing the socket parked on the HTTP listener for {}: an \
1499                     inherited socket arrived for the same address and is the one being \
1500                     activated",
1501                    log_module_context!(),
1502                    address
1503                );
1504                drop(parked);
1505                inherited
1506            }
1507            (Some(inherited), None) => inherited,
1508            (None, Some(parked)) => parked,
1509            (None, None) => server_bind(address, self.config.interface.as_deref()).map_err(
1510                |server_bind_error| ListenerError::Activation {
1511                    address,
1512                    error: server_bind_error.to_string(),
1513                },
1514            )?,
1515        };
1516
1517        // Once per listener, on whichever socket won above — freshly bound,
1518        // inherited over SCM_RIGHTS from a worker that may never have set it,
1519        // or parked by a failed registration. Every socket this listener
1520        // accepts from now on inherits it; see `nodelay_backlog` for the ones
1521        // already queued.
1522        if let Err(e) = SockRef::from(&listener).set_tcp_nodelay(true) {
1523            error!(
1524                "{} error setting nodelay on listen socket({:?}): {:?}",
1525                log_module_context!(),
1526                listener,
1527                e
1528            );
1529        }
1530
1531        let registration = registry
1532            .register(&mut listener, self.token, Interest::READABLE)
1533            .map_err(ListenerError::SocketRegistration);
1534        if let Err(error) = registration {
1535            // Park the socket instead of letting the `?` drop it. `register`
1536            // is fallible (EEXIST for a descriptor still registered because
1537            // `notify_deactivate_listener` only logs a failed deregister,
1538            // EBADF, ENOMEM), and a socket still held in a local is dropped —
1539            // and closed — on the way out. For an SCM-inherited socket that is
1540            // the sozu#1342 close in a second place: `Listeners::get_*` has
1541            // already removed the entry, so nothing else owns the descriptor.
1542            // It goes to `parked_listener`, never to `listener`, because every other
1543            // consumer of that field reads `Some` as "registered and live".
1544            self.parked_listener = Some(listener);
1545            return Err(error);
1546        }
1547
1548        self.listener = Some(listener);
1549        self.nodelay_backlog = true;
1550        self.active = true;
1551        Ok(self.token)
1552    }
1553
1554    /// Apply a partial-update patch to this listener's live configuration.
1555    ///
1556    /// Fields absent in the patch (i.e. `None`) are preserved unchanged.
1557    /// If `http_answers` is present only the listener-default templates are
1558    /// replaced; per-cluster overrides in `cluster_custom_answers` are kept.
1559    ///
1560    /// All-or-nothing (sozu-proxy/sozu#1703): the patch is validated, then
1561    /// applied to a copy of the configuration, and every fallible artifact
1562    /// (the rebuilt answer registry) is built from that copy. Only once all
1563    /// of it succeeded are the copy and the artifacts committed, so a refused
1564    /// patch leaves the listener exactly as it was. The copy is one
1565    /// `HttpListenerConfig` clone per patch, on the command path only.
1566    pub fn update_config(&mut self, patch: &UpdateHttpListenerConfig) -> Result<(), ListenerError> {
1567        // Defense-in-depth validation: main-process ConfigState::dispatch
1568        // validates before scatter, but a raw protobuf client or state replay
1569        // may reach the worker without that check. `StateError` lifts into
1570        // `ListenerError` via `From` so `?` suffices. The main process runs
1571        // the same checks (`ConfigState::update_http_listener` and, for the
1572        // templates, `validate_listener_request` in
1573        // `bin/src/command/requests.rs`), so both refuse the same patches.
1574        validate_h2_flood_knobs_http(patch)?;
1575        if let Some(ref hdr) = patch.sozu_id_header {
1576            validate_sozu_id_header(hdr)?;
1577        }
1578        if let Some(v) = patch.forwarded_headers {
1579            validate_forwarded_headers(v)?;
1580        }
1581        HttpAnswers::validate_patch_templates(patch.http_answers.as_ref(), &patch.answers)
1582            .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?;
1583
1584        let mut config = self.config.clone();
1585
1586        if let Some(v) = patch.public_address {
1587            config.public_address = Some(v);
1588        }
1589        if let Some(v) = patch.expect_proxy {
1590            config.expect_proxy = v;
1591        }
1592        if let Some(ref v) = patch.sticky_name {
1593            config.sticky_name = v.to_owned();
1594        }
1595        if let Some(v) = patch.front_timeout {
1596            config.front_timeout = v;
1597        }
1598        if let Some(v) = patch.back_timeout {
1599            config.back_timeout = v;
1600        }
1601        if let Some(v) = patch.connect_timeout {
1602            config.connect_timeout = v;
1603        }
1604        if let Some(v) = patch.request_timeout {
1605            config.request_timeout = v;
1606        }
1607        if let Some(ref v) = patch.sozu_id_header {
1608            config.sozu_id_header = Some(v.to_owned());
1609        }
1610        if let Some(v) = patch.elide_x_real_ip {
1611            config.elide_x_real_ip = Some(v);
1612        }
1613        if let Some(v) = patch.send_x_real_ip {
1614            config.send_x_real_ip = Some(v);
1615        }
1616        if let Some(v) = patch.forwarded_headers {
1617            config.forwarded_headers = Some(v);
1618        }
1619
1620        // H2 flood knobs
1621        if let Some(v) = patch.h2_max_rst_stream_per_window {
1622            config.h2_max_rst_stream_per_window = Some(v);
1623        }
1624        if let Some(v) = patch.h2_max_ping_per_window {
1625            config.h2_max_ping_per_window = Some(v);
1626        }
1627        if let Some(v) = patch.h2_max_settings_per_window {
1628            config.h2_max_settings_per_window = Some(v);
1629        }
1630        if let Some(v) = patch.h2_max_empty_data_per_window {
1631            config.h2_max_empty_data_per_window = Some(v);
1632        }
1633        if let Some(v) = patch.h2_max_continuation_frames {
1634            config.h2_max_continuation_frames = Some(v);
1635        }
1636        if let Some(v) = patch.h2_max_glitch_count {
1637            config.h2_max_glitch_count = Some(v);
1638        }
1639        if let Some(v) = patch.h2_initial_connection_window {
1640            config.h2_initial_connection_window = Some(v);
1641        }
1642        if let Some(v) = patch.h2_max_concurrent_streams {
1643            config.h2_max_concurrent_streams = Some(v);
1644        }
1645        if let Some(v) = patch.h2_stream_shrink_ratio {
1646            config.h2_stream_shrink_ratio = Some(v);
1647        }
1648        if let Some(v) = patch.h2_max_rst_stream_lifetime {
1649            config.h2_max_rst_stream_lifetime = Some(v);
1650        }
1651        if let Some(v) = patch.h2_max_rst_stream_abusive_lifetime {
1652            config.h2_max_rst_stream_abusive_lifetime = Some(v);
1653        }
1654        if let Some(v) = patch.h2_max_rst_stream_emitted_lifetime {
1655            config.h2_max_rst_stream_emitted_lifetime = Some(v);
1656        }
1657        if let Some(v) = patch.h2_max_header_list_size {
1658            config.h2_max_header_list_size = Some(v);
1659        }
1660        if let Some(v) = patch.h2_max_header_table_size {
1661            config.h2_max_header_table_size = Some(v);
1662        }
1663        if let Some(v) = patch.h2_max_header_fields {
1664            config.h2_max_header_fields = Some(v);
1665        }
1666        if let Some(v) = patch.h2_stream_refusal_percent {
1667            config.h2_stream_refusal_percent = Some(v);
1668        }
1669        if let Some(v) = patch.h2_stream_idle_timeout_seconds {
1670            config.h2_stream_idle_timeout_seconds = Some(v);
1671        }
1672        if let Some(v) = patch.h2_graceful_shutdown_deadline_seconds {
1673            config.h2_graceful_shutdown_deadline_seconds = Some(v);
1674        }
1675        if let Some(v) = patch.h2_max_window_update_stream0_per_window {
1676            config.h2_max_window_update_stream0_per_window = Some(v);
1677        }
1678
1679        // HTTP answers: merge legacy `http_answers` and the new `answers`
1680        // map on top of the candidate config, then build the listener-level
1681        // template registry from it. It is fallible, so it is built here and
1682        // published below with the config, never before.
1683        let answers_changed = patch.http_answers.is_some() || !patch.answers.is_empty();
1684        let new_answers = if answers_changed {
1685            if let Some(ref new_answers) = patch.http_answers {
1686                crate::sozu_command::state::merge_custom_http_answers(
1687                    &mut config.http_answers,
1688                    new_answers,
1689                );
1690            }
1691            for (code, body) in &patch.answers {
1692                if !body.is_empty() {
1693                    config.answers.insert(code.clone(), body.clone());
1694                }
1695            }
1696
1697            let mut answers_map = config.answers.clone();
1698            if let Some(ref legacy) = config.http_answers {
1699                crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1700            }
1701            Some(
1702                HttpAnswers::new(&answers_map)
1703                    .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?,
1704            )
1705        } else {
1706            None
1707        };
1708
1709        // Commit: nothing below can fail.
1710        self.config = config;
1711        if let Some(mut new_answers) = new_answers {
1712            // Per-cluster overrides in `HttpAnswers::cluster_answers` are
1713            // preserved across the rebuild.
1714            //
1715            // The rebuilt registry is PUBLISHED under a new `Rc`, not written
1716            // through the old one. Every request already in flight captured
1717            // the old handle when it arrived (`mux::Context::create_stream`)
1718            // and renders its default answers from that, so a reload landing
1719            // mid-request cannot change the page that request is about to
1720            // emit; requests arriving after this point capture the new handle.
1721            // That is the one-request staleness window of
1722            // `lib/src/protocol/mux/LIFECYCLE.md` §2.5.
1723            //
1724            // The per-cluster overrides are therefore COPIED, not
1725            // `mem::take`n: taking them would empty the registry those
1726            // in-flight requests still hold and strip their cluster templates
1727            // mid-response. The copy is a map clone over `Rc<Template>`, so
1728            // both registries share the compiled templates themselves.
1729            new_answers.cluster_answers = self.answers.borrow().cluster_answers.clone();
1730            self.answers = Rc::new(RefCell::new(new_answers));
1731        }
1732
1733        Ok(())
1734    }
1735
1736    pub fn add_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1737        self.fronts
1738            .add_http_front(&http_front)
1739            .map_err(ListenerError::AddFrontend)
1740    }
1741
1742    pub fn remove_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1743        debug!(
1744            "{} removing http_front {:?}",
1745            log_module_context!(),
1746            http_front
1747        );
1748        self.fronts
1749            .remove_http_front(&http_front)
1750            .map_err(ListenerError::RemoveFrontend)
1751    }
1752
1753    fn accept(&mut self) -> Result<(TcpStream, SocketAddr), AcceptError> {
1754        if let Some(ref sock) = self.listener {
1755            match sock.accept() {
1756                Ok((frontend_sock, peer)) => {
1757                    if self.nodelay_backlog
1758                        && let Err(e) = frontend_sock.set_nodelay(true)
1759                    {
1760                        error!(
1761                            "{} error setting nodelay on front socket({:?}): {:?}",
1762                            log_module_context!(),
1763                            frontend_sock,
1764                            e
1765                        );
1766                    }
1767                    Ok((frontend_sock, peer))
1768                }
1769                Err(e) if e.kind() == ErrorKind::WouldBlock => {
1770                    // The backlog queued before `activate()` is drained.
1771                    self.nodelay_backlog = false;
1772                    Err(AcceptError::WouldBlock)
1773                }
1774                Err(e) => {
1775                    error!("{} accept() IO error: {:?}", log_module_context!(), e);
1776                    Err(AcceptError::IoError)
1777                }
1778            }
1779        } else {
1780            error!(
1781                "{} cannot accept connections, no listening socket available",
1782                log_module_context!()
1783            );
1784            Err(AcceptError::IoError)
1785        }
1786    }
1787}
1788
1789impl ProxyConfiguration for HttpProxy {
1790    fn notify(&mut self, request: WorkerRequest) -> WorkerResponse {
1791        let request_id = request.id.clone();
1792
1793        let result = match request.content.request_type {
1794            Some(RequestType::AddCluster(cluster)) => {
1795                debug!(
1796                    "{} {} add cluster {:?}",
1797                    log_module_context!(),
1798                    request.id,
1799                    cluster
1800                );
1801                self.add_cluster(cluster)
1802            }
1803            Some(RequestType::RemoveCluster(cluster_id)) => {
1804                debug!(
1805                    "{} {} remove cluster {:?}",
1806                    log_module_context!(),
1807                    request_id,
1808                    cluster_id
1809                );
1810                self.remove_cluster(&cluster_id)
1811            }
1812            Some(
1813                RequestType::AddHttpFrontend(front)
1814                | RequestType::AddHttpFrontendWithSilentClose(front)
1815                | RequestType::AddHttpFrontendWithHeaderPredicates(front),
1816            ) => {
1817                debug!(
1818                    "{} {} add front {:?}",
1819                    log_module_context!(),
1820                    request_id,
1821                    front
1822                );
1823                self.add_http_frontend(front)
1824            }
1825            Some(
1826                RequestType::RemoveHttpFrontend(front)
1827                | RequestType::RemoveHttpFrontendWithHeaderPredicates(front),
1828            ) => {
1829                debug!(
1830                    "{} {} remove front {:?}",
1831                    log_module_context!(),
1832                    request_id,
1833                    front
1834                );
1835                self.remove_http_frontend(front)
1836            }
1837            Some(RequestType::RemoveListener(remove)) => {
1838                debug!(
1839                    "{} removing HTTP listener at address {:?}",
1840                    log_module_context!(),
1841                    remove.address
1842                );
1843                self.remove_listener(remove)
1844            }
1845            Some(RequestType::SoftStop(_)) => {
1846                debug!(
1847                    "{} {} processing soft shutdown",
1848                    log_module_context!(),
1849                    request_id
1850                );
1851                match self.soft_stop() {
1852                    Ok(()) => {
1853                        info!(
1854                            "{} {} soft stop successful",
1855                            log_module_context!(),
1856                            request_id
1857                        );
1858                        return WorkerResponse::processing(request.id);
1859                    }
1860                    Err(e) => Err(e),
1861                }
1862            }
1863            Some(RequestType::HardStop(_)) => {
1864                debug!(
1865                    "{} {} processing hard shutdown",
1866                    log_module_context!(),
1867                    request_id
1868                );
1869                match self.hard_stop() {
1870                    Ok(()) => {
1871                        info!(
1872                            "{} {} hard stop successful",
1873                            log_module_context!(),
1874                            request_id
1875                        );
1876                        return WorkerResponse::processing(request.id);
1877                    }
1878                    Err(e) => Err(e),
1879                }
1880            }
1881            Some(RequestType::Status(_)) => {
1882                debug!("{} {} status", log_module_context!(), request_id);
1883                Ok(())
1884            }
1885            other_command => {
1886                debug!(
1887                    "{} {} unsupported message for HTTP proxy, ignoring: {:?}",
1888                    log_module_context!(),
1889                    request.id,
1890                    other_command
1891                );
1892                Err(ProxyError::UnsupportedMessage)
1893            }
1894        };
1895
1896        match result {
1897            Ok(()) => {
1898                debug!("{} {} successful", log_module_context!(), request_id);
1899                WorkerResponse::ok(request_id)
1900            }
1901            Err(proxy_error) => {
1902                debug!(
1903                    "{} {} unsuccessful: {}",
1904                    log_module_context!(),
1905                    request_id,
1906                    proxy_error
1907                );
1908                WorkerResponse::error(request_id, proxy_error)
1909            }
1910        }
1911    }
1912
1913    fn accept(&mut self, token: ListenToken) -> Result<(TcpStream, SocketAddr), AcceptError> {
1914        if let Some(listener) = self.listeners.get(&Token(token.0)) {
1915            listener.borrow_mut().accept()
1916        } else {
1917            Err(AcceptError::IoError)
1918        }
1919    }
1920
1921    fn create_session(
1922        &mut self,
1923        mut frontend_sock: TcpStream,
1924        peer: SocketAddr,
1925        listener_token: ListenToken,
1926        wait_time: Duration,
1927        proxy: Rc<RefCell<Self>>,
1928    ) -> Result<(), AcceptError> {
1929        let listener = self
1930            .listeners
1931            .get(&Token(listener_token.0))
1932            .cloned()
1933            .ok_or(AcceptError::IoError)?;
1934
1935        // No `set_nodelay` here: the socket has `TCP_NODELAY` from its
1936        // listener, or from `HttpListener::accept` if it was queued before the
1937        // listener set it.
1938        let mut session_manager = self.sessions.borrow_mut();
1939        let slab_len_before = session_manager.slab.len();
1940        let session_entry = session_manager.slab.vacant_entry();
1941        let session_token = Token(session_entry.key());
1942        // The token handed to the new session, used for epoll registration and
1943        // every `remove_session(frontend_token)` call, MUST be the very slab
1944        // key this entry will occupy. A drift here would deregister/free the
1945        // wrong slot on close.
1946        debug_assert_eq!(
1947            session_token.0,
1948            session_entry.key(),
1949            "session token must equal the slab vacant-entry key"
1950        );
1951        let owned = listener.borrow();
1952
1953        if let Err(register_error) = self.registry.register(
1954            &mut frontend_sock,
1955            session_token,
1956            Interest::READABLE | Interest::WRITABLE,
1957        ) {
1958            error!(
1959                "{} error registering listen socket({:?}): {:?}",
1960                log_module_context!(),
1961                frontend_sock,
1962                register_error
1963            );
1964            return Err(AcceptError::RegisterError);
1965        }
1966
1967        let public_address: SocketAddr = match owned.config.public_address {
1968            Some(pub_addr) => pub_addr.into(),
1969            None => owned.config.address.into(),
1970        };
1971
1972        let session = HttpSession::new(
1973            Duration::from_secs(owned.config.back_timeout as u64),
1974            Duration::from_secs(owned.config.connect_timeout as u64),
1975            Duration::from_secs(owned.config.front_timeout as u64),
1976            Duration::from_secs(owned.config.request_timeout as u64),
1977            owned.config.expect_proxy,
1978            listener.clone(),
1979            Rc::downgrade(&self.pool),
1980            proxy,
1981            // `create_session` runs under the proxy's `borrow_mut`, so the
1982            // session cannot read the map through `proxy`; hand it over here.
1983            Rc::clone(&self.backends),
1984            public_address,
1985            frontend_sock,
1986            peer,
1987            session_token,
1988            wait_time,
1989        )?;
1990
1991        // The session's frontend token must be exactly the slab key we are
1992        // about to fill — the registration above and all later token lookups
1993        // depend on it.
1994        debug_assert_eq!(
1995            session.frontend_token, session_token,
1996            "session must own the frontend token it was created with"
1997        );
1998
1999        let session = Rc::new(RefCell::new(session));
2000        session_entry.insert(session);
2001        // Inserting into the previously-vacant entry grows the live session
2002        // count by exactly one (gauge-like ±1 pairing against close()'s
2003        // try_remove). `len()` is the count of occupied slots.
2004        debug_assert_eq!(
2005            session_manager.slab.len(),
2006            slab_len_before + 1,
2007            "creating a session must occupy exactly one new slab slot"
2008        );
2009
2010        Ok(())
2011    }
2012}
2013
2014impl L7Proxy for HttpProxy {
2015    fn kind(&self) -> ListenerType {
2016        ListenerType::Http
2017    }
2018
2019    fn register_socket(
2020        &self,
2021        source: &mut TcpStream,
2022        token: Token,
2023        interest: Interest,
2024    ) -> Result<(), std::io::Error> {
2025        self.registry.register(source, token, interest)
2026    }
2027
2028    fn add_session(&self, session: Rc<RefCell<dyn ProxySession>>) -> Token {
2029        let mut session_manager = self.sessions.borrow_mut();
2030        let len_before = session_manager.slab.len();
2031        let entry = session_manager.slab.vacant_entry();
2032        let token = Token(entry.key());
2033        let _entry = entry.insert(session);
2034        // The returned token is the slab key callers use to later remove this
2035        // session, and the insert occupied exactly one new slot.
2036        debug_assert_eq!(
2037            session_manager.slab.len(),
2038            len_before + 1,
2039            "add_session must occupy exactly one new slab slot"
2040        );
2041        debug_assert!(
2042            session_manager.slab.contains(token.0),
2043            "the returned token must index the freshly inserted session"
2044        );
2045        token
2046    }
2047
2048    fn remove_session(&self, token: Token) -> bool {
2049        let mut sessions = self.sessions.borrow_mut();
2050        let was_present = sessions.slab.contains(token.0);
2051        let len_before = sessions.slab.len();
2052        // Drain the session's `(cluster, ip)` accounting before the slab
2053        // slot is freed — once the slot is reused for a new session the
2054        // token would otherwise alias an unrelated set of entries. No-op
2055        // when the session never tracked anything (feature disabled, or
2056        // no request reached `Router::plan_connect`).
2057        sessions.untrack_all_cluster_ip(token);
2058        let removed = sessions.slab.try_remove(token.0).is_some();
2059        // Removal must report exactly whether the slot was occupied, and the
2060        // live count drops by one iff something was actually removed (±1
2061        // pairing against add_session / create_session). The slot is gone.
2062        debug_assert_eq!(
2063            removed, was_present,
2064            "try_remove reports presence iff the slot was occupied"
2065        );
2066        debug_assert_eq!(
2067            sessions.slab.len(),
2068            len_before - removed as usize,
2069            "slab len drops by exactly one iff a session was removed"
2070        );
2071        debug_assert!(
2072            !sessions.slab.contains(token.0),
2073            "the slot must be free after remove_session"
2074        );
2075        removed
2076    }
2077
2078    fn clusters(&self) -> &HashMap<ClusterId, Cluster> {
2079        &self.clusters
2080    }
2081
2082    fn sessions(&self) -> Rc<RefCell<SessionManager>> {
2083        self.sessions.clone()
2084    }
2085}
2086
2087pub mod testing {
2088    use crate::testing::*;
2089
2090    /// this function is not used, but is available for example and testing purposes
2091    pub fn start_http_worker(
2092        config: HttpListenerConfig,
2093        channel: ProxyChannel,
2094        max_buffers: usize,
2095        buffer_size: usize,
2096    ) -> anyhow::Result<()> {
2097        let address = config.address.into();
2098
2099        let ServerParts {
2100            event_loop,
2101            registry,
2102            sessions,
2103            pool,
2104            backends,
2105            client_scm_socket: _,
2106            server_scm_socket,
2107            server_config,
2108        } = prebuild_server(max_buffers, buffer_size, true)?;
2109
2110        let token = {
2111            let mut sessions = sessions.borrow_mut();
2112            let entry = sessions.slab.vacant_entry();
2113            let key = entry.key();
2114            let _ = entry.insert(Rc::new(RefCell::new(ListenSession {
2115                protocol: Protocol::HTTPListen,
2116            })));
2117            Token(key)
2118        };
2119
2120        let mut proxy = HttpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
2121        proxy
2122            .add_listener(config, token)
2123            .with_context(|| "Failed at creating adding the listener")?;
2124        proxy
2125            .activate_listener(&address, None)
2126            .with_context(|| "Failed at creating activating the listener")?;
2127
2128        let mut server = Server::new(
2129            event_loop,
2130            channel,
2131            server_scm_socket,
2132            sessions,
2133            pool,
2134            backends,
2135            Some(proxy),
2136            None,
2137            None,
2138            server_config,
2139            None,
2140            false,
2141        )
2142        .with_context(|| "Failed at creating server")?;
2143
2144        debug!("{} starting event loop", log_module_context!());
2145        server.run();
2146        debug!("{} ending event loop", log_module_context!());
2147        Ok(())
2148    }
2149}
2150
2151#[cfg(test)]
2152mod tests {
2153    extern crate tiny_http;
2154
2155    use std::{
2156        io::{Read, Write},
2157        net::TcpStream,
2158        str,
2159        sync::{Arc, Barrier},
2160        thread,
2161        time::Duration,
2162    };
2163
2164    use sozu_command::proto::command::SocketAddress;
2165
2166    use super::{testing::start_http_worker, *};
2167    use crate::sozu_command::{
2168        channel::Channel,
2169        config::ListenerBuilder,
2170        proto::command::{
2171            CustomHttpAnswers, LoadBalancingParams, PathRule, RulePosition, SoftStop,
2172            WorkerRequest, request::RequestType,
2173        },
2174        response::{Backend, HttpFrontend},
2175    };
2176
2177    /// Regression for sozu-proxy/sozu#1418: `get_h2_flood_config` built
2178    /// `H2FloodConfig` with a raw struct literal, so `H2FloodConfig::new`'s
2179    /// `.max(1)` clamp never ran on the only path that carries
2180    /// operator-configured thresholds. A `HttpListenerConfig` reaching the
2181    /// worker with `h2_max_rst_stream_per_window = Some(0)` — from a raw
2182    /// protobuf `AddHttpListener`, or a state file saved before config load
2183    /// gained its gate — handed that zero straight to `H2FloodDetector::new`,
2184    /// whose `check_flood` compares `count > threshold`: the first counted
2185    /// RST_STREAM of every H2 connection tripped the detector and the listener
2186    /// answered `ENHANCE_YOUR_CALM` to every client. In a debug build the
2187    /// detector's own pre-condition `debug_assert!` fired instead, at
2188    /// connection setup.
2189    ///
2190    /// Config load now rejects the value outright
2191    /// (`ConfigError::H2ThresholdBelowMinimum`); this is the structural
2192    /// backstop for anything that never passed through it.
2193    #[test]
2194    fn get_h2_flood_config_clamps_a_zero_threshold_that_bypassed_config_load() {
2195        let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2196            .to_http(None)
2197            .expect("could not create listener config");
2198        // Exactly what a raw protobuf `AddHttpListener` or a replayed state
2199        // file can still carry — `to_http` itself now refuses this value.
2200        config.h2_max_rst_stream_per_window = Some(0);
2201        config.h2_max_glitch_count = Some(0);
2202
2203        let listener =
2204            HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2205        let flood_config = listener.get_h2_flood_config();
2206
2207        assert!(
2208            flood_config.max_rst_stream_per_window() >= 1,
2209            "max_rst_stream_per_window must be clamped to >= 1, got {}",
2210            flood_config.max_rst_stream_per_window()
2211        );
2212        assert!(
2213            flood_config.max_glitch_count() >= 1,
2214            "max_glitch_count must be clamped to >= 1, got {}",
2215            flood_config.max_glitch_count()
2216        );
2217    }
2218
2219    /// Pin the listener-config → [`H2FloodConfig`] mapping, field by field
2220    /// (sozu-proxy/sozu#1418). `get_h2_flood_config` hands
2221    /// `H2FloodConfig::from_optional` thirteen positional arguments — ten
2222    /// `Option<u32>` and three `Option<u64>` — so a transposition inside
2223    /// either group compiles silently and quietly re-points a CVE mitigation
2224    /// at the wrong counter. `from_optional`'s own tests cover its internals;
2225    /// nothing covered the wiring into it until here. Every value is distinct,
2226    /// so any swap fails an assertion.
2227    #[test]
2228    fn get_h2_flood_config_maps_every_listener_knob_to_its_own_threshold() {
2229        let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2230            .to_http(None)
2231            .expect("could not create listener config");
2232        config.h2_max_rst_stream_per_window = Some(101);
2233        config.h2_max_ping_per_window = Some(102);
2234        config.h2_max_settings_per_window = Some(103);
2235        config.h2_max_empty_data_per_window = Some(104);
2236        config.h2_max_window_update_stream0_per_window = Some(105);
2237        config.h2_max_continuation_frames = Some(106);
2238        config.h2_max_glitch_count = Some(107);
2239        config.h2_max_rst_stream_lifetime = Some(108);
2240        config.h2_max_rst_stream_abusive_lifetime = Some(109);
2241        config.h2_max_rst_stream_emitted_lifetime = Some(110);
2242        config.h2_max_header_list_size = Some(111);
2243        config.h2_max_header_table_size = Some(112);
2244        config.h2_max_header_fields = Some(113);
2245        config.h2_stream_refusal_percent = Some(114);
2246
2247        let listener =
2248            HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2249        let flood = listener.get_h2_flood_config();
2250
2251        assert_eq!(flood.max_rst_stream_per_window(), 101);
2252        assert_eq!(flood.max_ping_per_window(), 102);
2253        assert_eq!(flood.max_settings_per_window(), 103);
2254        assert_eq!(flood.max_empty_data_per_window(), 104);
2255        assert_eq!(flood.max_window_update_stream0_per_window(), 105);
2256        assert_eq!(flood.max_continuation_frames(), 106);
2257        assert_eq!(flood.max_glitch_count(), 107);
2258        assert_eq!(flood.max_rst_stream_lifetime(), 108);
2259        assert_eq!(flood.max_rst_stream_abusive_lifetime(), 109);
2260        assert_eq!(flood.max_rst_stream_emitted_lifetime(), 110);
2261        assert_eq!(flood.max_header_list_size(), 111);
2262        assert_eq!(flood.max_header_table_size(), 112);
2263        assert_eq!(flood.max_header_fields(), 113);
2264        assert_eq!(flood.stream_refusal_percent(), 114);
2265    }
2266
2267    #[test]
2268    fn round_trip() {
2269        setup_test_logger!();
2270        let front_port = crate::testing::provide_port();
2271        let backend_server = Arc::new(
2272            tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2273        );
2274        let backend_port = backend_server
2275            .server_addr()
2276            .to_ip()
2277            .expect("tiny_http server should bind to IP address")
2278            .port();
2279
2280        let barrier = Arc::new(Barrier::new(2));
2281
2282        let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2283            .to_http(None)
2284            .expect("could not create listener config");
2285
2286        let (mut command, channel) =
2287            Channel::generate(1000, 10000).expect("should create a channel");
2288
2289        thread::scope(|s| {
2290            let backend_handle = backend_server.clone();
2291            let barrier_clone = barrier.to_owned();
2292            s.spawn(move || {
2293                setup_test_logger!();
2294                start_server(&backend_handle, barrier_clone);
2295            });
2296            barrier.wait();
2297
2298            s.spawn(move || {
2299                setup_test_logger!();
2300                start_http_worker(config, channel, 10, 16384)
2301                    .expect("could not start the http server");
2302            });
2303
2304            let front = RequestHttpFrontend {
2305                cluster_id: Some("cluster_1".to_owned()),
2306                address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2307                hostname: "localhost".to_owned(),
2308                path: PathRule::prefix("/".to_owned()),
2309                ..Default::default()
2310            };
2311            // A backend belongs to its cluster: add the cluster first.
2312            command
2313                .write_message(&WorkerRequest {
2314                    id: "ID_CLUSTER".to_owned(),
2315                    content: RequestType::AddCluster(Cluster {
2316                        cluster_id: "cluster_1".to_owned(),
2317                        ..Default::default()
2318                    })
2319                    .into(),
2320                })
2321                .expect("could not send AddCluster");
2322            command
2323                .write_message(&WorkerRequest {
2324                    id: "ID_ABCD".to_owned(),
2325                    content: RequestType::AddHttpFrontend(front).into(),
2326                })
2327                .expect("could not send AddHttpFrontend");
2328            let backend = Backend {
2329                cluster_id: "cluster_1".into(),
2330                backend_id: "cluster_1-0".to_owned(),
2331                address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2332                load_balancing_parameters: Some(LoadBalancingParams::default()),
2333                sticky_id: None,
2334                backup: None,
2335                unix_path: None,
2336            };
2337            command
2338                .write_message(&WorkerRequest {
2339                    id: "ID_EFGH".to_owned(),
2340                    content: RequestType::AddBackend(backend.to_add_backend()).into(),
2341                })
2342                .expect("could not send AddBackend");
2343
2344            println!("test received: {:?}", command.read_message());
2345            println!("test received: {:?}", command.read_message());
2346            println!("test received: {:?}", command.read_message());
2347
2348            let mut client =
2349                TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2350
2351            client
2352                .set_read_timeout(Some(Duration::new(1, 0)))
2353                .expect("could not set read timeout");
2354            let request = format!(
2355                "GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\nConnection: Close\r\n\r\n"
2356            );
2357            let w = client.write(request.as_bytes());
2358            println!("http client write: {w:?}");
2359
2360            barrier.wait();
2361            let mut buffer = [0; 4096];
2362            let mut index = 0;
2363
2364            // tiny_http responds with exactly 191 bytes for a "hello world" body
2365            // (headers + body). This is deterministic for a given tiny_http version.
2366            let expected_len = 191;
2367
2368            loop {
2369                assert!(index <= expected_len);
2370                if index == expected_len {
2371                    break;
2372                }
2373
2374                let r = client.read(&mut buffer[index..]);
2375                println!("http client read: {r:?}");
2376                match r {
2377                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2378                    Ok(sz) => {
2379                        index += sz;
2380                    }
2381                }
2382            }
2383            println!(
2384                "Response: {}",
2385                str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2386            );
2387
2388            // Gracefully stop the sozu worker so the scoped thread can join
2389            command
2390                .write_message(&WorkerRequest {
2391                    id: "ID_STOP".to_owned(),
2392                    content: RequestType::SoftStop(SoftStop {}).into(),
2393                })
2394                .expect("could not send SoftStop");
2395            // Unblock the backend server so its thread can exit
2396            backend_server.unblock();
2397        });
2398    }
2399
2400    #[test]
2401    fn keep_alive() {
2402        setup_test_logger!();
2403        let front_port = crate::testing::provide_port();
2404        let backend_server = Arc::new(
2405            tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2406        );
2407        let backend_port = backend_server
2408            .server_addr()
2409            .to_ip()
2410            .expect("tiny_http server should bind to IP address")
2411            .port();
2412
2413        let barrier = Arc::new(Barrier::new(2));
2414
2415        let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2416            .to_http(None)
2417            .expect("could not create listener config");
2418
2419        let (mut command, channel) =
2420            Channel::generate(1000, 10000).expect("should create a channel");
2421
2422        thread::scope(|s| {
2423            let backend_handle = backend_server.clone();
2424            let barrier_clone = barrier.to_owned();
2425            s.spawn(move || {
2426                setup_test_logger!();
2427                start_server(&backend_handle, barrier_clone);
2428            });
2429            barrier.wait();
2430
2431            s.spawn(move || {
2432                setup_test_logger!();
2433                start_http_worker(config, channel, 10, 16384)
2434                    .expect("could not start the http server");
2435            });
2436
2437            let front = RequestHttpFrontend {
2438                address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2439                hostname: "localhost".to_owned(),
2440                path: PathRule::prefix("/".to_owned()),
2441                cluster_id: Some("cluster_1".to_owned()),
2442                ..Default::default()
2443            };
2444            // A backend belongs to its cluster: add the cluster first.
2445            command
2446                .write_message(&WorkerRequest {
2447                    id: "ID_CLUSTER".to_owned(),
2448                    content: RequestType::AddCluster(Cluster {
2449                        cluster_id: "cluster_1".to_owned(),
2450                        ..Default::default()
2451                    })
2452                    .into(),
2453                })
2454                .expect("could not send AddCluster");
2455            command
2456                .write_message(&WorkerRequest {
2457                    id: "ID_ABCD".to_owned(),
2458                    content: RequestType::AddHttpFrontend(front).into(),
2459                })
2460                .expect("could not send AddHttpFrontend");
2461            let backend = Backend {
2462                address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2463                backend_id: "cluster_1-0".to_owned(),
2464                backup: None,
2465                cluster_id: "cluster_1".into(),
2466                load_balancing_parameters: Some(LoadBalancingParams::default()),
2467                sticky_id: None,
2468                unix_path: None,
2469            };
2470            command
2471                .write_message(&WorkerRequest {
2472                    id: "ID_EFGH".to_owned(),
2473                    content: RequestType::AddBackend(backend.to_add_backend()).into(),
2474                })
2475                .expect("could not send AddBackend");
2476
2477            println!("test received: {:?}", command.read_message());
2478            println!("test received: {:?}", command.read_message());
2479            println!("test received: {:?}", command.read_message());
2480
2481            let mut client =
2482                TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2483            client
2484                .set_read_timeout(Some(Duration::new(5, 0)))
2485                .expect("could not set read timeout");
2486
2487            // tiny_http responds with exactly 191 bytes for a "hello world" body
2488            // (headers + body). This is deterministic for a given tiny_http version.
2489            let expected_len = 191;
2490
2491            let request = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2492            let w = client
2493                .write(request.as_bytes())
2494                .expect("could not write first request");
2495            println!("http client write: {w:?}");
2496            barrier.wait();
2497
2498            let mut buffer = [0; 4096];
2499            let mut index = 0;
2500
2501            loop {
2502                assert!(index <= expected_len);
2503                if index == expected_len {
2504                    break;
2505                }
2506
2507                let r = client.read(&mut buffer[index..]);
2508                println!("http client read: {r:?}");
2509                match r {
2510                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2511                    Ok(sz) => {
2512                        index += sz;
2513                    }
2514                }
2515            }
2516
2517            println!(
2518                "Response: {}",
2519                str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2520            );
2521
2522            println!("first request ended, will send second one");
2523            let request2 = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2524            let w2 = client.write(request2.as_bytes());
2525            println!("http client write: {w2:?}");
2526            barrier.wait();
2527
2528            let mut buffer2 = [0; 4096];
2529            let mut index = 0;
2530
2531            loop {
2532                assert!(index <= expected_len);
2533                if index == expected_len {
2534                    break;
2535                }
2536
2537                let r2 = client.read(&mut buffer2[index..]);
2538                println!("http client read: {r2:?}");
2539                match r2 {
2540                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2541                    Ok(sz) => {
2542                        index += sz;
2543                    }
2544                }
2545            }
2546            println!(
2547                "Response: {}",
2548                str::from_utf8(&buffer2[..index]).expect("could not make string from buffer")
2549            );
2550
2551            // Gracefully stop the sozu worker so the scoped thread can join
2552            command
2553                .write_message(&WorkerRequest {
2554                    id: "ID_STOP".to_owned(),
2555                    content: RequestType::SoftStop(SoftStop {}).into(),
2556                })
2557                .expect("could not send SoftStop");
2558            // Unblock the backend server so its thread can exit
2559            backend_server.unblock();
2560        });
2561    }
2562
2563    use self::tiny_http::Response;
2564
2565    fn start_server(server: &tiny_http::Server, barrier: Arc<Barrier>) {
2566        let addr = server.server_addr();
2567        info!("starting web server on {:?}", addr);
2568        barrier.wait();
2569
2570        for request in server.incoming_requests() {
2571            info!(
2572                "backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
2573                request.method(),
2574                request.url(),
2575                request.headers()
2576            );
2577
2578            let response = Response::from_string("hello world");
2579            request
2580                .respond(response)
2581                .expect("could not respond to request");
2582            info!("backend web server sent response");
2583            barrier.wait();
2584            info!("server session stopped");
2585        }
2586
2587        println!("server on {addr:?} closed");
2588    }
2589
2590    #[test]
2591    fn frontend_from_request_test() {
2592        let cluster_id1 = ClusterId::from("cluster_1");
2593        let cluster_id2 = ClusterId::from("cluster_2");
2594        let cluster_id3 = ClusterId::from("cluster_3");
2595        let uri1 = "/".to_owned();
2596        let uri2 = "/yolo".to_owned();
2597        let uri3 = "/yolo/swag".to_owned();
2598
2599        let mut fronts = Router::new();
2600        fronts
2601            .add_http_front(&HttpFrontend {
2602                address: "0.0.0.0:80".parse().unwrap(),
2603                hostname: "lolcatho.st".to_owned(),
2604                method: None,
2605                path: PathRule::prefix(uri1),
2606                position: RulePosition::Tree,
2607                cluster_id: Some(cluster_id1),
2608                tags: None,
2609                redirect: None,
2610                redirect_scheme: None,
2611                redirect_template: None,
2612                rewrite_host: None,
2613                rewrite_path: None,
2614                rewrite_port: None,
2615                required_auth: None,
2616                headers: Vec::new(),
2617                hsts: None,
2618                header_predicates: Vec::new(),
2619            })
2620            .expect("Could not add http frontend");
2621        fronts
2622            .add_http_front(&HttpFrontend {
2623                address: "0.0.0.0:80".parse().unwrap(),
2624                hostname: "lolcatho.st".to_owned(),
2625                method: None,
2626                path: PathRule::prefix(uri2),
2627                position: RulePosition::Tree,
2628                cluster_id: Some(cluster_id2),
2629                tags: None,
2630                redirect: None,
2631                redirect_scheme: None,
2632                redirect_template: None,
2633                rewrite_host: None,
2634                rewrite_path: None,
2635                rewrite_port: None,
2636                required_auth: None,
2637                headers: Vec::new(),
2638                hsts: None,
2639                header_predicates: Vec::new(),
2640            })
2641            .expect("Could not add http frontend");
2642        fronts
2643            .add_http_front(&HttpFrontend {
2644                address: "0.0.0.0:80".parse().unwrap(),
2645                hostname: "lolcatho.st".to_owned(),
2646                method: None,
2647                path: PathRule::prefix(uri3),
2648                position: RulePosition::Tree,
2649                cluster_id: Some(cluster_id3),
2650                tags: None,
2651                redirect: None,
2652                redirect_scheme: None,
2653                redirect_template: None,
2654                rewrite_host: None,
2655                rewrite_path: None,
2656                rewrite_port: None,
2657                required_auth: None,
2658                headers: Vec::new(),
2659                hsts: None,
2660                header_predicates: Vec::new(),
2661            })
2662            .expect("Could not add http frontend");
2663        fronts
2664            .add_http_front(&HttpFrontend {
2665                address: "0.0.0.0:80".parse().unwrap(),
2666                hostname: "other.domain".to_owned(),
2667                method: None,
2668                path: PathRule::prefix("/test".to_owned()),
2669                position: RulePosition::Tree,
2670                cluster_id: Some("cluster_1".into()),
2671                tags: None,
2672                redirect: None,
2673                redirect_scheme: None,
2674                redirect_template: None,
2675                rewrite_host: None,
2676                rewrite_path: None,
2677                rewrite_port: None,
2678                required_auth: None,
2679                headers: Vec::new(),
2680                hsts: None,
2681                header_predicates: Vec::new(),
2682            })
2683            .expect("Could not add http frontend");
2684
2685        let address = SocketAddress::new_v4(127, 0, 0, 1, 1030);
2686
2687        let default_config = ListenerBuilder::new_http(address)
2688            .to_http(None)
2689            .expect("Could not create default HTTP listener config");
2690
2691        let listener = HttpListener {
2692            parked_listener: None,
2693            nodelay_backlog: false,
2694            listener: None,
2695            address: address.into(),
2696            fronts,
2697            answers: Rc::new(RefCell::new(HttpAnswers::new(&BTreeMap::new()).unwrap())),
2698            config: default_config,
2699            token: Token(0),
2700            active: true,
2701            tags: BTreeMap::new(),
2702        };
2703
2704        let frontend1 = listener.frontend_from_request(
2705            "lolcatho.st",
2706            "/",
2707            &Method::Get,
2708            &crate::router::NoHeaders,
2709        );
2710        let frontend2 = listener.frontend_from_request(
2711            "lolcatho.st",
2712            "/test",
2713            &Method::Get,
2714            &crate::router::NoHeaders,
2715        );
2716        let frontend3 = listener.frontend_from_request(
2717            "lolcatho.st",
2718            "/yolo/test",
2719            &Method::Get,
2720            &crate::router::NoHeaders,
2721        );
2722        let frontend4 = listener.frontend_from_request(
2723            "lolcatho.st",
2724            "/yolo/swag",
2725            &Method::Get,
2726            &crate::router::NoHeaders,
2727        );
2728        let frontend5 =
2729            listener.frontend_from_request("domain", "/", &Method::Get, &crate::router::NoHeaders);
2730        assert_eq!(
2731            frontend1
2732                .expect("should find frontend")
2733                .cluster_id
2734                .as_deref(),
2735            Some("cluster_1")
2736        );
2737        assert_eq!(
2738            frontend2
2739                .expect("should find frontend")
2740                .cluster_id
2741                .as_deref(),
2742            Some("cluster_1")
2743        );
2744        assert_eq!(
2745            frontend3
2746                .expect("should find frontend")
2747                .cluster_id
2748                .as_deref(),
2749            Some("cluster_2")
2750        );
2751        assert_eq!(
2752            frontend4
2753                .expect("should find frontend")
2754                .cluster_id
2755                .as_deref(),
2756            Some("cluster_3")
2757        );
2758        assert!(frontend5.is_err());
2759    }
2760
2761    /// sozu#322: the worker refuses a listener whose `forwarded_headers`
2762    /// wire value names no `ForwardedHeaders` variant — a raw protobuf
2763    /// `AddHttpListener` must not silently run the default — and honours a
2764    /// known one.
2765    #[test]
2766    fn a_listener_with_an_unknown_forwarded_headers_value_is_refused() {
2767        let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2768        let build = |forwarded_headers: Option<i32>| {
2769            let mut cfg = ListenerBuilder::new_http(address)
2770                .to_http(None)
2771                .expect("default HTTP listener config");
2772            cfg.forwarded_headers = forwarded_headers;
2773            HttpListener::new(cfg, Token(0))
2774        };
2775        assert!(build(Some(42)).is_err(), "an unknown mode must be refused");
2776        let listener = build(Some(ForwardedHeaders::Rfc7239 as i32)).expect("a known mode");
2777        assert_eq!(listener.get_forwarded_headers(), ForwardedHeaders::Rfc7239);
2778    }
2779
2780    #[test]
2781    fn h2_stream_idle_timeout_inherits_back_timeout() {
2782        let address = SocketAddress::new_v4(127, 0, 0, 1, 1040);
2783        let build = |back_timeout: u32, explicit: Option<u32>| -> HttpListener {
2784            let mut cfg = ListenerBuilder::new_http(address)
2785                .to_http(None)
2786                .expect("default HTTP listener config");
2787            cfg.back_timeout = back_timeout;
2788            cfg.h2_stream_idle_timeout_seconds = explicit;
2789            HttpListener::new(cfg, Token(0)).expect("build listener")
2790        };
2791
2792        // Knob unset: inherit back_timeout when it exceeds the 30s floor.
2793        assert_eq!(
2794            build(180, None).get_h2_stream_idle_timeout(),
2795            Duration::from_secs(180)
2796        );
2797
2798        // Knob unset, back_timeout below floor: stay at 30s to preserve the
2799        // slow-multiplex Slowloris mitigation.
2800        assert_eq!(
2801            build(5, None).get_h2_stream_idle_timeout(),
2802            Duration::from_secs(30)
2803        );
2804
2805        // Explicit values win in both directions — including below the floor,
2806        // so operators under attack can tighten the deadline.
2807        assert_eq!(
2808            build(180, Some(10)).get_h2_stream_idle_timeout(),
2809            Duration::from_secs(10)
2810        );
2811        assert_eq!(
2812            build(5, Some(600)).get_h2_stream_idle_timeout(),
2813            Duration::from_secs(600)
2814        );
2815
2816        // `Some(0)` is clamped to 1s to keep the deadline non-degenerate.
2817        assert_eq!(
2818            build(180, Some(0)).get_h2_stream_idle_timeout(),
2819            Duration::from_secs(1)
2820        );
2821    }
2822
2823    /// A listener answer reload PUBLISHES a new registry; it does not rewrite
2824    /// the one in-flight requests are holding.
2825    ///
2826    /// `mux::Context::create_stream` captures `HttpAnswers` by handle when a
2827    /// request arrives, and every `set_default_answer` site in the mux renders
2828    /// from that captured handle (`lib/src/protocol/mux/LIFECYCLE.md` §2.5).
2829    /// The capture is only a snapshot if a reload installs a *new* registry —
2830    /// rewriting the old one through its `RefCell` would reach every request
2831    /// already in flight and hand it a page its client never asked for.
2832    ///
2833    /// The second assertion guards the trap in the obvious implementation of
2834    /// the first: migrating the per-cluster overrides with `mem::take` empties
2835    /// the registry the in-flight requests still hold, stripping their cluster
2836    /// templates mid-response. They must be copied.
2837    ///
2838    /// To SEE THIS RED: in `HttpListener::update_config`, put the two publish
2839    /// lines back to their pre-fix form —
2840    /// `let preserved = std::mem::take(&mut self.answers.borrow_mut().cluster_answers);`
2841    /// `new_answers.cluster_answers = preserved;`
2842    /// `*self.answers.borrow_mut() = new_answers;`
2843    #[test]
2844    fn a_listener_answers_reload_publishes_a_new_registry() {
2845        let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2846        let config = ListenerBuilder::new_http(address)
2847            .to_http(None)
2848            .expect("default HTTP listener config");
2849        let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2850
2851        // A cluster override, as `HttpProxy::add_cluster` would install it.
2852        listener
2853            .answers
2854            .borrow_mut()
2855            .add_cluster_answers(
2856                "cluster_1",
2857                &BTreeMap::from([(
2858                    "503".to_owned(),
2859                    "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n".to_owned(),
2860                )]),
2861            )
2862            .expect("the cluster override must compile");
2863
2864        // What a request arriving now would capture.
2865        let captured = listener.get_answers().clone();
2866
2867        listener
2868            .update_config(&UpdateHttpListenerConfig {
2869                address,
2870                answers: BTreeMap::from([(
2871                    "404".to_owned(),
2872                    "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n".to_owned(),
2873                )]),
2874                ..Default::default()
2875            })
2876            .expect("the answers patch must apply");
2877
2878        assert!(
2879            !Rc::ptr_eq(&captured, listener.get_answers()),
2880            "a reload must publish a NEW answer registry: rewriting the captured \
2881             one reaches every request already in flight"
2882        );
2883        assert!(
2884            captured.borrow().cluster_answers.contains_key("cluster_1"),
2885            "the registry captured before the reload must keep its cluster \
2886             overrides: a request in flight would otherwise lose its custom \
2887             pages mid-response"
2888        );
2889        assert!(
2890            listener
2891                .get_answers()
2892                .borrow()
2893                .cluster_answers
2894                .contains_key("cluster_1"),
2895            "the published registry must carry the cluster overrides forward"
2896        );
2897    }
2898
2899    /// A patch the worker refuses leaves the listener exactly as it was
2900    /// (sozu-proxy/sozu#1703): its configuration and its published answer
2901    /// registry. Each patch carries fields that validate, then an answer
2902    /// template that does not parse — the failure the old code only hit
2903    /// after writing every other field into `self.config`, so the listener
2904    /// kept a patch it had refused, and a config its templates no longer
2905    /// matched.
2906    ///
2907    /// To SEE THIS RED: in `HttpListener::update_config`, drop the
2908    /// `validate_patch_templates` call from the up-front checks and write the
2909    /// fields into `self.config` instead of the candidate copy.
2910    #[test]
2911    fn a_rejected_patch_leaves_the_listener_unchanged() {
2912        let address = SocketAddress::new_v4(127, 0, 0, 1, 1042);
2913        let config = ListenerBuilder::new_http(address)
2914            .to_http(None)
2915            .expect("default HTTP listener config");
2916        let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2917
2918        let valid_prefix = UpdateHttpListenerConfig {
2919            address,
2920            front_timeout: Some(99),
2921            sozu_id_header: Some("X-Edge-Id".to_owned()),
2922            forwarded_headers: Some(ForwardedHeaders::Rfc7239 as i32),
2923            h2_max_rst_stream_per_window: Some(7),
2924            ..Default::default()
2925        };
2926        let rejected = [
2927            UpdateHttpListenerConfig {
2928                answers: BTreeMap::from([(
2929                    "404".to_owned(),
2930                    "not a valid http response".to_owned(),
2931                )]),
2932                ..valid_prefix.clone()
2933            },
2934            UpdateHttpListenerConfig {
2935                http_answers: Some(CustomHttpAnswers {
2936                    answer_503: Some("not a valid http response".to_owned()),
2937                    ..Default::default()
2938                }),
2939                ..valid_prefix.clone()
2940            },
2941        ];
2942        for patch in rejected {
2943            let config_before = listener.config.clone();
2944            let answers_before = listener.get_answers().clone();
2945            let error = listener
2946                .update_config(&patch)
2947                .expect_err("an unparseable template must be refused");
2948            assert!(
2949                matches!(error, ListenerError::TemplateParse(..)),
2950                "expected TemplateParse, got {error:?}"
2951            );
2952            assert_eq!(
2953                listener.config, config_before,
2954                "a refused patch must leave the listener configuration unchanged"
2955            );
2956            assert!(
2957                Rc::ptr_eq(&answers_before, listener.get_answers()),
2958                "a refused patch must not publish a new answer registry"
2959            );
2960        }
2961
2962        listener
2963            .update_config(&valid_prefix)
2964            .expect("the valid prefix alone must apply");
2965        assert_eq!(listener.config.front_timeout, 99);
2966        assert_eq!(listener.config.sozu_id_header.as_deref(), Some("X-Edge-Id"));
2967    }
2968
2969    /// A closed session issues no `EPOLL_CTL_DEL` for its front socket: the
2970    /// socket stays in the epoll set until the session drops, and its last
2971    /// close is what removes it. The second assertion is the half that makes
2972    /// skipping the deregister safe — nothing else holds the descriptor, so
2973    /// dropping the session really does take it out of the epoll set before
2974    /// its slab token can be handed to a new connection.
2975    ///
2976    /// To SEE THIS RED: in `HttpSession::close`, put back
2977    /// `let _ = proxy.registry.deregister(&mut mio::unix::SourceFd(&front_socket.as_raw_fd()));`
2978    /// before `proxy.remove_session`. The first assertion then fails.
2979    #[cfg(target_os = "linux")]
2980    #[test]
2981    fn closed_sessions_leave_their_sockets_to_close() {
2982        use std::os::fd::AsRawFd;
2983
2984        use crate::protocol::mux::test_support::epoll_watches;
2985
2986        let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
2987        let parts = crate::testing::prebuild_server(4, 16_384, false)
2988            .expect("test server parts must build");
2989        let registry = parts
2990            .registry
2991            .try_clone()
2992            .expect("the test registry must clone");
2993        let sessions = parts.sessions.clone();
2994        let listener_token = {
2995            let mut sessions = sessions.borrow_mut();
2996            let entry = sessions.slab.vacant_entry();
2997            let key = entry.key();
2998            entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
2999                protocol: Protocol::HTTPListen,
3000            })));
3001            Token(key)
3002        };
3003        let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3004        proxy
3005            .add_listener(
3006                ListenerBuilder::new_http(address)
3007                    .to_http(None)
3008                    .expect("test listener config must build"),
3009                listener_token,
3010            )
3011            .expect("test listener must register");
3012        let proxy = Rc::new(RefCell::new(proxy));
3013
3014        let peer_listener =
3015            std::net::TcpListener::bind("127.0.0.1:0").expect("bind the test peer listener");
3016        let _client = TcpStream::connect(
3017            peer_listener
3018                .local_addr()
3019                .expect("the test peer listener has an address"),
3020        )
3021        .expect("connect the test client");
3022        let (accepted, peer) = peer_listener.accept().expect("accept the test client");
3023        accepted
3024            .set_nonblocking(true)
3025            .expect("the front socket must go non-blocking");
3026        let front = mio::net::TcpStream::from_std(accepted);
3027        let front_fd = front.as_raw_fd();
3028
3029        let session_token = sessions.borrow().slab.vacant_key();
3030        proxy
3031            .borrow_mut()
3032            .create_session(
3033                front,
3034                peer,
3035                ListenToken(listener_token.0),
3036                Duration::ZERO,
3037                proxy.clone(),
3038            )
3039            .expect("the session must be created");
3040        assert!(
3041            sessions.borrow().slab.contains(session_token),
3042            "the session takes the slot that was vacant"
3043        );
3044        assert!(
3045            epoll_watches(&registry, front_fd),
3046            "precondition: the front socket is in the epoll set"
3047        );
3048
3049        // What `Server::shut_down_sessions_by_frontend_tokens` does.
3050        let session = sessions.borrow_mut().slab.remove(session_token);
3051        session.borrow_mut().close();
3052        assert!(
3053            epoll_watches(&registry, front_fd),
3054            "HttpSession::close must not deregister a front socket it is about \
3055             to close: the close removes it, the EPOLL_CTL_DEL is a wasted syscall"
3056        );
3057
3058        drop(session);
3059        assert!(
3060            !epoll_watches(&registry, front_fd),
3061            "dropping the session closes the front socket's only descriptor, \
3062             which must take it out of the epoll set"
3063        );
3064        drop(parts.event_loop);
3065    }
3066
3067    /// A connection accepted once the listener is live carries `TCP_NODELAY`
3068    /// without a per-connection `setsockopt(2)`, and is handed over with the
3069    /// peer address `accept(2)` returned (sozu-proxy/sozu#1586).
3070    ///
3071    /// The kernel copies `TCP_NODELAY` from a listening socket to each socket
3072    /// it accepts, at the moment the handshake completes. The listener sets it
3073    /// once in `activate`. The first accept drain still sets it per connection
3074    /// for whatever was already in the backlog, so this test drains that pass
3075    /// first, until `WouldBlock`, and only then opens the connection whose flag
3076    /// it reads: that one can only have it from the listener.
3077    ///
3078    /// To SEE THIS RED: in `HttpListener::activate`, delete the
3079    /// `SockRef::from(&listener).set_tcp_nodelay(true)` block. The listener then
3080    /// has no flag to pass on and the accepted socket reads `false`.
3081    #[test]
3082    fn a_steady_state_accepted_socket_inherits_nodelay_and_its_peer_address() {
3083        let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
3084        let parts = crate::testing::prebuild_server(4, 16_384, false)
3085            .expect("test server parts must build");
3086        let listener_token = {
3087            let mut sessions = parts.sessions.borrow_mut();
3088            let entry = sessions.slab.vacant_entry();
3089            let key = entry.key();
3090            entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
3091                protocol: Protocol::HTTPListen,
3092            })));
3093            Token(key)
3094        };
3095        let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3096        proxy
3097            .add_listener(
3098                ListenerBuilder::new_http(address)
3099                    .to_http(None)
3100                    .expect("test listener config must build"),
3101                listener_token,
3102            )
3103            .expect("test listener must register");
3104        proxy
3105            .activate_listener(&address.into(), None)
3106            .expect("test listener must activate");
3107
3108        // The first drain after the activation, with an empty backlog.
3109        assert!(
3110            matches!(
3111                proxy.accept(ListenToken(listener_token.0)),
3112                Err(AcceptError::WouldBlock)
3113            ),
3114            "precondition: nothing is queued on a freshly bound listener"
3115        );
3116
3117        let client =
3118            TcpStream::connect(SocketAddr::from(address)).expect("connect the test client");
3119        let (accepted, peer) = proxy
3120            .accept(ListenToken(listener_token.0))
3121            .expect("the completed connection must be accepted");
3122
3123        assert_eq!(
3124            socket2::SockRef::from(&accepted).tcp_nodelay().ok(),
3125            Some(true),
3126            "a socket accepted in steady state must inherit TCP_NODELAY from its listener"
3127        );
3128        assert_eq!(
3129            peer,
3130            client.local_addr().expect("the test client has an address"),
3131            "the peer address handed over must be the client's"
3132        );
3133        drop(parts.event_loop);
3134    }
3135}
3136
3137#[cfg(test)]
3138mod listener_sibling_tests {
3139    use sozu_command::{
3140        config::ListenerBuilder,
3141        proto::command::{HstsConfig, PathRule, RequestHttpFrontend, RulePosition, SocketAddress},
3142    };
3143
3144    use super::*;
3145    use crate::testing::{ServerParts, prebuild_server, provide_port};
3146
3147    fn proxy() -> HttpProxy {
3148        let ServerParts {
3149            registry,
3150            sessions,
3151            pool,
3152            backends,
3153            ..
3154        } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
3155        HttpProxy::new(registry, sessions, pool, backends)
3156    }
3157
3158    fn add(proxy: &mut HttpProxy, address: SocketAddress, interface: Option<&str>, token: usize) {
3159        let config = ListenerBuilder::new_http(address)
3160            .with_interface(interface)
3161            .to_http(None)
3162            .expect("could not build the listener config");
3163        proxy
3164            .add_listener(config, Token(token))
3165            .expect("could not add the listener");
3166    }
3167
3168    fn front(address: SocketAddress) -> RequestHttpFrontend {
3169        RequestHttpFrontend {
3170            cluster_id: Some("cluster-a".to_owned()),
3171            address,
3172            hostname: "example.com".to_owned(),
3173            path: PathRule::prefix("/".to_owned()),
3174            ..Default::default()
3175        }
3176    }
3177
3178    fn routes(proxy: &HttpProxy, token: usize) -> bool {
3179        proxy.listeners[&Token(token)]
3180            .borrow()
3181            .fronts
3182            .lookup("example.com", "/", &Method::Get)
3183            .is_ok()
3184    }
3185
3186    /// A listener added on an address that already serves frontends — `sozu
3187    /// listener http add --interface`, or a reload adding one — takes them
3188    /// over from the listener already there: frontends name their listener by
3189    /// address, so it must route what its sibling routes from its first
3190    /// connection (sozu-proxy/sozu#719).
3191    #[test]
3192    fn a_listener_added_next_to_a_sibling_takes_over_its_frontends() {
3193        let mut proxy = proxy();
3194        let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3195        add(&mut proxy, address, None, 0);
3196        proxy
3197            .add_http_frontend(front(address))
3198            .expect("add the frontend");
3199        add(&mut proxy, address, Some("lo"), 1);
3200
3201        assert!(routes(&proxy, 0));
3202        assert!(
3203            routes(&proxy, 1),
3204            "the listener added after the frontend must route it too"
3205        );
3206
3207        proxy
3208            .remove_http_frontend(front(address))
3209            .expect("remove the frontend");
3210        assert!(!routes(&proxy, 0));
3211        assert!(!routes(&proxy, 1));
3212    }
3213
3214    #[test]
3215    fn retained_frontends_are_not_replayed_when_a_sibling_already_has_them() {
3216        let mut proxy = proxy();
3217        let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3218        add(&mut proxy, address, None, 0);
3219        proxy
3220            .add_http_frontend(front(address))
3221            .expect("add the frontend");
3222        let sibling = ListenerBuilder::new_http(address)
3223            .with_interface(Some("lo"))
3224            .to_http(None)
3225            .expect("build sibling listener config");
3226
3227        proxy
3228            .add_listener_with_retained_frontends(sibling, Token(1), vec![front(address)])
3229            .expect("the sibling must clone routes without replaying duplicates");
3230
3231        assert!(routes(&proxy, 0));
3232        assert!(routes(&proxy, 1));
3233    }
3234
3235    #[test]
3236    fn failed_retained_frontend_replay_discards_only_the_new_listener() {
3237        let mut proxy = proxy();
3238        let kept_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3239        let rejected_address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3240        add(&mut proxy, kept_address, None, 0);
3241        let rejected = ListenerBuilder::new_http(rejected_address)
3242            .to_http(None)
3243            .expect("build rejected listener config");
3244        let invalid_plaintext_hsts = RequestHttpFrontend {
3245            hsts: Some(HstsConfig {
3246                enabled: Some(true),
3247                ..Default::default()
3248            }),
3249            ..front(rejected_address)
3250        };
3251
3252        assert!(
3253            proxy
3254                .add_listener_with_retained_frontends(
3255                    rejected,
3256                    Token(1),
3257                    vec![invalid_plaintext_hsts],
3258                )
3259                .is_err()
3260        );
3261        assert!(proxy.listeners.contains_key(&Token(0)));
3262        assert!(!proxy.listeners.contains_key(&Token(1)));
3263    }
3264
3265    /// Removing a frontend reaches every listener on the address even when one
3266    /// of them fails, and reports that failure: a sibling must never keep a
3267    /// route the others dropped.
3268    #[test]
3269    fn removing_a_frontend_reaches_every_listener_and_reports_a_failure() {
3270        let mut proxy = proxy();
3271        let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3272        add(&mut proxy, address, None, 0);
3273        add(&mut proxy, address, Some("lo"), 1);
3274        // Only the second listener holds the route, so removing it from the
3275        // first one fails (a `Pre` rule that is not there is an error).
3276        let pre = RequestHttpFrontend {
3277            position: RulePosition::Pre.into(),
3278            ..front(address)
3279        };
3280        let frontend = pre
3281            .clone()
3282            .to_frontend()
3283            .expect("the test frontend must convert");
3284        proxy.listeners[&Token(1)]
3285            .borrow_mut()
3286            .add_http_front(frontend)
3287            .expect("add the route to one listener");
3288
3289        assert!(
3290            proxy.remove_http_frontend(pre).is_err(),
3291            "the listener that lacked the route must report it"
3292        );
3293        assert!(
3294            !routes(&proxy, 1),
3295            "the failure on one listener must not spare its sibling's route"
3296        );
3297    }
3298}