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}