Skip to main content

churust_core/
engine.rs

1//! The hyper-based HTTP/1.1 serving engine that drives an [`App`] over a real
2//! socket.
3//!
4//! Most applications never call into this module directly — [`App::start`] and
5//! [`App::start_with_shutdown`] bind a listener and delegate to [`serve`]. It is
6//! public so advanced callers can drive serving with a custom address and
7//! shutdown signal.
8
9use crate::app::App;
10use crate::body::Body;
11#[cfg(feature = "tls")]
12use crate::tls::acceptor_from_pem;
13use bytes::Bytes;
14use futures_util::StreamExt;
15use http_body_util::{
16    combinators::UnsyncBoxBody, BodyDataStream, BodyExt, Full, Limited, StreamBody,
17};
18use hyper::body::Frame;
19use hyper::body::Incoming;
20use hyper::service::service_fn;
21use hyper::{Request as HyperRequest, Response as HyperResponse, StatusCode};
22use hyper_util::rt::TokioIo;
23use std::convert::Infallible;
24use std::future::Future;
25use std::net::SocketAddr;
26
27/// Convert a [`Body`] into the boxed hyper body the engine writes to the wire:
28/// `Full` for a buffered payload, `StreamBody` for a lazy stream.
29///
30/// The boxed body is the `!Sync` [`UnsyncBoxBody`] rather than the plan's
31/// `BoxBody`: `Body::Stream` wraps a `Pin<Box<dyn Stream + Send>>` (intentionally
32/// `Send` but not `Sync`), and the resolved `http-body-util` 0.1.3 `BodyExt::boxed`
33/// requires `Self: Send + Sync`. `boxed_unsync` only requires `Send`, and hyper
34/// accepts any `http_body::Body` response body, so behavior is unchanged.
35fn into_boxed_body(body: Body) -> UnsyncBoxBody<Bytes, std::io::Error> {
36    match body {
37        Body::Bytes(bytes) => Full::new(bytes)
38            .map_err(|never| match never {})
39            .boxed_unsync(),
40        Body::Stream(stream) => {
41            let frames = stream.map(|chunk| {
42                chunk
43                    .map(Frame::data)
44                    .map_err(|e| std::io::Error::other(e.to_string()))
45            });
46            StreamBody::new(frames).boxed_unsync()
47        }
48    }
49}
50
51/// Serve `app` on `addr` until `shutdown` resolves (graceful drain).
52///
53/// Uses HTTP/1.1 (`hyper::server::conn::http1::Builder`).  The plan
54/// referenced `auto::Builder` but that type's `serve_connection` returns a
55/// `Connection<'_, …>` that borrows the builder, which cannot be moved into a
56/// `tokio::spawn` closure.  Using `http1::Builder` directly returns an owned
57/// `Connection<I, S>` that is `'static`, compiles correctly, and still
58/// satisfies the HTTP/1.1-only requirement of Plan 1.
59///
60/// Prefer [`App::start`] / [`App::start_with_shutdown`] unless you need to
61/// control the bind address and shutdown future yourself.
62///
63/// [`App::start`]: crate::App::start
64/// [`App::start_with_shutdown`]: crate::App::start_with_shutdown
65///
66/// ```no_run
67/// use churust_core::{Churust, Call, engine};
68/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
69/// let app = Churust::server()
70///     .routing(|r| { r.get("/", |_c: Call| async { "hi" }); })
71///     .build();
72/// let addr = "127.0.0.1:8080".parse().unwrap();
73/// // Serve until Ctrl-C.
74/// engine::serve(app, addr, async { let _ = tokio::signal::ctrl_c().await; }).await?;
75/// # Ok::<(), std::io::Error>(())
76/// # });
77/// ```
78pub async fn serve<F>(app: App, addr: SocketAddr, shutdown: F) -> std::io::Result<()>
79where
80    F: Future<Output = ()> + Send + 'static,
81{
82    let listener = bind_tcp(addr, app.config().backlog)?;
83    let limits = std::sync::Arc::new(AcceptLimits::from(app.config()));
84    serve_listener(app, listener, limits, shutdown).await
85}
86
87/// Serve `app` on an already-bound listener until `shutdown` resolves.
88///
89/// Use this when the port must be known before the server starts — a test that
90/// needs the address, a socket passed in by a supervisor, or anything binding
91/// under its own policy.
92///
93/// It also closes a race that [`serve`] cannot: discovering a free port by
94/// binding, dropping, and letting `serve` bind again leaves a window in which
95/// something else can take it. Handing over the listener removes the window.
96///
97/// ```no_run
98/// use churust_core::{Churust, Call, engine};
99/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
100/// let app = Churust::server()
101///     .routing(|r| { r.get("/", |_c: Call| async { "hi" }); })
102///     .build();
103/// let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
104/// let addr = listener.local_addr()?;
105/// println!("listening on {addr}");
106/// engine::serve_on(app, listener, async { let _ = tokio::signal::ctrl_c().await; }).await?;
107/// # Ok::<(), std::io::Error>(())
108/// # });
109/// ```
110pub async fn serve_on<F>(
111    app: App,
112    listener: tokio::net::TcpListener,
113    shutdown: F,
114) -> std::io::Result<()>
115where
116    F: Future<Output = ()> + Send + 'static,
117{
118    let limits = std::sync::Arc::new(AcceptLimits::from(app.config()));
119    serve_listener(app, listener, limits, shutdown).await
120}
121
122/// Bind one address, with the backlog and socket options Churust wants.
123///
124/// Separate from serving so several addresses can be bound *before* any accept
125/// loop starts — see [`serve_many`].
126fn bind_tcp(addr: SocketAddr, backlog: u32) -> std::io::Result<tokio::net::TcpListener> {
127    // Bind through a socket so the backlog is ours to choose. tokio's
128    // TcpListener::bind hard-codes 1024, which is fine until it is not.
129    let socket = match addr {
130        std::net::SocketAddr::V4(_) => tokio::net::TcpSocket::new_v4()?,
131        std::net::SocketAddr::V6(_) => tokio::net::TcpSocket::new_v6()?,
132    };
133    // Without this a restart fails while the previous socket sits in
134    // TIME_WAIT — the single most common cause of "address already in use".
135    socket.set_reuseaddr(true)?;
136    socket.bind(addr)?;
137    socket.listen(backlog)
138}
139
140/// Serve on an already-bound listener until `shutdown` resolves.
141async fn serve_listener<F>(
142    app: App,
143    listener: tokio::net::TcpListener,
144    limits: std::sync::Arc<AcceptLimits>,
145    shutdown: F,
146) -> std::io::Result<()>
147where
148    F: Future<Output = ()> + Send + 'static,
149{
150    let conn_cfg = ConnSettings::from(app.config());
151    let shutdown_timeout_ms = app.config().shutdown_timeout_ms;
152    let drain = Drain::new();
153    let mut backoff = AcceptBackoff::default();
154    tokio::pin!(shutdown);
155
156    #[cfg(feature = "tls")]
157    let tls_acceptor = match &app.config().tls {
158        Some(t) => Some(acceptor_from_pem(&t.cert, &t.key)?),
159        None => None,
160    };
161
162    loop {
163        // Waiting for a slot and accepting are one cancellable step, raced
164        // against shutdown.
165        //
166        // Both halves can park indefinitely: `accept` when no client comes, and
167        // `connection_slot` when every permit is held. Awaiting either *outside*
168        // the `select!` means a saturated server never reaches the shutdown arm
169        // — SIGTERM would hang until the orchestrator escalates to SIGKILL,
170        // which is a worse failure than the unbounded drain this budget was
171        // added alongside. Both are cancel-safe: a cancelled slot returns its
172        // permit, and a cancelled `accept` has not taken a connection.
173        let acquire = async {
174            // Take the slot before accepting, so excess load waits in the
175            // kernel backlog — the client's problem — rather than as memory and
176            // descriptors in this process.
177            let slot = limits.connection_slot().await;
178            (slot, listener.accept().await)
179        };
180
181        tokio::select! {
182            // Shutdown wins a tie: with a signal pending there is no reason to
183            // begin serving one more connection.
184            biased;
185            _ = &mut shutdown => break,
186            (slot, accepted) = acquire => {
187                let (stream, peer) = match accepted {
188                    Ok(s) => {
189                        backoff.reset();
190                        s
191                    }
192                    Err(e) => {
193                        // A persistent error — EMFILE when the descriptor table
194                        // is full, which is exactly the state a flood drives
195                        // toward — would otherwise spin this loop at full tilt
196                        // and starve the very tasks that would free a slot.
197                        let pause = backoff.next_pause();
198                        tracing::warn!(error = %e, pause_ms = pause.as_millis() as u64, "accept failed");
199                        tokio::time::sleep(pause).await;
200                        continue;
201                    }
202                };
203                #[cfg(feature = "tls")]
204                {
205                    if let Some(acceptor) = tls_acceptor.clone() {
206                        let app = app.clone();
207                        let handshakes = limits.handshakes.clone();
208                        let handshake_timeout = limits.tls_handshake_timeout;
209                        // The token is taken *before* spawning, so a connection
210                        // that is still shaking hands when shutdown fires is
211                        // already counted — otherwise the drain could finish
212                        // while a handshake was in flight. The handshake itself
213                        // runs on the spawned task so a slow one does not block
214                        // the accept loop.
215                        let token = drain.token();
216                        let conn_builder_fut = async move {
217                            // Queueing for the handshake budget and performing
218                            // the handshake are one timed step.
219                            //
220                            // Handshakes get their own, much smaller budget: the
221                            // work is asymmetric, cheap to ask for and expensive
222                            // to answer, so the connection cap alone is too
223                            // loose a bound. But the deadline has to cover the
224                            // wait for that budget as well as the work it
225                            // guards, because the thing being bounded is how
226                            // long a peer that has not yet proved it can speak
227                            // TLS may hold a *connection* permit — and the
228                            // connection permit is taken before this task even
229                            // starts.
230                            //
231                            // Timing only the handshake turned the budget into
232                            // a rate limiter on failure: with the default 256
233                            // permits and a 10s deadline, stalled ClientHellos
234                            // expire 256 per 10s while the rest wait on
235                            // `acquire_owned` with no deadline at all. Filling
236                            // the connection budget that way costs an attacker
237                            // one TCP connect each and blocks the accept loop
238                            // for as long as it takes to drain — minutes, at
239                            // the default caps, rather than the advertised ten
240                            // seconds.
241                            //
242                            // The cost of covering the queue is that under
243                            // genuine handshake overload a legitimate client
244                            // can be dropped while still queued. That is load
245                            // shedding, and it is the behaviour the knob
246                            // promises: no peer holds a connection permit for
247                            // longer than this.
248                            let queue_and_shake = async {
249                                let permit = match &handshakes {
250                                    Some(sem) => sem.clone().acquire_owned().await.ok(),
251                                    None => None,
252                                };
253                                // Held alongside the result so it outlives the
254                                // handshake and is released deliberately below,
255                                // rather than at the end of this block.
256                                (acceptor.accept(stream).await, permit)
257                            };
258                            let (accepted, handshake_permit) = match handshake_timeout {
259                                Some(limit) => {
260                                    match tokio::time::timeout(limit, queue_and_shake).await {
261                                        Ok(pair) => pair,
262                                        Err(_) => {
263                                            // Dropping the future here cancels
264                                            // the acquire as well as the
265                                            // handshake, so a peer that timed
266                                            // out while queued returns nothing
267                                            // and holds nothing.
268                                            //
269                                            // Before the handshake completes
270                                            // there is no HTTP layer, so
271                                            // `header_read_timeout` cannot
272                                            // cover this case.
273                                            tracing::debug!(%peer, "TLS handshake timed out");
274                                            return;
275                                        }
276                                    }
277                                }
278                                None => queue_and_shake.await,
279                            };
280                            match accepted {
281                                Ok(tls_stream) => {
282                                    // The handshake budget is for handshakes; the
283                                    // connection budget takes over from here.
284                                    drop(handshake_permit);
285                                    serve_stream(app, tls_stream, conn_cfg, peer, token, slot).await;
286                                }
287                                // Certificate, protocol-version and SNI failures
288                                // all land here. Logged at debug because an
289                                // internet-facing port sees scanner noise
290                                // constantly, and a warning per probe is its own
291                                // denial of service on the log pipeline.
292                                Err(e) => tracing::debug!(%peer, error = %e, "TLS handshake failed"),
293                            }
294                        };
295                        tokio::spawn(conn_builder_fut);
296                        continue;
297                    }
298                }
299                serve_stream(app.clone(), stream, conn_cfg, peer, drain.token(), slot).await;
300            }
301        }
302    }
303
304    drain.wait(shutdown_timeout_ms).await;
305    Ok(())
306}
307
308/// Connection tracking for graceful shutdown: signal every live connection to
309/// wind down, then wait until the last one has finished.
310///
311/// hyper-util's `GracefulShutdown` cannot do this for us. `Watcher::watch`
312/// requires the sealed `GracefulConnection` trait, which hyper-util 0.1.x
313/// implements for `auto::Connection` but *not* for the upgradeable connection
314/// that WebSockets need — and the trait being sealed means we cannot supply
315/// the impl ourselves. So this reproduces the same two-part contract over the
316/// connection type we actually serve: `UpgradeableConnection` exposes
317/// `graceful_shutdown`, which is the only piece that was ever missing.
318struct Drain {
319    /// Fires once. Every connection task watches it and stops accepting new
320    /// requests on its connection when it flips.
321    signal: tokio::sync::watch::Sender<bool>,
322    /// Handed out by [`Drain::token`] and held for the lifetime of each
323    /// connection task. `wait` finishes when the last clone is dropped, which
324    /// is exactly when the last connection has finished.
325    guard: Option<tokio::sync::mpsc::Sender<()>>,
326    finished: tokio::sync::mpsc::Receiver<()>,
327}
328
329/// A connection's share of the drain: watch one end, hold the other.
330struct DrainToken {
331    signal: tokio::sync::watch::Receiver<bool>,
332    /// Never read. Dropping it is the signal that this connection is done.
333    _guard: tokio::sync::mpsc::Sender<()>,
334}
335
336impl Drain {
337    fn new() -> Self {
338        let (signal, _) = tokio::sync::watch::channel(false);
339        let (guard, finished) = tokio::sync::mpsc::channel(1);
340        Self {
341            signal,
342            guard: Some(guard),
343            finished,
344        }
345    }
346
347    fn token(&self) -> DrainToken {
348        DrainToken {
349            signal: self.signal.subscribe(),
350            // `guard` is Some for as long as the accept loop runs; `wait`
351            // takes it, so this cannot be called afterwards.
352            _guard: self.guard.clone().expect("token after wait"),
353        }
354    }
355
356    /// Tell every connection to wind down, then wait — for at most
357    /// `timeout_ms`, or indefinitely if that is zero.
358    ///
359    /// Bounding this is the point: one slow request must not delay exit
360    /// forever, because under an orchestrator that means being killed rather
361    /// than shutting down cleanly.
362    async fn wait(mut self, timeout_ms: u64) {
363        let _ = self.signal.send(true);
364        // Drop our own guard, or the count never reaches zero.
365        self.guard.take();
366
367        // `recv` resolves with `None` once every clone has been dropped.
368        let drained = async move { while self.finished.recv().await.is_some() {} };
369
370        if timeout_ms == 0 {
371            drained.await;
372        } else {
373            let grace = std::time::Duration::from_millis(timeout_ms);
374            if tokio::time::timeout(grace, drained).await.is_err() {
375                tracing_drain_timeout(timeout_ms);
376            }
377        }
378    }
379}
380
381/// How long an idle connection is given to put its GOAWAY (or HTTP/1 close) on
382/// the wire during shutdown before it is dropped.
383///
384/// Long enough for a frame to be written and flushed; short enough that a
385/// rolling restart is not held up by peers that have nothing to say. Not
386/// configurable: the tunable that matters is the grace period, and this is
387/// deliberately far below any sane value of it.
388const GOAWAY_LINGER: std::time::Duration = std::time::Duration::from_millis(250);
389
390/// The `Content-Type` of the refusals this module composes itself.
391///
392/// Spelled the same way [`Response::text`](crate::Response::text) spells it, so
393/// a `413` the engine wrote and a `413` a handler returned are indistinguishable
394/// to a client parsing the header.
395const TEXT_PLAIN: &str = "text/plain; charset=utf-8";
396
397/// A connection's share of the connection budget, held for as long as the
398/// connection is served. `None` when the budget is unlimited.
399type ConnSlot = Option<tokio::sync::OwnedSemaphorePermit>;
400
401/// A connection's share of *both* budgets, in a form that can outlive the
402/// connection future.
403///
404/// hyper resolves an upgraded connection the moment it dispatches the `101`,
405/// while the socket itself lives on in a detached task. Without handing that
406/// task a share, a live WebSocket held no permit and no drain token at all, so
407/// `max_connections` bounded nothing for WebSocket traffic and the drain could
408/// not see them. Cloning is what lets both the connection loop and the upgraded
409/// task hold it; the budget is returned when the last clone drops.
410#[derive(Clone)]
411pub(crate) struct ConnGuard(#[allow(dead_code)] std::sync::Arc<ConnGuardInner>);
412
413pub(crate) struct ConnGuardInner {
414    _slot: ConnSlot,
415    _token: DrainToken,
416}
417
418impl ConnGuard {
419    fn new(slot: ConnSlot, token: DrainToken) -> Self {
420        Self(std::sync::Arc::new(ConnGuardInner {
421            _slot: slot,
422            _token: token,
423        }))
424    }
425}
426
427/// How many connections, and how many TLS handshakes, may be in progress.
428///
429/// The listen backlog bounds what the kernel queues before the accept loop
430/// reaches it. These bound what the process takes on. Without them the only
431/// limits are what the OS will hand out, and the failure mode is the process
432/// dying rather than new connections waiting their turn.
433struct AcceptLimits {
434    connections: Option<std::sync::Arc<tokio::sync::Semaphore>>,
435    #[cfg(feature = "tls")]
436    handshakes: Option<std::sync::Arc<tokio::sync::Semaphore>>,
437    #[cfg(feature = "tls")]
438    tls_handshake_timeout: Option<std::time::Duration>,
439}
440
441impl AcceptLimits {
442    fn from(cfg: &crate::app::ServerConfig) -> Self {
443        // `0` means unlimited for every one of these, so a caller who does not
444        // want a bound is not forced to invent a large number.
445        let sem = |n: usize| (n > 0).then(|| std::sync::Arc::new(tokio::sync::Semaphore::new(n)));
446        Self {
447            connections: sem(cfg.max_connections),
448            #[cfg(feature = "tls")]
449            handshakes: sem(cfg.max_tls_handshakes),
450            #[cfg(feature = "tls")]
451            tls_handshake_timeout: (cfg.tls_handshake_timeout_ms > 0)
452                .then(|| std::time::Duration::from_millis(cfg.tls_handshake_timeout_ms)),
453        }
454    }
455
456    /// Wait until a connection may be served. Acquired before `accept`, so
457    /// excess load waits in the kernel backlog rather than in this process.
458    async fn connection_slot(&self) -> ConnSlot {
459        match &self.connections {
460            // `acquire_owned` only fails on a closed semaphore, and nothing
461            // closes these.
462            Some(sem) => sem.clone().acquire_owned().await.ok(),
463            None => None,
464        }
465    }
466}
467
468/// Capped exponential backoff for a failing `accept`.
469///
470/// Capped because an uncapped one turns a transient blip into minutes of
471/// unavailability; reset on success because the next failure is a new problem,
472/// not a continuation of the last one.
473#[derive(Default)]
474struct AcceptBackoff {
475    /// Zero means "no failure yet"; the next pause starts from `FIRST_MS`.
476    current_ms: u64,
477}
478
479impl AcceptBackoff {
480    const FIRST_MS: u64 = 5;
481    const MAX_MS: u64 = 1_000;
482
483    fn reset(&mut self) {
484        self.current_ms = 0;
485    }
486
487    fn next_pause(&mut self) -> std::time::Duration {
488        self.current_ms = match self.current_ms {
489            0 => Self::FIRST_MS,
490            n => (n * 2).min(Self::MAX_MS),
491        };
492        std::time::Duration::from_millis(self.current_ms)
493    }
494}
495
496/// When a connection last did anything, and whether it is doing something now.
497///
498/// hyper's HTTP/1 `keep_alive` knob is a `bool` — reuse the connection or do
499/// not — with no idle bound. Without one, an accepted connection that goes
500/// quiet is held until the client goes away, which costs a file descriptor and
501/// a task each and is the cheapest way to exhaust a server. This is the state
502/// the idle watchdog reads.
503struct ConnActivity {
504    /// Requests currently being served. Idle means zero: a handler slower than
505    /// the keep-alive period is busy, and closing its connection would be a
506    /// self-inflicted truncation.
507    in_flight: std::sync::atomic::AtomicUsize,
508    /// Milliseconds since `origin` at the end of the last request.
509    last_ms: std::sync::atomic::AtomicU64,
510    /// Fixed reference point, so activity is a cheap integer rather than a
511    /// mutex around an `Instant`.
512    origin: tokio::time::Instant,
513}
514
515/// Holds a connection's "a request is in flight" count for as long as it lives.
516///
517/// Dropping it records the activity timestamp and decrements. Being a guard
518/// rather than a pair of calls is the point: the future it lives in can be
519/// cancelled, and a leaked increment silently exempts the connection from every
520/// idle and shutdown bound there is.
521struct InFlight(std::sync::Arc<ConnActivity>);
522
523impl InFlight {
524    fn new(activity: std::sync::Arc<ConnActivity>) -> Self {
525        activity
526            .in_flight
527            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
528        Self(activity)
529    }
530}
531
532impl Drop for InFlight {
533    fn drop(&mut self) {
534        self.0.last_ms.store(
535            self.0.origin.elapsed().as_millis() as u64,
536            std::sync::atomic::Ordering::Relaxed,
537        );
538        self.0
539            .in_flight
540            .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
541    }
542}
543
544/// A response body that holds an [`InFlight`] guard until it is finished.
545///
546/// A transparent wrapper, not a re-stream: `size_hint` and `is_end_stream`
547/// delegate to the inner body so a buffered response keeps its exact length and
548/// hyper still frames it with `Content-Length`. Converting everything to a
549/// stream here would have made every response chunked.
550struct GuardedBody {
551    inner: UnsyncBoxBody<Bytes, std::io::Error>,
552    /// Dropped with the body — after the last frame, or when the client goes
553    /// away mid-transfer.
554    _guard: InFlight,
555}
556
557impl hyper::body::Body for GuardedBody {
558    type Data = Bytes;
559    type Error = std::io::Error;
560
561    fn poll_frame(
562        mut self: std::pin::Pin<&mut Self>,
563        cx: &mut std::task::Context<'_>,
564    ) -> std::task::Poll<Option<std::result::Result<Frame<Bytes>, std::io::Error>>> {
565        std::pin::Pin::new(&mut self.inner).poll_frame(cx)
566    }
567
568    fn size_hint(&self) -> hyper::body::SizeHint {
569        self.inner.size_hint()
570    }
571
572    fn is_end_stream(&self) -> bool {
573        self.inner.is_end_stream()
574    }
575}
576
577/// Keep `guard` alive until the body has been fully written.
578fn attach_guard(
579    body: UnsyncBoxBody<Bytes, std::io::Error>,
580    guard: InFlight,
581) -> UnsyncBoxBody<Bytes, std::io::Error> {
582    GuardedBody {
583        inner: body,
584        _guard: guard,
585    }
586    .boxed_unsync()
587}
588
589impl ConnActivity {
590    fn new() -> Self {
591        Self {
592            in_flight: std::sync::atomic::AtomicUsize::new(0),
593            last_ms: std::sync::atomic::AtomicU64::new(0),
594            origin: tokio::time::Instant::now(),
595        }
596    }
597
598    /// Whether a request is being served right now.
599    fn busy(&self) -> bool {
600        self.in_flight.load(std::sync::atomic::Ordering::Relaxed) > 0
601    }
602
603    /// `None` if the connection has been idle for at least `keep_alive_ms`,
604    /// otherwise how much longer to wait before asking again.
605    fn idle_for(&self, keep_alive_ms: u64) -> Option<std::time::Duration> {
606        if self.in_flight.load(std::sync::atomic::Ordering::Relaxed) > 0 {
607            // Busy. Check again a full period from now rather than computing a
608            // deadline against a request that has not finished.
609            return Some(std::time::Duration::from_millis(keep_alive_ms));
610        }
611        let last = self.last_ms.load(std::sync::atomic::Ordering::Relaxed);
612        let idle_ms = (self.origin.elapsed().as_millis() as u64).saturating_sub(last);
613        match keep_alive_ms.checked_sub(idle_ms) {
614            Some(0) | None => None,
615            Some(remaining) => Some(std::time::Duration::from_millis(remaining)),
616        }
617    }
618}
619
620/// The 24 bytes an HTTP/2 client sends before anything else (RFC 9113 §3.4).
621const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
622
623/// Whether `auto::Builder` has finished deciding which protocol this is.
624///
625/// The two header deadlines the engine configures — `http1().header_read_timeout`
626/// and the h2 keep-alive ping — belong to a `Connection` that hyper-util only
627/// builds *after* it has sniffed the preface, and the sniffing future itself
628/// (`ReadVersion`) holds no timer: it is a bare read of up to 24 bytes. A peer
629/// that connects and sends nothing, or that sends 23 of the 24 preface bytes,
630/// parks there holding a connection permit and a drain token with no deadline
631/// on it at all.
632///
633/// Reading this flag is what lets the connection loop bound that phase and stop
634/// bounding it the moment a protocol exists — which matters, because a live
635/// HTTP/2 client is allowed to sit idle far longer than the header deadline
636/// once it has handshaken.
637#[derive(Default)]
638struct Negotiated(std::sync::atomic::AtomicBool);
639
640impl Negotiated {
641    fn get(&self) -> bool {
642        self.0.load(std::sync::atomic::Ordering::Relaxed)
643    }
644
645    fn set(&self) {
646        self.0.store(true, std::sync::atomic::Ordering::Relaxed);
647    }
648}
649
650/// Wraps the socket to watch the bytes `ReadVersion` reads, and nothing else.
651///
652/// The condition below mirrors hyper-util's: it stops sniffing on the first
653/// read that diverges from the preface, on end of file, and once all 24 bytes
654/// have matched. Mirroring is safe to do here because the preface is a protocol
655/// constant rather than a hyper internal — hyper-util cannot decide differently
656/// without HTTP/2 itself changing.
657///
658/// Once `flag` is set the wrapper is a pure delegate; the comparison costs at
659/// most 24 byte tests per connection.
660struct Sniffing<S> {
661    inner: S,
662    flag: std::sync::Arc<Negotiated>,
663    /// Preface bytes matched so far, across however many reads they arrived in.
664    matched: usize,
665}
666
667impl<S> Sniffing<S> {
668    fn new(inner: S, flag: std::sync::Arc<Negotiated>) -> Self {
669        Self {
670            inner,
671            flag,
672            matched: 0,
673        }
674    }
675
676    /// Feed the bytes a single read produced.
677    fn observe(&mut self, fresh: &[u8]) {
678        // End of file. hyper-util stops sniffing here too, and the connection
679        // is about to end on its own.
680        if fresh.is_empty() {
681            self.flag.set();
682            return;
683        }
684        for &byte in fresh {
685            if byte != H2_PREFACE[self.matched] {
686                // Not HTTP/2: hyper-util builds an HTTP/1 connection, whose own
687                // header deadline takes over from here.
688                self.flag.set();
689                return;
690            }
691            self.matched += 1;
692            if self.matched == H2_PREFACE.len() {
693                self.flag.set();
694                return;
695            }
696        }
697    }
698}
699
700impl<S: tokio::io::AsyncRead + Unpin> tokio::io::AsyncRead for Sniffing<S> {
701    fn poll_read(
702        self: std::pin::Pin<&mut Self>,
703        cx: &mut std::task::Context<'_>,
704        buf: &mut tokio::io::ReadBuf<'_>,
705    ) -> std::task::Poll<std::io::Result<()>> {
706        let this = self.get_mut();
707        let before = buf.filled().len();
708        let polled = std::pin::Pin::new(&mut this.inner).poll_read(cx, buf);
709        if matches!(polled, std::task::Poll::Ready(Ok(()))) && !this.flag.get() {
710            // Copied out because `observe` takes `&mut self` while `buf` is
711            // still borrowed from the read above.
712            let fresh: Vec<u8> = buf.filled()[before..].to_vec();
713            this.observe(&fresh);
714        }
715        polled
716    }
717}
718
719impl<S: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for Sniffing<S> {
720    fn poll_write(
721        mut self: std::pin::Pin<&mut Self>,
722        cx: &mut std::task::Context<'_>,
723        buf: &[u8],
724    ) -> std::task::Poll<std::io::Result<usize>> {
725        std::pin::Pin::new(&mut self.inner).poll_write(cx, buf)
726    }
727
728    fn poll_flush(
729        mut self: std::pin::Pin<&mut Self>,
730        cx: &mut std::task::Context<'_>,
731    ) -> std::task::Poll<std::io::Result<()>> {
732        std::pin::Pin::new(&mut self.inner).poll_flush(cx)
733    }
734
735    fn poll_shutdown(
736        mut self: std::pin::Pin<&mut Self>,
737        cx: &mut std::task::Context<'_>,
738    ) -> std::task::Poll<std::io::Result<()>> {
739        std::pin::Pin::new(&mut self.inner).poll_shutdown(cx)
740    }
741
742    // Delegated rather than left to the default: hyper writes a response head
743    // and body as separate slices, and losing vectored writes here would turn
744    // every response into an extra syscall.
745    fn poll_write_vectored(
746        mut self: std::pin::Pin<&mut Self>,
747        cx: &mut std::task::Context<'_>,
748        bufs: &[std::io::IoSlice<'_>],
749    ) -> std::task::Poll<std::io::Result<usize>> {
750        std::pin::Pin::new(&mut self.inner).poll_write_vectored(cx, bufs)
751    }
752
753    fn is_write_vectored(&self) -> bool {
754        self.inner.is_write_vectored()
755    }
756}
757
758/// Per-connection settings, resolved once from the app config.
759///
760/// Grouped rather than passed individually: nine positional parameters is a
761/// call site nobody can read.
762#[derive(Clone, Copy)]
763struct ConnSettings {
764    max_body: usize,
765    timeout_ms: u64,
766    max_headers: usize,
767    header_read_timeout_ms: u64,
768    keep_alive_ms: u64,
769    h2_max_header_list_size: u32,
770    h2_max_concurrent_streams: u32,
771}
772
773impl ConnSettings {
774    fn from(cfg: &crate::app::ServerConfig) -> Self {
775        Self {
776            max_body: cfg.max_body_bytes,
777            timeout_ms: cfg.request_timeout_ms,
778            max_headers: cfg.max_headers,
779            header_read_timeout_ms: cfg.header_read_timeout_ms,
780            keep_alive_ms: cfg.keep_alive_ms,
781            h2_max_header_list_size: cfg.h2_max_header_list_size,
782            h2_max_concurrent_streams: cfg.h2_max_concurrent_streams,
783        }
784    }
785}
786
787/// Serve on several addresses at once, until `shutdown` resolves.
788///
789/// Useful for binding IPv4 and IPv6 separately, or for exposing an admin port
790/// alongside the public one. Each address gets its own accept loop; the first
791/// bind failure aborts, since a server that silently came up on half its
792/// addresses is worse than one that refused to start.
793pub async fn serve_many<F>(app: App, addrs: Vec<SocketAddr>, shutdown: F) -> std::io::Result<()>
794where
795    F: Future<Output = ()> + Send + 'static,
796{
797    if addrs.is_empty() {
798        return Err(std::io::Error::new(
799            std::io::ErrorKind::InvalidInput,
800            "no addresses to bind",
801        ));
802    }
803
804    // Bind everything first, so a failure is reported *before* any address
805    // starts serving. Binding inside the spawned tasks meant a failure was not
806    // observed until after `shutdown.await` — the process ran happily on the
807    // addresses that did work, said nothing, and surfaced the error only on the
808    // way out. That is exactly the half-up server the doc comment above rules
809    // out.
810    let mut listeners = Vec::with_capacity(addrs.len());
811    for addr in addrs {
812        match bind_tcp(addr, app.config().backlog) {
813            Ok(l) => listeners.push(l),
814            Err(e) => {
815                tracing::error!(%addr, error = %e, "bind failed; not starting");
816                return Err(e);
817            }
818        }
819    }
820
821    // One shutdown signal fans out to every loop.
822    let (tx, _) = tokio::sync::broadcast::channel::<()>(1);
823    let mut tasks = Vec::with_capacity(listeners.len());
824
825    // One budget for the process, not one per listener. Constructing it inside
826    // each accept loop multiplied `max_connections` and `max_tls_handshakes` by
827    // the number of bound addresses — binding v4 and v6 quietly doubled a cap
828    // documented as bounding "what the process serves at once".
829    let limits = std::sync::Arc::new(AcceptLimits::from(app.config()));
830
831    for listener in listeners {
832        let app = app.clone();
833        let limits = limits.clone();
834        let mut rx = tx.subscribe();
835        tasks.push(tokio::spawn(async move {
836            serve_listener(app, listener, limits, async move {
837                let _ = rx.recv().await;
838            })
839            .await
840        }));
841    }
842
843    shutdown.await;
844    let _ = tx.send(());
845
846    let mut first_err: Option<std::io::Error> = None;
847    for t in tasks {
848        let outcome = match t.await {
849            Ok(r) => r,
850            Err(e) => Err(std::io::Error::other(format!("listener task failed: {e}"))),
851        };
852        if let Err(e) = outcome {
853            first_err.get_or_insert(e);
854        }
855    }
856    match first_err {
857        Some(e) => Err(e),
858        None => Ok(()),
859    }
860}
861
862/// Serve on a Unix domain socket instead of TCP.
863///
864/// Useful behind a local reverse proxy: no TCP stack and no port to firewall.
865///
866/// The socket path is unlinked first if a *stale* file is present — a crashed
867/// process leaves one behind, and binding would otherwise fail forever. Stale
868/// is established rather than assumed: a socket node that still has a listener
869/// is left alone and the bind fails with [`AddrInUse`], so a second instance
870/// started on a path already in use refuses instead of taking it over.
871///
872/// # Permissions
873///
874/// The socket node is created under the process umask and this function does
875/// not chmod it. Do not rely on the node's own mode for access control: whether
876/// the permission bits on a socket are consulted at `connect(2)` is
877/// platform-dependent — Linux enforces them, some BSDs historically did not.
878/// What is enforced everywhere is path resolution, so put the socket in a
879/// directory whose permissions you control, and set the umask before calling if
880/// the node's own mode matters to you. Adjusting it afterwards is racy: the
881/// socket accepts connections from the moment it is bound.
882///
883/// Unix only; there is no Windows equivalent.
884///
885/// [`AddrInUse`]: std::io::ErrorKind::AddrInUse
886#[cfg(unix)]
887pub async fn serve_unix<F>(
888    app: App,
889    path: impl AsRef<std::path::Path>,
890    shutdown: F,
891) -> std::io::Result<()>
892where
893    F: Future<Output = ()> + Send + 'static,
894{
895    let path = path.as_ref();
896    // A leftover socket node from a crash blocks bind with EADDRINUSE, so one
897    // has to go — but only once it is known to be dead. The unlink used to be
898    // unconditional, which meant a second `serve_unix` on a path an instance
899    // was already serving deleted that instance's node and bound its own: the
900    // first process kept running on an inode nothing could reach any more,
901    // still reporting itself healthy, while every new connection went to the
902    // second. Probing with a connect distinguishes the two cases the way any
903    // other server does it — a refused connection means nobody is listening,
904    // and an accepted one means the path is genuinely in use.
905    unlink_if_stale(path).await?;
906
907    let listener = tokio::net::UnixListener::bind(path)?;
908    // Remember which inode this bind produced. At shutdown the node at `path`
909    // may no longer be ours, and removing whatever happens to be there then
910    // would leave a live successor serving a path with no socket on it. See
911    // the unlink at the end of this function.
912    let bound = socket_identity(path);
913    let conn_cfg = ConnSettings::from(app.config());
914    let shutdown_timeout_ms = app.config().shutdown_timeout_ms;
915
916    let drain = Drain::new();
917    let limits = std::sync::Arc::new(AcceptLimits::from(app.config()));
918    let mut backoff = AcceptBackoff::default();
919    let mut shutdown = std::pin::pin!(shutdown);
920
921    // A Unix peer has no IP; report the loopback address so `peer_addr` has a
922    // consistent shape rather than being absent only on this transport.
923    let peer = std::net::SocketAddr::from(([127, 0, 0, 1], 0));
924
925    loop {
926        // Same shape as the TCP loop: a saturated budget must not outrank the
927        // shutdown signal. See the comment there.
928        let acquire = async {
929            let slot = limits.connection_slot().await;
930            (slot, listener.accept().await)
931        };
932
933        tokio::select! {
934            biased;
935            _ = &mut shutdown => break,
936            (slot, accepted) = acquire => {
937                let (stream, _) = match accepted {
938                    Ok(s) => {
939                        backoff.reset();
940                        s
941                    }
942                    Err(e) => {
943                        let pause = backoff.next_pause();
944                        tracing::warn!(error = %e, pause_ms = pause.as_millis() as u64, "accept failed");
945                        tokio::time::sleep(pause).await;
946                        continue;
947                    }
948                };
949                serve_stream(app.clone(), stream, conn_cfg, peer, drain.token(), slot).await;
950            }
951        }
952    }
953
954    drain.wait(shutdown_timeout_ms).await;
955    // Leave no socket file behind for the next start to trip over — but only
956    // our own. If something else has taken the path over in the meantime, the
957    // node sitting there belongs to a listener that is still serving, and
958    // deleting it would take the successor off the air without either process
959    // noticing: it goes on accepting on an inode with no name, and the next
960    // client to resolve the path finds nothing. Comparing device and inode is
961    // exact where comparing paths is not.
962    if bound.is_some() && bound == socket_identity(path) {
963        let _ = std::fs::remove_file(path);
964    }
965    Ok(())
966}
967
968/// Identify the node currently at `path` by device and inode.
969///
970/// `None` means there is nothing there, or that it could not be stated — either
971/// way it is not something this process should claim, so the callers treat it
972/// as "not ours". `symlink_metadata` rather than `metadata`: a symlink pointing
973/// at the socket is a different node from the socket, and following it would
974/// let a link swapped in under us stand in for the real thing.
975#[cfg(unix)]
976fn socket_identity(path: &std::path::Path) -> Option<(u64, u64)> {
977    use std::os::unix::fs::MetadataExt;
978    let md = std::fs::symlink_metadata(path).ok()?;
979    Some((md.dev(), md.ino()))
980}
981
982/// Clear `path` for a fresh bind, refusing to disturb a socket still in use.
983///
984/// Returns `AddrInUse` when the path already has a listener behind it. Anything
985/// else there is removed, as before: a socket node that refuses connections is
986/// the leftover this exists to clean up, and a non-socket file at the path is
987/// removed too, because that is the behaviour `serve_unix` has always had and
988/// bind would fail on it regardless.
989#[cfg(unix)]
990async fn unlink_if_stale(path: &std::path::Path) -> std::io::Result<()> {
991    use std::os::unix::fs::FileTypeExt;
992
993    let Ok(md) = std::fs::symlink_metadata(path) else {
994        return Ok(()); // nothing in the way
995    };
996
997    // Only a socket can have a listener, so only a socket is worth probing.
998    // The probe is a real `connect(2)`, which is the only way to tell a live
999    // socket from an abandoned node: both look identical on disk, since neither
1000    // a crash nor a plain `drop` of the listener unlinks the name.
1001    if md.file_type().is_socket() && tokio::net::UnixStream::connect(path).await.is_ok() {
1002        return Err(std::io::Error::new(
1003            std::io::ErrorKind::AddrInUse,
1004            format!(
1005                "{} is already being served by another listener",
1006                path.display()
1007            ),
1008        ));
1009    }
1010
1011    // The removal is reported now rather than swallowed, because the bind that
1012    // follows would fail with a bare EADDRINUSE that says nothing about why the
1013    // path could not be cleared. A node that vanished between the stat and the
1014    // removal is not a failure: the path is clear, which is all this wanted.
1015    match std::fs::remove_file(path) {
1016        Err(e) if e.kind() != std::io::ErrorKind::NotFound => Err(e),
1017        _ => Ok(()),
1018    }
1019}
1020
1021async fn serve_stream<S>(
1022    app: App,
1023    stream: S,
1024    cfg: ConnSettings,
1025    peer: std::net::SocketAddr,
1026    token: DrainToken,
1027    slot: ConnSlot,
1028) where
1029    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
1030{
1031    // Taken as the raw stream rather than as an already-wrapped `TokioIo` so
1032    // the preface sniffing can be watched underneath it — see `Sniffing`.
1033    let negotiated = std::sync::Arc::new(Negotiated::default());
1034    let io = TokioIo::new(Sniffing::new(stream, negotiated.clone()));
1035
1036    // The idle watchdog needs to know when this connection last did anything.
1037    // hyper exposes no per-request hook, but the service *is* the per-request
1038    // hook: every request on this connection passes through the closure below.
1039    let activity = std::sync::Arc::new(ConnActivity::new());
1040    // Subscribe before the token is moved into the guard: the connection loop
1041    // watches the shutdown signal, the guard owns the drain's release side.
1042    let mut signal = token.signal.clone();
1043    // One share of the budget for this connection, cloned into any upgraded
1044    // socket so the permit outlives the HTTP connection future.
1045    let guard = ConnGuard::new(slot, token);
1046
1047    let svc = {
1048        let activity = activity.clone();
1049        let guard = guard.clone();
1050        service_fn(move |req: HyperRequest<Incoming>| {
1051            let app = app.clone();
1052            let activity = activity.clone();
1053            let conn_guard = guard.clone();
1054            async move {
1055                // Busy, not idle: a handler slower than the keep-alive period
1056                // must not have its own connection closed underneath it.
1057                //
1058                // An RAII guard rather than paired begin/end calls, because a
1059                // request future can be *dropped* mid-await — an HTTP/2 client
1060                // sending RST_STREAM makes hyper do exactly that — and a missed
1061                // decrement pinned the connection as busy forever, exempt from
1062                // the idle watchdog and from the shutdown linger.
1063                let in_flight = InFlight::new(activity);
1064                let res = handle(app, req, cfg.max_body, cfg.timeout_ms, peer, conn_guard).await;
1065                // The guard rides on the response body: the connection is still
1066                // working while bytes are being written, and releasing it when
1067                // the handler returned meant a large download or an SSE stream
1068                // counted as idle and was dropped during shutdown.
1069                res.map(|r| r.map(|body| attach_guard(body, in_flight)))
1070            }
1071        })
1072    };
1073    // One builder that negotiates HTTP/1.1 or HTTP/2 per connection: h2 over
1074    // TLS via ALPN, and h2c prior-knowledge in plaintext. The v1 engine used
1075    // `http1::Builder` directly with a note that `auto::Builder`'s
1076    // `serve_connection` returned a future the spawn closure could not own —
1077    // `serve_connection_with_upgrades` returns an owned one, which is what
1078    // makes this possible while keeping the WebSocket upgrade path working.
1079    let mut builder =
1080        hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new());
1081    builder.http1().max_headers(cfg.max_headers);
1082    // `max_headers` is HTTP/1 only — it counts headers, and h2 has no count,
1083    // only an encoded size. Both protocols need bounding, and one knob cannot
1084    // do it.
1085    builder
1086        .http2()
1087        .max_header_list_size(cfg.h2_max_header_list_size);
1088    // An h2 connection multiplexes many requests, so without this one
1089    // connection is an unbounded amount of concurrent work.
1090    builder.http2().max_concurrent_streams(
1091        (cfg.h2_max_concurrent_streams > 0).then_some(cfg.h2_max_concurrent_streams),
1092    );
1093    // `0` means no connection reuse: answer and close. Any other value means
1094    // reuse *bounded by an idle timeout*, which hyper has no knob for — see
1095    // the watchdog branch in the connection loop below.
1096    builder.http1().keep_alive(cfg.keep_alive_ms > 0);
1097    // Without this a client can hold a connection open indefinitely by
1098    // dribbling header bytes: the per-request timeout cannot help, because
1099    // there is no request until the header block is complete.
1100    if cfg.header_read_timeout_ms > 0 {
1101        // hyper panics if a timeout is configured without a timer to drive it.
1102        let deadline = std::time::Duration::from_millis(cfg.header_read_timeout_ms);
1103        builder.http1().timer(hyper_util::rt::TokioTimer::new());
1104        builder.http1().header_read_timeout(deadline);
1105        builder.http2().timer(hyper_util::rt::TokioTimer::new());
1106        // HTTP/2 has no header-read deadline to set: a header block arrives in
1107        // frames on an already-open connection, so hyper offers no equivalent
1108        // knob. Its equivalent question — is this peer actually still there? —
1109        // is answered by a keep-alive PING, which hyper leaves disabled by
1110        // default. Without it the documented slow-loris defence covered only
1111        // one of the two protocols served on the port: a peer that completed
1112        // the preface and then dribbled a partial HEADERS frame was bounded by
1113        // nothing but the idle watchdog, at `keep_alive_ms` rather than at the
1114        // 10s this knob advertises, while holding a connection permit.
1115        //
1116        // Ping at the deadline, drop at the deadline again, so a stalled peer
1117        // is gone within roughly twice the configured value and a live one that
1118        // simply has nothing to say answers and stays.
1119        builder.http2().keep_alive_interval(deadline);
1120        builder.http2().keep_alive_timeout(deadline);
1121    }
1122
1123    // The connection borrows the builder, which is the ownership problem the v1
1124    // engine hit and worked around by using `http1::Builder` instead. Moving
1125    // the builder into the task resolves it: the task owns both.
1126    tokio::spawn(async move {
1127        // `with_upgrades` is required for WebSockets and harmless otherwise: an
1128        // HTTP/2 connection never carries an HTTP/1 upgrade.
1129        let conn = builder.serve_connection_with_upgrades(io, svc);
1130        let mut conn = std::pin::pin!(conn);
1131        let mut winding_down = false;
1132
1133        // The idle deadline. Re-armed on every wake that finds the connection
1134        // busy or recently active, so the common case costs one timer per
1135        // connection rather than one per request.
1136        let idle_enabled = cfg.keep_alive_ms > 0;
1137        let idle = tokio::time::sleep(std::time::Duration::from_millis(if idle_enabled {
1138            cfg.keep_alive_ms
1139        } else {
1140            // Parked: the branch is disabled, but `select!` still needs a
1141            // future to name.
1142            u64::MAX / 2
1143        }));
1144        let mut idle = std::pin::pin!(idle);
1145
1146        // Parked until a shutdown signal arms it; see the signal branch below.
1147        let linger = tokio::time::sleep(std::time::Duration::from_secs(3_600));
1148        let mut linger = std::pin::pin!(linger);
1149        let mut lingering = false;
1150
1151        // The deadline for choosing a protocol at all. Both header deadlines
1152        // configured above belong to a connection hyper-util has not built yet,
1153        // and the sniffing it does first carries no timer, so this is the only
1154        // thing standing between a silent socket and a permit held for the life
1155        // of the process — the idle watchdog is a backstop at `keep_alive_ms`
1156        // rather than at the advertised deadline, and `keep_alive_ms` of 0
1157        // disables it outright.
1158        //
1159        // It stops applying at negotiation, not at the first request: an HTTP/2
1160        // client that has handshaken is entitled to idle for far longer than
1161        // the header deadline, and the h2 keep-alive ping is what asks whether
1162        // it is still there.
1163        let mut negotiation_armed = cfg.header_read_timeout_ms > 0;
1164        let negotiation =
1165            tokio::time::sleep(std::time::Duration::from_millis(if negotiation_armed {
1166                cfg.header_read_timeout_ms
1167            } else {
1168                // Parked, like the linger above: the branch is disabled, but
1169                // `select!` still needs a future to name.
1170                u64::MAX / 2
1171            }));
1172        let mut negotiation = std::pin::pin!(negotiation);
1173
1174        loop {
1175            tokio::select! {
1176                // Bias the connection: with both branches ready, finishing the
1177                // request in hand beats re-checking a signal we have already
1178                // acted on.
1179                biased;
1180                _ = conn.as_mut() => break,
1181                // `wait_for` rather than `changed` so a signal that fired
1182                // before this task got scheduled is still seen.
1183                _ = signal.wait_for(|fired| *fired), if !winding_down => {
1184                    winding_down = true;
1185                    // Finish the in-flight request, refuse further ones on this
1186                    // connection, then let the loop poll it to completion.
1187                    conn.as_mut().graceful_shutdown();
1188
1189                    // A connection with nothing in flight has nothing to drain,
1190                    // but it will not necessarily close on its own: an idle
1191                    // HTTP/2 connection is shut down by sending GOAWAY and then
1192                    // waiting for the *peer* to close, which an idle peer never
1193                    // does. Left alone, every shutdown costs the full grace
1194                    // period — a rolling restart would pay it every time.
1195                    //
1196                    // So give the GOAWAY (or the HTTP/1 close) a brief window to
1197                    // reach the wire and then stop. Only for connections with no
1198                    // request in flight; a busy one still gets the full grace.
1199                    if !activity.busy() {
1200                        linger.as_mut().reset(
1201                            tokio::time::Instant::now() + GOAWAY_LINGER,
1202                        );
1203                        lingering = true;
1204                    }
1205                }
1206                // The linger elapsed on an idle connection: drop it.
1207                _ = linger.as_mut(), if lingering => {
1208                    if !activity.busy() {
1209                        break;
1210                    }
1211                    // A request arrived between arming this and now — rare.
1212                    // Re-check later rather than clearing `winding_down`, which
1213                    // would let the shutdown branch fire again and turn this
1214                    // into a 250ms cycle of repeated `graceful_shutdown` calls.
1215                    // The grace period still bounds the wait overall.
1216                    linger
1217                        .as_mut()
1218                        .reset(tokio::time::Instant::now() + GOAWAY_LINGER);
1219                }
1220                // The peer never got as far as a protocol. Wound down the same
1221                // way the idle watchdog does rather than dropped outright, so
1222                // the socket is closed by hyper and anything already buffered
1223                // reaches the wire.
1224                _ = negotiation.as_mut(), if negotiation_armed && !winding_down => {
1225                    // `select!` evaluates a branch's precondition when it is
1226                    // *entered*, not when the branch fires. This one is entered
1227                    // before the peer has sent anything, so `negotiated` cannot
1228                    // be tested there: a connection that handshakes a
1229                    // millisecond later still arrives here with the branch
1230                    // armed, and closing it would drop every live HTTP/2 client
1231                    // that idles past the header deadline — which is exactly
1232                    // what `a_responsive_http2_client_is_not_dropped` exists to
1233                    // catch. Read the flag here, where it is current.
1234                    if negotiated.get() {
1235                        // Disarms the branch on the next pass, so a deadline
1236                        // that has already elapsed does not keep waking us.
1237                        negotiation_armed = false;
1238                    } else {
1239                        tracing::debug!(
1240                            %peer,
1241                            deadline_ms = cfg.header_read_timeout_ms,
1242                            "no protocol chosen within the header deadline; closing"
1243                        );
1244                        winding_down = true;
1245                        conn.as_mut().graceful_shutdown();
1246                        // Nothing has been served, so there is nothing to drain
1247                        // — but an idle connection does not necessarily close
1248                        // on its own, which is what this linger is for
1249                        // elsewhere too.
1250                        linger
1251                            .as_mut()
1252                            .reset(tokio::time::Instant::now() + GOAWAY_LINGER);
1253                        lingering = true;
1254                    }
1255                }
1256                _ = idle.as_mut(), if idle_enabled && !winding_down => {
1257                    match activity.idle_for(cfg.keep_alive_ms) {
1258                        // Nothing in flight and nothing recent: close it.
1259                        None => {
1260                            winding_down = true;
1261                            conn.as_mut().graceful_shutdown();
1262                            // Arm the linger here too. `graceful_shutdown` on an
1263                            // HTTP/2 connection sends GOAWAY and then waits for
1264                            // the peer, which an idle peer never answers — and
1265                            // with `winding_down` set every other branch is
1266                            // gated off, so the loop had nothing left to poll
1267                            // but a future that never resolves. The connection
1268                            // and its permit were then held for the life of the
1269                            // process; at the default cap that is a server that
1270                            // stops accepting anything at all.
1271                            linger
1272                                .as_mut()
1273                                .reset(tokio::time::Instant::now() + GOAWAY_LINGER);
1274                            lingering = true;
1275                        }
1276                        // Busy or recently active — wait out the remainder.
1277                        Some(remaining) => idle
1278                            .as_mut()
1279                            .reset(tokio::time::Instant::now() + remaining),
1280                    }
1281                }
1282            }
1283        }
1284        // The guard drops here — but only *this* clone. An upgraded WebSocket
1285        // holds its own, so the permit and the drain token are returned when
1286        // the socket task ends rather than when the 101 was dispatched.
1287        drop(guard);
1288    });
1289}
1290
1291/// Reported when the grace period expires with requests still in flight.
1292///
1293/// Not an error: exiting on time is the point, and the alternative is hanging.
1294fn tracing_drain_timeout(ms: u64) {
1295    tracing::warn!(
1296        grace_ms = ms,
1297        "graceful shutdown timed out; exiting with requests in flight"
1298    );
1299}
1300
1301/// Serve one request, and harden whatever comes back on the way out.
1302///
1303/// The hardening is here rather than only inside `respond` because `respond`
1304/// has several exits and two of them never reach the pipeline: a refusal is
1305/// composed and returned before `process_call` is ever called, so the
1306/// `SecurityHeaders` middleware — which is a pipeline middleware — could not
1307/// see it. A `413` for an oversized upload therefore went out with nothing but
1308/// `Connection: close`, while every other status the same server produced,
1309/// including a plain `404`, carried the full set.
1310///
1311/// Wrapping the whole function rather than patching each refusal is what keeps
1312/// that from happening again: any exit added later passes through here. It is
1313/// safe to run over a pipeline response too, because a header already present
1314/// is left alone, so the second application is a no-op.
1315async fn handle(
1316    app: App,
1317    req: HyperRequest<Incoming>,
1318    max_body: usize,
1319    timeout_ms: u64,
1320    peer: std::net::SocketAddr,
1321    conn_guard: ConnGuard,
1322) -> Result<HyperResponse<UnsyncBoxBody<Bytes, std::io::Error>>, Infallible> {
1323    let mut res = respond(app.clone(), req, max_body, timeout_ms, peer, conn_guard).await?;
1324    // `false`: this transport cannot tell TLS from plaintext by itself — the
1325    // stream arrives already decrypted, or never was — so the builder's own
1326    // certificate is the only evidence, and `apply_security_headers` consults
1327    // it. HTTP/3 is the case that can say `true`.
1328    app.apply_security_headers(res.headers_mut(), false);
1329    Ok(res)
1330}
1331
1332/// Turn one hyper request into one hyper response.
1333///
1334/// Everything below is about *whether* to dispatch: the two refusals return
1335/// without ever reaching the pipeline. `handle` above is what makes the answer
1336/// look the same either way.
1337async fn respond(
1338    app: App,
1339    req: HyperRequest<Incoming>,
1340    max_body: usize,
1341    timeout_ms: u64,
1342    peer: std::net::SocketAddr,
1343    conn_guard: ConnGuard,
1344) -> Result<HyperResponse<UnsyncBoxBody<Bytes, std::io::Error>>, Infallible> {
1345    // Refuse a body the client has already declared too large, before the
1346    // request is dispatched.
1347    //
1348    // Streaming made the cap lazy: `Limited` only trips when something reads
1349    // the body, so a handler that ignores it — or middleware that
1350    // short-circuits before an extractor runs — answered `200` for a request
1351    // the server had declared it would refuse. `Content-Length` is the client's
1352    // own statement of size, so this costs one header lookup and needs no
1353    // buffering. A chunked body still declares nothing and remains bounded by
1354    // the stream limit at the point it is read.
1355    if let Some(declared) = req
1356        .headers()
1357        .get(http::header::CONTENT_LENGTH)
1358        .and_then(|v| v.to_str().ok())
1359        .and_then(|v| v.parse::<u64>().ok())
1360    {
1361        if declared > max_body as u64 {
1362            let res = HyperResponse::builder()
1363                .status(StatusCode::PAYLOAD_TOO_LARGE)
1364                // Nothing will read the body, so the connection cannot be
1365                // reused: whatever the client is still sending would be read as
1366                // the next request.
1367                .header(http::header::CONNECTION, "close")
1368                // Every response this server composes says what its body is,
1369                // and this one did not: it went out as seventeen unlabelled
1370                // bytes, leaving the client's own sniffing to decide. Naming
1371                // the type is what the accompanying `nosniff` is there to
1372                // enforce, and the pair only means something together.
1373                .header(http::header::CONTENT_TYPE, TEXT_PLAIN)
1374                .body(into_boxed_body(Body::Bytes(bytes::Bytes::from_static(
1375                    b"Payload Too Large",
1376                ))))
1377                .expect("response build is infallible");
1378            return Ok(res);
1379        }
1380    }
1381
1382    // Only the WebSocket branch below consumes this. Discarded explicitly
1383    // rather than silencing the whole function, which would also hide a
1384    // genuinely unused variable.
1385    #[cfg(not(feature = "ws"))]
1386    let _ = &conn_guard;
1387
1388    // RFC 9112 §6.3 rule 3: a request carrying both `Transfer-Encoding` and
1389    // `Content-Length` is framed by the transfer coding, and "ought to be
1390    // handled as an error" — no legitimate client sends both.
1391    //
1392    // The danger is not what Churust does with the message; it is what an
1393    // intermediary in front of Churust did with it. A proxy that believes the
1394    // `Content-Length` forwards a different number of body bytes than this
1395    // server consumes, and the leftovers become the start of the *next* request
1396    // on a reused connection — request smuggling. Since we cannot know what is
1397    // upstream, refuse the ambiguity and close, which removes the desync
1398    // surface whatever the proxy decided.
1399    //
1400    // This only fires below hyper 1.11. From 1.11 hyper removes the
1401    // `Content-Length` from the parsed header map itself, so nothing here can
1402    // observe the ambiguity — and there is no supported way to ask for the
1403    // headers as they arrived (`HeaderCaseMap` is `pub(crate)`). Recovering the
1404    // `400` would mean parsing HTTP/1 message framing off the socket ahead of
1405    // hyper, which is a second framing implementation guarding the seam between
1406    // two framing implementations. Not worth it, because 1.11 also sets
1407    // `keep_alive = false` for this exact shape: the message is served, framed
1408    // by the transfer coding, and the connection closes — which is the desync
1409    // removal this branch existed to buy. `hyper = "1"` still resolves to
1410    // pre-1.11 versions, where it is the only thing standing there, so it stays.
1411    if req.headers().contains_key(http::header::TRANSFER_ENCODING)
1412        && req.headers().contains_key(http::header::CONTENT_LENGTH)
1413    {
1414        tracing::warn!(
1415            path = %req.uri().path(),
1416            "rejected a request with both Transfer-Encoding and Content-Length"
1417        );
1418        let res = HyperResponse::builder()
1419            .status(StatusCode::BAD_REQUEST)
1420            // Closing is the point: leaving the connection open would let
1421            // whatever the peer framed differently be read as a new request.
1422            .header(http::header::CONNECTION, "close")
1423            // As above: a body with no declared type is one the recipient has
1424            // to guess at.
1425            .header(http::header::CONTENT_TYPE, TEXT_PLAIN)
1426            .body(into_boxed_body(Body::Bytes(bytes::Bytes::from_static(
1427                b"Bad Request",
1428            ))))
1429            .expect("response build is infallible");
1430        return Ok(res);
1431    }
1432
1433    #[cfg(feature = "ws")]
1434    let ws_max_frame_bytes = app.config().ws_max_frame_bytes;
1435    #[cfg(feature = "ws")]
1436    let ws_max_message_bytes = app.config().ws_max_message_bytes;
1437    #[cfg(feature = "ws")]
1438    let ws_idle_timeout_ms = app.config().ws_idle_timeout_ms;
1439    #[cfg(feature = "ws")]
1440    let mut req = req;
1441    #[cfg(feature = "ws")]
1442    let on_upgrade = if crate::ws::is_upgrade_request(req.headers()) {
1443        Some(hyper::upgrade::on(&mut req))
1444    } else {
1445        None
1446    };
1447
1448    let (parts, body) = req.into_parts();
1449
1450    // The body is handed to the handler as a stream rather than collected here.
1451    // Collecting first made `max_body_bytes` a hard ceiling on upload size and
1452    // made memory scale with concurrent uploads. `Limited` still enforces the
1453    // cap; exceeding it now surfaces as an error item in the stream, which
1454    // `Call::try_receive_bytes` turns back into `413` for handlers that buffer.
1455    let limited = Limited::new(body, max_body);
1456    let body_stream: crate::call::BodyStream = Box::pin(BodyDataStream::new(limited).map(|r| {
1457        r.map_err(|e| {
1458            // Match the error's *type*, not its message. The message belongs
1459            // to http-body-util and can change in a patch release, which would
1460            // silently turn every oversized body into a `400`.
1461            if e.downcast_ref::<http_body_util::LengthLimitError>()
1462                .is_some()
1463            {
1464                crate::Error::new(StatusCode::PAYLOAD_TOO_LARGE, "request body too large")
1465            } else {
1466                crate::Error::bad_request(format!("error reading request body: {e}"))
1467            }
1468        })
1469    }));
1470
1471    #[cfg(feature = "ws")]
1472    let process = {
1473        let mut extensions = http::Extensions::new();
1474        extensions.insert(crate::call::PeerAddr(peer));
1475        if let Some(on_upgrade) = on_upgrade {
1476            extensions.insert(crate::ws::OnUpgradeHandle::new(on_upgrade));
1477            // So the upgraded socket keeps this connection's budget share.
1478            extensions.insert(conn_guard.clone());
1479            extensions.insert(crate::ws::WsIdleTimeout(ws_idle_timeout_ms));
1480            extensions.insert(crate::ws::WsLimits {
1481                max_frame_bytes: ws_max_frame_bytes,
1482                max_message_bytes: ws_max_message_bytes,
1483            });
1484        }
1485        app.process_call(
1486            crate::call::Call::new(parts.method, parts.uri, parts.headers, bytes::Bytes::new())
1487                .with_body_stream(body_stream),
1488            extensions,
1489        )
1490    };
1491    #[cfg(not(feature = "ws"))]
1492    let process = {
1493        let mut extensions = http::Extensions::new();
1494        extensions.insert(crate::call::PeerAddr(peer));
1495        app.process_call(
1496            crate::call::Call::new(parts.method, parts.uri, parts.headers, bytes::Bytes::new())
1497                .with_body_stream(body_stream),
1498            extensions,
1499        )
1500    };
1501
1502    let res = if timeout_ms == 0 {
1503        process.await
1504    } else {
1505        match tokio::time::timeout(std::time::Duration::from_millis(timeout_ms), process).await {
1506            Ok(res) => res,
1507            Err(_) => crate::response::Response::text("Request Timeout")
1508                .with_status(StatusCode::REQUEST_TIMEOUT),
1509        }
1510    };
1511
1512    let mut builder = HyperResponse::builder().status(res.status);
1513    if let Some(headers) = builder.headers_mut() {
1514        *headers = res.headers;
1515    }
1516    Ok(builder
1517        .body(into_boxed_body(res.body))
1518        .expect("response build is infallible"))
1519}