aws_smithy_http_server/serve/mod.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Serve utilities for running HTTP servers.
7//!
8//! This module provides a convenient [`serve`] function similar to `axum::serve`
9//! for easily serving Tower services with Hyper.
10//!
11//! ## When to Use This Module
12//!
13//! - Use [`serve`] when you need a simple, batteries-included HTTP server
14//! - For more control over the Hyper connection builder, use [`.configure_hyper()`](Serve::configure_hyper)
15//! - For Lambda environments, see the `aws-lambda` feature and `routing::lambda_handler`
16//!
17//! ## How It Works
18//!
19//! The `serve` function creates a connection acceptance loop that:
20//!
21//! 1. **Accepts connections** via the [`Listener`] trait (e.g., [`TcpListener`](tokio::net::TcpListener))
22//! 2. **Creates per-connection services** by calling the `make_service` with [`IncomingStream`]
23//! 3. **Converts Tower services to Hyper** using `TowerToHyperService`
24//! 4. **Spawns a task** for each connection to handle HTTP requests
25//!
26//! ```text
27//! ┌─────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────┐
28//! │Listener │─────▶│IncomingStream│─────▶│ make_service │─────▶│ Hyper │
29//! │ accept │ │ (io + addr) │ │ (Tower) │ │ spawn │
30//! └─────────┘ └──────────────┘ └──────────────┘ └────────┘
31//! ```
32//!
33//! The [`IncomingStream`] provides connection metadata to the service factory,
34//! allowing per-connection customization based on remote address or IO type
35//!
36//! ## HTTP Protocol Selection
37//!
38//! By default, `serve` uses Hyper's automatic HTTP/1 or HTTP/2 selection with
39//! HTTP upgrade support when both protocols are enabled. Customize settings with
40//! [`.configure_hyper()`](Serve::configure_hyper).
41//!
42//! Set `builder.http1_only()` or `builder.http2_only()` in `configure_hyper`
43//! to serve only that protocol. Other protocols are rejected by Hyper before
44//! reaching the request service. Without either setting, both protocols are
45//! automatically detected.
46//!
47//! When both protocols are enabled, `serve` uses Hyper's upgrade-capable API.
48//! With `http1_only()` or `http2_only()`, it uses the regular connection API to
49//! preserve the protocol restriction. HTTP/1-only upgrades require a custom
50//! serve loop using Hyper's native HTTP/1 builder.
51//!
52//! ## Graceful Shutdown
53//!
54//! Graceful shutdown is zero-cost when not used - no watch channels are allocated
55//! and no `tokio::select!` overhead is incurred. Call
56//! [`.with_graceful_shutdown(signal)`](Serve::with_graceful_shutdown) to enable it:
57//!
58//! ```ignore
59//! serve(listener, service)
60//! .with_graceful_shutdown(async {
61//! tokio::signal::ctrl_c().await.expect("failed to listen for Ctrl+C");
62//! })
63//! .await
64//! ```
65//!
66//! This ensures in-flight requests complete before shutdown. Use
67//! [`.with_shutdown_timeout(duration)`](ServeWithGracefulShutdown::with_shutdown_timeout)
68//! to set a maximum wait time.
69//!
70//! ## Common Patterns
71//!
72//! ### Limiting Concurrent Connections
73//!
74//! `serve` limits concurrently accepted connections to 16,384 by default. Configure
75//! or disable the limit on the server future:
76//!
77//! ```rust,ignore
78//! serve(listener, app.into_make_service())
79//! .max_connections(1000)
80//! .await?;
81//! ```
82//!
83//! [`ListenerExt::limit_connections`] remains available for listener-level composition.
84//! The connection permit stays attached to the socket across HTTP upgrades.
85//! Applications that downcast upgraded IO with
86//! `hyper_util::server::conn::auto::upgrade::downcast` must use
87//! `TokioIo<ConnLimiterIo<L::Io>>` as the IO type. [`IncomingStream`] continues
88//! to expose the listener's original IO type to the service factory.
89//!
90//! A one-minute header-read deadline starts when the connection task begins driving
91//! Hyper and ends when Hyper delivers the first complete request head to its service.
92//! It includes protocol negotiation and parsing for either HTTP version. Connection
93//! service creation, Tower readiness, request middleware, and body reads are excluded.
94//! Configure it with [`Serve::header_read_timeout`]. The same setting is applied to
95//! Hyper's HTTP/1 header-read timeout for subsequent requests. **Header-read timeout
96//! protection for every request is currently supported only for HTTP/1.** For HTTP/2,
97//! only the first request head on the connection is covered; later streams have no
98//! header-read deadline. HTTP/2 keep-alive pings do not provide that protection.
99//!
100//! ### Accessing Connection Information
101//!
102//! Use `.into_make_service_with_connect_info::<T>()` to access connection metadata
103//! in your handlers:
104//!
105//! ```rust,ignore
106//! use std::net::SocketAddr;
107//! use aws_smithy_http_server::request::connect_info::ConnectInfo;
108//!
109//! // In your handler:
110//! async fn my_handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
111//! format!("Request from: {}", addr)
112//! }
113//!
114//! // When serving:
115//! serve(
116//! listener,
117//! app.into_make_service_with_connect_info::<SocketAddr>()
118//! ).await?;
119//! ```
120//!
121//! ### Custom TCP Settings
122//!
123//! Use [`ListenerExt::tap_io`] to configure TCP options:
124//!
125//! ```rust,ignore
126//! use aws_smithy_http_server::serve::ListenerExt;
127//!
128//! let listener = TcpListener::bind("127.0.0.1:3000")
129//! .await?
130//! .tap_io(|stream| {
131//! let _ = stream.set_nodelay(true);
132//! });
133//!
134//! serve(listener, app.into_make_service()).await?;
135//! ```
136//!
137//! ## Timeouts and Connection Management
138//!
139//! ### Available Timeout Types
140//!
141//! | Timeout Type | What It Does | How to Configure |
142//! |--------------|--------------|------------------|
143//! | **Header Read** | Per-request protection currently supports HTTP/1 only; HTTP/2 covers only the connection's first request head | 1 minute by default; override via [`.header_read_timeout()`](Serve::header_read_timeout) |
144//! | **Request** | Time limit for processing one request | Tower's `TimeoutLayer` |
145//! | **Connection Duration** | Total connection lifetime limit | Custom accept loop with `tokio::time::timeout` |
146//! | **HTTP/2 Keep-Alive** | Idle timeout between HTTP/2 requests | 20 s ping interval by default; override via `.configure_hyper()` with `.http2().keep_alive_*()` |
147//!
148//! **Examples:**
149//! - `examples/header_read_timeout.rs` - Configure header read timeout
150//! - `examples/request_timeout.rs` - Add request-level timeouts
151//! - `examples/custom_accept_loop.rs` - Implement connection duration limits
152//! - `examples/http2_keepalive.rs` - Configure HTTP/2 keep-alive
153//! - `examples/connection_limiting.rs` - Limit concurrent connections
154//! - `examples/request_concurrency_limiting.rs` - Limit concurrent requests
155//!
156//! ### Connection Duration vs Idle Timeout
157//!
158//! **Connection duration timeout**: Closes the connection after N seconds total, regardless of activity.
159//! Implemented with `tokio::time::timeout` wrapping the connection future.
160//!
161//! **Idle timeout**: Closes the connection only when inactive between requests.
162//! - HTTP/2: Available via `.keep_alive_interval()` and `.keep_alive_timeout()`
163//! - HTTP/1.1: Not available without modifying Hyper
164//!
165//! See `examples/custom_accept_loop.rs` for a working connection duration timeout example.
166//!
167//! ### Connection Limiting vs Request Limiting
168//!
169//! **Connection limiting** (`.limit_connections()`): Limits the number of TCP connections.
170//! Use this to prevent socket/file descriptor exhaustion.
171//!
172//! **Request limiting** (`ConcurrencyLimitLayer`): Limits in-flight requests.
173//! Use this to prevent work queue exhaustion. With HTTP/2, one connection can have multiple
174//! requests in flight simultaneously.
175//!
176//! Most applications should use both - they protect different layers.
177//!
178//! ## Troubleshooting
179//!
180//! ### Type Errors
181//!
182//! If you encounter complex error messages about trait bounds, check:
183//!
184//! 1. **Service Error Type**: Your service must have `Error = Infallible`
185//! ```rust,ignore
186//! // ✓ Correct - handlers return responses, not Results
187//! async fn handler() -> Response<Body> { ... }
188//!
189//! // ✗ Wrong - cannot use Result<Response, E>
190//! async fn handler() -> Result<Response<Body>, MyError> { ... }
191//! ```
192//!
193//! 2. **MakeService Wrapper**: Use the correct wrapper for your service:
194//! ```rust,ignore
195//! use aws_smithy_http_server::routing::IntoMakeService;
196//!
197//! // For Smithy services:
198//! app.into_make_service()
199//!
200//! // For services with middleware:
201//! IntoMakeService::new(service)
202//! ```
203//!
204//! ### Graceful Shutdown Not Working
205//!
206//! If graceful shutdown doesn't wait for connections:
207//!
208//! - Ensure you call `.with_graceful_shutdown()` **before** `.await`
209//! - The signal future must be `Send + 'static`
210//! - Consider adding a timeout with `.with_shutdown_timeout()`
211//!
212//! ### Connection Limit Not Applied
213//!
214//! Remember that `.limit_connections()` applies to the listener **before** passing
215//! it to `serve()`:
216//!
217//! ```rust,ignore
218//! // ✓ Correct
219//! let listener = TcpListener::bind("127.0.0.1:3000")
220//! .await?
221//! .limit_connections(100);
222//! serve(listener, app.into_make_service()).await?;
223//!
224//! // ✗ Wrong - limit_connections must be called on listener
225//! serve(TcpListener::bind("127.0.0.1:3000").await?, app.into_make_service())
226//! .limit_connections(100) // This method doesn't exist on Serve
227//! .await?;
228//! ```
229//!
230//! ## Advanced: Custom Connection Handling
231//!
232//! If you need per-connection customization (e.g., different Hyper settings based on
233//! the remote address), you can implement your own connection loop using the building
234//! blocks provided by this module:
235//!
236//! ```rust,ignore
237//! use aws_smithy_http_server::routing::IntoMakeService;
238//! use aws_smithy_http_server::serve::Listener;
239//! use hyper_util::rt::{TokioExecutor, TokioIo};
240//! use hyper_util::server::conn::auto::Builder;
241//! use hyper_util::service::TowerToHyperService;
242//! use tower::ServiceExt;
243//!
244//! let mut listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
245//! let make_service = app.into_make_service_with_connect_info::<SocketAddr>();
246//!
247//! loop {
248//! let (stream, remote_addr) = listener.accept().await?;
249//! let io = TokioIo::new(stream);
250//!
251//! // Per-connection Hyper configuration
252//! let mut builder = Builder::new(TokioExecutor::new());
253//! if remote_addr.ip().is_loopback() {
254//! builder = builder.http2_only(); // Local connections use HTTP/2
255//! } else {
256//! builder = builder.http1().keep_alive(true); // External use HTTP/1
257//! }
258//!
259//! let tower_service = make_service
260//! .ready()
261//! .await?
262//! .call(IncomingStream { io: &io, remote_addr })
263//! .await?;
264//!
265//! let hyper_service = TowerToHyperService::new(tower_service);
266//!
267//! tokio::spawn(async move {
268//! if let Err(err) = builder.serve_connection(io, hyper_service).await {
269//! eprintln!("Error serving connection: {}", err);
270//! }
271//! });
272//! }
273//! ```
274//!
275//! This approach provides complete flexibility while still leveraging the efficient
276//! Hyper and Tower integration provided by this module.
277//!
278//! Portions of the implementation are adapted from axum
279//! (<https://github.com/tokio-rs/axum>), which is licensed under the MIT License.
280//! Copyright (c) 2019 Axum Contributors
281
282use std::convert::Infallible;
283use std::error::Error as StdError;
284use std::fmt::{self, Debug};
285use std::future::{Future, IntoFuture};
286use std::io;
287use std::marker::PhantomData;
288use std::pin::Pin;
289use std::sync::Arc;
290use std::time::Duration;
291
292use http_body::Body as HttpBody;
293use hyper::body::Incoming;
294use hyper_util::rt::{TokioExecutor, TokioIo, TokioTimer};
295use hyper_util::server::conn::auto::Builder;
296use hyper_util::server::graceful::{GracefulConnection, GracefulShutdown, Watcher};
297use hyper_util::service::TowerToHyperService;
298use tokio::sync::OwnedSemaphorePermit;
299use tower::{Service, ServiceExt as _};
300
301mod header_read_timeout;
302mod listener;
303
304use self::header_read_timeout::{HeaderReadDeadline, HeaderReadTimeoutService};
305
306use self::listener::ConnectionLimit;
307pub use self::listener::{ConnLimiter, ConnLimiterIo, Listener, ListenerExt, TapIo};
308
309use crate::settings::{default_connection_builder, ServeSettings};
310pub use crate::settings::{
311 DEFAULT_HEADER_READ_TIMEOUT, DEFAULT_HTTP2_KEEP_ALIVE_INTERVAL, DEFAULT_HTTP2_KEEP_ALIVE_TIMEOUT,
312 DEFAULT_MAX_CONNECTIONS, DEFAULT_SHUTDOWN_TIMEOUT,
313};
314
315// ============================================================================
316// Type Bounds Documentation
317// ============================================================================
318//
319// ## Body Bounds (B)
320// HTTP response bodies must satisfy:
321// - `B: HttpBody + Send + 'static` - Implement the body trait and be sendable
322// - `B::Data: Send` - Data chunks must be sendable across threads
323// - `B::Error: Into<Box<dyn StdError + Send + Sync>>` - Errors must be convertible
324//
325// ## Service Bounds (S)
326//
327// The `S` type parameter represents a **per-connection HTTP service** - a Tower service
328// that handles individual HTTP requests and returns HTTP responses.
329//
330// Required bounds:
331// - `S: Service<http::Request<Incoming>, Response = http::Response<B>, Error = Infallible>`
332//
333// This is the core Tower Service trait. It means:
334// * **Input**: Takes an HTTP request with a streaming body (`Incoming` from Hyper)
335// * **Output**: Returns an HTTP response with body type `B`
336// * **Error**: Must be `Infallible`, meaning the service never returns errors at the
337// Tower level. Any application errors must be converted into HTTP responses
338// (e.g., 500 Internal Server Error) before reaching this layer.
339//
340// - `S: Clone + Send + 'static`
341// * **Clone**: Each HTTP/1.1 or HTTP/2 connection may handle multiple requests
342// sequentially or concurrently. The service must be cloneable so each request
343// can get its own copy.
344// * **Send**: The service will be moved into a spawned Tokio task, so it must be
345// safe to send across thread boundaries.
346// * **'static**: No borrowed references - the service must own all its data since
347// it will outlive the connection setup phase.
348//
349// - `S::Future: Send`
350// The future returned by `Service::call()` must also be `Send` so it can be
351// polled from any thread in Tokio's thread pool.
352//
353// ## MakeService Bounds (M)
354//
355// The `M` type parameter represents a **service factory** - a Tower service that
356// creates a new `S` service for each incoming connection. This allows customizing
357// services based on connection metadata (remote address, TLS info, etc.).
358//
359// Connection Info → Service Factory → Per-Connection Service
360//
361// Required bounds:
362// - `M: for<'a> Service<IncomingStream<'a, L>, Error = Infallible, Response = S>`
363//
364// This is the service factory itself:
365// * **Input**: `IncomingStream<'a, L>` - A struct containing connection metadata:
366// - `io: &'a TokioIo<L::Io>` - A borrowed reference to the connection's IO stream
367// - `remote_addr: L::Addr` - The remote address of the client
368//
369// * **Output**: Returns a new `S` service instance for this specific connection
370//
371// * **Error**: Must be `Infallible` - service creation must never fail
372//
373// * **Higher-Rank Trait Bound (`for<'a>`)**: The factory must work
374// with `IncomingStream` that borrows the IO with *any* lifetime `'a`. This is
375// necessary because the IO is borrowed only temporarily during service creation,
376// and we don't know the specific lifetime at compile time.
377//
378// - `for<'a> <M as Service<IncomingStream<'a, L>>>::Future: Send`
379//
380// The future returned by calling the make_service must be `Send` for any lifetime,
381// so it can be awaited across threads while creating the service.
382//
383// ## Example Flow
384//
385// ```text
386// 1. Listener.accept() → (io, remote_addr)
387// 2. make_service.call(IncomingStream { io: &io, remote_addr }) → Future<Output = S>
388// 3. service.call(request) → Future<Output = Response>
389// 4. Repeat step 3 for each request on the connection
390// ```
391//
392// ## Why These Bounds Matter
393//
394// 1. **Services can be spawned onto Tokio tasks** (Send + 'static)
395// 2. **Multiple requests can be handled per connection** (Clone)
396// 3. **Error handling is infallible** - errors become HTTP responses, not Tower errors
397// 4. **The MakeService works with borrowed connection info** - via HRTB with IncomingStream
398// This allows inspection of connection metadata without transferring ownership
399//
400// ============================================================================
401
402/// An incoming stream that bundles connection information.
403///
404/// This struct serves as the request type for the `make_service` Tower service,
405/// allowing it to access connection-level metadata when creating per-connection services.
406///
407/// # Purpose
408///
409/// In Tower/Hyper's model, `make_service` is called once per connection to create
410/// a service that handles all HTTP requests on that connection. `IncomingStream`
411/// provides the connection information needed to customize service creation based on:
412/// - The remote address (for logging or access control)
413/// - The underlying IO type (for connection configuration)
414///
415/// # Design
416///
417/// This type holds a **reference** to the IO rather than ownership because:
418/// - The actual IO is still needed by Hyper to serve the connection after `make_service` returns
419/// - The `make_service` only needs to inspect connection metadata, not take ownership
420///
421/// # Lifetime Safety
422///
423/// The lifetime `'a` ensures the reference to IO remains valid only during the
424/// `make_service.call()` invocation. After the service is created, the IO is
425/// moved into a spawned task to handle the connection. This is safe because:
426///
427/// ```text
428/// let io = TokioIo::new(stream); // IO created
429/// let service = make_service.call(
430/// IncomingStream { io: &io, .. } // Borrowed during call
431/// ).await; // Borrow ends
432/// tokio::spawn(serve_connection(io, ..)); // IO moved to task
433/// ```
434///
435/// The borrow checker guarantees the reference doesn't outlive the IO object.
436///
437/// Used with [`serve`] and [`crate::routing::IntoMakeServiceWithConnectInfo`].
438#[derive(Debug)]
439pub struct IncomingStream<'a, L>
440where
441 L: Listener,
442{
443 /// Reference to the IO for this connection
444 pub io: &'a TokioIo<L::Io>,
445 /// Remote address of the client
446 pub remote_addr: L::Addr,
447}
448
449impl<L> IncomingStream<'_, L>
450where
451 L: Listener,
452{
453 /// Get a reference to the inner IO type.
454 pub fn io(&self) -> &L::Io {
455 self.io.inner()
456 }
457
458 /// Returns the remote address that this stream is bound to.
459 pub fn remote_addr(&self) -> &L::Addr {
460 &self.remote_addr
461 }
462}
463
464/// Serve the service with the supplied listener.
465///
466/// By default, this limits concurrently accepted connections to 16,384. Use
467/// [`Serve::max_connections`] to configure the limit or
468/// [`Serve::disable_connection_limit`] to disable it.
469///
470/// A one-minute deadline bounds the first request head for either HTTP version and
471/// subsequent HTTP/1 header reads. Per-request header-read timeout protection is
472/// currently HTTP/1-only; later HTTP/2 streams have no header-read deadline. The
473/// default connection builder also applies a 20-second HTTP/2 keep-alive ping interval,
474/// which does not enforce a header-read deadline.
475///
476/// This implementation provides zero-cost abstraction for shutdown coordination.
477/// When graceful shutdown is not used, there is no runtime overhead - no watch channels
478/// are allocated and no `tokio::select!` is used.
479///
480/// It supports both HTTP/1 as well as HTTP/2.
481///
482/// This function accepts services wrapped with [`crate::routing::IntoMakeService`] or
483/// [`crate::routing::IntoMakeServiceWithConnectInfo`].
484///
485/// For generated Smithy services, use `.into_make_service()` or
486/// `.into_make_service_with_connect_info::<C>()`. For services wrapped with
487/// Tower middleware, use `IntoMakeService::new(service)`.
488///
489/// # Error Handling
490///
491/// Note that both `make_service` and the generated service must have `Error = Infallible`.
492/// This means:
493/// - Your service factory cannot fail when creating per-connection services
494/// - Your request handlers cannot return errors (use proper HTTP error responses instead)
495///
496/// If you need fallible service creation, consider handling errors within your
497/// `make_service` implementation and returning a service that produces error responses.
498///
499/// # Examples
500///
501/// Serving a Smithy service with a TCP listener:
502///
503/// ```rust,ignore
504/// use tokio::net::TcpListener;
505///
506/// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
507/// aws_smithy_http_server::serve(listener, app.into_make_service()).await.unwrap();
508/// ```
509///
510/// Serving with middleware applied:
511///
512/// ```rust,ignore
513/// use tokio::net::TcpListener;
514/// use tower::Layer;
515/// use tower_http::timeout::TimeoutLayer;
516/// use http::StatusCode;
517/// use std::time::Duration;
518/// use aws_smithy_http_server::routing::IntoMakeService;
519///
520/// let app = /* ... build service ... */;
521/// let app = TimeoutLayer::new(Duration::from_secs(30)).layer(app);
522///
523/// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
524/// aws_smithy_http_server::serve(listener, IntoMakeService::new(app)).await.unwrap();
525/// ```
526///
527/// For graceful shutdown:
528///
529/// ```rust,ignore
530/// use tokio::net::TcpListener;
531/// use tokio::signal;
532///
533/// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
534/// aws_smithy_http_server::serve(listener, app.into_make_service())
535/// .with_graceful_shutdown(async {
536/// signal::ctrl_c().await.expect("failed to listen for Ctrl+C");
537/// })
538/// .await
539/// .unwrap();
540/// ```
541///
542/// With connection info:
543///
544/// ```rust,ignore
545/// use tokio::net::TcpListener;
546/// use std::net::SocketAddr;
547///
548/// let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
549/// aws_smithy_http_server::serve(
550/// listener,
551/// app.into_make_service_with_connect_info::<SocketAddr>()
552/// )
553/// .await
554/// .unwrap();
555/// ```
556pub fn serve<L, M, S, B>(listener: L, make_service: M) -> Serve<L, M, S, B>
557where
558 L: Listener,
559 // Body bounds: see module documentation for details
560 B: HttpBody + Send + 'static,
561 B::Data: Send,
562 B::Error: Into<Box<dyn StdError + Send + Sync>>,
563 // Service bounds: see module documentation for details
564 S: Service<http::Request<Incoming>, Response = http::Response<B>, Error = Infallible> + Clone + Send + 'static,
565 S::Future: Send,
566 // MakeService bounds: see module documentation for details
567 M: for<'a> Service<IncomingStream<'a, L>, Error = Infallible, Response = S>,
568{
569 Serve {
570 listener,
571 make_service,
572 settings: ServeSettings::default(),
573 _marker: PhantomData,
574 }
575}
576
577/// A server future that serves HTTP connections.
578///
579/// This is the return type of [`serve`]. It implements [`IntoFuture`], so
580/// you can directly `.await` it:
581///
582/// ```ignore
583/// serve(listener, service).await?;
584/// ```
585///
586/// Before awaiting, you can configure it:
587/// - [`configure_hyper`](Self::configure_hyper) - Configure Hyper's automatic connection builder
588/// - [`max_connections`](Self::max_connections) - Configure the accepted-connection limit
589/// - [`header_read_timeout`](Self::header_read_timeout) - Configure the request-header deadline
590/// - [`with_graceful_shutdown`](Self::with_graceful_shutdown) - Enable graceful shutdown
591/// - [`local_addr`](Self::local_addr) - Get the bound address
592///
593/// Created by [`serve`].
594#[must_use = "Serve does nothing until you `.await` or call `.into_future()` on it"]
595pub struct Serve<L, M, S, B> {
596 listener: L,
597 make_service: M,
598 settings: ServeSettings,
599 _marker: PhantomData<(S, B)>,
600}
601
602impl<L, M, S, B> fmt::Debug for Serve<L, M, S, B>
603where
604 L: fmt::Debug,
605{
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 f.debug_struct("Serve")
608 .field("listener", &self.listener)
609 .field("has_hyper_config", &self.settings.hyper_builder.is_some())
610 .field("max_connections", &self.settings.max_connections)
611 .field("header_read_timeout", &self.settings.header_read_timeout)
612 .finish_non_exhaustive()
613 }
614}
615
616impl<L, M, S, B> Serve<L, M, S, B>
617where
618 L: Listener,
619{
620 /// Configure the underlying Hyper connection builder.
621 ///
622 /// This allows you to customize Hyper's HTTP/1 and HTTP/2 settings,
623 /// such as timeouts, max concurrent streams, keep-alive behavior, etc.
624 ///
625 /// **Note:** Calling this replaces the default builder entirely, opting out of the built-in
626 /// HTTP/2 defaults. You are responsible for setting your own HTTP/2 timeouts.
627 /// The connection limit and [`Self::header_read_timeout`] remain enabled.
628 /// When HTTP/1 is enabled, `serve` supplies its timer and applies
629 /// `header_read_timeout` after this configuration, overriding any HTTP/1
630 /// header-read timeout set on the builder.
631 ///
632 /// `http1_only()` and `http2_only()` restrict the accepted protocol and use
633 /// Hyper's regular connection API without HTTP/1 upgrades. When both
634 /// protocols are enabled, `serve` uses `serve_connection_with_upgrades`.
635 /// For HTTP/1-only upgrades, use a custom serve loop.
636 ///
637 /// The configuration callback runs once when this method is called. The
638 /// configured builder is shared across connections.
639 ///
640 /// # Example
641 ///
642 /// ```ignore
643 /// use std::time::Duration;
644 ///
645 /// serve(listener, service)
646 /// .configure_hyper(|mut builder| {
647 /// builder.http1().keep_alive(true);
648 /// builder.http2().max_concurrent_streams(200);
649 /// builder
650 /// })
651 /// .await?;
652 /// ```
653 ///
654 /// # Advanced: Per-Connection Configuration
655 ///
656 /// If you need per-connection customization (e.g., different settings based on
657 /// the remote address), you can implement your own connection loop. See the
658 /// module-level documentation for examples.
659 pub fn configure_hyper<F>(mut self, f: F) -> Self
660 where
661 F: FnOnce(
662 hyper_util::server::conn::auto::Builder<hyper_util::rt::TokioExecutor>,
663 ) -> hyper_util::server::conn::auto::Builder<hyper_util::rt::TokioExecutor>,
664 {
665 let builder = Builder::new(TokioExecutor::new());
666 self.settings.hyper_builder = Some(Arc::new(f(builder)));
667 self
668 }
669
670 /// Set the maximum number of concurrently accepted connections.
671 ///
672 /// The default limit is 16,384. Once the limit is reached, `serve` stops
673 /// accepting connections until an existing connection closes.
674 ///
675 /// Passing `0` disables the limit, equivalent to
676 /// [`disable_connection_limit`](Self::disable_connection_limit). Values
677 /// above [`tokio::sync::Semaphore::MAX_PERMITS`] are clamped to that value.
678 pub fn max_connections(mut self, max: usize) -> Self {
679 self.settings.max_connections = (max > 0).then_some(max);
680 self
681 }
682
683 /// Disable the default accepted-connection limit.
684 pub fn disable_connection_limit(mut self) -> Self {
685 self.settings.max_connections = None;
686 self
687 }
688
689 /// Set the timeout for receiving a complete request head.
690 ///
691 /// The default is one minute. The first-request deadline starts when the
692 /// connection task begins driving Hyper, after `make_service` finishes. It
693 /// includes protocol negotiation and header parsing and does not reset as
694 /// bytes arrive. It ends before Tower readiness, middleware, or body reads.
695 ///
696 /// The same setting configures Hyper's HTTP/1 header-read timeout for later
697 /// requests. **Per-request header-read timeout protection is currently supported
698 /// only for HTTP/1.** HTTP/2 is covered only until the first request head arrives;
699 /// later HTTP/2 streams have no header-read deadline. This deadline does not
700 /// limit body reads or time spent waiting for server events.
701 /// This setting takes precedence over the HTTP/1 header-read timeout supplied
702 /// through [`Self::configure_hyper`]. Pass `None` or `Some(Duration::ZERO)` to
703 /// disable both deadlines.
704 pub fn header_read_timeout(mut self, timeout: Option<Duration>) -> Self {
705 self.settings.header_read_timeout = timeout.filter(|timeout| !timeout.is_zero());
706 self
707 }
708
709 /// Enable graceful shutdown for the server.
710 pub fn with_graceful_shutdown<F>(self, signal: F) -> ServeWithGracefulShutdown<L, M, S, F, B>
711 where
712 F: Future<Output = ()> + Send + 'static,
713 {
714 ServeWithGracefulShutdown { serve: self, signal }
715 }
716
717 /// Returns the local address this server is bound to.
718 pub fn local_addr(&self) -> io::Result<L::Addr> {
719 self.listener.local_addr()
720 }
721}
722
723// Implement IntoFuture so we can await Serve directly
724impl<L, M, S, B> IntoFuture for Serve<L, M, S, B>
725where
726 L: Listener,
727 L::Addr: Debug,
728 // Body bounds
729 B: HttpBody + Send + 'static,
730 B::Data: Send,
731 B::Error: Into<Box<dyn StdError + Send + Sync>>,
732 // Service bounds
733 S: Service<http::Request<Incoming>, Response = http::Response<B>, Error = Infallible> + Clone + Send + 'static,
734 S::Future: Send,
735 // MakeService bounds
736 M: for<'a> Service<IncomingStream<'a, L>, Error = Infallible, Response = S> + Send + 'static,
737 for<'a> <M as Service<IncomingStream<'a, L>>>::Future: Send,
738{
739 type Output = io::Result<()>;
740 type IntoFuture = Pin<Box<dyn Future<Output = io::Result<()>> + Send>>;
741
742 fn into_future(self) -> Self::IntoFuture {
743 Box::pin(async move {
744 let Self {
745 mut listener,
746 mut make_service,
747 settings:
748 ServeSettings {
749 hyper_builder,
750 max_connections,
751 header_read_timeout,
752 ..
753 },
754 _marker,
755 } = self;
756 let connection_limiter = max_connections.map(ConnectionLimit::new).map(Arc::new);
757 let hyper_builder = connection_builder(hyper_builder, header_read_timeout);
758
759 loop {
760 let connection_permit = acquire_connection_permit(connection_limiter.as_ref()).await;
761 let (io, remote_addr) = listener.accept().await;
762 handle_connection::<L, M, S, B>(
763 &mut make_service,
764 io,
765 remote_addr,
766 &hyper_builder,
767 header_read_timeout,
768 None,
769 connection_permit,
770 )
771 .await;
772 }
773 })
774 }
775}
776
777/// A server future with graceful shutdown enabled.
778///
779/// This type is created by calling [`Serve::with_graceful_shutdown`]. It implements
780/// [`IntoFuture`], so you can directly `.await` it.
781///
782/// When the shutdown signal completes, the server will:
783/// 1. Stop accepting new connections
784/// 2. Wait for all in-flight requests to complete (or until timeout if configured)
785/// 3. Return once all connections are closed
786///
787/// Configure the shutdown timeout with [`with_shutdown_timeout`](Self::with_shutdown_timeout).
788///
789/// Created by [`Serve::with_graceful_shutdown`].
790#[must_use = "ServeWithGracefulShutdown does nothing until you `.await` or call `.into_future()` on it"]
791pub struct ServeWithGracefulShutdown<L, M, S, F, B> {
792 serve: Serve<L, M, S, B>,
793 signal: F,
794}
795
796impl<L, M, S, F, B> fmt::Debug for ServeWithGracefulShutdown<L, M, S, F, B>
797where
798 L: Listener + fmt::Debug,
799{
800 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801 f.debug_struct("ServeWithGracefulShutdown")
802 .field("listener", &self.serve.listener)
803 .field("has_hyper_config", &self.serve.settings.hyper_builder.is_some())
804 .field("shutdown_timeout", &self.serve.settings.shutdown_timeout)
805 .field("max_connections", &self.serve.settings.max_connections)
806 .field("header_read_timeout", &self.serve.settings.header_read_timeout)
807 .finish_non_exhaustive()
808 }
809}
810
811impl<L: Listener, M, S, F, B> ServeWithGracefulShutdown<L, M, S, F, B> {
812 /// Set a timeout for graceful shutdown.
813 ///
814 /// If the timeout expires before all connections complete, a warning is logged
815 /// and the server returns successfully. Note that this does **not** forcibly
816 /// terminate connections - it only stops waiting for them.
817 ///
818 /// # Example
819 ///
820 /// ```rust,ignore
821 /// use std::time::Duration;
822 ///
823 /// serve(listener, app.into_make_service())
824 /// .with_graceful_shutdown(shutdown_signal())
825 /// .with_shutdown_timeout(Duration::from_secs(30))
826 /// .await?; // Returns Ok(()) even if timeout expires
827 /// ```
828 pub fn with_shutdown_timeout(mut self, timeout: Duration) -> Self {
829 self.serve.settings.shutdown_timeout = Some(timeout);
830 self
831 }
832
833 /// Set the maximum number of concurrently accepted connections.
834 ///
835 /// The default limit is 16,384. Once the limit is reached, `serve` stops
836 /// accepting connections until an existing connection closes.
837 ///
838 /// Passing `0` disables the limit, equivalent to
839 /// [`disable_connection_limit`](Self::disable_connection_limit). Values
840 /// above [`tokio::sync::Semaphore::MAX_PERMITS`] are clamped to that value.
841 pub fn max_connections(mut self, max: usize) -> Self {
842 self.serve = self.serve.max_connections(max);
843 self
844 }
845
846 /// Disable the default accepted-connection limit.
847 pub fn disable_connection_limit(mut self) -> Self {
848 self.serve = self.serve.disable_connection_limit();
849 self
850 }
851
852 /// Set the timeout for receiving a complete request head.
853 ///
854 /// The default is one minute. Pass `None` or `Some(Duration::ZERO)` to disable it.
855 /// See [`Serve::header_read_timeout`] for details.
856 pub fn header_read_timeout(mut self, timeout: Option<Duration>) -> Self {
857 self.serve = self.serve.header_read_timeout(timeout);
858 self
859 }
860
861 /// Returns the local address this server is bound to.
862 pub fn local_addr(&self) -> io::Result<L::Addr> {
863 self.serve.local_addr()
864 }
865}
866
867// Implement IntoFuture so we can await WithGracefulShutdown directly
868impl<L, M, S, F, B> IntoFuture for ServeWithGracefulShutdown<L, M, S, F, B>
869where
870 L: Listener,
871 L::Addr: Debug,
872 // Body bounds
873 B: HttpBody + Send + 'static,
874 B::Data: Send,
875 B::Error: Into<Box<dyn StdError + Send + Sync>>,
876 // Service bounds
877 S: Service<http::Request<Incoming>, Response = http::Response<B>, Error = Infallible> + Clone + Send + 'static,
878 S::Future: Send,
879 // MakeService bounds
880 M: for<'a> Service<IncomingStream<'a, L>, Error = Infallible, Response = S> + Send + 'static,
881 for<'a> <M as Service<IncomingStream<'a, L>>>::Future: Send,
882 // Shutdown signal
883 F: Future<Output = ()> + Send + 'static,
884{
885 type Output = io::Result<()>;
886 type IntoFuture = Pin<Box<dyn Future<Output = io::Result<()>> + Send>>;
887
888 fn into_future(self) -> Self::IntoFuture {
889 Box::pin(async move {
890 let Self {
891 serve:
892 Serve {
893 mut listener,
894 mut make_service,
895 settings:
896 ServeSettings {
897 hyper_builder,
898 max_connections,
899 header_read_timeout,
900 shutdown_timeout,
901 },
902 _marker,
903 },
904 signal,
905 } = self;
906 let connection_limiter = max_connections.map(ConnectionLimit::new).map(Arc::new);
907 let hyper_builder = connection_builder(hyper_builder, header_read_timeout);
908
909 // Initialize graceful shutdown
910 let graceful = GracefulShutdown::new();
911 let mut signal = std::pin::pin!(signal);
912
913 loop {
914 let connection_permit = tokio::select! {
915 permit = acquire_connection_permit(connection_limiter.as_ref()) => permit,
916 _ = signal.as_mut() => {
917 tracing::trace!("received graceful shutdown signal, not accepting new connections");
918 break;
919 }
920 };
921
922 tokio::select! {
923 (io, remote_addr) = listener.accept() => {
924 handle_connection::<L, M, S, B>(
925 &mut make_service,
926 io,
927 remote_addr,
928 &hyper_builder,
929 header_read_timeout,
930 Some(&graceful),
931 connection_permit,
932 )
933 .await;
934 }
935 _ = signal.as_mut() => {
936 tracing::trace!("received graceful shutdown signal, not accepting new connections");
937 break;
938 }
939 }
940 }
941
942 drop(listener);
943
944 tracing::trace!("waiting for in-flight connections to finish");
945
946 // Wait for all in-flight connections (with optional timeout)
947 match shutdown_timeout {
948 Some(timeout) => match tokio::time::timeout(timeout, graceful.shutdown()).await {
949 Ok(_) => {
950 tracing::trace!("all in-flight connections completed during graceful shutdown");
951 }
952 Err(_) => {
953 tracing::warn!(
954 timeout_secs = timeout.as_secs(),
955 "graceful shutdown timeout expired, some connections may not have completed"
956 );
957 }
958 },
959 None => {
960 graceful.shutdown().await;
961 tracing::trace!("all in-flight connections completed during graceful shutdown");
962 }
963 }
964
965 Ok(())
966 })
967 }
968}
969
970/// Connection handling function.
971///
972/// Handles connections with HTTP upgrade support and optional graceful shutdown.
973async fn handle_connection<L, M, S, B>(
974 make_service: &mut M,
975 conn_io: <L as Listener>::Io,
976 remote_addr: <L as Listener>::Addr,
977 hyper_builder: &Arc<Builder<TokioExecutor>>,
978 header_read_timeout: Option<Duration>,
979 graceful: Option<&GracefulShutdown>,
980 connection_permit: Option<OwnedSemaphorePermit>,
981) where
982 L: Listener,
983 L::Addr: Debug,
984 // Body bounds
985 B: HttpBody + Send + 'static,
986 B::Data: Send,
987 B::Error: Into<Box<dyn StdError + Send + Sync>>,
988 // Service bounds
989 S: Service<http::Request<Incoming>, Response = http::Response<B>, Error = Infallible> + Clone + Send + 'static,
990 S::Future: Send,
991 // MakeService bounds
992 M: for<'a> Service<IncomingStream<'a, L>, Error = Infallible, Response = S> + Send + 'static,
993 for<'a> <M as Service<IncomingStream<'a, L>>>::Future: Send,
994{
995 let watcher = graceful.map(|g| g.watcher());
996 let tokio_io = TokioIo::new(conn_io);
997
998 tracing::trace!("connection {remote_addr:?} accepted");
999
1000 make_service
1001 .ready()
1002 .await
1003 .expect("make_service error type is Infallible and cannot fail");
1004
1005 let tower_service = make_service
1006 .call(IncomingStream {
1007 io: &tokio_io,
1008 remote_addr,
1009 })
1010 .await
1011 .expect("make_service error type is Infallible and cannot fail");
1012
1013 let hyper_service = TowerToHyperService::new(tower_service);
1014
1015 let builder = Arc::clone(hyper_builder);
1016
1017 // Preserve IncomingStream's original IO type while constructing the service.
1018 // Hyper then owns the permit through the IO, including after an HTTP upgrade.
1019 let tokio_io = TokioIo::new(ConnLimiterIo::new(tokio_io.into_inner(), connection_permit));
1020 tokio::spawn(async move {
1021 let result = if let Some(timeout) = header_read_timeout {
1022 let (service, deadline) = HeaderReadTimeoutService::new(hyper_service, timeout);
1023 serve_connection(&builder, tokio_io, service, watcher, Some(deadline)).await
1024 } else {
1025 serve_connection(&builder, tokio_io, hyper_service, watcher, None).await
1026 };
1027
1028 if let Err(err) = result {
1029 tracing::trace!(error = ?err, "failed to serve connection");
1030 }
1031 });
1032}
1033
1034async fn serve_connection<I, S, B>(
1035 builder: &Builder<TokioExecutor>,
1036 io: I,
1037 service: S,
1038 watcher: Option<Watcher>,
1039 header_deadline: Option<HeaderReadDeadline>,
1040) -> io::Result<()>
1041where
1042 I: hyper::rt::Read + hyper::rt::Write + Unpin + Send + 'static,
1043 S: hyper::service::Service<http::Request<Incoming>, Response = http::Response<B>> + Send + 'static,
1044 S::Future: Send + 'static,
1045 S::Error: Into<Box<dyn StdError + Send + Sync>>,
1046 B: HttpBody + Send + 'static,
1047 B::Data: Send,
1048 B::Error: Into<Box<dyn StdError + Send + Sync>>,
1049{
1050 // Hyper's upgrade-capable API ignores http1_only()/http2_only(), so use
1051 // it only when both protocols are enabled.
1052 if builder.is_http1_available() && builder.is_http2_available() {
1053 let connection = builder.serve_connection_with_upgrades(io, service);
1054 drive_connection(connection, watcher, header_deadline).await
1055 } else {
1056 let connection = builder.serve_connection(io, service);
1057 drive_connection(connection, watcher, header_deadline).await
1058 }
1059}
1060
1061async fn drive_connection<C>(
1062 connection: C,
1063 watcher: Option<Watcher>,
1064 header_deadline: Option<HeaderReadDeadline>,
1065) -> io::Result<()>
1066where
1067 C: GracefulConnection,
1068 C::Error: Into<Box<dyn StdError + Send + Sync>>,
1069{
1070 if let Some(watcher) = watcher {
1071 let connection = watcher.watch(connection);
1072 if let Some(deadline) = header_deadline {
1073 deadline.run(connection).await
1074 } else {
1075 connection.await.map_err(io::Error::other)
1076 }
1077 } else if let Some(deadline) = header_deadline {
1078 deadline.run(connection).await
1079 } else {
1080 connection.await.map_err(io::Error::other)
1081 }
1082}
1083
1084fn connection_builder(
1085 configured: Option<Arc<Builder<TokioExecutor>>>,
1086 header_read_timeout: Option<Duration>,
1087) -> Arc<Builder<TokioExecutor>> {
1088 let mut builder = configured
1089 .map(Arc::unwrap_or_clone)
1090 .unwrap_or_else(default_connection_builder);
1091 if builder.is_http1_available() {
1092 builder
1093 .http1()
1094 .timer(TokioTimer::new())
1095 .header_read_timeout(header_read_timeout);
1096 }
1097 Arc::new(builder)
1098}
1099
1100async fn acquire_connection_permit(connection_limiter: Option<&Arc<ConnectionLimit>>) -> Option<OwnedSemaphorePermit> {
1101 match connection_limiter {
1102 Some(limit) => Some(limit.acquire().await),
1103 None => None,
1104 }
1105}