Graceful HTTP Shutdown (Rust)
Topics: TcpListener, connection draining, Condvar, shutdown lifecycle
Problem
Run an HTTP server that, on a shutdown signal, stops accepting new connections but lets in-flight
requests finish — and gives up after a drain deadline. This is what makes a rolling deploy /
pod eviction lossless instead of dropping live requests. Unlike the other HTTP challenges, this one
uses a real std::net server (the lifecycle is the point).
pub struct Forced; // returned when the drain deadline passes with requests still in flight
pub fn run<H>(listener: TcpListener, handler: H, cancel: Cancel, drain_timeout: Duration)
-> Result<(), Forced>
where H: Fn(Request) -> Response + Send + Sync + 'static;
- Serve
handler on listener, one thread per connection (serve_conn is provided), until
cancel fires.
- On cancel, stop accepting new connections and wait up to
drain_timeout for in-flight
requests to finish.
- Return
Ok(()) if they drain in time, or Err(Forced) if the deadline passes first.
You implement run (the accept loop + in-flight tracking + drain); the HTTP plumbing (serve_conn)
is provided.
Key concepts
- Non-blocking accept:
set_nonblocking(true) + a short poll lets the accept loop observe the
cancel token instead of blocking forever.
- In-flight tracking +
Condvar: count active requests under a Mutex; on shutdown, dropping
the listener stops new work, and Condvar::wait_timeout blocks until the count hits zero or the
drain deadline — that timeout is the signal to return Forced.
- Drain vs. force: draining preserves correctness for active requests; the deadline bounds how
long shutdown can take so a stuck handler can't block a deploy forever. (Remaining handlers are
left to finish on their own — the time bound is what matters; Go's
srv.Close additionally severs
their sockets.)
Grading
Your solution.rs is compiled together with a trusted tests.rs (which include!s it) using
rustc --test. Only solution.rs is yours to edit.
Sign in to submit your solution.