Crate tower_server

Source
Expand description

High-level hyper server interfacing with tower-service.

§Features:

  • rustls integration
  • Graceful shutdown using CancellationToken from tokio_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 Arced 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;

Modules§

signalsignal
Signal support.
tls
TLS support.

Structs§

Builder
Server configuration.
TowerServer
A bound server, ready for running accept-loop using a tower service.

Enums§

Scheme
Desired HTTP scheme.

Type Aliases§

ConnectionMiddleware
The type of the connection middleware.