Skip to main content

actix_http/encoding/
mod.rs

1//! Streaming compression and decompression of HTTP bodies.
2//!
3//! [`Decoder`] decompresses a payload stream using a given content encoding or the
4//! `Content-Encoding` header. [`Encoder`] compresses a response body and updates its response head.
5//! The caller is responsible for choosing a response encoding acceptable to the client.
6//!
7//! # Supported Encodings
8//!
9//! Supports a subset of the encodings in the [IANA HTTP Content Coding Registry]. Codec support is
10//! controlled by crate features:
11//!
12//! - `compress-brotli`: Brotli (`br`).
13//! - `compress-gzip`: Gzip (`gzip`) and zlib-wrapped DEFLATE (`deflate`).
14//! - `compress-zstd`: Zstandard (`zstd`).
15//!
16//! This module is available when at least one of these features is enabled. Identity encoding and
17//! encodings whose feature is disabled pass through without compression or decompression.
18//!
19//! [IANA HTTP Content Coding Registry]: https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding
20//!
21//! # Streaming
22//!
23//! Bodies are processed as they are polled. Output chunk boundaries can differ from input chunk
24//! boundaries. Larger chunks are processed on Tokio's blocking thread pool, so poll these adapters
25//! within a Tokio runtime.
26
27use std::io;
28
29use bytes::{Bytes, BytesMut};
30
31mod decoder;
32mod encoder;
33
34pub use self::{decoder::Decoder, encoder::Encoder};
35
36/// Special-purpose writer for streaming (de-)compression and tracking size limits.
37///
38/// Pre-allocates 8KiB of capacity.
39struct Writer {
40    buf: BytesMut,
41
42    /// When limit set, number of allowed bytes remaining.
43    remaining: Option<usize>,
44}
45
46impl Writer {
47    fn new() -> Writer {
48        Writer {
49            buf: BytesMut::with_capacity(8192),
50            remaining: None,
51        }
52    }
53
54    fn set_limit(&mut self, limit: usize) {
55        self.remaining = Some(limit);
56    }
57
58    fn take(&mut self) -> Bytes {
59        self.buf.split().freeze()
60    }
61}
62
63impl io::Write for Writer {
64    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
65        if let Some(remaining) = &mut self.remaining {
66            if buf.len() > *remaining {
67                *remaining = 0;
68                return Err(io::ErrorKind::FileTooLarge.into());
69            }
70
71            *remaining -= buf.len();
72        }
73
74        self.buf.extend_from_slice(buf);
75        Ok(buf.len())
76    }
77
78    fn flush(&mut self) -> io::Result<()> {
79        Ok(())
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use std::io::Write as _;
86
87    use super::Writer;
88
89    #[test]
90    fn writer_rejects_overflow_without_copying() {
91        let mut writer = Writer::new();
92        writer.set_limit(3);
93        writer.write_all(b"ab").unwrap();
94        assert!(writer.write_all(b"cdef").is_err());
95
96        assert_eq!(writer.take().as_ref(), b"ab");
97
98        assert!(writer.write_all(b"more output").is_err());
99        assert!(writer.take().is_empty());
100    }
101
102    #[test]
103    fn writer_zero_limit_rejects_output() {
104        let mut writer = Writer::new();
105        writer.set_limit(0);
106        assert!(writer.write_all(b"ab").is_err());
107
108        assert!(writer.take().is_empty());
109    }
110
111    #[test]
112    fn taking_output_does_not_reset_limit() {
113        let mut writer = Writer::new();
114        writer.set_limit(3);
115        writer.write_all(b"ab").unwrap();
116        assert_eq!(writer.take().as_ref(), b"ab");
117
118        writer.write_all(b"c").unwrap();
119        assert!(writer.write_all(b"d").is_err());
120        assert_eq!(writer.take().as_ref(), b"c");
121    }
122
123    #[test]
124    fn writer_max_limit_does_not_wrap() {
125        let mut writer = Writer::new();
126        writer.set_limit(usize::MAX);
127        writer.write_all(b"abc").unwrap();
128
129        assert_eq!(writer.take().as_ref(), b"abc");
130    }
131
132    #[test]
133    fn writer_is_unlimited_by_default() {
134        let mut writer = Writer::new();
135        let data = vec![0; 1024 * 1024];
136        writer.write_all(&data).unwrap();
137
138        assert_eq!(writer.take().as_ref(), data);
139    }
140}