Overview of Tower HTTP middleware
mainhttp crate, making it compatible with any Rust library or framework that implements the http interface, such as hyper.repository·main·Indexed 21 days ago
https://github.com/tower-rs/tower-httpA collection of middleware and utilities for HTTP clients and servers in Rust, version 0.7.0. It provides functionality such as logging (Trace), compression, decompression, redirection handling, and request/response header manipulation. Designed to work with the standard http crate and frameworks like Axum, Tonic, and Warp. It includes the ServiceBuilderExt trait for chaining HTTP-specific layers and a response classification system via the ClassifyResponse trait.
http crate, making it compatible with any Rust library or framework that implements the http interface, such as hyper.To run the key/value store example built with warp, use cargo run with the specific binary and set the RUST_LOG environment variable to enable trace logging for both the example crate and tower_http.
RUST_LOG=warp_key_value_store=trace,tower_http=trace \
cargo run --bin warp-key-value-storeThe tonic-key-value-store example demonstrates a simple key/value store using a gRPC API and a client built with tonic. You can run the server, set values, get values, or subscribe to a stream of new keys using the provided CLI interface.
### Running the server
RUST_LOG=tonic_key_value_store=trace,tower_http=trace \
cargo run --bin tonic-key-value-store -- -p 3000 server
### Setting a value
echo "Hello, World" | RUST_LOG=tower_http=trace cargo run --bin tonic-key-value-store -- -p 3000 set -k foo
### Getting a value
RUST_LOG=tower_http=trace cargo run --bin tonic-key-value-store -- -p 3000 get -k foo
### Subscribing to new keys
RUST_LOG=tower_http=trace cargo run --bin tonic-key-value-store -- -p 3000 subscribeTo run the axum-key-value-store example with trace-level logging enabled for both the application and tower_http, use the following command:
RUST_LOG=axum_key_value_store=trace,tower_http=trace \
cargo run --bin axum-key-value-storeThe repository contains several examples demonstrating how to integrate Tower HTTP with different web frameworks and protocols:
axum-key-value-store for an HTTP API implementation using the Axum framework.tonic-key-value-store for a gRPC API and client implementation using Tonic.warp-key-value-store for an HTTP API implementation using the Warp framework.tower-http, your project must use a Minimum Supported Rust Version (MSRV) of 1.65.tower-http is a library providing HTTP-specific middleware and utilities built on top of the tower ecosystem.
All middleware uses the http and http-body crates as their core abstractions. This ensures compatibility with any HTTP library or framework that implements these crates, including hyper, tonic, and warp.
Response classification is the process of determining whether an HTTP response (or an error) should be treated as a success or a failure. This is primarily used by middleware like logging, metrics, or retry policies to react to the outcome of a request.
There are three main levels of abstraction:
ClassifyResponse: The core trait. It attempts to classify a response immediately (ClassifiedResponse::Ready) or indicates that classification must wait until the response body stream ends (ClassifiedResponse::RequiresEos).ClassifyEos: Used for streaming responses (like gRPC) where the final status might only be available in the response trailers at the End of Stream (EOS).MakeClassifier: A factory trait used when the classification logic depends on information from the incoming Request (e.g., the URI or HTTP method).If your classifier does not depend on the request, you can use SharedClassifier to wrap a ClassifyResponse implementation into a MakeClassifier.
// Example of a simple classifier that only cares about errors
#[derive(Clone, Copy)]
struct MyClassifier;
impl ClassifyResponse for MyClassifier {
type FailureClass = String;
type ClassifyEos = NeverClassifyEos<Self::FailureClass>;
fn classify_response<B>(self, _res: &Response<B>) -> ClassifiedResponse<Self::FailureClass, Self::ClassifyEos> {
ClassifiedResponse::Ready(Ok(()))
}
fn classify_error<E>(self, error: &E) -> Self::FailureClass
where E: std::fmt::Display + 'static,
{
error.to_string()
}
}
// Use SharedClassifier to turn it into a MakeClassifier
let make_classifier = SharedClassifier::new(MyClassifier);ServeFile supports standard HTTP conditional request headers to optimize bandwidth and caching:
If-Modified-Since: If the file has not been modified since the date provided in the header, the service returns 304 Not Modified and an empty body.If-Unmodified-Since: If the file has been modified since the date provided, the service returns 425 Precondition Failed.It also automatically includes the Last-Modified header in successful responses.
ServiceExt trait provides a convenient way to wrap any tower::Service with tower-http middleware using method chaining. This trait is implemented for all types T that implement Service, allowing you to call middleware methods directly on your service instance.The ServiceBuilderExt trait extends tower::ServiceBuilder with specialized methods for adding tower-http middleware. This allows you to chain HTTP-specific layers (like compression, tracing, or header manipulation) directly onto a ServiceBuilder instance.
Note that many of these methods are gated by Cargo features (e.g., compression-gzip, trace, set-header). Ensure the required feature is enabled in your Cargo.toml to access the corresponding method.
use http::{Request, Response, header::HeaderName};
use bytes::Bytes;
use http_body_util::Full;
use std::{time::Duration, convert::Infallible};
use tower::{ServiceBuilder, ServiceExt, Service};
use tower_http::ServiceBuilderExt;
async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::default()))
}
#[tokio::main]
async fn main() {
let service = ServiceBuilder::new()
// Methods from tower
.timeout(Duration::from_secs(30))
// Methods from tower-http
.trace_for_http()
.propagate_header(HeaderName::from_static("x-request-id"))
.service_fn(handle);
let mut service = service;
service.ready().await.unwrap().call(Request::new(Full::default())).await.unwrap();
}A TimeoutBody is a wrapper around an http_body::Body that enforces a timeout on the interval between consecutive data frames.
Frame polls. It resets every time a new frame is successfully produced.Duration.TimeoutError.Timeout middleware:Timeout (from crate::timeout::Timeout) applies to the entire request future and does not reset as bytes are processed.TimeoutBody is specifically for asynchronous body streaming, which occurs outside the standard Tower service stack's future lifecycle.use http_body_util::Full;
use bytes::Bytes;
use std::time::Duration;
use tower::ServiceBuilder;
use tower_http::timeout::RequestBodyTimeoutLayer;
// Example of applying a body timeout layer to a service
let svc = ServiceBuilder::new()
.layer(RequestBodyTimeoutLayer::new(Duration::from_secs(30)))
.service_fn(handle);