Skip to main content

altair_server/
server.rs

1//! `Server` — the constructed runtime that binds, serves, and shuts down.
2
3use crate::builder::ServerBuilder;
4use crate::error::{Error, Result};
5use crate::shutdown::shutdown_signal;
6use axum::Router;
7use std::net::SocketAddr;
8use std::time::Duration;
9use tokio::net::TcpListener;
10
11/// Configured server, bound to a TCP listener.
12///
13/// Build via [`Server::builder`].
14pub struct Server {
15    router: Router<()>,
16    listener: TcpListener,
17    local_addr: SocketAddr,
18    shutdown_timeout: Option<Duration>,
19}
20
21impl Server {
22    /// Start building a new server.
23    pub fn builder() -> ServerBuilder {
24        ServerBuilder::new()
25    }
26
27    /// Internal constructor used by [`ServerBuilder::build`].
28    pub(crate) fn from_parts(
29        router: Router<()>,
30        listener: TcpListener,
31        local_addr: SocketAddr,
32        shutdown_timeout: Option<Duration>,
33    ) -> Self {
34        Self {
35            router,
36            listener,
37            local_addr,
38            shutdown_timeout,
39        }
40    }
41
42    /// Actual bound socket address.
43    ///
44    /// Useful when `bind_addr("0.0.0.0:0")` was used so the OS chose a port.
45    #[must_use]
46    pub fn local_addr(&self) -> SocketAddr {
47        self.local_addr
48    }
49
50    /// Bind the listener and serve forever (until SIGINT / SIGTERM).
51    ///
52    /// Returns `Ok(())` after graceful shutdown completes.
53    pub async fn run(self) -> Result<()> {
54        self.run_with_shutdown(shutdown_signal()).await
55    }
56
57    /// Bind and serve until the given future resolves.
58    ///
59    /// If [`ServerBuilder::shutdown_timeout`] was set, the post-shutdown
60    /// drain is bounded — in-flight requests still running after the
61    /// deadline cause this method to return
62    /// [`Error::ShutdownTimeout`] instead of waiting forever.
63    pub async fn run_with_shutdown<F>(self, shutdown: F) -> Result<()>
64    where
65        F: std::future::Future<Output = ()> + Send + 'static,
66    {
67        let timeout = self.shutdown_timeout;
68        let serve = axum::serve(self.listener, self.router).with_graceful_shutdown(shutdown);
69        match timeout {
70            Some(d) => match tokio::time::timeout(d, serve).await {
71                Ok(res) => res.map_err(Error::from),
72                Err(_) => Err(Error::ShutdownTimeout(d)),
73            },
74            None => serve.await.map_err(Error::from),
75        }
76    }
77}