tower_async_http/compression/
layer.rs1use super::{Compression, Predicate};
2use crate::compression::predicate::DefaultPredicate;
3use crate::compression::CompressionLevel;
4use crate::compression_utils::AcceptEncoding;
5use tower_async_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
127 use crate::test_helpers::Body;
128
129 use http::{header::ACCEPT_ENCODING, Request, Response};
130 use http_body_util::BodyExt;
131 use std::convert::Infallible;
132 use tokio::fs::File;
133 use tokio_util::io::ReaderStream;
134 use tower_async::{Service, ServiceBuilder};
135
136 async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
137 let file = File::open("Cargo.toml").await.expect("file missing");
139 let stream = ReaderStream::new(file);
141 let body = Body::from_stream(stream);
143 Ok(Response::new(body))
145 }
146
147 #[tokio::test]
148 async fn accept_encoding_configuration_works() -> Result<(), crate::BoxError> {
149 let deflate_only_layer = CompressionLayer::new()
150 .quality(CompressionLevel::Best)
151 .no_br()
152 .no_gzip();
153
154 let service = ServiceBuilder::new()
155 .layer(deflate_only_layer)
157 .service_fn(handle);
158
159 let request = Request::builder()
161 .header(ACCEPT_ENCODING, "gzip, deflate, br")
162 .body(Body::empty())?;
163
164 let response = service.call(request).await?;
165
166 assert_eq!(response.headers()["content-encoding"], "deflate");
167
168 let body = response.into_body();
170 let bytes = body.collect().await.unwrap().to_bytes();
171
172 let deflate_bytes_len = bytes.len();
173
174 let br_only_layer = CompressionLayer::new()
175 .quality(CompressionLevel::Best)
176 .no_gzip()
177 .no_deflate();
178
179 let service = ServiceBuilder::new()
180 .layer(br_only_layer)
182 .service_fn(handle);
183
184 let request = Request::builder()
186 .header(ACCEPT_ENCODING, "gzip, deflate, br")
187 .body(Body::empty())?;
188
189 let response = service.call(request).await?;
190
191 assert_eq!(response.headers()["content-encoding"], "br");
192
193 let body = response.into_body();
195 let bytes = body.collect().await.unwrap().to_bytes();
196
197 let br_byte_length = bytes.len();
198
199 assert!(br_byte_length < deflate_bytes_len * 9 / 10);
202
203 Ok(())
204 }
205}