rama_http/layer/compression/
layer.rs1use super::predicate::DefaultPredicate;
2use super::{Compression, Predicate};
3use crate::headers::encoding::AcceptEncoding;
4use crate::layer::util::compression::CompressionLevel;
5use rama_core::Layer;
6
7#[derive(Clone, Debug)]
14pub struct CompressionLayer<P = DefaultPredicate> {
15 accept: AcceptEncoding,
16 predicate: P,
17 respect_content_encoding_if_possible: bool,
18 quality: CompressionLevel,
19 enforce_not_acceptable: bool,
20}
21
22impl<P: Default> Default for CompressionLayer<P> {
23 fn default() -> Self {
24 Self {
25 accept: AcceptEncoding::default(),
26 predicate: P::default(),
27 respect_content_encoding_if_possible: false,
28 quality: CompressionLevel::default(),
29 enforce_not_acceptable: true,
30 }
31 }
32}
33
34impl<S, P> Layer<S> for CompressionLayer<P>
35where
36 P: Predicate,
37{
38 type Service = Compression<S, P>;
39
40 fn layer(&self, inner: S) -> Self::Service {
41 Compression {
42 inner,
43 accept: self.accept,
44 predicate: self.predicate.clone(),
45 respect_content_encoding_if_possible: self.respect_content_encoding_if_possible,
46 quality: self.quality,
47 enforce_not_acceptable: self.enforce_not_acceptable,
48 }
49 }
50
51 fn into_layer(self, inner: S) -> Self::Service {
52 Compression {
53 inner,
54 accept: self.accept,
55 predicate: self.predicate,
56 respect_content_encoding_if_possible: self.respect_content_encoding_if_possible,
57 quality: self.quality,
58 enforce_not_acceptable: self.enforce_not_acceptable,
59 }
60 }
61}
62
63impl CompressionLayer {
64 #[must_use]
66 pub fn new() -> Self {
67 Self::default()
68 }
69
70 pub fn with_compress_predicate<C>(self, predicate: C) -> CompressionLayer<C>
72 where
73 C: Predicate,
74 {
75 CompressionLayer {
76 accept: self.accept,
77 predicate,
78 respect_content_encoding_if_possible: self.respect_content_encoding_if_possible,
79 quality: self.quality,
80 enforce_not_acceptable: self.enforce_not_acceptable,
81 }
82 }
83}
84
85impl<P> CompressionLayer<P> {
86 rama_utils::macros::generate_set_and_with! {
87 pub fn gzip(mut self, enable: bool) -> Self {
89 self.accept.set_gzip(enable);
90 self
91 }
92 }
93
94 rama_utils::macros::generate_set_and_with! {
95 pub fn deflate(mut self, enable: bool) -> Self {
97 self.accept.set_deflate(enable);
98 self
99 }
100 }
101
102 rama_utils::macros::generate_set_and_with! {
103 pub fn br(mut self, enable: bool) -> Self {
105 self.accept.set_br(enable);
106 self
107 }
108 }
109
110 rama_utils::macros::generate_set_and_with! {
111 pub fn zstd(mut self, enable: bool) -> Self {
113 self.accept.set_zstd(enable);
114 self
115 }
116 }
117
118 rama_utils::macros::generate_set_and_with! {
119 pub fn quality(mut self, quality: CompressionLevel) -> Self {
121 self.quality = quality;
122 self
123 }
124 }
125
126 rama_utils::macros::generate_set_and_with! {
127 pub fn respect_content_encoding_if_possible(mut self) -> Self {
133 self.respect_content_encoding_if_possible = true;
134 self
135 }
136 }
137
138 rama_utils::macros::generate_set_and_with! {
139 pub fn enforce_not_acceptable(mut self, enable: bool) -> Self {
146 self.enforce_not_acceptable = enable;
147 self
148 }
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 use crate::{Request, Response, body::util::BodyExt, header::ACCEPT_ENCODING};
157 use rama_core::Service;
158 use rama_core::service::service_fn;
159 use rama_core::stream::io::ReaderStream;
160 use rama_http_types::Body;
161 use std::convert::Infallible;
162 use tokio::fs::File;
163
164 async fn handle(_req: Request) -> Result<Response, Infallible> {
165 let file = File::open("Cargo.toml").await.expect("file missing");
167 let stream = ReaderStream::new(file);
169 let body = Body::from_stream(stream);
171 Ok(Response::new(body))
173 }
174
175 #[tokio::test]
176 async fn accept_encoding_configuration_works() -> Result<(), rama_core::error::BoxError> {
177 use std::io::Read;
178
179 fn decode<R: Read>(mut r: R) -> std::io::Result<Vec<u8>> {
180 let mut buf = Vec::new();
181 r.read_to_end(&mut buf)?;
182 Ok(buf)
183 }
184
185 let expected = tokio::fs::read("Cargo.toml").await?;
187
188 let deflate_only_layer = CompressionLayer::new()
192 .with_quality(CompressionLevel::Best)
193 .with_br(false)
194 .with_gzip(false);
195
196 let service = deflate_only_layer.into_layer(service_fn(handle));
197
198 let request = Request::builder()
199 .header(ACCEPT_ENCODING, "gzip, deflate, br")
200 .body(Body::empty())?;
201
202 let response = service.serve(request).await?;
203
204 assert_eq!(response.headers()["content-encoding"], "deflate");
205
206 let deflate_body = response.into_body().collect().await?.to_bytes();
207
208 let decoded = decode(flate2::bufread::ZlibDecoder::new(&deflate_body[..]))?;
211 assert_eq!(decoded, expected);
212
213 let br_only_layer = CompressionLayer::new()
215 .with_quality(CompressionLevel::Best)
216 .with_gzip(false)
217 .with_deflate(false);
218
219 let service = br_only_layer.into_layer(service_fn(handle));
220
221 let request = Request::builder()
222 .header(ACCEPT_ENCODING, "gzip, deflate, br")
223 .body(Body::empty())?;
224
225 let response = service.serve(request).await?;
226
227 assert_eq!(response.headers()["content-encoding"], "br");
228
229 let br_body = response.into_body().collect().await?.to_bytes();
230
231 let decoded = decode(brotli::Decompressor::new(&br_body[..], 4096))?;
233 assert_eq!(decoded, expected);
234
235 Ok(())
236 }
237
238 #[tokio::test]
239 async fn zstd_is_web_safe() -> Result<(), rama_core::error::BoxError> {
240 async fn zeroes(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
244 Ok(Response::new(Body::from(vec![0u8; 18_874_368])))
245 }
246 let zstd_layer = CompressionLayer::new()
251 .with_quality(CompressionLevel::Best)
252 .with_br(false)
253 .with_deflate(false)
254 .with_gzip(false);
255
256 let service = zstd_layer.into_layer(service_fn(zeroes));
257
258 let request = Request::builder()
259 .header(ACCEPT_ENCODING, "zstd")
260 .body(Body::empty())?;
261
262 let response = service.serve(request).await?;
263
264 assert_eq!(response.headers()["content-encoding"], "zstd");
265
266 let body = response.into_body();
267 let bytes = body.collect().await?.to_bytes();
268 let mut dec = zstd::Decoder::new(&*bytes)?;
269 dec.window_log_max(23)?; std::io::copy(&mut dec, &mut std::io::sink())?;
272
273 Ok(())
274 }
275}