chio_http_serve/lib.rs
1//! Graceful shutdown, connection drain, and server hygiene for Chio HTTP
2//! services.
3//!
4//! Every Chio process that binds an `axum::serve` listener shares the same three
5//! obligations when the platform stops it (SIGTERM on a deploy, scale event, or
6//! node rotation):
7//!
8//! 1. Notice the stop signal instead of dying on the runtime default action.
9//! 2. Stop accepting, let in-flight requests finish, then exit, all inside a
10//! bounded window so a stuck request cannot hold the process open forever.
11//! 3. Refuse abusive load (oversized bodies, slow requests, connection floods)
12//! with an explicit denial rather than unbounded resource growth.
13//!
14//! This crate centralizes those obligations so the serve sites converge on one
15//! implementation instead of drifting apart:
16//!
17//! - [`ShutdownController`] installs a single signal handler and fans the result
18//! out over a [`tokio::sync::watch`] channel that the serve loop and every
19//! cooperating background task observe.
20//! - [`apply_server_hygiene`] wraps a [`Router`](axum::Router) with a request
21//! timeout, a global concurrency limit fronted by load shedding, and an
22//! optional body-size cap.
23//! - [`MaxConnListener`] caps the number of simultaneously accepted connections
24//! with back-pressure at the accept loop.
25//! - [`run_until_drained`] runs the serve future, bounds only the post-signal
26//! drain (never the healthy uptime), and then runs a flush hook so a store
27//! with a queued write is either made durable or surfaced as a loud error.
28//!
29//! The posture is fail-closed throughout. A signal handler that cannot be
30//! installed logs and degrades to the platform default rather than crash-looping
31//! at startup, and each hygiene limit denies (413 / 408 / 503) rather than
32//! accepting work it cannot bound.
33
34#![forbid(unsafe_code)]
35#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
36
37mod drain;
38mod hygiene;
39mod listener;
40mod signal;
41
42pub use drain::{run_until_drained, DrainOutcome, ServeError};
43pub use hygiene::{
44 apply_server_hygiene, ServeHygieneConfig, DEFAULT_DRAIN_TIMEOUT,
45 DEFAULT_MAX_CONCURRENT_REQUESTS, DEFAULT_MAX_CONNECTIONS, DEFAULT_REQUEST_TIMEOUT,
46};
47pub use listener::{CappedPeerAddr, MaxConnListener, PermittedIo};
48pub use signal::{shutdown_signal, ShutdownController};
49
50#[cfg(test)]
51mod tests;