7 releases
new 0.3.1 | Feb 7, 2025 |
---|---|
0.3.0 | Jan 19, 2025 |
0.2.3 | Jan 18, 2025 |
0.1.0 | Dec 30, 2024 |
#584 in HTTP server
831 downloads per month
Used in authly-common
26KB
361 lines
tower-server
Server utility for running hyper HTTP servers with tower-service.
Integrates with rustls and implements graceful shutdown using tokio_util/CancellationToken.
lib.rs
:
High-level hyper server interfacing with tower-service.
Features:
rustls
integration- Graceful shutdown using
CancellationToken
fromtokio_util
. - Optional connnection middleware for handling the remote address
- Dynamic TLS reconfiguration without restarting server, for e.g. certificate rotation
- Optional TLS connection middleware, for example for mTLS integration
Example usage using Axum with graceful shutdown:
#[cfg(feature = "signal")]
// Uses the built-in termination signal:
let shutdown_token = tower_server::signal::termination_signal();
#[cfg(not(feature = "signal"))]
// Configure the shutdown token manually:
let shutdown_token = tokio_util::sync::CancellationToken::default();
let server = tower_server::Builder::new("0.0.0.0:8080".parse().unwrap())
.with_graceful_shutdown(shutdown_token)
.bind()
.await
.unwrap();
server.serve(axum::Router::new()).await;
Example using connection middleware
#[derive(Clone)]
struct RemoteAddr(std::net::SocketAddr);
let server = tower_server::Builder::new("0.0.0.0:8080".parse().unwrap())
.with_connection_middleware(|req, remote_addr| {
req.extensions_mut().insert(RemoteAddr(remote_addr));
})
.bind()
.await
.unwrap();
server.serve(axum::Router::new()).await;
Example using TLS connection middleware
use rustls_pki_types::CertificateDer;
use hyper::body::Incoming;
#[derive(Clone)]
struct PeerCertMiddleware;
/// A request extension that includes the mTLS peer certificate
#[derive(Clone)]
struct PeerCertificate(CertificateDer<'static>);
impl tower_server::tls::TlsConnectionMiddleware for PeerCertMiddleware {
type Data = Option<PeerCertificate>;
/// Step 1: Extract data from the rustls server connection.
/// At this stage of TLS handshake the http::Request doesn't yet exist.
fn data(&self, connection: &rustls::ServerConnection) -> Self::Data {
Some(PeerCertificate(connection.peer_certificates()?.first()?.clone()))
}
/// Step 2: The http::Request now exists, and the request extension can be injected.
fn call(&self, req: &mut http::Request<Incoming>, data: &Option<PeerCertificate>) {
if let Some(peer_certificate) = data {
req.extensions_mut().insert(peer_certificate.clone());
}
}
}
let server = tower_server::Builder::new("0.0.0.0:443".parse().unwrap())
.with_scheme(tower_server::Scheme::Https)
.with_tls_connection_middleware(PeerCertMiddleware)
.with_tls_config(
rustls::server::ServerConfig::builder()
// Instead of this, actually configure client authentication here:
.with_no_client_auth()
// just a compiling example for setting a cert resolver, replace this with your actual config:
.with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new()))
)
.bind()
.await
.unwrap();
server.serve(axum::Router::new()).await;
Example using dynamically chaning TLS configuration
tls::TlsConfigurer is implemented for futures_util::stream::BoxStream of [Arc]ed rustls::server::ServerConfigs:
use futures_util::StreamExt;
let initial_tls_config = Arc::new(
rustls::server::ServerConfig::builder()
.with_no_client_auth()
.with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new()))
);
let tls_config_rotation = futures_util::stream::unfold((), |_| async move {
// renews after a fixed delay:
tokio::time::sleep(Duration::from_secs(10)).await;
// just for illustration purposes, replace with your own ServerConfig:
let renewed_config = Arc::new(
rustls::server::ServerConfig::builder()
.with_no_client_auth()
.with_cert_resolver(Arc::new(rustls::server::ResolvesServerCertUsingSni::new()))
);
Some((renewed_config, ()))
});
let server = tower_server::Builder::new("0.0.0.0:443".parse().unwrap())
.with_scheme(tower_server::Scheme::Https)
.with_tls_config(
// takes the initial config, which resolves without delay,
// chained together with the subsequent dynamic updates:
futures_util::stream::iter([initial_tls_config])
.chain(tls_config_rotation)
.boxed()
)
.bind()
.await
.unwrap();
server.serve(axum::Router::new()).await;
Dependencies
~14–24MB
~446K SLoC