Skip to main content

tower_http/
compression_utils.rs

1//! Types used by compression and decompression middleware.
2
3use crate::{content_encoding::SupportedEncodings, BoxError};
4use bytes::{Buf, Bytes, BytesMut};
5use futures_core::Stream;
6use http::HeaderValue;
7use http_body::{Body, Frame};
8use pin_project_lite::pin_project;
9use std::{
10    io,
11    pin::Pin,
12    task::{ready, Context, Poll},
13};
14use tokio::io::AsyncRead;
15use tokio_util::io::StreamReader;
16
17#[derive(Debug, Clone, Copy)]
18pub(crate) struct AcceptEncoding {
19    pub(crate) gzip: bool,
20    pub(crate) deflate: bool,
21    pub(crate) br: bool,
22    pub(crate) zstd: bool,
23}
24
25impl AcceptEncoding {
26    #[allow(dead_code)]
27    pub(crate) fn to_header_value(self) -> Option<HeaderValue> {
28        let accept = match (self.gzip(), self.deflate(), self.br(), self.zstd()) {
29            (true, true, true, false) => "gzip,deflate,br",
30            (true, true, false, false) => "gzip,deflate",
31            (true, false, true, false) => "gzip,br",
32            (true, false, false, false) => "gzip",
33            (false, true, true, false) => "deflate,br",
34            (false, true, false, false) => "deflate",
35            (false, false, true, false) => "br",
36            (true, true, true, true) => "zstd,gzip,deflate,br",
37            (true, true, false, true) => "zstd,gzip,deflate",
38            (true, false, true, true) => "zstd,gzip,br",
39            (true, false, false, true) => "zstd,gzip",
40            (false, true, true, true) => "zstd,deflate,br",
41            (false, true, false, true) => "zstd,deflate",
42            (false, false, true, true) => "zstd,br",
43            (false, false, false, true) => "zstd",
44            (false, false, false, false) => return None,
45        };
46        Some(HeaderValue::from_static(accept))
47    }
48
49    #[allow(dead_code)]
50    pub(crate) fn set_gzip(&mut self, enable: bool) {
51        self.gzip = enable;
52    }
53
54    #[allow(dead_code)]
55    pub(crate) fn set_deflate(&mut self, enable: bool) {
56        self.deflate = enable;
57    }
58
59    #[allow(dead_code)]
60    pub(crate) fn set_br(&mut self, enable: bool) {
61        self.br = enable;
62    }
63
64    #[allow(dead_code)]
65    pub(crate) fn set_zstd(&mut self, enable: bool) {
66        self.zstd = enable;
67    }
68}
69
70impl SupportedEncodings for AcceptEncoding {
71    #[allow(dead_code)]
72    fn gzip(&self) -> bool {
73        #[cfg(any(feature = "decompression-gzip", feature = "compression-gzip"))]
74        return self.gzip;
75
76        #[cfg(not(any(feature = "decompression-gzip", feature = "compression-gzip")))]
77        return false;
78    }
79
80    #[allow(dead_code)]
81    fn deflate(&self) -> bool {
82        #[cfg(any(feature = "decompression-deflate", feature = "compression-deflate"))]
83        return self.deflate;
84
85        #[cfg(not(any(feature = "decompression-deflate", feature = "compression-deflate")))]
86        return false;
87    }
88
89    #[allow(dead_code)]
90    fn br(&self) -> bool {
91        #[cfg(any(feature = "decompression-br", feature = "compression-br"))]
92        return self.br;
93
94        #[cfg(not(any(feature = "decompression-br", feature = "compression-br")))]
95        return false;
96    }
97
98    #[allow(dead_code)]
99    fn zstd(&self) -> bool {
100        #[cfg(any(feature = "decompression-zstd", feature = "compression-zstd"))]
101        return self.zstd;
102
103        #[cfg(not(any(feature = "decompression-zstd", feature = "compression-zstd")))]
104        return false;
105    }
106}
107
108impl Default for AcceptEncoding {
109    fn default() -> Self {
110        AcceptEncoding {
111            gzip: true,
112            deflate: true,
113            br: true,
114            zstd: true,
115        }
116    }
117}
118
119/// A `Body` that has been converted into an `AsyncRead`.
120pub(crate) type AsyncReadBody<B> =
121    StreamReader<StreamErrorIntoIoError<BodyIntoStream<B>, <B as Body>::Error>, <B as Body>::Data>;
122
123/// Trait for applying some decorator to an `AsyncRead`
124pub(crate) trait DecorateAsyncRead {
125    type Input: AsyncRead;
126    type Output: AsyncRead;
127
128    /// Apply the decorator
129    fn apply(input: Self::Input, quality: CompressionLevel) -> Self::Output;
130
131    /// Get a pinned mutable reference to the original input.
132    ///
133    /// This is necessary to implement `Body::poll_trailers`.
134    fn get_pin_mut(pinned: Pin<&mut Self::Output>) -> Pin<&mut Self::Input>;
135}
136
137pin_project! {
138    /// `Body` that has been decorated by an `AsyncRead`
139    pub(crate) struct WrapBody<M: DecorateAsyncRead> {
140        #[pin]
141        // rust-analyer thinks this field is private if its `pub(crate)` but works fine when its
142        // `pub`
143        pub read: M::Output,
144        // A buffer to temporarily store the data read from the underlying body.
145        // Reused as much as possible to optimize allocations.
146        buf: BytesMut,
147        read_all_data: bool,
148    }
149}
150
151impl<M: DecorateAsyncRead> WrapBody<M> {
152    const INTERNAL_BUF_CAPACITY: usize = 4096;
153}
154
155impl<M: DecorateAsyncRead> WrapBody<M> {
156    #[allow(dead_code)]
157    pub(crate) fn new<B>(body: B, quality: CompressionLevel) -> Self
158    where
159        B: Body,
160        M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
161    {
162        // convert `Body` into a `Stream`
163        let stream = BodyIntoStream::new(body);
164
165        // an adapter that converts the error type into `io::Error` while storing the actual error
166        // `StreamReader` requires the error type is `io::Error`
167        let stream = StreamErrorIntoIoError::<_, B::Error>::new(stream);
168
169        // convert `Stream` into an `AsyncRead`
170        let read = StreamReader::new(stream);
171
172        // apply decorator to `AsyncRead` yielding another `AsyncRead`
173        let read = M::apply(read, quality);
174
175        Self {
176            read,
177            buf: BytesMut::with_capacity(Self::INTERNAL_BUF_CAPACITY),
178            read_all_data: false,
179        }
180    }
181}
182
183impl<B, M> Body for WrapBody<M>
184where
185    B: Body,
186    B::Error: Into<BoxError>,
187    M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
188{
189    type Data = Bytes;
190    type Error = BoxError;
191
192    fn poll_frame(
193        self: Pin<&mut Self>,
194        cx: &mut Context<'_>,
195    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
196        let mut this = self.project();
197
198        if !*this.read_all_data {
199            if this.buf.capacity() == 0 {
200                this.buf.reserve(Self::INTERNAL_BUF_CAPACITY);
201            }
202
203            let result = tokio_util::io::poll_read_buf(this.read.as_mut(), cx, &mut this.buf);
204
205            match ready!(result) {
206                Ok(0) => {
207                    *this.read_all_data = true;
208                }
209                Ok(_) => {
210                    let chunk = this.buf.split().freeze();
211                    return Poll::Ready(Some(Ok(Frame::data(chunk))));
212                }
213                Err(err) => {
214                    let body_error: Option<B::Error> = M::get_pin_mut(this.read.as_mut())
215                        .get_pin_mut()
216                        .project()
217                        .error
218                        .take();
219
220                    let read_some_data = M::get_pin_mut(this.read.as_mut())
221                        .get_pin_mut()
222                        .project()
223                        .read_some_data;
224
225                    if let Some(body_error) = body_error {
226                        return Poll::Ready(Some(Err(body_error.into())));
227                    } else if err.raw_os_error() == Some(SENTINEL_ERROR_CODE) {
228                        // SENTINEL_ERROR_CODE only gets used when storing
229                        // an underlying body error
230                        unreachable!()
231                    } else if *read_some_data {
232                        return Poll::Ready(Some(Err(err.into())));
233                    }
234                }
235            }
236        }
237
238        // poll any remaining frames, such as trailers
239        let body = M::get_pin_mut(this.read).get_pin_mut().get_pin_mut();
240        match ready!(body.poll_frame(cx)) {
241            Some(Ok(frame)) if frame.is_trailers() => Poll::Ready(Some(Ok(
242                frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))
243            ))),
244            Some(Ok(frame)) => match frame.into_data() {
245                // Payload after the decompressor reported end-of-stream means
246                // the body and the compressed stream disagree about where the
247                // content ends.
248                Ok(data) if data.has_remaining() => Poll::Ready(Some(Err(
249                    "there are extra bytes after body has been decompressed".into(),
250                ))),
251                // An empty data frame carries nothing, but it is not the end of
252                // the body: trailers may still follow it. Ending the stream here
253                // would strand them, and `Body`'s contract forbids polling once
254                // `None` has been returned, so they could never be recovered.
255                Ok(mut data) => {
256                    Poll::Ready(Some(Ok(Frame::data(data.copy_to_bytes(data.remaining())))))
257                }
258                Err(frame) => Poll::Ready(Some(Ok(
259                    frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))
260                ))),
261            },
262            Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
263            None => Poll::Ready(None),
264        }
265    }
266}
267
268pin_project! {
269    pub(crate) struct BodyIntoStream<B>
270    where
271        B: Body,
272    {
273        #[pin]
274        body: B,
275        yielded_all_data: bool,
276        non_data_frame: Option<Frame<B::Data>>,
277    }
278}
279
280#[allow(dead_code)]
281impl<B> BodyIntoStream<B>
282where
283    B: Body,
284{
285    pub(crate) fn new(body: B) -> Self {
286        Self {
287            body,
288            yielded_all_data: false,
289            non_data_frame: None,
290        }
291    }
292
293    /// Get a reference to the inner body
294    pub(crate) fn get_ref(&self) -> &B {
295        &self.body
296    }
297
298    /// Get a mutable reference to the inner body
299    pub(crate) fn get_mut(&mut self) -> &mut B {
300        &mut self.body
301    }
302
303    /// Get a pinned mutable reference to the inner body
304    pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
305        self.project().body
306    }
307
308    /// Consume `self`, returning the inner body
309    pub(crate) fn into_inner(self) -> B {
310        self.body
311    }
312}
313
314impl<B> Stream for BodyIntoStream<B>
315where
316    B: Body,
317{
318    type Item = Result<B::Data, B::Error>;
319
320    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
321        loop {
322            let this = self.as_mut().project();
323
324            if *this.yielded_all_data {
325                return Poll::Ready(None);
326            }
327
328            match std::task::ready!(this.body.poll_frame(cx)) {
329                Some(Ok(frame)) => match frame.into_data() {
330                    Ok(data) => return Poll::Ready(Some(Ok(data))),
331                    Err(frame) => {
332                        *this.yielded_all_data = true;
333                        *this.non_data_frame = Some(frame);
334                    }
335                },
336                Some(Err(err)) => return Poll::Ready(Some(Err(err))),
337                None => {
338                    *this.yielded_all_data = true;
339                }
340            }
341        }
342    }
343}
344
345impl<B> Body for BodyIntoStream<B>
346where
347    B: Body,
348{
349    type Data = B::Data;
350    type Error = B::Error;
351
352    fn poll_frame(
353        mut self: Pin<&mut Self>,
354        cx: &mut Context<'_>,
355    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
356        // First drive the stream impl. This consumes all data frames and buffer at most one
357        // trailers frame.
358        if let Some(frame) = std::task::ready!(self.as_mut().poll_next(cx)) {
359            return Poll::Ready(Some(frame.map(Frame::data)));
360        }
361
362        let this = self.project();
363
364        // Yield the trailers frame `poll_next` hit.
365        if let Some(frame) = this.non_data_frame.take() {
366            return Poll::Ready(Some(Ok(frame)));
367        }
368
369        // Yield any remaining frames in the body. There shouldn't be any after the trailers but
370        // you never know.
371        this.body.poll_frame(cx)
372    }
373
374    #[inline]
375    fn size_hint(&self) -> http_body::SizeHint {
376        self.body.size_hint()
377    }
378}
379
380pin_project! {
381    pub(crate) struct StreamErrorIntoIoError<S, E> {
382        #[pin]
383        inner: S,
384        error: Option<E>,
385        read_some_data: bool
386    }
387}
388
389impl<S, E> StreamErrorIntoIoError<S, E> {
390    pub(crate) fn new(inner: S) -> Self {
391        Self {
392            inner,
393            error: None,
394            read_some_data: false,
395        }
396    }
397
398    /// Get a reference to the inner body
399    pub(crate) fn get_ref(&self) -> &S {
400        &self.inner
401    }
402
403    /// Get a mutable reference to the inner inner
404    pub(crate) fn get_mut(&mut self) -> &mut S {
405        &mut self.inner
406    }
407
408    /// Get a pinned mutable reference to the inner inner
409    pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
410        self.project().inner
411    }
412
413    /// Consume `self`, returning the inner inner
414    pub(crate) fn into_inner(self) -> S {
415        self.inner
416    }
417}
418
419impl<S, T, E> Stream for StreamErrorIntoIoError<S, E>
420where
421    S: Stream<Item = Result<T, E>>,
422{
423    type Item = Result<T, io::Error>;
424
425    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
426        let this = self.project();
427        match ready!(this.inner.poll_next(cx)) {
428            None => Poll::Ready(None),
429            Some(Ok(value)) => {
430                *this.read_some_data = true;
431                Poll::Ready(Some(Ok(value)))
432            }
433            Some(Err(err)) => {
434                *this.error = Some(err);
435                Poll::Ready(Some(Err(io::Error::from_raw_os_error(SENTINEL_ERROR_CODE))))
436            }
437        }
438    }
439}
440
441pub(crate) const SENTINEL_ERROR_CODE: i32 = -837459418;
442
443/// Level of compression data should be compressed with.
444#[non_exhaustive]
445#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
446pub enum CompressionLevel {
447    /// Fastest quality of compression, usually produces bigger size.
448    Fastest,
449    /// Best quality of compression, usually produces the smallest size.
450    Best,
451    /// Default quality of compression defined by the selected compression
452    /// algorithm.
453    #[default]
454    Default,
455    /// Precise quality based on the underlying compression algorithms'
456    /// qualities.
457    ///
458    /// The interpretation of this depends on the algorithm chosen and the
459    /// specific implementation backing it.
460    ///
461    /// Qualities are implicitly clamped to the algorithm's maximum.
462    Precise(i32),
463}
464
465#[cfg(any(
466    feature = "compression-br",
467    feature = "compression-gzip",
468    feature = "compression-deflate",
469    feature = "compression-zstd"
470))]
471use async_compression::Level as AsyncCompressionLevel;
472
473#[cfg(any(
474    feature = "compression-br",
475    feature = "compression-gzip",
476    feature = "compression-deflate",
477    feature = "compression-zstd"
478))]
479impl CompressionLevel {
480    pub(crate) fn into_async_compression(self) -> AsyncCompressionLevel {
481        match self {
482            CompressionLevel::Fastest => AsyncCompressionLevel::Fastest,
483            CompressionLevel::Best => AsyncCompressionLevel::Best,
484            CompressionLevel::Default => AsyncCompressionLevel::Default,
485            CompressionLevel::Precise(quality) => AsyncCompressionLevel::Precise(quality),
486        }
487    }
488}