Middleware
Recipe | Crates | Categories |
---|---|---|
Tower | ||
Tower HTTP | ||
Alternatives |
Tower
tower
⮳ is a library of modular and reusable components for building robust networking clients and servers.
Tower provides a simple core abstraction, the tower::Service
⮳ trait, which represents an asynchronous function taking a request and returning either a response or an error. It can be used to model both clients and servers.
An additional abstraction, the tower::Layer
⮳ trait, is used to compose middleware with Services. A tower::Layer
⮳ is a function taking a Service of one type and returning a Service of a different type. The tower::ServiceBuilder
⮳ type is used to add middleware to a service by composing it with multiple Layers. The tower::Layer
⮳ trait can be used to write reusable components that can be applied to very different kinds of services; for example, it can be applied to services operating on different protocols, and to both the client and server side of a network transaction.
A number of third-party libraries support tower
⮳ and the tower::Service
⮳ trait: hyper
⮳, tonic
⮳ (gRPC).
Tower HTTP
Tower HTTP
⮳ contains HTTP specific Tower utilities.
use std::iter::once;
use std::sync::Arc;
use bytes::Bytes;
use http::header::HeaderName;
use http::header::AUTHORIZATION;
use http::header::CONTENT_TYPE;
use http::Request;
use http::Response;
use http_body_util::Full;
use tower::BoxError;
use tower::ServiceBuilder;
use tower_http::add_extension::AddExtensionLayer;
use tower_http::compression::CompressionLayer;
use tower_http::propagate_header::PropagateHeaderLayer;
use tower_http::sensitive_headers::SetSensitiveRequestHeadersLayer;
use tower_http::set_header::SetResponseHeaderLayer;
use tower_http::trace::TraceLayer;
// use tower_http::validate_request::ValidateRequestHeaderLayer;
// Our request handler. This is where we would implement the
// application logic for responding to HTTP requests...
async fn handler(
_request: Request<Full<Bytes>>,
) -> Result<Response<Full<Bytes>>, BoxError> {
let empty_body = Full::new(Bytes::new());
let builder = Response::builder()
.header("X-Custom-Foo", "bar")
.status(http::status::StatusCode::OK);
Ok(builder.body(empty_body).unwrap())
}
struct DatabaseConnectionPool;
impl DatabaseConnectionPool {
fn new() -> Self {
DatabaseConnectionPool
}
}
// Shared state across all request handlers -
// in this case, a pool of database connections.
struct State {
pool: DatabaseConnectionPool,
}
#[tokio::main]
async fn main() {
// Construct the shared state.
let state = State {
pool: DatabaseConnectionPool::new(),
};
let content_length_from_response = 0;
// Use tower's `ServiceBuilder` API to build a stack of tower
// middleware wrapping our request handler.
let _service = ServiceBuilder::new()
// Mark the `Authorization` request header as sensitive
// so it doesn't show in logs
.layer(SetSensitiveRequestHeadersLayer::new(once(AUTHORIZATION)))
// High level logging of requests and responses
.layer(TraceLayer::new_for_http())
// Share an `Arc<State>` with all requests
.layer(AddExtensionLayer::new(Arc::new(state)))
// Compress responses
.layer(CompressionLayer::new())
// Propagate `X-Request-Id`s from requests to responses
.layer(PropagateHeaderLayer::new(HeaderName::from_static(
"x-request-id",
)))
// If the response has a known size set the `Content-Length` header
.layer(SetResponseHeaderLayer::overriding(
CONTENT_TYPE,
content_length_from_response,
))
//// Authorize requests using a token
//.layer(ValidateRequestHeaderLayer::bearer("passwordlol"))
//// Accept only application/json, application/* and */*
//// in a request's ACCEPT header
//.layer(ValidateRequestHeaderLayer::accept("application/json"))
// Wrap the `Service` in our middleware stack
.service_fn(handler);
}
Alternatives
A modular toolkit for building async web apps