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