tower_async_http/
builder.rs

1use tower_async::ServiceBuilder;
2
3#[cfg(feature = "trace")]
4use crate::classify::{GrpcErrorsAsFailures, ServerErrorsAsFailures, SharedClassifier};
5
6#[allow(unused_imports)]
7use http::header::HeaderName;
8#[allow(unused_imports)]
9use tower_async_layer::Stack;
10
11/// Extension trait that adds methods to [`tower_async::ServiceBuilder`] for adding middleware from
12/// tower-http.
13///
14/// [`Service`]: tower_async::Service
15///
16/// # Example
17///
18/// ```rust
19/// use http::{Request, Response, header::HeaderName};
20/// use http_body_util::Full;
21/// use bytes::Bytes;
22/// use std::{time::Duration, convert::Infallible};
23/// use tower_async::{ServiceBuilder, ServiceExt, Service};
24/// use tower_async_http::ServiceBuilderExt;
25///
26/// async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
27///     Ok(Response::new(Full::<Bytes>::default()))
28/// }
29///
30/// # #[tokio::main]
31/// # async fn main() {
32/// let service = ServiceBuilder::new()
33///     // Methods from tower
34///     .timeout(Duration::from_secs(30))
35///     // Methods from tower-http
36///     .trace_for_http()
37///     .compression()
38///     .propagate_header(HeaderName::from_static("x-request-id"))
39///     .service_fn(handle);
40/// # let mut service = service;
41/// # service.call(Request::new(Full::<Bytes>::default())).await.unwrap();
42/// # }
43/// ```
44#[cfg(feature = "util")]
45// ^ work around rustdoc not inferring doc(cfg)s for cfg's from surrounding scopes
46pub trait ServiceBuilderExt<L>: crate::sealed::Sealed<L> + Sized {
47    /// Propagate a header from the request to the response.
48    ///
49    /// See [`tower_async_http::propagate_header`] for more details.
50    ///
51    /// [`tower_async_http::propagate_header`]: crate::propagate_header
52    #[cfg(feature = "propagate-header")]
53    fn propagate_header(
54        self,
55        header: HeaderName,
56    ) -> ServiceBuilder<Stack<crate::propagate_header::PropagateHeaderLayer, L>>;
57
58    /// Add some shareable value to [request extensions].
59    ///
60    /// See [`tower_async_http::add_extension`] for more details.
61    ///
62    /// [`tower_async_http::add_extension`]: crate::add_extension
63    /// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
64    #[cfg(feature = "add-extension")]
65    fn add_extension<T>(
66        self,
67        value: T,
68    ) -> ServiceBuilder<Stack<crate::add_extension::AddExtensionLayer<T>, L>>;
69
70    /// Apply a transformation to the request body.
71    ///
72    /// See [`tower_async_http::map_request_body`] for more details.
73    ///
74    /// [`tower_async_http::map_request_body`]: crate::map_request_body
75    #[cfg(feature = "map-request-body")]
76    fn map_request_body<F>(
77        self,
78        f: F,
79    ) -> ServiceBuilder<Stack<crate::map_request_body::MapRequestBodyLayer<F>, L>>;
80
81    /// Apply a transformation to the response body.
82    ///
83    /// See [`tower_async_http::map_response_body`] for more details.
84    ///
85    /// [`tower_async_http::map_response_body`]: crate::map_response_body
86    #[cfg(feature = "map-response-body")]
87    fn map_response_body<F>(
88        self,
89        f: F,
90    ) -> ServiceBuilder<Stack<crate::map_response_body::MapResponseBodyLayer<F>, L>>;
91
92    /// Compresses response bodies.
93    ///
94    /// See [`tower_async_http::compression`] for more details.
95    ///
96    /// [`tower_async_http::compression`]: crate::compression
97    #[cfg(any(
98        feature = "compression-br",
99        feature = "compression-deflate",
100        feature = "compression-gzip",
101        feature = "compression-zstd",
102    ))]
103    fn compression(self) -> ServiceBuilder<Stack<crate::compression::CompressionLayer, L>>;
104
105    /// Decompress response bodies.
106    ///
107    /// See [`tower_async_http::decompression`] for more details.
108    ///
109    /// [`tower_async_http::decompression`]: crate::decompression
110    #[cfg(any(
111        feature = "decompression-br",
112        feature = "decompression-deflate",
113        feature = "decompression-gzip",
114        feature = "decompression-zstd",
115    ))]
116    fn decompression(self) -> ServiceBuilder<Stack<crate::decompression::DecompressionLayer, L>>;
117
118    /// High level tracing that classifies responses using HTTP status codes.
119    ///
120    /// This method does not support customizing the output, to do that use [`TraceLayer`]
121    /// instead.
122    ///
123    /// See [`tower_http::trace`] for more details.
124    ///
125    /// [`tower_http::trace`]: crate::trace
126    /// [`TraceLayer`]: crate::trace::TraceLayer
127    #[cfg(feature = "trace")]
128    fn trace_for_http(
129        self,
130    ) -> ServiceBuilder<Stack<crate::trace::TraceLayer<SharedClassifier<ServerErrorsAsFailures>>, L>>;
131
132    /// High level tracing that classifies responses using gRPC headers.
133    ///
134    /// This method does not support customizing the output, to do that use [`TraceLayer`]
135    /// instead.
136    ///
137    /// See [`tower_http::trace`] for more details.
138    ///
139    /// [`tower_http::trace`]: crate::trace
140    /// [`TraceLayer`]: crate::trace::TraceLayer
141    #[cfg(feature = "trace")]
142    fn trace_for_grpc(
143        self,
144    ) -> ServiceBuilder<Stack<crate::trace::TraceLayer<SharedClassifier<GrpcErrorsAsFailures>>, L>>;
145
146    /// Follow redirect responses using the [`Standard`] policy.
147    ///
148    /// See [`tower_async_http::follow_redirect`] for more details.
149    ///
150    /// [`tower_async_http::follow_redirect`]: crate::follow_redirect
151    /// [`Standard`]: crate::follow_redirect::policy::Standard
152    #[cfg(feature = "follow-redirect")]
153    fn follow_redirects(
154        self,
155    ) -> ServiceBuilder<
156        Stack<
157            crate::follow_redirect::FollowRedirectLayer<crate::follow_redirect::policy::Standard>,
158            L,
159        >,
160    >;
161
162    /// Mark headers as [sensitive] on both requests and responses.
163    ///
164    /// See [`tower_async_http::sensitive_headers`] for more details.
165    ///
166    /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
167    /// [`tower_async_http::sensitive_headers`]: crate::sensitive_headers
168    #[cfg(feature = "sensitive-headers")]
169    fn sensitive_headers<I>(
170        self,
171        headers: I,
172    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveHeadersLayer, L>>
173    where
174        I: IntoIterator<Item = HeaderName>;
175
176    /// Mark headers as [sensitive] on both requests.
177    ///
178    /// See [`tower_async_http::sensitive_headers`] for more details.
179    ///
180    /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
181    /// [`tower_async_http::sensitive_headers`]: crate::sensitive_headers
182    #[cfg(feature = "sensitive-headers")]
183    fn sensitive_request_headers(
184        self,
185        headers: std::sync::Arc<[HeaderName]>,
186    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveRequestHeadersLayer, L>>;
187
188    /// Mark headers as [sensitive] on both responses.
189    ///
190    /// See [`tower_async_http::sensitive_headers`] for more details.
191    ///
192    /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
193    /// [`tower_async_http::sensitive_headers`]: crate::sensitive_headers
194    #[cfg(feature = "sensitive-headers")]
195    fn sensitive_response_headers(
196        self,
197        headers: std::sync::Arc<[HeaderName]>,
198    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveResponseHeadersLayer, L>>;
199
200    /// Insert a header into the request.
201    ///
202    /// If a previous value exists for the same header, it is removed and replaced with the new
203    /// header value.
204    ///
205    /// See [`tower_async_http::set_header`] for more details.
206    ///
207    /// [`tower_async_http::set_header`]: crate::set_header
208    #[cfg(feature = "set-header")]
209    fn override_request_header<M>(
210        self,
211        header_name: HeaderName,
212        make: M,
213    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>>;
214
215    /// Append a header into the request.
216    ///
217    /// If previous values exist, the header will have multiple values.
218    ///
219    /// See [`tower_async_http::set_header`] for more details.
220    ///
221    /// [`tower_async_http::set_header`]: crate::set_header
222    #[cfg(feature = "set-header")]
223    fn append_request_header<M>(
224        self,
225        header_name: HeaderName,
226        make: M,
227    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>>;
228
229    /// Insert a header into the request, if the header is not already present.
230    ///
231    /// See [`tower_async_http::set_header`] for more details.
232    ///
233    /// [`tower_async_http::set_header`]: crate::set_header
234    #[cfg(feature = "set-header")]
235    fn insert_request_header_if_not_present<M>(
236        self,
237        header_name: HeaderName,
238        make: M,
239    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>>;
240
241    /// Insert a header into the response.
242    ///
243    /// If a previous value exists for the same header, it is removed and replaced with the new
244    /// header value.
245    ///
246    /// See [`tower_async_http::set_header`] for more details.
247    ///
248    /// [`tower_async_http::set_header`]: crate::set_header
249    #[cfg(feature = "set-header")]
250    fn override_response_header<M>(
251        self,
252        header_name: HeaderName,
253        make: M,
254    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>>;
255
256    /// Append a header into the response.
257    ///
258    /// If previous values exist, the header will have multiple values.
259    ///
260    /// See [`tower_async_http::set_header`] for more details.
261    ///
262    /// [`tower_async_http::set_header`]: crate::set_header
263    #[cfg(feature = "set-header")]
264    fn append_response_header<M>(
265        self,
266        header_name: HeaderName,
267        make: M,
268    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>>;
269
270    /// Insert a header into the response, if the header is not already present.
271    ///
272    /// See [`tower_async_http::set_header`] for more details.
273    ///
274    /// [`tower_async_http::set_header`]: crate::set_header
275    #[cfg(feature = "set-header")]
276    fn insert_response_header_if_not_present<M>(
277        self,
278        header_name: HeaderName,
279        make: M,
280    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>>;
281
282    /// Add request id header and extension.
283    ///
284    /// See [`tower_async_http::request_id`] for more details.
285    ///
286    /// [`tower_async_http::request_id`]: crate::request_id
287    #[cfg(feature = "request-id")]
288    fn set_request_id<M>(
289        self,
290        header_name: HeaderName,
291        make_request_id: M,
292    ) -> ServiceBuilder<Stack<crate::request_id::SetRequestIdLayer<M>, L>>
293    where
294        M: crate::request_id::MakeRequestId;
295
296    /// Add request id header and extension, using `x-request-id` as the header name.
297    ///
298    /// See [`tower_async_http::request_id`] for more details.
299    ///
300    /// [`tower_async_http::request_id`]: crate::request_id
301    #[cfg(feature = "request-id")]
302    fn set_x_request_id<M>(
303        self,
304        make_request_id: M,
305    ) -> ServiceBuilder<Stack<crate::request_id::SetRequestIdLayer<M>, L>>
306    where
307        M: crate::request_id::MakeRequestId,
308    {
309        self.set_request_id(
310            HeaderName::from_static(crate::request_id::X_REQUEST_ID),
311            make_request_id,
312        )
313    }
314
315    /// Propgate request ids from requests to responses.
316    ///
317    /// See [`tower_async_http::request_id`] for more details.
318    ///
319    /// [`tower_async_http::request_id`]: crate::request_id
320    #[cfg(feature = "request-id")]
321    fn propagate_request_id(
322        self,
323        header_name: HeaderName,
324    ) -> ServiceBuilder<Stack<crate::request_id::PropagateRequestIdLayer, L>>;
325
326    /// Propgate request ids from requests to responses, using `x-request-id` as the header name.
327    ///
328    /// See [`tower_async_http::request_id`] for more details.
329    ///
330    /// [`tower_async_http::request_id`]: crate::request_id
331    #[cfg(feature = "request-id")]
332    fn propagate_x_request_id(
333        self,
334    ) -> ServiceBuilder<Stack<crate::request_id::PropagateRequestIdLayer, L>> {
335        self.propagate_request_id(HeaderName::from_static(crate::request_id::X_REQUEST_ID))
336    }
337
338    /// Catch panics and convert them into `500 Internal Server` responses.
339    ///
340    /// See [`tower_async_http::catch_panic`] for more details.
341    ///
342    /// [`tower_async_http::catch_panic`]: crate::catch_panic
343    #[cfg(feature = "catch-panic")]
344    fn catch_panic(
345        self,
346    ) -> ServiceBuilder<
347        Stack<crate::catch_panic::CatchPanicLayer<crate::catch_panic::DefaultResponseForPanic>, L>,
348    >;
349
350    /// Intercept requests with over-sized payloads and convert them into
351    /// `413 Payload Too Large` responses.
352    ///
353    /// See [`tower_async_http::limit`] for more details.
354    ///
355    /// [`tower_async_http::limit`]: crate::limit
356    #[cfg(feature = "limit")]
357    fn request_body_limit(
358        self,
359        limit: usize,
360    ) -> ServiceBuilder<Stack<crate::limit::RequestBodyLimitLayer, L>>;
361
362    /// Remove trailing slashes from paths.
363    ///
364    /// See [`tower_async_http::normalize_path`] for more details.
365    ///
366    /// [`tower_async_http::normalize_path`]: crate::normalize_path
367    #[cfg(feature = "normalize-path")]
368    fn trim_trailing_slash(
369        self,
370    ) -> ServiceBuilder<Stack<crate::normalize_path::NormalizePathLayer, L>>;
371}
372
373impl<L> crate::sealed::Sealed<L> for ServiceBuilder<L> {}
374
375impl<L> ServiceBuilderExt<L> for ServiceBuilder<L> {
376    #[cfg(feature = "propagate-header")]
377    fn propagate_header(
378        self,
379        header: HeaderName,
380    ) -> ServiceBuilder<Stack<crate::propagate_header::PropagateHeaderLayer, L>> {
381        self.layer(crate::propagate_header::PropagateHeaderLayer::new(header))
382    }
383
384    #[cfg(feature = "add-extension")]
385    fn add_extension<T>(
386        self,
387        value: T,
388    ) -> ServiceBuilder<Stack<crate::add_extension::AddExtensionLayer<T>, L>> {
389        self.layer(crate::add_extension::AddExtensionLayer::new(value))
390    }
391
392    #[cfg(feature = "map-request-body")]
393    fn map_request_body<F>(
394        self,
395        f: F,
396    ) -> ServiceBuilder<Stack<crate::map_request_body::MapRequestBodyLayer<F>, L>> {
397        self.layer(crate::map_request_body::MapRequestBodyLayer::new(f))
398    }
399
400    #[cfg(feature = "map-response-body")]
401    fn map_response_body<F>(
402        self,
403        f: F,
404    ) -> ServiceBuilder<Stack<crate::map_response_body::MapResponseBodyLayer<F>, L>> {
405        self.layer(crate::map_response_body::MapResponseBodyLayer::new(f))
406    }
407
408    #[cfg(any(
409        feature = "compression-br",
410        feature = "compression-deflate",
411        feature = "compression-gzip",
412        feature = "compression-zstd",
413    ))]
414    fn compression(self) -> ServiceBuilder<Stack<crate::compression::CompressionLayer, L>> {
415        self.layer(crate::compression::CompressionLayer::new())
416    }
417
418    #[cfg(any(
419        feature = "decompression-br",
420        feature = "decompression-deflate",
421        feature = "decompression-gzip",
422        feature = "decompression-zstd",
423    ))]
424    fn decompression(self) -> ServiceBuilder<Stack<crate::decompression::DecompressionLayer, L>> {
425        self.layer(crate::decompression::DecompressionLayer::new())
426    }
427
428    #[cfg(feature = "trace")]
429    fn trace_for_http(
430        self,
431    ) -> ServiceBuilder<Stack<crate::trace::TraceLayer<SharedClassifier<ServerErrorsAsFailures>>, L>>
432    {
433        self.layer(crate::trace::TraceLayer::new_for_http())
434    }
435
436    #[cfg(feature = "trace")]
437    fn trace_for_grpc(
438        self,
439    ) -> ServiceBuilder<Stack<crate::trace::TraceLayer<SharedClassifier<GrpcErrorsAsFailures>>, L>>
440    {
441        self.layer(crate::trace::TraceLayer::new_for_grpc())
442    }
443
444    #[cfg(feature = "follow-redirect")]
445    fn follow_redirects(
446        self,
447    ) -> ServiceBuilder<
448        Stack<
449            crate::follow_redirect::FollowRedirectLayer<crate::follow_redirect::policy::Standard>,
450            L,
451        >,
452    > {
453        self.layer(crate::follow_redirect::FollowRedirectLayer::new())
454    }
455
456    #[cfg(feature = "sensitive-headers")]
457    fn sensitive_headers<I>(
458        self,
459        headers: I,
460    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveHeadersLayer, L>>
461    where
462        I: IntoIterator<Item = HeaderName>,
463    {
464        self.layer(crate::sensitive_headers::SetSensitiveHeadersLayer::new(
465            headers,
466        ))
467    }
468
469    #[cfg(feature = "sensitive-headers")]
470    fn sensitive_request_headers(
471        self,
472        headers: std::sync::Arc<[HeaderName]>,
473    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveRequestHeadersLayer, L>> {
474        self.layer(crate::sensitive_headers::SetSensitiveRequestHeadersLayer::from_shared(headers))
475    }
476
477    #[cfg(feature = "sensitive-headers")]
478    fn sensitive_response_headers(
479        self,
480        headers: std::sync::Arc<[HeaderName]>,
481    ) -> ServiceBuilder<Stack<crate::sensitive_headers::SetSensitiveResponseHeadersLayer, L>> {
482        self.layer(crate::sensitive_headers::SetSensitiveResponseHeadersLayer::from_shared(headers))
483    }
484
485    #[cfg(feature = "set-header")]
486    fn override_request_header<M>(
487        self,
488        header_name: HeaderName,
489        make: M,
490    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>> {
491        self.layer(crate::set_header::SetRequestHeaderLayer::overriding(
492            header_name,
493            make,
494        ))
495    }
496
497    #[cfg(feature = "set-header")]
498    fn append_request_header<M>(
499        self,
500        header_name: HeaderName,
501        make: M,
502    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>> {
503        self.layer(crate::set_header::SetRequestHeaderLayer::appending(
504            header_name,
505            make,
506        ))
507    }
508
509    #[cfg(feature = "set-header")]
510    fn insert_request_header_if_not_present<M>(
511        self,
512        header_name: HeaderName,
513        make: M,
514    ) -> ServiceBuilder<Stack<crate::set_header::SetRequestHeaderLayer<M>, L>> {
515        self.layer(crate::set_header::SetRequestHeaderLayer::if_not_present(
516            header_name,
517            make,
518        ))
519    }
520
521    #[cfg(feature = "set-header")]
522    fn override_response_header<M>(
523        self,
524        header_name: HeaderName,
525        make: M,
526    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>> {
527        self.layer(crate::set_header::SetResponseHeaderLayer::overriding(
528            header_name,
529            make,
530        ))
531    }
532
533    #[cfg(feature = "set-header")]
534    fn append_response_header<M>(
535        self,
536        header_name: HeaderName,
537        make: M,
538    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>> {
539        self.layer(crate::set_header::SetResponseHeaderLayer::appending(
540            header_name,
541            make,
542        ))
543    }
544
545    #[cfg(feature = "set-header")]
546    fn insert_response_header_if_not_present<M>(
547        self,
548        header_name: HeaderName,
549        make: M,
550    ) -> ServiceBuilder<Stack<crate::set_header::SetResponseHeaderLayer<M>, L>> {
551        self.layer(crate::set_header::SetResponseHeaderLayer::if_not_present(
552            header_name,
553            make,
554        ))
555    }
556
557    #[cfg(feature = "request-id")]
558    fn set_request_id<M>(
559        self,
560        header_name: HeaderName,
561        make_request_id: M,
562    ) -> ServiceBuilder<Stack<crate::request_id::SetRequestIdLayer<M>, L>>
563    where
564        M: crate::request_id::MakeRequestId,
565    {
566        self.layer(crate::request_id::SetRequestIdLayer::new(
567            header_name,
568            make_request_id,
569        ))
570    }
571
572    #[cfg(feature = "request-id")]
573    fn propagate_request_id(
574        self,
575        header_name: HeaderName,
576    ) -> ServiceBuilder<Stack<crate::request_id::PropagateRequestIdLayer, L>> {
577        self.layer(crate::request_id::PropagateRequestIdLayer::new(header_name))
578    }
579
580    #[cfg(feature = "catch-panic")]
581    fn catch_panic(
582        self,
583    ) -> ServiceBuilder<
584        Stack<crate::catch_panic::CatchPanicLayer<crate::catch_panic::DefaultResponseForPanic>, L>,
585    > {
586        self.layer(crate::catch_panic::CatchPanicLayer::new())
587    }
588
589    #[cfg(feature = "limit")]
590    fn request_body_limit(
591        self,
592        limit: usize,
593    ) -> ServiceBuilder<Stack<crate::limit::RequestBodyLimitLayer, L>> {
594        self.layer(crate::limit::RequestBodyLimitLayer::new(limit))
595    }
596
597    #[cfg(feature = "normalize-path")]
598    fn trim_trailing_slash(
599        self,
600    ) -> ServiceBuilder<Stack<crate::normalize_path::NormalizePathLayer, L>> {
601        self.layer(crate::normalize_path::NormalizePathLayer::trim_trailing_slash())
602    }
603}