tower_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].
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//! # Example server
13//!
14//! This example shows how to apply middleware from `tower-http` to a [`Service`] and then run
15//! that service using [hyper] and [hyper-util].
16//!
17//! ```rust,no_run
18//! use tower_http::{
19//! compression::CompressionLayer,
20//! trace::TraceLayer,
21//! };
22//! use tower::{ServiceBuilder, service_fn, BoxError};
23//! use http::{Request, Response};
24//! use std::net::{SocketAddr, Ipv6Addr};
25//! use bytes::Bytes;
26//! use http_body_util::Full;
27//! use hyper_util::rt::{TokioIo, TokioExecutor};
28//! use hyper_util::service::TowerToHyperService;
29//!
30//! # async fn handler(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, BoxError> {
31//! # Ok(Response::new(Full::new(Bytes::from("hello"))))
32//! # }
33//!
34//! #[tokio::main]
35//! async fn main() -> Result<(), BoxError> {
36//! let addr = SocketAddr::from((Ipv6Addr::LOCALHOST, 8000));
37//! let listener = tokio::net::TcpListener::bind(addr).await?;
38//! println!("Listening on http://{}", addr);
39//!
40//! loop {
41//! let (socket, _) = listener.accept().await?;
42//!
43//! tokio::spawn(async move {
44//! let socket = TokioIo::new(socket);
45//!
46//! // Build our middleware stack
47//! let service = ServiceBuilder::new()
48//! .layer(TraceLayer::new_for_http())
49//! .layer(CompressionLayer::new())
50//! .service_fn(handler);
51//!
52//! // Wrap it for hyper
53//! let hyper_service = TowerToHyperService::new(service);
54//!
55//! if let Err(err) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
56//! .serve_connection(socket, hyper_service)
57//! .await
58//! {
59//! eprintln!("failed to serve connection: {err:#}");
60//! }
61//! });
62//! }
63//! }
64//! ```
65//!
66//! Keep in mind that while this example uses [hyper], tower-http supports any HTTP
67//! client/server implementation that uses the [http] and [http-body] crates.
68//!
69//! # Example client
70//!
71//! tower-http middleware can also be applied to HTTP clients:
72//!
73//! ```rust,no_run
74//! use tower_http::{
75//! decompression::DecompressionLayer,
76//! set_header::SetRequestHeaderLayer,
77//! trace::TraceLayer,
78//! classify::StatusInRangeAsFailures,
79//! };
80//! use tower::{ServiceBuilder, Service, ServiceExt};
81//! use hyper_util::{rt::TokioExecutor, client::legacy::Client};
82//! use http_body_util::Full;
83//! use bytes::Bytes;
84//! use http::{Request, HeaderValue, header::USER_AGENT};
85//!
86//! #[tokio::main]
87//! async fn main() {
88//! let client = Client::builder(TokioExecutor::new()).build_http();
89//! let mut client = ServiceBuilder::new()
90//! // Add tracing and consider server errors and client
91//! // errors as failures.
92//! .layer(TraceLayer::new(
93//! StatusInRangeAsFailures::new(400..=599).into_make_classifier()
94//! ))
95//! // Set a `User-Agent` header on all requests.
96//! .layer(SetRequestHeaderLayer::overriding(
97//! USER_AGENT,
98//! HeaderValue::from_static("tower-http demo")
99//! ))
100//! // Decompress response bodies
101//! .layer(DecompressionLayer::new())
102//! // Wrap a `Client` in our middleware stack.
103//! // This is possible because `Client` implements
104//! // `tower::Service`.
105//! .service(client);
106//!
107//! // Make a request
108//! let request = Request::builder()
109//! .uri("http://example.com")
110//! .body(Full::<Bytes>::default())
111//! .unwrap();
112//!
113//! let response = client
114//! .ready()
115//! .await
116//! .unwrap()
117//! .call(request)
118//! .await
119//! .unwrap();
120//! }
121//! ```
122//!
123//! # Feature Flags
124//!
125//! All middleware are disabled by default and can be enabled using [cargo features].
126//!
127//! For example, to enable the [`Trace`] middleware, add the "trace" feature flag in
128//! your `Cargo.toml`:
129//!
130//! ```toml
131//! tower-http = { version = "0.1", features = ["trace"] }
132//! ```
133//!
134//! You can use `"full"` to enable everything:
135//!
136//! ```toml
137//! tower-http = { version = "0.1", features = ["full"] }
138//! ```
139//!
140//! # Getting Help
141//!
142//! If you're new to tower its [guides] might help. In the tower-http repo we also have a [number
143//! of examples][examples] showing how to put everything together. You're also welcome to ask in
144//! the [`#tower` Discord channel][chat] or open an [issue] with your question.
145//!
146//! [tower]: https://crates.io/crates/tower
147//! [http]: https://crates.io/crates/http
148//! [http-body]: https://crates.io/crates/http-body
149//! [hyper]: https://crates.io/crates/hyper
150//! [hyper-util]: https://crates.io/crates/hyper-util
151//! [guides]: https://github.com/tower-rs/tower/tree/master/guides
152//! [tonic]: https://crates.io/crates/tonic
153//! [warp]: https://crates.io/crates/warp
154//! [cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
155//! [`AddExtension`]: crate::add_extension::AddExtension
156//! [`Service`]: https://docs.rs/tower/latest/tower/trait.Service.html
157//! [chat]: https://discord.gg/tokio
158//! [issue]: https://github.com/tower-rs/tower-http/issues/new
159//! [`Trace`]: crate::trace::Trace
160//! [examples]: https://github.com/tower-rs/tower-http/tree/master/examples
161
162#![warn(
163 clippy::all,
164 clippy::dbg_macro,
165 clippy::todo,
166 clippy::empty_enums,
167 clippy::enum_glob_use,
168 clippy::mem_forget,
169 clippy::unused_self,
170 clippy::filter_map_next,
171 clippy::needless_continue,
172 clippy::needless_borrow,
173 clippy::match_wildcard_for_single_variants,
174 clippy::if_let_mutex,
175 clippy::await_holding_lock,
176 clippy::imprecise_flops,
177 clippy::suboptimal_flops,
178 clippy::lossy_float_literal,
179 clippy::rest_pat_in_fully_bound_structs,
180 clippy::fn_params_excessive_bools,
181 clippy::exit,
182 clippy::inefficient_to_string,
183 clippy::linkedlist,
184 clippy::macro_use_imports,
185 clippy::option_option,
186 clippy::verbose_file_reads,
187 clippy::unnested_or_patterns,
188 rust_2018_idioms,
189 future_incompatible,
190 nonstandard_style,
191 missing_docs
192)]
193#![deny(unreachable_pub)]
194#![allow(
195 elided_lifetimes_in_paths,
196 // TODO: Remove this once the MSRV bumps to 1.42.0 or above.
197 clippy::match_like_matches_macro,
198 clippy::type_complexity
199)]
200#![forbid(unsafe_code)]
201#![cfg_attr(docsrs, feature(doc_cfg))]
202#![cfg_attr(test, allow(clippy::float_cmp))]
203
204#[macro_use]
205pub(crate) mod macros;
206
207#[cfg(test)]
208mod test_helpers;
209
210#[cfg(feature = "auth")]
211pub mod auth;
212
213#[cfg(feature = "set-header")]
214pub mod set_header;
215
216#[cfg(feature = "propagate-header")]
217pub mod propagate_header;
218
219#[cfg(any(
220 feature = "compression-br",
221 feature = "compression-deflate",
222 feature = "compression-gzip",
223 feature = "compression-zstd",
224))]
225pub mod compression;
226
227#[cfg(feature = "add-extension")]
228pub mod add_extension;
229
230#[cfg(feature = "sensitive-headers")]
231pub mod sensitive_headers;
232
233#[cfg(any(
234 feature = "decompression-br",
235 feature = "decompression-deflate",
236 feature = "decompression-gzip",
237 feature = "decompression-zstd",
238))]
239pub mod decompression;
240
241#[cfg(any(
242 feature = "compression-br",
243 feature = "compression-deflate",
244 feature = "compression-gzip",
245 feature = "compression-zstd",
246 feature = "decompression-br",
247 feature = "decompression-deflate",
248 feature = "decompression-gzip",
249 feature = "decompression-zstd",
250 feature = "fs" // Used for serving precompressed static files as well
251))]
252mod content_encoding;
253
254#[cfg(any(
255 feature = "compression-br",
256 feature = "compression-deflate",
257 feature = "compression-gzip",
258 feature = "compression-zstd",
259 feature = "decompression-br",
260 feature = "decompression-deflate",
261 feature = "decompression-gzip",
262 feature = "decompression-zstd",
263))]
264mod compression_utils;
265
266#[cfg(any(
267 feature = "compression-br",
268 feature = "compression-deflate",
269 feature = "compression-gzip",
270 feature = "compression-zstd",
271 feature = "decompression-br",
272 feature = "decompression-deflate",
273 feature = "decompression-gzip",
274 feature = "decompression-zstd",
275))]
276pub use compression_utils::CompressionLevel;
277
278#[cfg(feature = "map-response-body")]
279pub mod map_response_body;
280
281#[cfg(feature = "map-request-body")]
282pub mod map_request_body;
283
284#[cfg(feature = "trace")]
285pub mod trace;
286
287#[cfg(feature = "follow-redirect")]
288pub mod follow_redirect;
289
290#[cfg(feature = "limit")]
291pub mod limit;
292
293#[cfg(feature = "metrics")]
294pub mod metrics;
295
296#[cfg(feature = "cors")]
297pub mod cors;
298
299#[cfg(feature = "request-id")]
300pub mod request_id;
301
302#[cfg(feature = "catch-panic")]
303pub mod catch_panic;
304
305#[cfg(feature = "set-status")]
306pub mod set_status;
307
308#[cfg(feature = "timeout")]
309pub mod timeout;
310
311#[cfg(feature = "normalize-path")]
312pub mod normalize_path;
313
314#[cfg(feature = "on-early-drop")]
315pub mod on_early_drop;
316
317pub mod classify;
318pub mod services;
319
320#[cfg(feature = "util")]
321mod builder;
322#[cfg(feature = "util")]
323mod service_ext;
324
325#[cfg(feature = "util")]
326#[doc(inline)]
327pub use self::{builder::ServiceBuilderExt, service_ext::ServiceExt};
328
329#[cfg(feature = "validate-request")]
330pub mod validate_request;
331
332#[cfg(any(
333 feature = "catch-panic",
334 feature = "decompression-br",
335 feature = "decompression-deflate",
336 feature = "decompression-gzip",
337 feature = "decompression-zstd",
338 feature = "fs",
339 feature = "limit",
340))]
341pub mod body;
342
343/// The latency unit used to report latencies by middleware.
344#[non_exhaustive]
345#[derive(Copy, Clone, Debug)]
346pub enum LatencyUnit {
347 /// Use seconds.
348 Seconds,
349 /// Use milliseconds.
350 Millis,
351 /// Use microseconds.
352 Micros,
353 /// Use nanoseconds.
354 Nanos,
355}
356
357/// Alias for a type-erased error type.
358pub type BoxError = Box<dyn std::error::Error + Send + Sync>;