rama_http/layer/decompression/
service.rs1use super::{DecompressedFrom, DecompressionBody, body::BodyInner};
2use crate::headers::encoding::{AcceptEncoding, SupportedEncodings};
3use crate::layer::remove_header::remove_payload_metadata_headers;
4use crate::layer::util::compression::{CompressionLevel, WrapBody};
5use crate::{
6 Request, Response, StreamingBody,
7 header::{self, ACCEPT_ENCODING},
8};
9use rama_core::error::{BoxError, ErrorContext as _};
10use rama_core::{
11 Service,
12 matcher::service::{ServiceMatch, ServiceMatcher},
13};
14use rama_utils::macros::define_inner_service_accessors;
15use std::convert::Infallible;
16
17#[derive(Debug, Clone)]
24pub struct Decompression<S, M = DefaultDecompressionMatcher> {
25 pub(crate) inner: S,
26 pub(crate) accept: AcceptEncoding,
27 pub(crate) insert_accept_encoding_header: bool,
28 pub(crate) tolerate_decode_errors: bool,
29 pub(crate) matcher: M,
30}
31
32impl<S> Decompression<S> {
33 pub fn new(service: S) -> Self {
35 Self {
36 inner: service,
37 accept: AcceptEncoding::default(),
38 insert_accept_encoding_header: true,
39 tolerate_decode_errors: false,
40 matcher: DefaultDecompressionMatcher,
41 }
42 }
43}
44
45impl<S, M> Decompression<S, M> {
46 define_inner_service_accessors!();
47
48 rama_utils::macros::generate_set_and_with! {
49 pub fn insert_accept_encoding_header(mut self, insert: bool) -> Self {
54 self.insert_accept_encoding_header = insert;
55 self
56 }
57 }
58
59 pub fn with_matcher<T>(self, matcher: T) -> Decompression<S, T> {
66 Decompression {
67 inner: self.inner,
68 accept: self.accept,
69 insert_accept_encoding_header: self.insert_accept_encoding_header,
70 tolerate_decode_errors: self.tolerate_decode_errors,
71 matcher,
72 }
73 }
74
75 rama_utils::macros::generate_set_and_with! {
76 pub fn tolerate_decode_errors(mut self, tolerate: bool) -> Self {
84 self.tolerate_decode_errors = tolerate;
85 self
86 }
87 }
88
89 rama_utils::macros::generate_set_and_with! {
90 pub fn gzip(mut self, enable: bool) -> Self {
92 self.accept.set_gzip(enable);
93 self
94 }
95 }
96
97 rama_utils::macros::generate_set_and_with! {
98 pub fn deflate(mut self, enable: bool) -> Self {
100 self.accept.set_deflate(enable);
101 self
102 }
103 }
104
105 rama_utils::macros::generate_set_and_with! {
106 pub fn br(mut self, enable: bool) -> Self {
108 self.accept.set_br(enable);
109 self
110 }
111 }
112
113 rama_utils::macros::generate_set_and_with! {
114 pub fn zstd(mut self, enable: bool) -> Self {
116 self.accept.set_zstd(enable);
117 self
118 }
119 }
120}
121
122#[derive(Debug, Clone, Copy, Default)]
123pub struct DefaultDecompressionMatcher;
127
128#[derive(Debug, Clone, Copy, Default)]
129pub struct DefaultResponseDecompressionMatcher;
133
134impl<ReqBody> ServiceMatcher<Request<ReqBody>> for DefaultDecompressionMatcher
135where
136 ReqBody: Send + 'static,
137{
138 type Service = DefaultResponseDecompressionMatcher;
139 type Error = Infallible;
140 type ModifiedInput = Request<ReqBody>;
141
142 async fn match_service(
143 &self,
144 input: Request<ReqBody>,
145 ) -> Result<ServiceMatch<Self::ModifiedInput, Self::Service>, Self::Error> {
146 Ok(ServiceMatch {
147 input,
148 service: Some(DefaultResponseDecompressionMatcher),
149 })
150 }
151}
152
153impl<ResBody> ServiceMatcher<Response<ResBody>> for DefaultResponseDecompressionMatcher
154where
155 ResBody: Send + 'static,
156{
157 type Service = ();
158 type Error = Infallible;
159 type ModifiedInput = Response<ResBody>;
160
161 async fn match_service(
162 &self,
163 input: Response<ResBody>,
164 ) -> Result<ServiceMatch<Self::ModifiedInput, Self::Service>, Self::Error> {
165 Ok(ServiceMatch {
166 input,
167 service: Some(()),
168 })
169 }
170}
171
172impl<S, M, ReqBody, ResBody> Service<Request<ReqBody>> for Decompression<S, M>
173where
174 S: Service<Request<ReqBody>, Output = Response<ResBody>, Error: Into<BoxError>>,
175 M: ServiceMatcher<
176 Request<ReqBody>,
177 ModifiedInput = Request<ReqBody>,
178 Service: ServiceMatcher<
179 Response<ResBody>,
180 ModifiedInput = Response<ResBody>,
181 Service = (),
182 Error: Into<BoxError>,
183 >,
184 Error: Into<BoxError>,
185 >,
186 ReqBody: Send + 'static,
187 ResBody: StreamingBody<Data: Send + 'static, Error: Send + 'static> + Send + 'static,
188{
189 type Output = Response<DecompressionBody<ResBody>>;
190 type Error = BoxError;
191
192 async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
193 let ServiceMatch {
194 input: mut req,
195 service: maybe_response_matcher,
196 } = self
197 .matcher
198 .match_service(req)
199 .await
200 .context("decompression matcher: request")?;
201
202 if self.insert_accept_encoding_header
203 && let header::Entry::Vacant(entry) = req.headers_mut().entry(ACCEPT_ENCODING)
204 && let Some(accept) = self.accept.maybe_to_header_value()
205 {
206 entry.insert(accept);
207 }
208
209 let res = self.inner.serve(req).await.context("inner::serve")?;
210
211 let ServiceMatch {
212 input: res,
213 service: should_decompress,
214 } = if let Some(response_matcher) = maybe_response_matcher {
215 response_matcher
216 .into_match_service(res)
217 .await
218 .context("decompression matcher: response")?
219 } else {
220 ServiceMatch {
221 input: res,
222 service: None,
223 }
224 };
225
226 let (mut parts, body) = res.into_parts();
227
228 let res = if should_decompress.is_some()
229 && let header::Entry::Occupied(entry) = parts.headers.entry(header::CONTENT_ENCODING)
230 {
231 let maybe_marker = match entry.get().as_bytes() {
232 b"gzip" if self.accept.gzip() => Some(DecompressedFrom::Gzip),
233 b"deflate" if self.accept.deflate() => Some(DecompressedFrom::Deflate),
234 b"br" if self.accept.br() => Some(DecompressedFrom::Brotli),
235 b"zstd" if self.accept.zstd() => Some(DecompressedFrom::Zstd),
236 _ => None,
237 };
238
239 let Some(marker) = maybe_marker else {
240 return Ok(Response::from_parts(
241 parts,
242 DecompressionBody::new(BodyInner::identity(body)),
243 ));
244 };
245
246 let body = match marker {
247 DecompressedFrom::Gzip => DecompressionBody::new(BodyInner::gzip(
248 WrapBody::new(body, CompressionLevel::default())
249 .with_tolerate_decode_errors(self.tolerate_decode_errors),
250 )),
251 DecompressedFrom::Deflate => DecompressionBody::new(BodyInner::deflate(
252 WrapBody::new(body, CompressionLevel::default())
253 .with_tolerate_decode_errors(self.tolerate_decode_errors),
254 )),
255 DecompressedFrom::Brotli => DecompressionBody::new(BodyInner::brotli(
256 WrapBody::new(body, CompressionLevel::default())
257 .with_tolerate_decode_errors(self.tolerate_decode_errors),
258 )),
259 DecompressedFrom::Zstd => DecompressionBody::new(BodyInner::zstd(
260 WrapBody::new(body, CompressionLevel::default())
261 .with_tolerate_decode_errors(self.tolerate_decode_errors),
262 )),
263 };
264
265 entry.remove();
266 remove_payload_metadata_headers(&mut parts.headers);
267 parts.extensions.insert(marker);
268
269 Response::from_parts(parts, body)
270 } else {
271 Response::from_parts(parts, DecompressionBody::new(BodyInner::identity(body)))
272 };
273
274 Ok(res)
275 }
276}