tower_async_http/
lib.rs

1//! `async fn(HttpRequest) -> Result<HttpResponse, Error>`
2//!
3//! # Overview
4//!
5//! tower-http is a library that provides HTTP-specific middleware and utilities built on top of
6//! [tower-async].
7//!
8//! All middleware uses the [http] and [http-body] crates as the HTTP abstractions. That means
9//! they're compatible with any library or framework that also uses those crates, such as
10//! [hyper], [tonic], and [warp].
11//!
12//! Note that for these frameworks you do need to use the `tower-async-bridge` crate to convert
13//! between the `tower-async` and `tower` abstractions.
14//!
15//! # Example server
16//!
17//! This example shows how to apply middleware from tower-http to a [`Service`] and then run
18//! that service using [hyper].
19//!
20//! ```rust,no_run
21//! use std::{sync::Arc, net::SocketAddr, convert::Infallible, iter::once};
22//!
23//! use http::{Request, Response, StatusCode, header::{AUTHORIZATION, CONTENT_TYPE, HeaderName}};
24//! use hyper::body::Incoming;
25//! use hyper_util::rt::{TokioExecutor, TokioIo};
26//! use hyper_util::server::conn::auto::Builder;
27//! use tokio::net::TcpListener;
28//!
29//! use tower_async::{ServiceBuilder, BoxError};
30//! use tower_async_http::{
31//!     ServiceBuilderExt,
32//!     add_extension::AddExtensionLayer,
33//!     compression::CompressionLayer,
34//!     propagate_header::PropagateHeaderLayer,
35//!     sensitive_headers::SetSensitiveRequestHeadersLayer,
36//!     set_header::SetResponseHeaderLayer,
37//!     validate_request::ValidateRequestHeaderLayer,
38//! };
39//! use tower_async_hyper::{HyperBody, TowerHyperServiceExt};
40//!
41//! # struct DatabaseConnectionPool;
42//! # impl DatabaseConnectionPool {
43//! #     fn new() -> DatabaseConnectionPool { DatabaseConnectionPool }
44//! # }
45//! # fn content_length_from_response<B>(_: &http::Response<B>) -> Option<http::HeaderValue> { None }
46//! # async fn update_in_flight_requests_metric(count: usize) {}
47//!
48//! // Our request handler. This is where we would implement the application logic
49//! // for responding to HTTP requests...
50//! async fn handler(request: Request<HyperBody>) -> Result<Response<HyperBody>, BoxError> {
51//!     // ...
52//!     # todo!()
53//! }
54//!
55//! // Shared state across all request handlers --- in this case, a pool of database connections.
56//! struct State {
57//!     pool: DatabaseConnectionPool,
58//! }
59//!
60//! #[tokio::main]
61//! async fn main() -> Result<(), BoxError> {
62//!     // Construct the shared state.
63//!     let state = State {
64//!         pool: DatabaseConnectionPool::new(),
65//!     };
66//!
67//!     // Use tower's `ServiceBuilder` API to build a stack of tower middleware
68//!     // wrapping our request handler.
69//!     let service = ServiceBuilder::new()
70//!         // To be able to use `Body` `Default` middleware
71//!         .map_request_body(HyperBody::from)
72//!         // Mark the `Authorization` request header as sensitive so it doesn't show in logs
73//!         .layer(SetSensitiveRequestHeadersLayer::new(once(AUTHORIZATION)))
74//!         // Share an `Arc<State>` with all requests
75//!         .layer(AddExtensionLayer::new(Arc::new(state)))
76//!         // Compress responses
77//!         .compression()
78//!         // Propagate `X-Request-Id`s from requests to responses
79//!         .layer(PropagateHeaderLayer::new(HeaderName::from_static("x-request-id")))
80//!         // If the response has a known size set the `Content-Length` header
81//!         .layer(SetResponseHeaderLayer::overriding(CONTENT_TYPE, content_length_from_response))
82//!         // Authorize requests using a token
83//!         .layer(ValidateRequestHeaderLayer::bearer("passwordlol"))
84//!         // Accept only application/json, application/* and */* in a request's ACCEPT header
85//!         .layer(ValidateRequestHeaderLayer::accept("application/json"))
86//!         // Wrap a `Service` in our middleware stack
87//!         .service_fn(handler);
88//!
89//!     let addr: SocketAddr = ([127, 0, 0, 1], 8080).into();
90//!     let listener = TcpListener::bind(addr).await?;
91//!
92//!     loop {
93//!         let (stream, _) = listener.accept().await?;
94//!         let service = service.clone().into_hyper_service();
95//!         tokio::spawn(async move {
96//!             let stream = TokioIo::new(stream);
97//!             let result = Builder::new(TokioExecutor::new())
98//!                 .serve_connection(stream, service)
99//!                 .await;
100//!             if let Err(e) = result {
101//!                 eprintln!("server connection error: {}", e);
102//!             }
103//!         });
104//!     }
105//! }
106//! ```
107//!
108//! Keep in mind that while this example uses [hyper], tower-http supports any HTTP
109//! client/server implementation that uses the [http] and [http-body] crates.
110//!
111//! # Example client
112//!
113//! tower-http middleware can also be applied to HTTP clients:
114//!
115//! ```rust,no_run
116//! use tower_async_http::{
117//!     decompression::DecompressionLayer,
118//!     set_header::SetRequestHeaderLayer,
119//! };
120//! use tower_async_bridge::AsyncServiceExt;
121//! use tower_async::{ServiceBuilder, Service, ServiceExt};
122//! use http_body_util::Full;
123//! use bytes::Bytes;
124//! use http::{Request, HeaderValue, header::USER_AGENT};
125//! use hyper_util::{client::legacy::Client, rt::TokioExecutor};
126//!
127//! #[tokio::main]
128//! async fn main() {
129//!     let mut client = ServiceBuilder::new()
130//!         // Set a `User-Agent` header on all requests.
131//!         .layer(SetRequestHeaderLayer::overriding(
132//!             USER_AGENT,
133//!             HeaderValue::from_static("tower-http demo")
134//!         ))
135//!         // Decompress response bodies
136//!         .layer(DecompressionLayer::new())
137//!         // Wrap a `hyper::Client` in our middleware stack.
138//!         // This is possible because `hyper::Client` implements
139//!         // `tower_async::Service`.
140//!         .service(Client::builder(TokioExecutor::new()).build_http().into_async());
141//!
142//!     // Make a request
143//!     let request = Request::builder()
144//!         .uri("http://example.com")
145//!         .body(Full::<Bytes>::default())
146//!         .unwrap();
147//!
148//!     let response = client
149//!         .call(request)
150//!         .await
151//!         .unwrap();
152//! }
153//! ```
154//!
155//! # Feature Flags
156//!
157//! All middleware are disabled by default and can be enabled using [cargo features].
158//!
159//! For example, to enable the [`Timeout`] middleware, add the "timeout" feature flag in
160//! your `Cargo.toml`:
161//!
162//! ```toml
163//! tower-async-http = { version = "0.2", features = ["timeout"] }
164//! ```
165//!
166//! You can use `"full"` to enable everything:
167//!
168//! ```toml
169//! tower-async-http = { version = "0.2", features = ["full"] }
170//! ```
171//!
172//! [tower-async]: https://crates.io/crates/tower-async
173//! [http]: https://crates.io/crates/http
174//! [http-body]: https://crates.io/crates/http-body
175//! [hyper]: https://crates.io/crates/hyper
176//! [tonic]: https://crates.io/crates/tonic
177//! [warp]: https://crates.io/crates/warp
178//! [cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
179//! [`AddExtension`]: crate::add_extension::AddExtension
180//! [`Service`]: https://docs.rs/tower-async/latest/tower-async/trait.Service.html
181//! [`Timeout`]: crate::timeout::Timeout
182
183#![warn(
184    clippy::all,
185    clippy::dbg_macro,
186    clippy::todo,
187    clippy::empty_enum,
188    clippy::enum_glob_use,
189    clippy::mem_forget,
190    clippy::unused_self,
191    clippy::filter_map_next,
192    clippy::needless_continue,
193    clippy::needless_borrow,
194    clippy::match_wildcard_for_single_variants,
195    clippy::if_let_mutex,
196    clippy::mismatched_target_os,
197    clippy::await_holding_lock,
198    clippy::match_on_vec_items,
199    clippy::imprecise_flops,
200    clippy::suboptimal_flops,
201    clippy::lossy_float_literal,
202    clippy::rest_pat_in_fully_bound_structs,
203    clippy::fn_params_excessive_bools,
204    clippy::exit,
205    clippy::inefficient_to_string,
206    clippy::linkedlist,
207    clippy::macro_use_imports,
208    clippy::option_option,
209    clippy::verbose_file_reads,
210    clippy::unnested_or_patterns,
211    rust_2018_idioms,
212    future_incompatible,
213    nonstandard_style,
214    missing_docs
215)]
216#![deny(unreachable_pub)]
217#![allow(elided_lifetimes_in_paths, clippy::type_complexity)]
218#![forbid(unsafe_code)]
219#![cfg_attr(docsrs, feature(doc_auto_cfg))]
220#![cfg_attr(test, allow(clippy::float_cmp))]
221
222#[macro_use]
223pub(crate) mod macros;
224
225#[cfg(test)]
226mod test_helpers;
227
228#[cfg(feature = "auth")]
229pub mod auth;
230
231#[cfg(feature = "set-header")]
232pub mod set_header;
233
234#[cfg(feature = "propagate-header")]
235pub mod propagate_header;
236
237#[cfg(any(
238    feature = "compression-br",
239    feature = "compression-deflate",
240    feature = "compression-gzip",
241    feature = "compression-zstd",
242))]
243pub mod compression;
244
245#[cfg(feature = "add-extension")]
246pub mod add_extension;
247
248#[cfg(feature = "sensitive-headers")]
249pub mod sensitive_headers;
250
251#[cfg(any(
252    feature = "decompression-br",
253    feature = "decompression-deflate",
254    feature = "decompression-gzip",
255    feature = "decompression-zstd",
256))]
257pub mod decompression;
258
259#[cfg(any(
260    feature = "compression-br",
261    feature = "compression-deflate",
262    feature = "compression-gzip",
263    feature = "compression-zstd",
264    feature = "decompression-br",
265    feature = "decompression-deflate",
266    feature = "decompression-gzip",
267    feature = "decompression-zstd",
268    feature = "fs" // Used for serving precompressed static files as well
269))]
270mod content_encoding;
271
272#[cfg(any(
273    feature = "compression-br",
274    feature = "compression-deflate",
275    feature = "compression-gzip",
276    feature = "compression-zstd",
277    feature = "decompression-br",
278    feature = "decompression-deflate",
279    feature = "decompression-gzip",
280    feature = "decompression-zstd",
281))]
282mod compression_utils;
283
284#[cfg(any(
285    feature = "compression-br",
286    feature = "compression-deflate",
287    feature = "compression-gzip",
288    feature = "compression-zstd",
289    feature = "decompression-br",
290    feature = "decompression-deflate",
291    feature = "decompression-gzip",
292    feature = "decompression-zstd",
293))]
294pub use compression_utils::CompressionLevel;
295
296#[cfg(feature = "map-response-body")]
297pub mod map_response_body;
298
299#[cfg(feature = "map-request-body")]
300pub mod map_request_body;
301
302#[cfg(feature = "trace")]
303pub mod trace;
304
305#[cfg(feature = "follow-redirect")]
306pub mod follow_redirect;
307
308#[cfg(feature = "limit")]
309pub mod limit;
310
311#[cfg(feature = "cors")]
312pub mod cors;
313
314#[cfg(feature = "request-id")]
315pub mod request_id;
316
317#[cfg(feature = "catch-panic")]
318pub mod catch_panic;
319
320#[cfg(feature = "set-status")]
321pub mod set_status;
322
323#[cfg(feature = "timeout")]
324pub mod timeout;
325
326#[cfg(feature = "normalize-path")]
327pub mod normalize_path;
328
329pub mod classify;
330pub mod services;
331
332#[cfg(feature = "util")]
333mod builder;
334
335#[cfg(feature = "util")]
336#[doc(inline)]
337pub use self::builder::ServiceBuilderExt;
338
339#[cfg(feature = "validate-request")]
340pub mod validate_request;
341
342/// The latency unit used to report latencies by middleware.
343#[non_exhaustive]
344#[derive(Copy, Clone, Debug)]
345pub enum LatencyUnit {
346    /// Use seconds.
347    Seconds,
348    /// Use milliseconds.
349    Millis,
350    /// Use microseconds.
351    Micros,
352    /// Use nanoseconds.
353    Nanos,
354}
355
356/// Alias for a type-erased error type.
357pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
358
359mod sealed {
360    #[allow(unreachable_pub)]
361    pub trait Sealed<T> {}
362}