tower_http/compression/
layer.rs1use super::{Compression, Predicate};
2use crate::compression::predicate::DefaultPredicate;
3use crate::compression::CompressionLevel;
4use crate::compression_utils::AcceptEncoding;
5use tower_layer::Layer;
6
7#[derive(Clone, Debug, Default)]
14pub struct CompressionLayer<P = DefaultPredicate> {
15 accept: AcceptEncoding,
16 predicate: P,
17 quality: CompressionLevel,
18}
19
20impl<S, P> Layer<S> for CompressionLayer<P>
21where
22 P: Predicate,
23{
24 type Service = Compression<S, P>;
25
26 fn layer(&self, inner: S) -> Self::Service {
27 Compression {
28 inner,
29 accept: self.accept,
30 predicate: self.predicate.clone(),
31 quality: self.quality,
32 }
33 }
34}
35
36impl CompressionLayer {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 #[cfg(feature = "compression-gzip")]
44 pub fn gzip(mut self, enable: bool) -> Self {
45 self.accept.set_gzip(enable);
46 self
47 }
48
49 #[cfg(feature = "compression-deflate")]
51 pub fn deflate(mut self, enable: bool) -> Self {
52 self.accept.set_deflate(enable);
53 self
54 }
55
56 #[cfg(feature = "compression-br")]
58 pub fn br(mut self, enable: bool) -> Self {
59 self.accept.set_br(enable);
60 self
61 }
62
63 #[cfg(feature = "compression-zstd")]
65 pub fn zstd(mut self, enable: bool) -> Self {
66 self.accept.set_zstd(enable);
67 self
68 }
69
70 pub fn quality(mut self, quality: CompressionLevel) -> Self {
72 self.quality = quality;
73 self
74 }
75
76 pub fn no_gzip(mut self) -> Self {
80 self.accept.set_gzip(false);
81 self
82 }
83
84 pub fn no_deflate(mut self) -> Self {
88 self.accept.set_deflate(false);
89 self
90 }
91
92 pub fn no_br(mut self) -> Self {
96 self.accept.set_br(false);
97 self
98 }
99
100 pub fn no_zstd(mut self) -> Self {
104 self.accept.set_zstd(false);
105 self
106 }
107
108 pub fn compress_when<C>(self, predicate: C) -> CompressionLayer<C>
112 where
113 C: Predicate,
114 {
115 CompressionLayer {
116 accept: self.accept,
117 predicate,
118 quality: self.quality,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::test_helpers::Body;
127 use http::{header::ACCEPT_ENCODING, Request, Response};
128 use http_body_util::BodyExt;
129 use std::convert::Infallible;
130 use tokio::fs::File;
131 use tokio_util::io::ReaderStream;
132 use tower::{Service, ServiceBuilder, ServiceExt};
133
134 async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
135 let file = File::open("Cargo.toml").await.expect("file missing");
137 let stream = ReaderStream::new(file);
139 let body = Body::from_stream(stream);
141 Ok(Response::new(body))
143 }
144
145 #[tokio::test]
146 async fn accept_encoding_configuration_works() -> Result<(), crate::BoxError> {
147 let deflate_only_layer = CompressionLayer::new()
148 .quality(CompressionLevel::Best)
149 .no_br()
150 .no_gzip();
151
152 let mut service = ServiceBuilder::new()
153 .layer(deflate_only_layer)
155 .service_fn(handle);
156
157 let request = Request::builder()
159 .header(ACCEPT_ENCODING, "gzip, deflate, br")
160 .body(Body::empty())?;
161
162 let response = service.ready().await?.call(request).await?;
163
164 assert_eq!(response.headers()["content-encoding"], "deflate");
165
166 let body = response.into_body();
168 let bytes = body.collect().await.unwrap().to_bytes();
169
170 let deflate_bytes_len = bytes.len();
171
172 let br_only_layer = CompressionLayer::new()
173 .quality(CompressionLevel::Best)
174 .no_gzip()
175 .no_deflate();
176
177 let mut service = ServiceBuilder::new()
178 .layer(br_only_layer)
180 .service_fn(handle);
181
182 let request = Request::builder()
184 .header(ACCEPT_ENCODING, "gzip, deflate, br")
185 .body(Body::empty())?;
186
187 let response = service.ready().await?.call(request).await?;
188
189 assert_eq!(response.headers()["content-encoding"], "br");
190
191 let body = response.into_body();
193 let bytes = body.collect().await.unwrap().to_bytes();
194
195 let br_byte_length = bytes.len();
196
197 assert!(br_byte_length < deflate_bytes_len * 9 / 10);
200
201 Ok(())
202 }
203
204 #[tokio::test]
207 async fn zstd_is_web_safe() -> Result<(), crate::BoxError> {
208 async fn zeroes(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
209 Ok(Response::new(Body::from(vec![0u8; 18_874_368])))
210 }
211 let zstd_layer = CompressionLayer::new()
216 .quality(CompressionLevel::Best)
217 .no_br()
218 .no_deflate()
219 .no_gzip();
220
221 let mut service = ServiceBuilder::new().layer(zstd_layer).service_fn(zeroes);
222
223 let request = Request::builder()
224 .header(ACCEPT_ENCODING, "zstd")
225 .body(Body::empty())?;
226
227 let response = service.ready().await?.call(request).await?;
228
229 assert_eq!(response.headers()["content-encoding"], "zstd");
230
231 let body = response.into_body();
232 let bytes = body.collect().await?.to_bytes();
233 let mut dec = zstd::Decoder::new(&*bytes)?;
234 dec.window_log_max(23)?; std::io::copy(&mut dec, &mut std::io::sink())?;
237
238 Ok(())
239 }
240}