lambda_web/
brotli.rs

1// SPDX-License-Identifier: MIT
2//!
3//! Brotli ransparent compression
4//! Supports
5//!   Content-Encoding: br
6//!
7
8///
9/// Trait to check if reponse should be compressed
10///
11pub(crate) trait ResponseCompression {
12    /// Content-Encoding header value
13    fn content_encoding<'a>(&'a self) -> Option<&'a str>;
14
15    /// Content-Type header value
16    fn content_type<'a>(&'a self) -> Option<&'a str>;
17
18    /// Can this response be compressed?
19    #[cfg(feature = "br")]
20    fn can_brotli_compress(&self) -> bool {
21        // Check already compressed
22        if self.content_encoding().is_some() {
23            // Already compressed
24            return false;
25        }
26
27        // Get Content-type header value
28        if let Some(header_val) = self.content_type() {
29            let ctype = header_val.trim().to_ascii_lowercase();
30
31            // Compress when text types
32            ctype.starts_with("text/")
33                || ctype.starts_with("application/json")
34                || ctype.starts_with("application/xhtml")
35                || ctype.starts_with("application/xml")
36                || ctype.starts_with("application/wasm")
37                || ctype.starts_with("image/svg")
38        } else {
39            // No content-type
40            false
41        }
42    }
43
44    // Without Brotli support, always returns false
45    #[cfg(not(feature = "br"))]
46    fn can_brotli_compress(&self) -> bool {
47        false
48    }
49}
50
51/// Compress response using Brotli, base64 encode it, and return encoded string.
52#[cfg(feature = "br")]
53pub(crate) fn compress_response_body<'a>(body: &[u8]) -> String {
54    // Compress parameter
55    let cfg = brotli::enc::BrotliEncoderParams {
56        quality: 4,
57        ..Default::default()
58    };
59
60    // Do Brotli compression
61    let mut body_reader = std::io::Cursor::new(body);
62    let mut compressed_base64 = base64::write::EncoderStringWriter::new(base64::STANDARD);
63    let _sz = brotli::BrotliCompress(&mut body_reader, &mut compressed_base64, &cfg);
64
65    compressed_base64.into_inner()
66}
67
68// No Brotli compression, only base64 encoding
69#[cfg(not(feature = "br"))]
70pub(crate) fn compress_response_body<'a>(body: &[u8]) -> String {
71    base64::encode(body)
72}