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::{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 last_event(&self) -> Instant {
743        self.last_event
744    }
745
746    fn print_session(&self) {
747        self.state.print_state("HTTP");
748        error!("{} Metrics: {:?}", log_context!(self), self.metrics);
749    }
750
751    fn frontend_token(&self) -> Token {
752        self.frontend_token
753    }
754}
755
756pub type Hostname = String;
757
758/// Cleartext HTTP/1.x listener.
759///
760/// # HTTP/2 over cleartext (h2c) is NOT supported
761///
762/// RFC 7540 §3.2 specified an `Upgrade: h2c` mechanism to negotiate HTTP/2
763/// over a cleartext TCP connection, with a companion prior-knowledge
764/// variant in §3.4. Both paths are intentionally absent from this listener:
765///
766/// - No `Upgrade: h2c` handler: the HTTP/1.1 state machine forwards
767///   `Upgrade` headers to the backend but never responds `101 Switching
768///   Protocols` with an HTTP/2 connection preface.
769/// - No prior-knowledge detection: the listener does not sniff the
770///   24-byte `PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n` magic string; a client
771///   that opens a TCP connection and immediately sends the preface will
772///   be interpreted as a malformed HTTP/1 request and rejected with 400.
773///
774/// RFC 9113 (the current HTTP/2 RFC, obsoleting 7540) formally deprecates
775/// the `Upgrade: h2c` mechanism. Clients that want HTTP/2 MUST use the
776/// TLS ALPN path (`HttpsListener`, selector `h2`) instead. This is
777/// consistent with the industry consensus (nginx, envoy, cloudflare) and
778/// removes an entire class of cleartext-preface smuggling primitives.
779pub struct HttpListener {
780    active: bool,
781    address: SocketAddr,
782    answers: Rc<RefCell<HttpAnswers>>,
783    config: HttpListenerConfig,
784    fronts: Router,
785    listener: Option<MioTcpListener>,
786    /// A socket this listener holds but has NOT registered.
787    ///
788    /// `activate()` parks an inherited socket here when `Registry::register`
789    /// fails, instead of letting the `?` drop and close a descriptor
790    /// `Listeners::get_*` has already removed from the SCM table (sozu#1342).
791    /// The next activation reuses it rather than binding a second one.
792    ///
793    /// Deliberately NOT `listener`: every other consumer reads that field's
794    /// `Some` as "registered and live" — `soft_stop` and `hard_stop`
795    /// deregister it and report the failure, `give_back_listener` hands it
796    /// back as an activated socket, `accept` accepts on it. A parked socket is
797    /// none of those things, and putting it there made a failed registration
798    /// answer `ENOENT` from `deregister` and fail the whole soft stop.
799    parked_listener: Option<MioTcpListener>,
800    /// Set by `activate()`, cleared by the first `accept()` that answers
801    /// `WouldBlock`: while set, `accept()` sets `TCP_NODELAY` on each socket
802    /// it returns.
803    ///
804    /// `activate()` sets `TCP_NODELAY` on the listening socket, and the kernel
805    /// copies it to every socket whose handshake completes afterwards, so a
806    /// steady-state accept needs no `setsockopt(2)` of its own
807    /// (sozu-proxy/sozu#1586). A connection that completed its handshake
808    /// BEFORE that call does not get it: the flag is copied when the
809    /// handshake completes, not when the socket is accepted. That is the
810    /// backlog of a socket inherited from a worker that never set the flag,
811    /// and whatever a fresh socket queued between `listen(2)` and
812    /// `activate()`. The first accept drain takes exactly that backlog.
813    nodelay_backlog: bool,
814    tags: BTreeMap<String, CachedTags>,
815    token: Token,
816}
817
818impl ListenerHandler for HttpListener {
819    fn get_addr(&self) -> &SocketAddr {
820        &self.address
821    }
822
823    fn get_tags(&self, key: &str) -> Option<&CachedTags> {
824        self.tags.get(key)
825    }
826
827    fn set_tags(&mut self, key: String, tags: Option<BTreeMap<String, String>>) {
828        match tags {
829            Some(tags) => self.tags.insert(key, CachedTags::new(tags)),
830            None => self.tags.remove(&key),
831        };
832    }
833
834    fn protocol(&self) -> Protocol {
835        Protocol::HTTP
836    }
837
838    fn public_address(&self) -> SocketAddr {
839        self.config
840            .public_address
841            .map(|addr| addr.into())
842            .unwrap_or(self.address)
843    }
844}
845
846impl L7ListenerHandler for HttpListener {
847    fn get_sticky_name(&self) -> &str {
848        &self.config.sticky_name
849    }
850
851    fn get_sozu_id_header(&self) -> &str {
852        self.config
853            .sozu_id_header
854            .as_deref()
855            .filter(|s| !s.is_empty())
856            .unwrap_or("Sozu-Id")
857    }
858
859    fn get_connect_timeout(&self) -> u32 {
860        self.config.connect_timeout
861    }
862
863    // redundant, already called once in extract_route
864    fn frontend_from_request(
865        &self,
866        host: &str,
867        uri: &str,
868        method: &Method,
869    ) -> Result<RouteResult, FrontendFromRequestError> {
870        let start = Instant::now();
871        let (remaining_input, (hostname, _)) = match hostname_and_port(host.as_bytes()) {
872            Ok(tuple) => tuple,
873            Err(parse_error) => {
874                // parse_error contains a slice of given_host, which should NOT escape this scope
875                return Err(FrontendFromRequestError::HostParse {
876                    host: host.to_owned(),
877                    error: parse_error.to_string(),
878                });
879            }
880        };
881        if remaining_input != &b""[..] {
882            return Err(FrontendFromRequestError::InvalidCharsAfterHost(
883                host.to_owned(),
884            ));
885        }
886
887        /*if port == Some(&b"80"[..]) {
888        // it is alright to call from_utf8_unchecked,
889        // we already verified that there are only ascii
890        // chars in there
891          unsafe { from_utf8_unchecked(hostname) }
892        } else {
893          host
894        }
895        */
896        // SAFETY: `hostname` was just produced by `hostname_and_port`
897        // (`lib/src/protocol/kawa_h1/parser.rs`), which only accepts
898        // bytes matching `is_hostname_char` (alphanumeric, `-`, `.`, plus
899        // `_` under the tolerant-http1-parser feature). All accepted
900        // bytes are ASCII (≤ 0x7F), so the slice is valid single-byte UTF-8.
901        let host = unsafe { from_utf8_unchecked(hostname) };
902
903        let route = self.fronts.lookup(host, uri, method).map_err(|e| {
904            incr!(names::http::FAILED_BACKEND_MATCHING);
905            FrontendFromRequestError::NoClusterFound(e)
906        })?;
907
908        let now = Instant::now();
909
910        if let Some(cluster) = route.cluster_id.as_deref() {
911            time!(
912                names::event_loop::FRONTEND_MATCHING_TIME,
913                cluster,
914                (now - start).as_millis()
915            );
916        }
917
918        Ok(route)
919    }
920
921    fn get_answers(&self) -> &Rc<RefCell<HttpAnswers>> {
922        &self.answers
923    }
924
925    fn get_h2_flood_config(&self) -> crate::protocol::mux::H2FloodConfig {
926        // `H2FloodConfig`'s fields are private and `from_optional` applies the
927        // `.max(1)` clamp `new` carries (sozu-proxy/sozu#1418). This used to be
928        // a raw struct literal, which skipped that clamp on the only path that
929        // carries operator-set thresholds: config load rejects an out-of-range
930        // knob (`ConfigError::H2ThresholdBelowMinimum`), and the clamp is the
931        // structural backstop for a raw protobuf `AddHttpListener` or a
932        // state file saved before that gate existed.
933        crate::protocol::mux::H2FloodConfig::from_optional(
934            self.config.h2_max_rst_stream_per_window,
935            self.config.h2_max_ping_per_window,
936            self.config.h2_max_settings_per_window,
937            self.config.h2_max_empty_data_per_window,
938            self.config.h2_max_window_update_stream0_per_window,
939            self.config.h2_max_continuation_frames,
940            self.config.h2_max_glitch_count,
941            self.config.h2_max_rst_stream_lifetime,
942            self.config.h2_max_rst_stream_abusive_lifetime,
943            self.config.h2_max_rst_stream_emitted_lifetime,
944            self.config.h2_max_header_list_size,
945            self.config.h2_max_header_table_size,
946            self.config.h2_max_header_fields,
947            self.config.h2_stream_refusal_percent,
948        )
949    }
950
951    fn get_h2_connection_config(&self) -> crate::protocol::mux::H2ConnectionConfig {
952        crate::protocol::mux::H2ConnectionConfig::from_optional(
953            self.config.h2_initial_connection_window,
954            self.config.h2_max_concurrent_streams,
955            self.config.h2_stream_shrink_ratio,
956        )
957    }
958
959    fn get_h2_stream_idle_timeout(&self) -> std::time::Duration {
960        // Inherit `back_timeout` when the knob is unset so listeners tuned for
961        // long-running backends do not cancel streams at the 30 s security
962        // floor. The `max(30, …)` keeps the baseline slow-multiplex mitigation
963        // when `back_timeout` is shorter than 30 s. Explicit values (including
964        // ones below 30 s) win — operators under a slow-multiplex attack can
965        // lower the per-stream deadline to cap buffer pinning.
966        let seconds = self
967            .config
968            .h2_stream_idle_timeout_seconds
969            .map(|s| u64::from(s.max(1)))
970            .unwrap_or_else(|| u64::from(self.config.back_timeout).max(30));
971        std::time::Duration::from_secs(seconds)
972    }
973
974    fn get_h2_graceful_shutdown_deadline(&self) -> Option<std::time::Duration> {
975        match self.config.h2_graceful_shutdown_deadline_seconds {
976            None => Some(std::time::Duration::from_secs(5)),
977            Some(0) => None,
978            Some(s) => Some(std::time::Duration::from_secs(u64::from(s))),
979        }
980    }
981
982    fn get_elide_x_real_ip(&self) -> bool {
983        self.config.elide_x_real_ip.unwrap_or(false)
984    }
985
986    fn get_send_x_real_ip(&self) -> bool {
987        self.config.send_x_real_ip.unwrap_or(false)
988    }
989
990    fn get_forwarded_headers(&self) -> ForwardedHeaders {
991        // An unknown wire value reads as the default, like prost's own
992        // getter; `update_config` and the main-process state refuse one.
993        self.config.forwarded_headers()
994    }
995}
996
997pub struct HttpProxy {
998    backends: Rc<RefCell<BackendMap>>,
999    clusters: HashMap<ClusterId, Cluster>,
1000    listeners: HashMap<Token, Rc<RefCell<HttpListener>>>,
1001    pool: Rc<RefCell<Pool>>,
1002    registry: Registry,
1003    sessions: Rc<RefCell<SessionManager>>,
1004}
1005
1006impl HttpProxy {
1007    pub fn new(
1008        registry: Registry,
1009        sessions: Rc<RefCell<SessionManager>>,
1010        pool: Rc<RefCell<Pool>>,
1011        backends: Rc<RefCell<BackendMap>>,
1012    ) -> HttpProxy {
1013        HttpProxy {
1014            backends,
1015            clusters: HashMap::new(),
1016            listeners: HashMap::new(),
1017            pool,
1018            registry,
1019            sessions,
1020        }
1021    }
1022
1023    pub fn add_listener(
1024        &mut self,
1025        config: HttpListenerConfig,
1026        token: Token,
1027    ) -> Result<Token, ProxyError> {
1028        if self.listeners.contains_key(&token) {
1029            return Err(ProxyError::ListenerAlreadyPresent);
1030        }
1031        let mut http_listener =
1032            HttpListener::new(config, token).map_err(ProxyError::AddListener)?;
1033        // Frontends name their listener by address alone, so a listener added
1034        // next to one already on its address (on another interface) takes
1035        // over the routes that one serves: every listener on an address holds
1036        // the same routes, whenever it was added (sozu-proxy/sozu#719).
1037        if let Some(sibling) = self
1038            .listeners_at(http_listener.address)
1039            .ok()
1040            .and_then(|l| l.into_iter().next())
1041        {
1042            let sibling = sibling.borrow();
1043            http_listener.fronts = sibling.fronts.clone();
1044            http_listener.tags = sibling.tags.clone();
1045        }
1046        match self.listeners.entry(token) {
1047            Entry::Vacant(entry) => {
1048                entry.insert(Rc::new(RefCell::new(http_listener)));
1049                Ok(token)
1050            }
1051            _ => Err(ProxyError::ListenerAlreadyPresent),
1052        }
1053    }
1054
1055    pub fn get_listener(&self, token: &Token) -> Option<Rc<RefCell<HttpListener>>> {
1056        self.listeners.get(token).cloned()
1057    }
1058
1059    pub fn remove_listener(&mut self, remove: RemoveListener) -> Result<(), ProxyError> {
1060        let len = self.listeners.len();
1061        let remove_address = remove.listener_key();
1062        self.listeners
1063            .retain(|_, l| !l.borrow().is(&remove_address));
1064
1065        if !self.listeners.len() < len {
1066            info!(
1067                "{} no HTTP listener to remove at {}",
1068                log_module_context!(),
1069                remove_address
1070            );
1071        }
1072        Ok(())
1073    }
1074
1075    /// What this proxy would do with a socket handed to
1076    /// [`activate_listener`](Self::activate_listener) for `addr`. See
1077    /// [`crate::InheritedSocketFate`].
1078    ///
1079    /// The event loop is single-threaded, so nothing can change the listener's
1080    /// `active` flag between this answer and the `activate_listener` call that
1081    /// acts on it.
1082    pub fn inherited_socket_fate(&self, addr: &ListenerKey) -> crate::InheritedSocketFate {
1083        use crate::InheritedSocketFate;
1084        match self
1085            .listeners
1086            .values()
1087            .find(|listener| listener.borrow().is(addr))
1088        {
1089            None => InheritedSocketFate::Unclaimed,
1090            Some(listener) if listener.borrow().active => InheritedSocketFate::Refused,
1091            Some(_) => InheritedSocketFate::Adopted,
1092        }
1093    }
1094
1095    pub fn activate_listener(
1096        &self,
1097        addr: &ListenerKey,
1098        tcp_listener: Option<MioTcpListener>,
1099    ) -> Result<Token, ProxyError> {
1100        let listener = self
1101            .listeners
1102            .values()
1103            .find(|listener| listener.borrow().is(addr))
1104            .ok_or(ProxyError::NoListenerFound(addr.clone()))?;
1105
1106        listener
1107            .borrow_mut()
1108            .activate(&self.registry, tcp_listener)
1109            .map_err(|listener_error| ProxyError::ListenerActivation {
1110                address: addr.address,
1111                listener_error,
1112            })
1113    }
1114
1115    /// The slab token reserved for the listener at `address`, if this proxy
1116    /// holds one.
1117    ///
1118    /// A listener owns exactly one slab slot for its whole `AddListener` ->
1119    /// `RemoveListener` lifetime, and no `remove_listener` implementation
1120    /// touches the session slab. `Server` therefore reads the token here
1121    /// BEFORE dropping the listener, so that reserved slot is released exactly
1122    /// once, at the end of the lifetime.
1123    pub fn listener_token(&self, address: &ListenerKey) -> Option<Token> {
1124        self.listeners
1125            .iter()
1126            .find(|(_, listener)| listener.borrow().is(address))
1127            .map(|(token, _)| *token)
1128    }
1129
1130    pub fn give_back_listeners(&mut self) -> Vec<(ListenerKey, MioTcpListener)> {
1131        self.listeners
1132            .values()
1133            .filter_map(|listener| {
1134                let mut owned = listener.borrow_mut();
1135                if let Some(listener) = owned.listener.take() {
1136                    // Reset `active` so a subsequent `activate()` re-binds
1137                    // instead of short-circuiting on the stale flag.
1138                    owned.active = false;
1139                    return Some((owned.key(), listener));
1140                }
1141
1142                None
1143            })
1144            .collect()
1145    }
1146
1147    pub fn give_back_listener(
1148        &mut self,
1149        address: &ListenerKey,
1150    ) -> Result<(Token, MioTcpListener), ProxyError> {
1151        let listener = self
1152            .listeners
1153            .values()
1154            .find(|listener| listener.borrow().is(address))
1155            .ok_or(ProxyError::NoListenerFound(address.clone()))?;
1156
1157        let mut owned = listener.borrow_mut();
1158
1159        let taken_listener = owned
1160            .listener
1161            .take()
1162            .ok_or(ProxyError::UnactivatedListener)?;
1163
1164        // Reset `active` so a subsequent `activate()` re-binds instead of
1165        // short-circuiting on the stale flag.
1166        owned.active = false;
1167
1168        Ok((owned.token, taken_listener))
1169    }
1170
1171    /// Every listener on `address`, whatever its interface. Frontends and
1172    /// certificates name their listener by address alone, so they apply to
1173    /// all the listeners sharing that address on different interfaces.
1174    fn listeners_at(
1175        &self,
1176        address: SocketAddr,
1177    ) -> Result<Vec<Rc<RefCell<HttpListener>>>, ProxyError> {
1178        let mut listeners: Vec<_> = self
1179            .listeners
1180            .iter()
1181            .filter(|(_, listener)| listener.borrow().address == address)
1182            .map(|(token, listener)| (*token, listener.clone()))
1183            .collect();
1184        // Token order, so a failure is reported the same way on every run.
1185        listeners.sort_by_key(|(token, _)| *token);
1186        let listeners: Vec<_> = listeners
1187            .into_iter()
1188            .map(|(_, listener)| listener)
1189            .collect();
1190        if listeners.is_empty() {
1191            return Err(ProxyError::NoListenerFound(address.into()));
1192        }
1193        Ok(listeners)
1194    }
1195
1196    /// Apply a partial-update patch to the identified HTTP listener.
1197    pub fn update_listener(&mut self, patch: UpdateHttpListenerConfig) -> Result<(), ProxyError> {
1198        let address: std::net::SocketAddr = patch.address.into();
1199        let listener = self
1200            .listeners
1201            .values()
1202            .find(|l| l.borrow().address == address)
1203            .ok_or(ProxyError::NoListenerFound(address.into()))?;
1204        listener
1205            .borrow_mut()
1206            .update_config(&patch)
1207            .map_err(|listener_error| ProxyError::ListenerActivation {
1208                address,
1209                listener_error,
1210            })
1211    }
1212
1213    pub fn add_cluster(&mut self, mut cluster: Cluster) -> Result<(), ProxyError> {
1214        // Reconcile the legacy single-status `answer_503` field with the
1215        // new map. The new map wins on collision.
1216        let mut overrides = cluster.answers.clone();
1217        if let Some(answer_503) = cluster.answer_503.take() {
1218            overrides.entry("503".to_owned()).or_insert(answer_503);
1219        }
1220        if !overrides.is_empty() {
1221            for listener in self.listeners.values() {
1222                listener
1223                    .borrow()
1224                    .answers
1225                    .borrow_mut()
1226                    .add_cluster_answers(&cluster.cluster_id, &overrides)
1227                    .map_err(|(name, error)| {
1228                        ProxyError::AddCluster(ListenerError::TemplateParse(name, error))
1229                    })?;
1230            }
1231        }
1232        self.clusters
1233            .insert(cluster.cluster_id.as_str().into(), cluster);
1234        Ok(())
1235    }
1236
1237    pub fn remove_cluster(&mut self, cluster_id: &str) -> Result<(), ProxyError> {
1238        self.clusters.remove(cluster_id);
1239
1240        for listener in self.listeners.values() {
1241            listener
1242                .borrow()
1243                .answers
1244                .borrow_mut()
1245                .remove_cluster_answers(cluster_id);
1246        }
1247        Ok(())
1248    }
1249
1250    pub fn add_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1251        // RFC 6797 §7.2: `Strict-Transport-Security` MUST NOT be sent over
1252        // plaintext HTTP. Reject any AddHttpFrontend that ships an enabled
1253        // HSTS policy before it ever touches the routing trie. This is
1254        // defense in depth on top of the TOML config-load reject in
1255        // `command/src/config.rs`; sites that build a `RequestHttpFrontend`
1256        // outside the TOML path (`sozu frontend http add`, programmatic
1257        // IPC senders) are caught here.
1258        // Reject ANY hsts field on a plain-HTTP frontend, not just
1259        // `enabled = true`. There is no listener-default HSTS to inherit
1260        // on an HTTP listener (the field doesn't exist on
1261        // `HttpListenerConfig`), so the explicit-disable signal
1262        // (`enabled = false`) has nothing to suppress on this surface.
1263        // Carrying any `hsts` field here is a misconfiguration rather
1264        // than a deliberate choice.
1265        if front.hsts.is_some() {
1266            incr!(names::http::HSTS_SUPPRESSED_PLAINTEXT);
1267            return Err(ProxyError::HstsOnPlainHttp(front.address.into()));
1268        }
1269
1270        let front = front.clone().to_frontend().map_err(|request_error| {
1271            ProxyError::WrongInputFrontend {
1272                front: Box::new(front),
1273                error: request_error.to_string(),
1274            }
1275        })?;
1276
1277        for listener in self.listeners_at(front.address)? {
1278            let mut listener = listener.borrow_mut();
1279            let hostname = front.hostname.to_owned();
1280            let tags = front.tags.to_owned();
1281
1282            listener
1283                .add_http_front(front.clone())
1284                .map_err(ProxyError::AddFrontend)?;
1285            listener.set_tags(hostname, tags);
1286        }
1287        Ok(())
1288    }
1289
1290    pub fn remove_http_frontend(&mut self, front: RequestHttpFrontend) -> Result<(), ProxyError> {
1291        let front = front.clone().to_frontend().map_err(|request_error| {
1292            ProxyError::WrongInputFrontend {
1293                front: Box::new(front),
1294                error: request_error.to_string(),
1295            }
1296        })?;
1297
1298        // Every listener on the address drops the route, even when another
1299        // one fails: a sibling must never keep a route the others removed.
1300        // The first failure, in token order, is the one reported.
1301        let mut result = Ok(());
1302        for listener in self.listeners_at(front.address)? {
1303            let mut listener = listener.borrow_mut();
1304            let hostname = front.hostname.to_owned();
1305
1306            let removed = listener
1307                .remove_http_front(front.clone())
1308                .map_err(ProxyError::RemoveFrontend);
1309            if result.is_ok() {
1310                result = removed;
1311            }
1312
1313            if !listener.fronts.has_hostname(&hostname) {
1314                listener.set_tags(hostname, None);
1315            }
1316        }
1317        result
1318    }
1319
1320    pub fn soft_stop(&mut self) -> Result<(), ProxyError> {
1321        let listeners = std::mem::take(&mut self.listeners);
1322        let mut socket_errors = vec![];
1323        for l in listeners.values() {
1324            if let Some(mut sock) = l.borrow_mut().listener.take() {
1325                debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1326                if let Err(e) = self.registry.deregister(&mut sock) {
1327                    let error = format!("socket {sock:?}: {e:?}");
1328                    socket_errors.push(error);
1329                }
1330            }
1331        }
1332
1333        if !socket_errors.is_empty() {
1334            return Err(ProxyError::SoftStop {
1335                proxy_protocol: "HTTP".to_string(),
1336                error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1337            });
1338        }
1339
1340        Ok(())
1341    }
1342
1343    pub fn hard_stop(&mut self) -> Result<(), ProxyError> {
1344        let mut listeners = std::mem::take(&mut self.listeners);
1345        let mut socket_errors = vec![];
1346        for (_, l) in listeners.drain() {
1347            if let Some(mut sock) = l.borrow_mut().listener.take() {
1348                debug!("{} deregistering socket {:?}", log_module_context!(), sock);
1349                if let Err(e) = self.registry.deregister(&mut sock) {
1350                    let error = format!("socket {sock:?}: {e:?}");
1351                    socket_errors.push(error);
1352                }
1353            }
1354        }
1355
1356        if !socket_errors.is_empty() {
1357            return Err(ProxyError::HardStop {
1358                proxy_protocol: "HTTP".to_string(),
1359                error: format!("Error deregistering listen sockets: {socket_errors:?}"),
1360            });
1361        }
1362
1363        Ok(())
1364    }
1365}
1366
1367impl HttpListener {
1368    /// This listener's identity: its address, plus the network interface its
1369    /// socket is bound to when it has one.
1370    pub fn key(&self) -> ListenerKey {
1371        ListenerKey::new(self.address, self.config.interface.as_deref())
1372    }
1373
1374    /// Whether this listener is the one `key` names.
1375    pub fn is(&self, key: &ListenerKey) -> bool {
1376        key.matches(&self.address, self.config.interface.as_deref())
1377    }
1378
1379    pub fn new(config: HttpListenerConfig, token: Token) -> Result<HttpListener, ListenerError> {
1380        // Defense in depth, as `update_config`: the main-process state
1381        // refuses an unknown mode, a raw protobuf client may not.
1382        if let Some(v) = config.forwarded_headers {
1383            validate_forwarded_headers(v)?;
1384        }
1385        let answers = Self::build_answers(&config)?;
1386        Ok(HttpListener {
1387            active: false,
1388            address: config.address.into(),
1389            answers: Rc::new(RefCell::new(answers)),
1390            config,
1391            fronts: Router::new(),
1392            listener: None,
1393            parked_listener: None,
1394            nodelay_backlog: false,
1395            tags: BTreeMap::new(),
1396            token,
1397        })
1398    }
1399
1400    /// Build the listener's HTTP answer templates from its config, reconciling
1401    /// the legacy `http_answers` per-status fields with the new `answers`
1402    /// template map (the new map wins on collision; legacy fields fill any
1403    /// status not yet migrated). Shared by [`Self::new`] and
1404    /// [`Self::validate_config`] so the master-side pre-commit check cannot
1405    /// drift from worker construction.
1406    fn build_answers(config: &HttpListenerConfig) -> Result<HttpAnswers, ListenerError> {
1407        let mut answers_map = config.answers.clone();
1408        if let Some(ref legacy) = config.http_answers {
1409            crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1410        }
1411        HttpAnswers::new(&answers_map)
1412            .map_err(|(name, error)| ListenerError::TemplateParse(name, error))
1413    }
1414
1415    /// Validate that a worker can build this HTTP listener configuration (its
1416    /// answer templates) WITHOUT constructing the full listener or binding a
1417    /// socket. The main process calls this before committing an
1418    /// `AddHttpListener` to its `ConfigState` and fanning it out, so an invalid
1419    /// listener never reserves its address and blocks a corrected reload
1420    /// (sozu#1301).
1421    ///
1422    /// Buildability only. The H2 knob floors are deliberately NOT checked
1423    /// here (sozu#1418): a worker *can* build a listener whose threshold is
1424    /// out of range — `H2FloodConfig::from_optional` clamps it — so refusing
1425    /// it is a policy the command socket applies to what an operator is
1426    /// authoring, not a statement about what a worker can construct. That
1427    /// gate lives beside its audience, in
1428    /// `bin/src/command/requests.rs::validate_h2_knob_floors`.
1429    pub fn validate_config(config: &HttpListenerConfig) -> Result<(), ListenerError> {
1430        Self::build_answers(config)?;
1431        Ok(())
1432    }
1433
1434    pub fn activate(
1435        &mut self,
1436        registry: &Registry,
1437        tcp_listener: Option<MioTcpListener>,
1438    ) -> Result<Token, ListenerError> {
1439        if self.active {
1440            return Ok(self.token);
1441        }
1442        let address: SocketAddr = self.config.address.into();
1443
1444        // Reuse a socket a previous attempt parked here after a failed
1445        // registration, rather than binding a second one. Taking it
1446        // unconditionally is what keeps the two from coexisting: if the caller
1447        // ALSO handed one over, the parked socket is disposed of deliberately
1448        // below instead of being silently overwritten.
1449        let parked = self.parked_listener.take();
1450        let mut listener = match (tcp_listener, parked) {
1451            (Some(inherited), Some(parked)) => {
1452                // Not reachable while `Listeners::get_*` removes the entry it
1453                // hands over and `scm_listeners` is written once: a parked
1454                // socket means this address's entry was already consumed, so
1455                // the caller cannot have another. Neither of those is enforced
1456                // here, so dispose of the loser explicitly and loudly rather
1457                // than letting an assignment close it — that silent close is
1458                // sozu#1342 itself.
1459                warn!(
1460                    "{} closing the socket parked on the HTTP listener for {}: an \
1461                     inherited socket arrived for the same address and is the one being \
1462                     activated",
1463                    log_module_context!(),
1464                    address
1465                );
1466                drop(parked);
1467                inherited
1468            }
1469            (Some(inherited), None) => inherited,
1470            (None, Some(parked)) => parked,
1471            (None, None) => server_bind(address, self.config.interface.as_deref()).map_err(
1472                |server_bind_error| ListenerError::Activation {
1473                    address,
1474                    error: server_bind_error.to_string(),
1475                },
1476            )?,
1477        };
1478
1479        // Once per listener, on whichever socket won above — freshly bound,
1480        // inherited over SCM_RIGHTS from a worker that may never have set it,
1481        // or parked by a failed registration. Every socket this listener
1482        // accepts from now on inherits it; see `nodelay_backlog` for the ones
1483        // already queued.
1484        if let Err(e) = SockRef::from(&listener).set_tcp_nodelay(true) {
1485            error!(
1486                "{} error setting nodelay on listen socket({:?}): {:?}",
1487                log_module_context!(),
1488                listener,
1489                e
1490            );
1491        }
1492
1493        let registration = registry
1494            .register(&mut listener, self.token, Interest::READABLE)
1495            .map_err(ListenerError::SocketRegistration);
1496        if let Err(error) = registration {
1497            // Park the socket instead of letting the `?` drop it. `register`
1498            // is fallible (EEXIST for a descriptor still registered because
1499            // `notify_deactivate_listener` only logs a failed deregister,
1500            // EBADF, ENOMEM), and a socket still held in a local is dropped —
1501            // and closed — on the way out. For an SCM-inherited socket that is
1502            // the sozu#1342 close in a second place: `Listeners::get_*` has
1503            // already removed the entry, so nothing else owns the descriptor.
1504            // It goes to `parked_listener`, never to `listener`, because every other
1505            // consumer of that field reads `Some` as "registered and live".
1506            self.parked_listener = Some(listener);
1507            return Err(error);
1508        }
1509
1510        self.listener = Some(listener);
1511        self.nodelay_backlog = true;
1512        self.active = true;
1513        Ok(self.token)
1514    }
1515
1516    /// Apply a partial-update patch to this listener's live configuration.
1517    ///
1518    /// Fields absent in the patch (i.e. `None`) are preserved unchanged.
1519    /// If `http_answers` is present only the listener-default templates are
1520    /// replaced; per-cluster overrides in `cluster_custom_answers` are kept.
1521    ///
1522    /// All-or-nothing (sozu-proxy/sozu#1703): the patch is validated, then
1523    /// applied to a copy of the configuration, and every fallible artifact
1524    /// (the rebuilt answer registry) is built from that copy. Only once all
1525    /// of it succeeded are the copy and the artifacts committed, so a refused
1526    /// patch leaves the listener exactly as it was. The copy is one
1527    /// `HttpListenerConfig` clone per patch, on the command path only.
1528    pub fn update_config(&mut self, patch: &UpdateHttpListenerConfig) -> Result<(), ListenerError> {
1529        // Defense-in-depth validation: main-process ConfigState::dispatch
1530        // validates before scatter, but a raw protobuf client or state replay
1531        // may reach the worker without that check. `StateError` lifts into
1532        // `ListenerError` via `From` so `?` suffices. The main process runs
1533        // the same checks (`ConfigState::update_http_listener` and, for the
1534        // templates, `validate_listener_request` in
1535        // `bin/src/command/requests.rs`), so both refuse the same patches.
1536        validate_h2_flood_knobs_http(patch)?;
1537        if let Some(ref hdr) = patch.sozu_id_header {
1538            validate_sozu_id_header(hdr)?;
1539        }
1540        if let Some(v) = patch.forwarded_headers {
1541            validate_forwarded_headers(v)?;
1542        }
1543        HttpAnswers::validate_patch_templates(patch.http_answers.as_ref(), &patch.answers)
1544            .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?;
1545
1546        let mut config = self.config.clone();
1547
1548        if let Some(v) = patch.public_address {
1549            config.public_address = Some(v);
1550        }
1551        if let Some(v) = patch.expect_proxy {
1552            config.expect_proxy = v;
1553        }
1554        if let Some(ref v) = patch.sticky_name {
1555            config.sticky_name = v.to_owned();
1556        }
1557        if let Some(v) = patch.front_timeout {
1558            config.front_timeout = v;
1559        }
1560        if let Some(v) = patch.back_timeout {
1561            config.back_timeout = v;
1562        }
1563        if let Some(v) = patch.connect_timeout {
1564            config.connect_timeout = v;
1565        }
1566        if let Some(v) = patch.request_timeout {
1567            config.request_timeout = v;
1568        }
1569        if let Some(ref v) = patch.sozu_id_header {
1570            config.sozu_id_header = Some(v.to_owned());
1571        }
1572        if let Some(v) = patch.elide_x_real_ip {
1573            config.elide_x_real_ip = Some(v);
1574        }
1575        if let Some(v) = patch.send_x_real_ip {
1576            config.send_x_real_ip = Some(v);
1577        }
1578        if let Some(v) = patch.forwarded_headers {
1579            config.forwarded_headers = Some(v);
1580        }
1581
1582        // H2 flood knobs
1583        if let Some(v) = patch.h2_max_rst_stream_per_window {
1584            config.h2_max_rst_stream_per_window = Some(v);
1585        }
1586        if let Some(v) = patch.h2_max_ping_per_window {
1587            config.h2_max_ping_per_window = Some(v);
1588        }
1589        if let Some(v) = patch.h2_max_settings_per_window {
1590            config.h2_max_settings_per_window = Some(v);
1591        }
1592        if let Some(v) = patch.h2_max_empty_data_per_window {
1593            config.h2_max_empty_data_per_window = Some(v);
1594        }
1595        if let Some(v) = patch.h2_max_continuation_frames {
1596            config.h2_max_continuation_frames = Some(v);
1597        }
1598        if let Some(v) = patch.h2_max_glitch_count {
1599            config.h2_max_glitch_count = Some(v);
1600        }
1601        if let Some(v) = patch.h2_initial_connection_window {
1602            config.h2_initial_connection_window = Some(v);
1603        }
1604        if let Some(v) = patch.h2_max_concurrent_streams {
1605            config.h2_max_concurrent_streams = Some(v);
1606        }
1607        if let Some(v) = patch.h2_stream_shrink_ratio {
1608            config.h2_stream_shrink_ratio = Some(v);
1609        }
1610        if let Some(v) = patch.h2_max_rst_stream_lifetime {
1611            config.h2_max_rst_stream_lifetime = Some(v);
1612        }
1613        if let Some(v) = patch.h2_max_rst_stream_abusive_lifetime {
1614            config.h2_max_rst_stream_abusive_lifetime = Some(v);
1615        }
1616        if let Some(v) = patch.h2_max_rst_stream_emitted_lifetime {
1617            config.h2_max_rst_stream_emitted_lifetime = Some(v);
1618        }
1619        if let Some(v) = patch.h2_max_header_list_size {
1620            config.h2_max_header_list_size = Some(v);
1621        }
1622        if let Some(v) = patch.h2_max_header_table_size {
1623            config.h2_max_header_table_size = Some(v);
1624        }
1625        if let Some(v) = patch.h2_max_header_fields {
1626            config.h2_max_header_fields = Some(v);
1627        }
1628        if let Some(v) = patch.h2_stream_refusal_percent {
1629            config.h2_stream_refusal_percent = Some(v);
1630        }
1631        if let Some(v) = patch.h2_stream_idle_timeout_seconds {
1632            config.h2_stream_idle_timeout_seconds = Some(v);
1633        }
1634        if let Some(v) = patch.h2_graceful_shutdown_deadline_seconds {
1635            config.h2_graceful_shutdown_deadline_seconds = Some(v);
1636        }
1637        if let Some(v) = patch.h2_max_window_update_stream0_per_window {
1638            config.h2_max_window_update_stream0_per_window = Some(v);
1639        }
1640
1641        // HTTP answers: merge legacy `http_answers` and the new `answers`
1642        // map on top of the candidate config, then build the listener-level
1643        // template registry from it. It is fallible, so it is built here and
1644        // published below with the config, never before.
1645        let answers_changed = patch.http_answers.is_some() || !patch.answers.is_empty();
1646        let new_answers = if answers_changed {
1647            if let Some(ref new_answers) = patch.http_answers {
1648                crate::sozu_command::state::merge_custom_http_answers(
1649                    &mut config.http_answers,
1650                    new_answers,
1651                );
1652            }
1653            for (code, body) in &patch.answers {
1654                if !body.is_empty() {
1655                    config.answers.insert(code.clone(), body.clone());
1656                }
1657            }
1658
1659            let mut answers_map = config.answers.clone();
1660            if let Some(ref legacy) = config.http_answers {
1661                crate::protocol::http::answers::merge_legacy_into_map(&mut answers_map, legacy);
1662            }
1663            Some(
1664                HttpAnswers::new(&answers_map)
1665                    .map_err(|(name, error)| ListenerError::TemplateParse(name, error))?,
1666            )
1667        } else {
1668            None
1669        };
1670
1671        // Commit: nothing below can fail.
1672        self.config = config;
1673        if let Some(mut new_answers) = new_answers {
1674            // Per-cluster overrides in `HttpAnswers::cluster_answers` are
1675            // preserved across the rebuild.
1676            //
1677            // The rebuilt registry is PUBLISHED under a new `Rc`, not written
1678            // through the old one. Every request already in flight captured
1679            // the old handle when it arrived (`mux::Context::create_stream`)
1680            // and renders its default answers from that, so a reload landing
1681            // mid-request cannot change the page that request is about to
1682            // emit; requests arriving after this point capture the new handle.
1683            // That is the one-request staleness window of
1684            // `lib/src/protocol/mux/LIFECYCLE.md` §2.5.
1685            //
1686            // The per-cluster overrides are therefore COPIED, not
1687            // `mem::take`n: taking them would empty the registry those
1688            // in-flight requests still hold and strip their cluster templates
1689            // mid-response. The copy is a map clone over `Rc<Template>`, so
1690            // both registries share the compiled templates themselves.
1691            new_answers.cluster_answers = self.answers.borrow().cluster_answers.clone();
1692            self.answers = Rc::new(RefCell::new(new_answers));
1693        }
1694
1695        Ok(())
1696    }
1697
1698    pub fn add_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1699        self.fronts
1700            .add_http_front(&http_front)
1701            .map_err(ListenerError::AddFrontend)
1702    }
1703
1704    pub fn remove_http_front(&mut self, http_front: HttpFrontend) -> Result<(), ListenerError> {
1705        debug!(
1706            "{} removing http_front {:?}",
1707            log_module_context!(),
1708            http_front
1709        );
1710        self.fronts
1711            .remove_http_front(&http_front)
1712            .map_err(ListenerError::RemoveFrontend)
1713    }
1714
1715    fn accept(&mut self) -> Result<(TcpStream, SocketAddr), AcceptError> {
1716        if let Some(ref sock) = self.listener {
1717            match sock.accept() {
1718                Ok((frontend_sock, peer)) => {
1719                    if self.nodelay_backlog
1720                        && let Err(e) = frontend_sock.set_nodelay(true)
1721                    {
1722                        error!(
1723                            "{} error setting nodelay on front socket({:?}): {:?}",
1724                            log_module_context!(),
1725                            frontend_sock,
1726                            e
1727                        );
1728                    }
1729                    Ok((frontend_sock, peer))
1730                }
1731                Err(e) if e.kind() == ErrorKind::WouldBlock => {
1732                    // The backlog queued before `activate()` is drained.
1733                    self.nodelay_backlog = false;
1734                    Err(AcceptError::WouldBlock)
1735                }
1736                Err(e) => {
1737                    error!("{} accept() IO error: {:?}", log_module_context!(), e);
1738                    Err(AcceptError::IoError)
1739                }
1740            }
1741        } else {
1742            error!(
1743                "{} cannot accept connections, no listening socket available",
1744                log_module_context!()
1745            );
1746            Err(AcceptError::IoError)
1747        }
1748    }
1749}
1750
1751impl ProxyConfiguration for HttpProxy {
1752    fn notify(&mut self, request: WorkerRequest) -> WorkerResponse {
1753        let request_id = request.id.clone();
1754
1755        let result = match request.content.request_type {
1756            Some(RequestType::AddCluster(cluster)) => {
1757                debug!(
1758                    "{} {} add cluster {:?}",
1759                    log_module_context!(),
1760                    request.id,
1761                    cluster
1762                );
1763                self.add_cluster(cluster)
1764            }
1765            Some(RequestType::RemoveCluster(cluster_id)) => {
1766                debug!(
1767                    "{} {} remove cluster {:?}",
1768                    log_module_context!(),
1769                    request_id,
1770                    cluster_id
1771                );
1772                self.remove_cluster(&cluster_id)
1773            }
1774            Some(RequestType::AddHttpFrontend(front)) => {
1775                debug!(
1776                    "{} {} add front {:?}",
1777                    log_module_context!(),
1778                    request_id,
1779                    front
1780                );
1781                self.add_http_frontend(front)
1782            }
1783            Some(RequestType::RemoveHttpFrontend(front)) => {
1784                debug!(
1785                    "{} {} remove front {:?}",
1786                    log_module_context!(),
1787                    request_id,
1788                    front
1789                );
1790                self.remove_http_frontend(front)
1791            }
1792            Some(RequestType::RemoveListener(remove)) => {
1793                debug!(
1794                    "{} removing HTTP listener at address {:?}",
1795                    log_module_context!(),
1796                    remove.address
1797                );
1798                self.remove_listener(remove)
1799            }
1800            Some(RequestType::SoftStop(_)) => {
1801                debug!(
1802                    "{} {} processing soft shutdown",
1803                    log_module_context!(),
1804                    request_id
1805                );
1806                match self.soft_stop() {
1807                    Ok(()) => {
1808                        info!(
1809                            "{} {} soft stop successful",
1810                            log_module_context!(),
1811                            request_id
1812                        );
1813                        return WorkerResponse::processing(request.id);
1814                    }
1815                    Err(e) => Err(e),
1816                }
1817            }
1818            Some(RequestType::HardStop(_)) => {
1819                debug!(
1820                    "{} {} processing hard shutdown",
1821                    log_module_context!(),
1822                    request_id
1823                );
1824                match self.hard_stop() {
1825                    Ok(()) => {
1826                        info!(
1827                            "{} {} hard stop successful",
1828                            log_module_context!(),
1829                            request_id
1830                        );
1831                        return WorkerResponse::processing(request.id);
1832                    }
1833                    Err(e) => Err(e),
1834                }
1835            }
1836            Some(RequestType::Status(_)) => {
1837                debug!("{} {} status", log_module_context!(), request_id);
1838                Ok(())
1839            }
1840            other_command => {
1841                debug!(
1842                    "{} {} unsupported message for HTTP proxy, ignoring: {:?}",
1843                    log_module_context!(),
1844                    request.id,
1845                    other_command
1846                );
1847                Err(ProxyError::UnsupportedMessage)
1848            }
1849        };
1850
1851        match result {
1852            Ok(()) => {
1853                debug!("{} {} successful", log_module_context!(), request_id);
1854                WorkerResponse::ok(request_id)
1855            }
1856            Err(proxy_error) => {
1857                debug!(
1858                    "{} {} unsuccessful: {}",
1859                    log_module_context!(),
1860                    request_id,
1861                    proxy_error
1862                );
1863                WorkerResponse::error(request_id, proxy_error)
1864            }
1865        }
1866    }
1867
1868    fn accept(&mut self, token: ListenToken) -> Result<(TcpStream, SocketAddr), AcceptError> {
1869        if let Some(listener) = self.listeners.get(&Token(token.0)) {
1870            listener.borrow_mut().accept()
1871        } else {
1872            Err(AcceptError::IoError)
1873        }
1874    }
1875
1876    fn create_session(
1877        &mut self,
1878        mut frontend_sock: TcpStream,
1879        peer: SocketAddr,
1880        listener_token: ListenToken,
1881        wait_time: Duration,
1882        proxy: Rc<RefCell<Self>>,
1883    ) -> Result<(), AcceptError> {
1884        let listener = self
1885            .listeners
1886            .get(&Token(listener_token.0))
1887            .cloned()
1888            .ok_or(AcceptError::IoError)?;
1889
1890        // No `set_nodelay` here: the socket has `TCP_NODELAY` from its
1891        // listener, or from `HttpListener::accept` if it was queued before the
1892        // listener set it.
1893        let mut session_manager = self.sessions.borrow_mut();
1894        let slab_len_before = session_manager.slab.len();
1895        let session_entry = session_manager.slab.vacant_entry();
1896        let session_token = Token(session_entry.key());
1897        // The token handed to the new session, used for epoll registration and
1898        // every `remove_session(frontend_token)` call, MUST be the very slab
1899        // key this entry will occupy. A drift here would deregister/free the
1900        // wrong slot on close.
1901        debug_assert_eq!(
1902            session_token.0,
1903            session_entry.key(),
1904            "session token must equal the slab vacant-entry key"
1905        );
1906        let owned = listener.borrow();
1907
1908        if let Err(register_error) = self.registry.register(
1909            &mut frontend_sock,
1910            session_token,
1911            Interest::READABLE | Interest::WRITABLE,
1912        ) {
1913            error!(
1914                "{} error registering listen socket({:?}): {:?}",
1915                log_module_context!(),
1916                frontend_sock,
1917                register_error
1918            );
1919            return Err(AcceptError::RegisterError);
1920        }
1921
1922        let public_address: SocketAddr = match owned.config.public_address {
1923            Some(pub_addr) => pub_addr.into(),
1924            None => owned.config.address.into(),
1925        };
1926
1927        let session = HttpSession::new(
1928            Duration::from_secs(owned.config.back_timeout as u64),
1929            Duration::from_secs(owned.config.connect_timeout as u64),
1930            Duration::from_secs(owned.config.front_timeout as u64),
1931            Duration::from_secs(owned.config.request_timeout as u64),
1932            owned.config.expect_proxy,
1933            listener.clone(),
1934            Rc::downgrade(&self.pool),
1935            proxy,
1936            // `create_session` runs under the proxy's `borrow_mut`, so the
1937            // session cannot read the map through `proxy`; hand it over here.
1938            Rc::clone(&self.backends),
1939            public_address,
1940            frontend_sock,
1941            peer,
1942            session_token,
1943            wait_time,
1944        )?;
1945
1946        // The session's frontend token must be exactly the slab key we are
1947        // about to fill — the registration above and all later token lookups
1948        // depend on it.
1949        debug_assert_eq!(
1950            session.frontend_token, session_token,
1951            "session must own the frontend token it was created with"
1952        );
1953
1954        let session = Rc::new(RefCell::new(session));
1955        session_entry.insert(session);
1956        // Inserting into the previously-vacant entry grows the live session
1957        // count by exactly one (gauge-like ±1 pairing against close()'s
1958        // try_remove). `len()` is the count of occupied slots.
1959        debug_assert_eq!(
1960            session_manager.slab.len(),
1961            slab_len_before + 1,
1962            "creating a session must occupy exactly one new slab slot"
1963        );
1964
1965        Ok(())
1966    }
1967}
1968
1969impl L7Proxy for HttpProxy {
1970    fn kind(&self) -> ListenerType {
1971        ListenerType::Http
1972    }
1973
1974    fn register_socket(
1975        &self,
1976        source: &mut TcpStream,
1977        token: Token,
1978        interest: Interest,
1979    ) -> Result<(), std::io::Error> {
1980        self.registry.register(source, token, interest)
1981    }
1982
1983    fn add_session(&self, session: Rc<RefCell<dyn ProxySession>>) -> Token {
1984        let mut session_manager = self.sessions.borrow_mut();
1985        let len_before = session_manager.slab.len();
1986        let entry = session_manager.slab.vacant_entry();
1987        let token = Token(entry.key());
1988        let _entry = entry.insert(session);
1989        // The returned token is the slab key callers use to later remove this
1990        // session, and the insert occupied exactly one new slot.
1991        debug_assert_eq!(
1992            session_manager.slab.len(),
1993            len_before + 1,
1994            "add_session must occupy exactly one new slab slot"
1995        );
1996        debug_assert!(
1997            session_manager.slab.contains(token.0),
1998            "the returned token must index the freshly inserted session"
1999        );
2000        token
2001    }
2002
2003    fn remove_session(&self, token: Token) -> bool {
2004        let mut sessions = self.sessions.borrow_mut();
2005        let was_present = sessions.slab.contains(token.0);
2006        let len_before = sessions.slab.len();
2007        // Drain the session's `(cluster, ip)` accounting before the slab
2008        // slot is freed — once the slot is reused for a new session the
2009        // token would otherwise alias an unrelated set of entries. No-op
2010        // when the session never tracked anything (feature disabled, or
2011        // no request reached `Router::plan_connect`).
2012        sessions.untrack_all_cluster_ip(token);
2013        let removed = sessions.slab.try_remove(token.0).is_some();
2014        // Removal must report exactly whether the slot was occupied, and the
2015        // live count drops by one iff something was actually removed (±1
2016        // pairing against add_session / create_session). The slot is gone.
2017        debug_assert_eq!(
2018            removed, was_present,
2019            "try_remove reports presence iff the slot was occupied"
2020        );
2021        debug_assert_eq!(
2022            sessions.slab.len(),
2023            len_before - removed as usize,
2024            "slab len drops by exactly one iff a session was removed"
2025        );
2026        debug_assert!(
2027            !sessions.slab.contains(token.0),
2028            "the slot must be free after remove_session"
2029        );
2030        removed
2031    }
2032
2033    fn clusters(&self) -> &HashMap<ClusterId, Cluster> {
2034        &self.clusters
2035    }
2036
2037    fn sessions(&self) -> Rc<RefCell<SessionManager>> {
2038        self.sessions.clone()
2039    }
2040}
2041
2042pub mod testing {
2043    use crate::testing::*;
2044
2045    /// this function is not used, but is available for example and testing purposes
2046    pub fn start_http_worker(
2047        config: HttpListenerConfig,
2048        channel: ProxyChannel,
2049        max_buffers: usize,
2050        buffer_size: usize,
2051    ) -> anyhow::Result<()> {
2052        let address = config.address.into();
2053
2054        let ServerParts {
2055            event_loop,
2056            registry,
2057            sessions,
2058            pool,
2059            backends,
2060            client_scm_socket: _,
2061            server_scm_socket,
2062            server_config,
2063        } = prebuild_server(max_buffers, buffer_size, true)?;
2064
2065        let token = {
2066            let mut sessions = sessions.borrow_mut();
2067            let entry = sessions.slab.vacant_entry();
2068            let key = entry.key();
2069            let _ = entry.insert(Rc::new(RefCell::new(ListenSession {
2070                protocol: Protocol::HTTPListen,
2071            })));
2072            Token(key)
2073        };
2074
2075        let mut proxy = HttpProxy::new(registry, sessions.clone(), pool.clone(), backends.clone());
2076        proxy
2077            .add_listener(config, token)
2078            .with_context(|| "Failed at creating adding the listener")?;
2079        proxy
2080            .activate_listener(&address, None)
2081            .with_context(|| "Failed at creating activating the listener")?;
2082
2083        let mut server = Server::new(
2084            event_loop,
2085            channel,
2086            server_scm_socket,
2087            sessions,
2088            pool,
2089            backends,
2090            Some(proxy),
2091            None,
2092            None,
2093            server_config,
2094            None,
2095            false,
2096        )
2097        .with_context(|| "Failed at creating server")?;
2098
2099        debug!("{} starting event loop", log_module_context!());
2100        server.run();
2101        debug!("{} ending event loop", log_module_context!());
2102        Ok(())
2103    }
2104}
2105
2106#[cfg(test)]
2107mod tests {
2108    extern crate tiny_http;
2109
2110    use std::{
2111        io::{Read, Write},
2112        net::TcpStream,
2113        str,
2114        sync::{Arc, Barrier},
2115        thread,
2116        time::Duration,
2117    };
2118
2119    use sozu_command::proto::command::SocketAddress;
2120
2121    use super::{testing::start_http_worker, *};
2122    use crate::sozu_command::{
2123        channel::Channel,
2124        config::ListenerBuilder,
2125        proto::command::{
2126            CustomHttpAnswers, LoadBalancingParams, PathRule, RulePosition, SoftStop,
2127            WorkerRequest, request::RequestType,
2128        },
2129        response::{Backend, HttpFrontend},
2130    };
2131
2132    /// Regression for sozu-proxy/sozu#1418: `get_h2_flood_config` built
2133    /// `H2FloodConfig` with a raw struct literal, so `H2FloodConfig::new`'s
2134    /// `.max(1)` clamp never ran on the only path that carries
2135    /// operator-configured thresholds. A `HttpListenerConfig` reaching the
2136    /// worker with `h2_max_rst_stream_per_window = Some(0)` — from a raw
2137    /// protobuf `AddHttpListener`, or a state file saved before config load
2138    /// gained its gate — handed that zero straight to `H2FloodDetector::new`,
2139    /// whose `check_flood` compares `count > threshold`: the first counted
2140    /// RST_STREAM of every H2 connection tripped the detector and the listener
2141    /// answered `ENHANCE_YOUR_CALM` to every client. In a debug build the
2142    /// detector's own pre-condition `debug_assert!` fired instead, at
2143    /// connection setup.
2144    ///
2145    /// Config load now rejects the value outright
2146    /// (`ConfigError::H2ThresholdBelowMinimum`); this is the structural
2147    /// backstop for anything that never passed through it.
2148    #[test]
2149    fn get_h2_flood_config_clamps_a_zero_threshold_that_bypassed_config_load() {
2150        let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2151            .to_http(None)
2152            .expect("could not create listener config");
2153        // Exactly what a raw protobuf `AddHttpListener` or a replayed state
2154        // file can still carry — `to_http` itself now refuses this value.
2155        config.h2_max_rst_stream_per_window = Some(0);
2156        config.h2_max_glitch_count = Some(0);
2157
2158        let listener =
2159            HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2160        let flood_config = listener.get_h2_flood_config();
2161
2162        assert!(
2163            flood_config.max_rst_stream_per_window() >= 1,
2164            "max_rst_stream_per_window must be clamped to >= 1, got {}",
2165            flood_config.max_rst_stream_per_window()
2166        );
2167        assert!(
2168            flood_config.max_glitch_count() >= 1,
2169            "max_glitch_count must be clamped to >= 1, got {}",
2170            flood_config.max_glitch_count()
2171        );
2172    }
2173
2174    /// Pin the listener-config → [`H2FloodConfig`] mapping, field by field
2175    /// (sozu-proxy/sozu#1418). `get_h2_flood_config` hands
2176    /// `H2FloodConfig::from_optional` thirteen positional arguments — ten
2177    /// `Option<u32>` and three `Option<u64>` — so a transposition inside
2178    /// either group compiles silently and quietly re-points a CVE mitigation
2179    /// at the wrong counter. `from_optional`'s own tests cover its internals;
2180    /// nothing covered the wiring into it until here. Every value is distinct,
2181    /// so any swap fails an assertion.
2182    #[test]
2183    fn get_h2_flood_config_maps_every_listener_knob_to_its_own_threshold() {
2184        let mut config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, 8080))
2185            .to_http(None)
2186            .expect("could not create listener config");
2187        config.h2_max_rst_stream_per_window = Some(101);
2188        config.h2_max_ping_per_window = Some(102);
2189        config.h2_max_settings_per_window = Some(103);
2190        config.h2_max_empty_data_per_window = Some(104);
2191        config.h2_max_window_update_stream0_per_window = Some(105);
2192        config.h2_max_continuation_frames = Some(106);
2193        config.h2_max_glitch_count = Some(107);
2194        config.h2_max_rst_stream_lifetime = Some(108);
2195        config.h2_max_rst_stream_abusive_lifetime = Some(109);
2196        config.h2_max_rst_stream_emitted_lifetime = Some(110);
2197        config.h2_max_header_list_size = Some(111);
2198        config.h2_max_header_table_size = Some(112);
2199        config.h2_max_header_fields = Some(113);
2200        config.h2_stream_refusal_percent = Some(114);
2201
2202        let listener =
2203            HttpListener::new(config, Token(0)).expect("could not build the HTTP listener");
2204        let flood = listener.get_h2_flood_config();
2205
2206        assert_eq!(flood.max_rst_stream_per_window(), 101);
2207        assert_eq!(flood.max_ping_per_window(), 102);
2208        assert_eq!(flood.max_settings_per_window(), 103);
2209        assert_eq!(flood.max_empty_data_per_window(), 104);
2210        assert_eq!(flood.max_window_update_stream0_per_window(), 105);
2211        assert_eq!(flood.max_continuation_frames(), 106);
2212        assert_eq!(flood.max_glitch_count(), 107);
2213        assert_eq!(flood.max_rst_stream_lifetime(), 108);
2214        assert_eq!(flood.max_rst_stream_abusive_lifetime(), 109);
2215        assert_eq!(flood.max_rst_stream_emitted_lifetime(), 110);
2216        assert_eq!(flood.max_header_list_size(), 111);
2217        assert_eq!(flood.max_header_table_size(), 112);
2218        assert_eq!(flood.max_header_fields(), 113);
2219        assert_eq!(flood.stream_refusal_percent(), 114);
2220    }
2221
2222    #[test]
2223    fn round_trip() {
2224        setup_test_logger!();
2225        let front_port = crate::testing::provide_port();
2226        let backend_server = Arc::new(
2227            tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2228        );
2229        let backend_port = backend_server
2230            .server_addr()
2231            .to_ip()
2232            .expect("tiny_http server should bind to IP address")
2233            .port();
2234
2235        let barrier = Arc::new(Barrier::new(2));
2236
2237        let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2238            .to_http(None)
2239            .expect("could not create listener config");
2240
2241        let (mut command, channel) =
2242            Channel::generate(1000, 10000).expect("should create a channel");
2243
2244        thread::scope(|s| {
2245            let backend_handle = backend_server.clone();
2246            let barrier_clone = barrier.to_owned();
2247            s.spawn(move || {
2248                setup_test_logger!();
2249                start_server(&backend_handle, barrier_clone);
2250            });
2251            barrier.wait();
2252
2253            s.spawn(move || {
2254                setup_test_logger!();
2255                start_http_worker(config, channel, 10, 16384)
2256                    .expect("could not start the http server");
2257            });
2258
2259            let front = RequestHttpFrontend {
2260                cluster_id: Some("cluster_1".to_owned()),
2261                address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2262                hostname: "localhost".to_owned(),
2263                path: PathRule::prefix("/".to_owned()),
2264                ..Default::default()
2265            };
2266            command
2267                .write_message(&WorkerRequest {
2268                    id: "ID_ABCD".to_owned(),
2269                    content: RequestType::AddHttpFrontend(front).into(),
2270                })
2271                .expect("could not send AddHttpFrontend");
2272            let backend = Backend {
2273                cluster_id: "cluster_1".into(),
2274                backend_id: "cluster_1-0".to_owned(),
2275                address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2276                load_balancing_parameters: Some(LoadBalancingParams::default()),
2277                sticky_id: None,
2278                backup: None,
2279            };
2280            command
2281                .write_message(&WorkerRequest {
2282                    id: "ID_EFGH".to_owned(),
2283                    content: RequestType::AddBackend(backend.to_add_backend()).into(),
2284                })
2285                .expect("could not send AddBackend");
2286
2287            println!("test received: {:?}", command.read_message());
2288            println!("test received: {:?}", command.read_message());
2289
2290            let mut client =
2291                TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2292
2293            client
2294                .set_read_timeout(Some(Duration::new(1, 0)))
2295                .expect("could not set read timeout");
2296            let request = format!(
2297                "GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\nConnection: Close\r\n\r\n"
2298            );
2299            let w = client.write(request.as_bytes());
2300            println!("http client write: {w:?}");
2301
2302            barrier.wait();
2303            let mut buffer = [0; 4096];
2304            let mut index = 0;
2305
2306            // tiny_http responds with exactly 191 bytes for a "hello world" body
2307            // (headers + body). This is deterministic for a given tiny_http version.
2308            let expected_len = 191;
2309
2310            loop {
2311                assert!(index <= expected_len);
2312                if index == expected_len {
2313                    break;
2314                }
2315
2316                let r = client.read(&mut buffer[index..]);
2317                println!("http client read: {r:?}");
2318                match r {
2319                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2320                    Ok(sz) => {
2321                        index += sz;
2322                    }
2323                }
2324            }
2325            println!(
2326                "Response: {}",
2327                str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2328            );
2329
2330            // Gracefully stop the sozu worker so the scoped thread can join
2331            command
2332                .write_message(&WorkerRequest {
2333                    id: "ID_STOP".to_owned(),
2334                    content: RequestType::SoftStop(SoftStop {}).into(),
2335                })
2336                .expect("could not send SoftStop");
2337            // Unblock the backend server so its thread can exit
2338            backend_server.unblock();
2339        });
2340    }
2341
2342    #[test]
2343    fn keep_alive() {
2344        setup_test_logger!();
2345        let front_port = crate::testing::provide_port();
2346        let backend_server = Arc::new(
2347            tiny_http::Server::http("127.0.0.1:0").expect("could not create tiny_http server"),
2348        );
2349        let backend_port = backend_server
2350            .server_addr()
2351            .to_ip()
2352            .expect("tiny_http server should bind to IP address")
2353            .port();
2354
2355        let barrier = Arc::new(Barrier::new(2));
2356
2357        let config = ListenerBuilder::new_http(SocketAddress::new_v4(127, 0, 0, 1, front_port))
2358            .to_http(None)
2359            .expect("could not create listener config");
2360
2361        let (mut command, channel) =
2362            Channel::generate(1000, 10000).expect("should create a channel");
2363
2364        thread::scope(|s| {
2365            let backend_handle = backend_server.clone();
2366            let barrier_clone = barrier.to_owned();
2367            s.spawn(move || {
2368                setup_test_logger!();
2369                start_server(&backend_handle, barrier_clone);
2370            });
2371            barrier.wait();
2372
2373            s.spawn(move || {
2374                setup_test_logger!();
2375                start_http_worker(config, channel, 10, 16384)
2376                    .expect("could not start the http server");
2377            });
2378
2379            let front = RequestHttpFrontend {
2380                address: SocketAddress::new_v4(127, 0, 0, 1, front_port),
2381                hostname: "localhost".to_owned(),
2382                path: PathRule::prefix("/".to_owned()),
2383                cluster_id: Some("cluster_1".to_owned()),
2384                ..Default::default()
2385            };
2386            command
2387                .write_message(&WorkerRequest {
2388                    id: "ID_ABCD".to_owned(),
2389                    content: RequestType::AddHttpFrontend(front).into(),
2390                })
2391                .expect("could not send AddHttpFrontend");
2392            let backend = Backend {
2393                address: SocketAddress::new_v4(127, 0, 0, 1, backend_port).into(),
2394                backend_id: "cluster_1-0".to_owned(),
2395                backup: None,
2396                cluster_id: "cluster_1".into(),
2397                load_balancing_parameters: Some(LoadBalancingParams::default()),
2398                sticky_id: None,
2399            };
2400            command
2401                .write_message(&WorkerRequest {
2402                    id: "ID_EFGH".to_owned(),
2403                    content: RequestType::AddBackend(backend.to_add_backend()).into(),
2404                })
2405                .expect("could not send AddBackend");
2406
2407            println!("test received: {:?}", command.read_message());
2408            println!("test received: {:?}", command.read_message());
2409
2410            let mut client =
2411                TcpStream::connect(("127.0.0.1", front_port)).expect("could not connect to sozu");
2412            client
2413                .set_read_timeout(Some(Duration::new(5, 0)))
2414                .expect("could not set read timeout");
2415
2416            // tiny_http responds with exactly 191 bytes for a "hello world" body
2417            // (headers + body). This is deterministic for a given tiny_http version.
2418            let expected_len = 191;
2419
2420            let request = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2421            let w = client
2422                .write(request.as_bytes())
2423                .expect("could not write first request");
2424            println!("http client write: {w:?}");
2425            barrier.wait();
2426
2427            let mut buffer = [0; 4096];
2428            let mut index = 0;
2429
2430            loop {
2431                assert!(index <= expected_len);
2432                if index == expected_len {
2433                    break;
2434                }
2435
2436                let r = client.read(&mut buffer[index..]);
2437                println!("http client read: {r:?}");
2438                match r {
2439                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2440                    Ok(sz) => {
2441                        index += sz;
2442                    }
2443                }
2444            }
2445
2446            println!(
2447                "Response: {}",
2448                str::from_utf8(&buffer[..index]).expect("could not make string from buffer")
2449            );
2450
2451            println!("first request ended, will send second one");
2452            let request2 = format!("GET / HTTP/1.1\r\nHost: localhost:{front_port}\r\n\r\n");
2453            let w2 = client.write(request2.as_bytes());
2454            println!("http client write: {w2:?}");
2455            barrier.wait();
2456
2457            let mut buffer2 = [0; 4096];
2458            let mut index = 0;
2459
2460            loop {
2461                assert!(index <= expected_len);
2462                if index == expected_len {
2463                    break;
2464                }
2465
2466                let r2 = client.read(&mut buffer2[index..]);
2467                println!("http client read: {r2:?}");
2468                match r2 {
2469                    Err(e) => panic!("client request should not fail. Error: {e:?}"),
2470                    Ok(sz) => {
2471                        index += sz;
2472                    }
2473                }
2474            }
2475            println!(
2476                "Response: {}",
2477                str::from_utf8(&buffer2[..index]).expect("could not make string from buffer")
2478            );
2479
2480            // Gracefully stop the sozu worker so the scoped thread can join
2481            command
2482                .write_message(&WorkerRequest {
2483                    id: "ID_STOP".to_owned(),
2484                    content: RequestType::SoftStop(SoftStop {}).into(),
2485                })
2486                .expect("could not send SoftStop");
2487            // Unblock the backend server so its thread can exit
2488            backend_server.unblock();
2489        });
2490    }
2491
2492    use self::tiny_http::Response;
2493
2494    fn start_server(server: &tiny_http::Server, barrier: Arc<Barrier>) {
2495        let addr = server.server_addr();
2496        info!("starting web server on {:?}", addr);
2497        barrier.wait();
2498
2499        for request in server.incoming_requests() {
2500            info!(
2501                "backend web server got request -> method: {:?}, url: {:?}, headers: {:?}",
2502                request.method(),
2503                request.url(),
2504                request.headers()
2505            );
2506
2507            let response = Response::from_string("hello world");
2508            request
2509                .respond(response)
2510                .expect("could not respond to request");
2511            info!("backend web server sent response");
2512            barrier.wait();
2513            info!("server session stopped");
2514        }
2515
2516        println!("server on {addr:?} closed");
2517    }
2518
2519    #[test]
2520    fn frontend_from_request_test() {
2521        let cluster_id1 = ClusterId::from("cluster_1");
2522        let cluster_id2 = ClusterId::from("cluster_2");
2523        let cluster_id3 = ClusterId::from("cluster_3");
2524        let uri1 = "/".to_owned();
2525        let uri2 = "/yolo".to_owned();
2526        let uri3 = "/yolo/swag".to_owned();
2527
2528        let mut fronts = Router::new();
2529        fronts
2530            .add_http_front(&HttpFrontend {
2531                address: "0.0.0.0:80".parse().unwrap(),
2532                hostname: "lolcatho.st".to_owned(),
2533                method: None,
2534                path: PathRule::prefix(uri1),
2535                position: RulePosition::Tree,
2536                cluster_id: Some(cluster_id1),
2537                tags: None,
2538                redirect: None,
2539                redirect_scheme: None,
2540                redirect_template: None,
2541                rewrite_host: None,
2542                rewrite_path: None,
2543                rewrite_port: None,
2544                required_auth: None,
2545                headers: Vec::new(),
2546                hsts: None,
2547            })
2548            .expect("Could not add http frontend");
2549        fronts
2550            .add_http_front(&HttpFrontend {
2551                address: "0.0.0.0:80".parse().unwrap(),
2552                hostname: "lolcatho.st".to_owned(),
2553                method: None,
2554                path: PathRule::prefix(uri2),
2555                position: RulePosition::Tree,
2556                cluster_id: Some(cluster_id2),
2557                tags: None,
2558                redirect: None,
2559                redirect_scheme: None,
2560                redirect_template: None,
2561                rewrite_host: None,
2562                rewrite_path: None,
2563                rewrite_port: None,
2564                required_auth: None,
2565                headers: Vec::new(),
2566                hsts: None,
2567            })
2568            .expect("Could not add http frontend");
2569        fronts
2570            .add_http_front(&HttpFrontend {
2571                address: "0.0.0.0:80".parse().unwrap(),
2572                hostname: "lolcatho.st".to_owned(),
2573                method: None,
2574                path: PathRule::prefix(uri3),
2575                position: RulePosition::Tree,
2576                cluster_id: Some(cluster_id3),
2577                tags: None,
2578                redirect: None,
2579                redirect_scheme: None,
2580                redirect_template: None,
2581                rewrite_host: None,
2582                rewrite_path: None,
2583                rewrite_port: None,
2584                required_auth: None,
2585                headers: Vec::new(),
2586                hsts: None,
2587            })
2588            .expect("Could not add http frontend");
2589        fronts
2590            .add_http_front(&HttpFrontend {
2591                address: "0.0.0.0:80".parse().unwrap(),
2592                hostname: "other.domain".to_owned(),
2593                method: None,
2594                path: PathRule::prefix("/test".to_owned()),
2595                position: RulePosition::Tree,
2596                cluster_id: Some("cluster_1".into()),
2597                tags: None,
2598                redirect: None,
2599                redirect_scheme: None,
2600                redirect_template: None,
2601                rewrite_host: None,
2602                rewrite_path: None,
2603                rewrite_port: None,
2604                required_auth: None,
2605                headers: Vec::new(),
2606                hsts: None,
2607            })
2608            .expect("Could not add http frontend");
2609
2610        let address = SocketAddress::new_v4(127, 0, 0, 1, 1030);
2611
2612        let default_config = ListenerBuilder::new_http(address)
2613            .to_http(None)
2614            .expect("Could not create default HTTP listener config");
2615
2616        let listener = HttpListener {
2617            parked_listener: None,
2618            nodelay_backlog: false,
2619            listener: None,
2620            address: address.into(),
2621            fronts,
2622            answers: Rc::new(RefCell::new(HttpAnswers::new(&BTreeMap::new()).unwrap())),
2623            config: default_config,
2624            token: Token(0),
2625            active: true,
2626            tags: BTreeMap::new(),
2627        };
2628
2629        let frontend1 = listener.frontend_from_request("lolcatho.st", "/", &Method::Get);
2630        let frontend2 = listener.frontend_from_request("lolcatho.st", "/test", &Method::Get);
2631        let frontend3 = listener.frontend_from_request("lolcatho.st", "/yolo/test", &Method::Get);
2632        let frontend4 = listener.frontend_from_request("lolcatho.st", "/yolo/swag", &Method::Get);
2633        let frontend5 = listener.frontend_from_request("domain", "/", &Method::Get);
2634        assert_eq!(
2635            frontend1
2636                .expect("should find frontend")
2637                .cluster_id
2638                .as_deref(),
2639            Some("cluster_1")
2640        );
2641        assert_eq!(
2642            frontend2
2643                .expect("should find frontend")
2644                .cluster_id
2645                .as_deref(),
2646            Some("cluster_1")
2647        );
2648        assert_eq!(
2649            frontend3
2650                .expect("should find frontend")
2651                .cluster_id
2652                .as_deref(),
2653            Some("cluster_2")
2654        );
2655        assert_eq!(
2656            frontend4
2657                .expect("should find frontend")
2658                .cluster_id
2659                .as_deref(),
2660            Some("cluster_3")
2661        );
2662        assert!(frontend5.is_err());
2663    }
2664
2665    /// sozu#322: the worker refuses a listener whose `forwarded_headers`
2666    /// wire value names no `ForwardedHeaders` variant — a raw protobuf
2667    /// `AddHttpListener` must not silently run the default — and honours a
2668    /// known one.
2669    #[test]
2670    fn a_listener_with_an_unknown_forwarded_headers_value_is_refused() {
2671        let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2672        let build = |forwarded_headers: Option<i32>| {
2673            let mut cfg = ListenerBuilder::new_http(address)
2674                .to_http(None)
2675                .expect("default HTTP listener config");
2676            cfg.forwarded_headers = forwarded_headers;
2677            HttpListener::new(cfg, Token(0))
2678        };
2679        assert!(build(Some(42)).is_err(), "an unknown mode must be refused");
2680        let listener = build(Some(ForwardedHeaders::Rfc7239 as i32)).expect("a known mode");
2681        assert_eq!(listener.get_forwarded_headers(), ForwardedHeaders::Rfc7239);
2682    }
2683
2684    #[test]
2685    fn h2_stream_idle_timeout_inherits_back_timeout() {
2686        let address = SocketAddress::new_v4(127, 0, 0, 1, 1040);
2687        let build = |back_timeout: u32, explicit: Option<u32>| -> HttpListener {
2688            let mut cfg = ListenerBuilder::new_http(address)
2689                .to_http(None)
2690                .expect("default HTTP listener config");
2691            cfg.back_timeout = back_timeout;
2692            cfg.h2_stream_idle_timeout_seconds = explicit;
2693            HttpListener::new(cfg, Token(0)).expect("build listener")
2694        };
2695
2696        // Knob unset: inherit back_timeout when it exceeds the 30s floor.
2697        assert_eq!(
2698            build(180, None).get_h2_stream_idle_timeout(),
2699            Duration::from_secs(180)
2700        );
2701
2702        // Knob unset, back_timeout below floor: stay at 30s to preserve the
2703        // slow-multiplex Slowloris mitigation.
2704        assert_eq!(
2705            build(5, None).get_h2_stream_idle_timeout(),
2706            Duration::from_secs(30)
2707        );
2708
2709        // Explicit values win in both directions — including below the floor,
2710        // so operators under attack can tighten the deadline.
2711        assert_eq!(
2712            build(180, Some(10)).get_h2_stream_idle_timeout(),
2713            Duration::from_secs(10)
2714        );
2715        assert_eq!(
2716            build(5, Some(600)).get_h2_stream_idle_timeout(),
2717            Duration::from_secs(600)
2718        );
2719
2720        // `Some(0)` is clamped to 1s to keep the deadline non-degenerate.
2721        assert_eq!(
2722            build(180, Some(0)).get_h2_stream_idle_timeout(),
2723            Duration::from_secs(1)
2724        );
2725    }
2726
2727    /// A listener answer reload PUBLISHES a new registry; it does not rewrite
2728    /// the one in-flight requests are holding.
2729    ///
2730    /// `mux::Context::create_stream` captures `HttpAnswers` by handle when a
2731    /// request arrives, and every `set_default_answer` site in the mux renders
2732    /// from that captured handle (`lib/src/protocol/mux/LIFECYCLE.md` §2.5).
2733    /// The capture is only a snapshot if a reload installs a *new* registry —
2734    /// rewriting the old one through its `RefCell` would reach every request
2735    /// already in flight and hand it a page its client never asked for.
2736    ///
2737    /// The second assertion guards the trap in the obvious implementation of
2738    /// the first: migrating the per-cluster overrides with `mem::take` empties
2739    /// the registry the in-flight requests still hold, stripping their cluster
2740    /// templates mid-response. They must be copied.
2741    ///
2742    /// To SEE THIS RED: in `HttpListener::update_config`, put the two publish
2743    /// lines back to their pre-fix form —
2744    /// `let preserved = std::mem::take(&mut self.answers.borrow_mut().cluster_answers);`
2745    /// `new_answers.cluster_answers = preserved;`
2746    /// `*self.answers.borrow_mut() = new_answers;`
2747    #[test]
2748    fn a_listener_answers_reload_publishes_a_new_registry() {
2749        let address = SocketAddress::new_v4(127, 0, 0, 1, 1041);
2750        let config = ListenerBuilder::new_http(address)
2751            .to_http(None)
2752            .expect("default HTTP listener config");
2753        let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2754
2755        // A cluster override, as `HttpProxy::add_cluster` would install it.
2756        listener
2757            .answers
2758            .borrow_mut()
2759            .add_cluster_answers(
2760                "cluster_1",
2761                &BTreeMap::from([(
2762                    "503".to_owned(),
2763                    "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n".to_owned(),
2764                )]),
2765            )
2766            .expect("the cluster override must compile");
2767
2768        // What a request arriving now would capture.
2769        let captured = listener.get_answers().clone();
2770
2771        listener
2772            .update_config(&UpdateHttpListenerConfig {
2773                address,
2774                answers: BTreeMap::from([(
2775                    "404".to_owned(),
2776                    "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n".to_owned(),
2777                )]),
2778                ..Default::default()
2779            })
2780            .expect("the answers patch must apply");
2781
2782        assert!(
2783            !Rc::ptr_eq(&captured, listener.get_answers()),
2784            "a reload must publish a NEW answer registry: rewriting the captured \
2785             one reaches every request already in flight"
2786        );
2787        assert!(
2788            captured.borrow().cluster_answers.contains_key("cluster_1"),
2789            "the registry captured before the reload must keep its cluster \
2790             overrides: a request in flight would otherwise lose its custom \
2791             pages mid-response"
2792        );
2793        assert!(
2794            listener
2795                .get_answers()
2796                .borrow()
2797                .cluster_answers
2798                .contains_key("cluster_1"),
2799            "the published registry must carry the cluster overrides forward"
2800        );
2801    }
2802
2803    /// A patch the worker refuses leaves the listener exactly as it was
2804    /// (sozu-proxy/sozu#1703): its configuration and its published answer
2805    /// registry. Each patch carries fields that validate, then an answer
2806    /// template that does not parse — the failure the old code only hit
2807    /// after writing every other field into `self.config`, so the listener
2808    /// kept a patch it had refused, and a config its templates no longer
2809    /// matched.
2810    ///
2811    /// To SEE THIS RED: in `HttpListener::update_config`, drop the
2812    /// `validate_patch_templates` call from the up-front checks and write the
2813    /// fields into `self.config` instead of the candidate copy.
2814    #[test]
2815    fn a_rejected_patch_leaves_the_listener_unchanged() {
2816        let address = SocketAddress::new_v4(127, 0, 0, 1, 1042);
2817        let config = ListenerBuilder::new_http(address)
2818            .to_http(None)
2819            .expect("default HTTP listener config");
2820        let mut listener = HttpListener::new(config, Token(0)).expect("build listener");
2821
2822        let valid_prefix = UpdateHttpListenerConfig {
2823            address,
2824            front_timeout: Some(99),
2825            sozu_id_header: Some("X-Edge-Id".to_owned()),
2826            forwarded_headers: Some(ForwardedHeaders::Rfc7239 as i32),
2827            h2_max_rst_stream_per_window: Some(7),
2828            ..Default::default()
2829        };
2830        let rejected = [
2831            UpdateHttpListenerConfig {
2832                answers: BTreeMap::from([(
2833                    "404".to_owned(),
2834                    "not a valid http response".to_owned(),
2835                )]),
2836                ..valid_prefix.clone()
2837            },
2838            UpdateHttpListenerConfig {
2839                http_answers: Some(CustomHttpAnswers {
2840                    answer_503: Some("not a valid http response".to_owned()),
2841                    ..Default::default()
2842                }),
2843                ..valid_prefix.clone()
2844            },
2845        ];
2846        for patch in rejected {
2847            let config_before = listener.config.clone();
2848            let answers_before = listener.get_answers().clone();
2849            let error = listener
2850                .update_config(&patch)
2851                .expect_err("an unparseable template must be refused");
2852            assert!(
2853                matches!(error, ListenerError::TemplateParse(..)),
2854                "expected TemplateParse, got {error:?}"
2855            );
2856            assert_eq!(
2857                listener.config, config_before,
2858                "a refused patch must leave the listener configuration unchanged"
2859            );
2860            assert!(
2861                Rc::ptr_eq(&answers_before, listener.get_answers()),
2862                "a refused patch must not publish a new answer registry"
2863            );
2864        }
2865
2866        listener
2867            .update_config(&valid_prefix)
2868            .expect("the valid prefix alone must apply");
2869        assert_eq!(listener.config.front_timeout, 99);
2870        assert_eq!(listener.config.sozu_id_header.as_deref(), Some("X-Edge-Id"));
2871    }
2872
2873    /// A closed session issues no `EPOLL_CTL_DEL` for its front socket: the
2874    /// socket stays in the epoll set until the session drops, and its last
2875    /// close is what removes it. The second assertion is the half that makes
2876    /// skipping the deregister safe — nothing else holds the descriptor, so
2877    /// dropping the session really does take it out of the epoll set before
2878    /// its slab token can be handed to a new connection.
2879    ///
2880    /// To SEE THIS RED: in `HttpSession::close`, put back
2881    /// `let _ = proxy.registry.deregister(&mut mio::unix::SourceFd(&front_socket.as_raw_fd()));`
2882    /// before `proxy.remove_session`. The first assertion then fails.
2883    #[cfg(target_os = "linux")]
2884    #[test]
2885    fn closed_sessions_leave_their_sockets_to_close() {
2886        use std::os::fd::AsRawFd;
2887
2888        use crate::protocol::mux::test_support::epoll_watches;
2889
2890        let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
2891        let parts = crate::testing::prebuild_server(4, 16_384, false)
2892            .expect("test server parts must build");
2893        let registry = parts
2894            .registry
2895            .try_clone()
2896            .expect("the test registry must clone");
2897        let sessions = parts.sessions.clone();
2898        let listener_token = {
2899            let mut sessions = sessions.borrow_mut();
2900            let entry = sessions.slab.vacant_entry();
2901            let key = entry.key();
2902            entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
2903                protocol: Protocol::HTTPListen,
2904            })));
2905            Token(key)
2906        };
2907        let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
2908        proxy
2909            .add_listener(
2910                ListenerBuilder::new_http(address)
2911                    .to_http(None)
2912                    .expect("test listener config must build"),
2913                listener_token,
2914            )
2915            .expect("test listener must register");
2916        let proxy = Rc::new(RefCell::new(proxy));
2917
2918        let peer_listener =
2919            std::net::TcpListener::bind("127.0.0.1:0").expect("bind the test peer listener");
2920        let _client = TcpStream::connect(
2921            peer_listener
2922                .local_addr()
2923                .expect("the test peer listener has an address"),
2924        )
2925        .expect("connect the test client");
2926        let (accepted, peer) = peer_listener.accept().expect("accept the test client");
2927        accepted
2928            .set_nonblocking(true)
2929            .expect("the front socket must go non-blocking");
2930        let front = mio::net::TcpStream::from_std(accepted);
2931        let front_fd = front.as_raw_fd();
2932
2933        let session_token = sessions.borrow().slab.vacant_key();
2934        proxy
2935            .borrow_mut()
2936            .create_session(
2937                front,
2938                peer,
2939                ListenToken(listener_token.0),
2940                Duration::ZERO,
2941                proxy.clone(),
2942            )
2943            .expect("the session must be created");
2944        assert!(
2945            sessions.borrow().slab.contains(session_token),
2946            "the session takes the slot that was vacant"
2947        );
2948        assert!(
2949            epoll_watches(&registry, front_fd),
2950            "precondition: the front socket is in the epoll set"
2951        );
2952
2953        // What `Server::shut_down_sessions_by_frontend_tokens` does.
2954        let session = sessions.borrow_mut().slab.remove(session_token);
2955        session.borrow_mut().close();
2956        assert!(
2957            epoll_watches(&registry, front_fd),
2958            "HttpSession::close must not deregister a front socket it is about \
2959             to close: the close removes it, the EPOLL_CTL_DEL is a wasted syscall"
2960        );
2961
2962        drop(session);
2963        assert!(
2964            !epoll_watches(&registry, front_fd),
2965            "dropping the session closes the front socket's only descriptor, \
2966             which must take it out of the epoll set"
2967        );
2968        drop(parts.event_loop);
2969    }
2970
2971    /// A connection accepted once the listener is live carries `TCP_NODELAY`
2972    /// without a per-connection `setsockopt(2)`, and is handed over with the
2973    /// peer address `accept(2)` returned (sozu-proxy/sozu#1586).
2974    ///
2975    /// The kernel copies `TCP_NODELAY` from a listening socket to each socket
2976    /// it accepts, at the moment the handshake completes. The listener sets it
2977    /// once in `activate`. The first accept drain still sets it per connection
2978    /// for whatever was already in the backlog, so this test drains that pass
2979    /// first, until `WouldBlock`, and only then opens the connection whose flag
2980    /// it reads: that one can only have it from the listener.
2981    ///
2982    /// To SEE THIS RED: in `HttpListener::activate`, delete the
2983    /// `SockRef::from(&listener).set_tcp_nodelay(true)` block. The listener then
2984    /// has no flag to pass on and the accepted socket reads `false`.
2985    #[test]
2986    fn a_steady_state_accepted_socket_inherits_nodelay_and_its_peer_address() {
2987        let address = SocketAddress::new_v4(127, 0, 0, 1, crate::testing::provide_port());
2988        let parts = crate::testing::prebuild_server(4, 16_384, false)
2989            .expect("test server parts must build");
2990        let listener_token = {
2991            let mut sessions = parts.sessions.borrow_mut();
2992            let entry = sessions.slab.vacant_entry();
2993            let key = entry.key();
2994            entry.insert(Rc::new(RefCell::new(crate::server::ListenSession {
2995                protocol: Protocol::HTTPListen,
2996            })));
2997            Token(key)
2998        };
2999        let mut proxy = HttpProxy::new(parts.registry, parts.sessions, parts.pool, parts.backends);
3000        proxy
3001            .add_listener(
3002                ListenerBuilder::new_http(address)
3003                    .to_http(None)
3004                    .expect("test listener config must build"),
3005                listener_token,
3006            )
3007            .expect("test listener must register");
3008        proxy
3009            .activate_listener(&address.into(), None)
3010            .expect("test listener must activate");
3011
3012        // The first drain after the activation, with an empty backlog.
3013        assert!(
3014            matches!(
3015                proxy.accept(ListenToken(listener_token.0)),
3016                Err(AcceptError::WouldBlock)
3017            ),
3018            "precondition: nothing is queued on a freshly bound listener"
3019        );
3020
3021        let client =
3022            TcpStream::connect(SocketAddr::from(address)).expect("connect the test client");
3023        let (accepted, peer) = proxy
3024            .accept(ListenToken(listener_token.0))
3025            .expect("the completed connection must be accepted");
3026
3027        assert_eq!(
3028            socket2::SockRef::from(&accepted).tcp_nodelay().ok(),
3029            Some(true),
3030            "a socket accepted in steady state must inherit TCP_NODELAY from its listener"
3031        );
3032        assert_eq!(
3033            peer,
3034            client.local_addr().expect("the test client has an address"),
3035            "the peer address handed over must be the client's"
3036        );
3037        drop(parts.event_loop);
3038    }
3039}
3040
3041#[cfg(test)]
3042mod listener_sibling_tests {
3043    use sozu_command::{
3044        config::ListenerBuilder,
3045        proto::command::{PathRule, RequestHttpFrontend, RulePosition, SocketAddress},
3046    };
3047
3048    use super::*;
3049    use crate::testing::{ServerParts, prebuild_server, provide_port};
3050
3051    fn proxy() -> HttpProxy {
3052        let ServerParts {
3053            registry,
3054            sessions,
3055            pool,
3056            backends,
3057            ..
3058        } = prebuild_server(16, 16384, false).expect("could not prebuild a test server");
3059        HttpProxy::new(registry, sessions, pool, backends)
3060    }
3061
3062    fn add(proxy: &mut HttpProxy, address: SocketAddress, interface: Option<&str>, token: usize) {
3063        let config = ListenerBuilder::new_http(address)
3064            .with_interface(interface)
3065            .to_http(None)
3066            .expect("could not build the listener config");
3067        proxy
3068            .add_listener(config, Token(token))
3069            .expect("could not add the listener");
3070    }
3071
3072    fn front(address: SocketAddress) -> RequestHttpFrontend {
3073        RequestHttpFrontend {
3074            cluster_id: Some("cluster-a".to_owned()),
3075            address,
3076            hostname: "example.com".to_owned(),
3077            path: PathRule::prefix("/".to_owned()),
3078            ..Default::default()
3079        }
3080    }
3081
3082    fn routes(proxy: &HttpProxy, token: usize) -> bool {
3083        proxy.listeners[&Token(token)]
3084            .borrow()
3085            .fronts
3086            .lookup("example.com", "/", &Method::Get)
3087            .is_ok()
3088    }
3089
3090    /// A listener added on an address that already serves frontends — `sozu
3091    /// listener http add --interface`, or a reload adding one — takes them
3092    /// over from the listener already there: frontends name their listener by
3093    /// address, so it must route what its sibling routes from its first
3094    /// connection (sozu-proxy/sozu#719).
3095    #[test]
3096    fn a_listener_added_next_to_a_sibling_takes_over_its_frontends() {
3097        let mut proxy = proxy();
3098        let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3099        add(&mut proxy, address, None, 0);
3100        proxy
3101            .add_http_frontend(front(address))
3102            .expect("add the frontend");
3103        add(&mut proxy, address, Some("lo"), 1);
3104
3105        assert!(routes(&proxy, 0));
3106        assert!(
3107            routes(&proxy, 1),
3108            "the listener added after the frontend must route it too"
3109        );
3110
3111        proxy
3112            .remove_http_frontend(front(address))
3113            .expect("remove the frontend");
3114        assert!(!routes(&proxy, 0));
3115        assert!(!routes(&proxy, 1));
3116    }
3117
3118    /// Removing a frontend reaches every listener on the address even when one
3119    /// of them fails, and reports that failure: a sibling must never keep a
3120    /// route the others dropped.
3121    #[test]
3122    fn removing_a_frontend_reaches_every_listener_and_reports_a_failure() {
3123        let mut proxy = proxy();
3124        let address = SocketAddress::new_v4(0, 0, 0, 0, provide_port());
3125        add(&mut proxy, address, None, 0);
3126        add(&mut proxy, address, Some("lo"), 1);
3127        // Only the second listener holds the route, so removing it from the
3128        // first one fails (a `Pre` rule that is not there is an error).
3129        let pre = RequestHttpFrontend {
3130            position: RulePosition::Pre.into(),
3131            ..front(address)
3132        };
3133        let frontend = pre
3134            .clone()
3135            .to_frontend()
3136            .expect("the test frontend must convert");
3137        proxy.listeners[&Token(1)]
3138            .borrow_mut()
3139            .add_http_front(frontend)
3140            .expect("add the route to one listener");
3141
3142        assert!(
3143            proxy.remove_http_frontend(pre).is_err(),
3144            "the listener that lacked the route must report it"
3145        );
3146        assert!(
3147            !routes(&proxy, 1),
3148            "the failure on one listener must not spare its sibling's route"
3149        );
3150    }
3151}