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