Skip to main content

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