tower_http/compression/service.rs
1use super::{CompressionBody, CompressionLayer, ResponseFuture};
2use crate::compression::predicate::{DefaultPredicate, Predicate};
3use crate::compression::CompressionLevel;
4use crate::{compression_utils::AcceptEncoding, content_encoding::Encoding};
5use http::{Request, Response};
6use http_body::Body;
7use std::task::{Context, Poll};
8use tower_service::Service;
9
10/// Compress response bodies of the underlying service.
11///
12/// This uses the `Accept-Encoding` header to pick an appropriate encoding and adds the
13/// `Content-Encoding` header to responses.
14///
15/// See the [module docs](crate::compression) for more details.
16#[derive(Clone, Copy)]
17pub struct Compression<S, P = DefaultPredicate> {
18 pub(crate) inner: S,
19 pub(crate) accept: AcceptEncoding,
20 pub(crate) predicate: P,
21 pub(crate) quality: CompressionLevel,
22}
23
24impl<S> Compression<S, DefaultPredicate> {
25 /// Creates a new `Compression` wrapping the `service`.
26 pub fn new(service: S) -> Compression<S, DefaultPredicate> {
27 Self {
28 inner: service,
29 accept: AcceptEncoding::default(),
30 predicate: DefaultPredicate::default(),
31 quality: CompressionLevel::default(),
32 }
33 }
34}
35
36impl<S, P> Compression<S, P> {
37 define_inner_service_accessors!();
38
39 /// Returns a new [`Layer`] that wraps services with a `Compression` middleware.
40 ///
41 /// [`Layer`]: tower_layer::Layer
42 pub fn layer() -> CompressionLayer {
43 CompressionLayer::new()
44 }
45
46 /// Sets whether to enable the gzip encoding.
47 #[cfg(feature = "compression-gzip")]
48 pub fn gzip(mut self, enable: bool) -> Self {
49 self.accept.set_gzip(enable);
50 self
51 }
52
53 /// Sets whether to enable the Deflate encoding.
54 #[cfg(feature = "compression-deflate")]
55 pub fn deflate(mut self, enable: bool) -> Self {
56 self.accept.set_deflate(enable);
57 self
58 }
59
60 /// Sets whether to enable the Brotli encoding.
61 #[cfg(feature = "compression-br")]
62 pub fn br(mut self, enable: bool) -> Self {
63 self.accept.set_br(enable);
64 self
65 }
66
67 /// Sets whether to enable the Zstd encoding.
68 #[cfg(feature = "compression-zstd")]
69 pub fn zstd(mut self, enable: bool) -> Self {
70 self.accept.set_zstd(enable);
71 self
72 }
73
74 /// Sets the compression quality.
75 pub fn quality(mut self, quality: CompressionLevel) -> Self {
76 self.quality = quality;
77 self
78 }
79
80 /// Disables the gzip encoding.
81 ///
82 /// This method is available even if the `gzip` crate feature is disabled.
83 pub fn no_gzip(mut self) -> Self {
84 self.accept.set_gzip(false);
85 self
86 }
87
88 /// Disables the Deflate encoding.
89 ///
90 /// This method is available even if the `deflate` crate feature is disabled.
91 pub fn no_deflate(mut self) -> Self {
92 self.accept.set_deflate(false);
93 self
94 }
95
96 /// Disables the Brotli encoding.
97 ///
98 /// This method is available even if the `br` crate feature is disabled.
99 pub fn no_br(mut self) -> Self {
100 self.accept.set_br(false);
101 self
102 }
103
104 /// Disables the Zstd encoding.
105 ///
106 /// This method is available even if the `zstd` crate feature is disabled.
107 pub fn no_zstd(mut self) -> Self {
108 self.accept.set_zstd(false);
109 self
110 }
111
112 /// Replace the current compression predicate.
113 ///
114 /// Predicates are used to determine whether a response should be compressed or not.
115 ///
116 /// The default predicate is [`DefaultPredicate`]. See its documentation for more
117 /// details on which responses it wont compress.
118 ///
119 /// # Changing the compression predicate
120 ///
121 /// ```
122 /// use tower_http::compression::{
123 /// Compression,
124 /// predicate::{Predicate, NotForContentType, DefaultPredicate},
125 /// };
126 /// use tower::util::service_fn;
127 ///
128 /// // Placeholder service_fn
129 /// let service = service_fn(|_: ()| async {
130 /// Ok::<_, std::io::Error>(http::Response::new(()))
131 /// });
132 ///
133 /// // build our custom compression predicate
134 /// // its recommended to still include `DefaultPredicate` as part of
135 /// // custom predicates
136 /// let predicate = DefaultPredicate::new()
137 /// // don't compress responses who's `content-type` starts with `application/json`
138 /// .and(NotForContentType::new("application/json"));
139 ///
140 /// let service = Compression::new(service).compress_when(predicate);
141 /// ```
142 ///
143 /// See [`predicate`](super::predicate) for more utilities for building compression predicates.
144 ///
145 /// Responses that are already compressed (ie have a `content-encoding` header) will _never_ be
146 /// recompressed, regardless what they predicate says.
147 pub fn compress_when<C>(self, predicate: C) -> Compression<S, C>
148 where
149 C: Predicate,
150 {
151 Compression {
152 inner: self.inner,
153 accept: self.accept,
154 predicate,
155 quality: self.quality,
156 }
157 }
158}
159
160impl<ReqBody, ResBody, S, P> Service<Request<ReqBody>> for Compression<S, P>
161where
162 S: Service<Request<ReqBody>, Response = Response<ResBody>>,
163 ResBody: Body,
164 P: Predicate,
165{
166 type Response = Response<CompressionBody<ResBody>>;
167 type Error = S::Error;
168 type Future = ResponseFuture<S::Future, P>;
169
170 #[inline]
171 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
172 self.inner.poll_ready(cx)
173 }
174
175 fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
176 let encoding = Encoding::from_headers(req.headers(), self.accept);
177
178 ResponseFuture {
179 inner: self.inner.call(req),
180 encoding,
181 predicate: self.predicate.clone(),
182 quality: self.quality,
183 }
184 }
185}