Skip to main content

trillium_static_compiled/
encoding.rs

1/// A precompressed content coding baked into the binary at compile time.
2///
3/// Variants correspond to the [HTTP content-coding tokens][content-coding]
4/// returned by [`Encoding::token`] and used in the `Content-Encoding`
5/// response header.
6///
7/// [content-coding]: https://www.rfc-editor.org/rfc/rfc9110.html#name-content-codings
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum Encoding {
10    /// Brotli (`br`).
11    Brotli,
12    /// Zstandard (`zstd`).
13    Zstd,
14    /// Gzip (`gzip`).
15    Gzip,
16}
17
18impl Encoding {
19    /// The HTTP content-coding token for this encoding.
20    pub const fn token(self) -> &'static str {
21        match self {
22            Self::Brotli => "br",
23            Self::Zstd => "zstd",
24            Self::Gzip => "gzip",
25        }
26    }
27}
28
29/// Returns true if `accept` (an `Accept-Encoding` header value) permits the
30/// given encoding token. Honors q-values and the `*` wildcard, with explicit
31/// named tokens overriding the wildcard.
32pub(crate) fn accept_encoding_allows(accept: &str, encoding: &str) -> bool {
33    let mut wildcard_ok = false;
34    let mut named_ok = None;
35    for part in accept.split(',') {
36        let mut iter = part.trim().split(';');
37        let token = iter.next().unwrap_or("").trim();
38        let q = iter
39            .find_map(|p| {
40                p.trim()
41                    .strip_prefix("q=")
42                    .and_then(|q| q.parse::<f32>().ok())
43            })
44            .unwrap_or(1.0);
45        if token.eq_ignore_ascii_case(encoding) {
46            named_ok = Some(q > 0.0);
47        } else if token == "*" && named_ok.is_none() {
48            wildcard_ok = q > 0.0;
49        }
50    }
51    named_ok.unwrap_or(wildcard_ok)
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn token_round_trip() {
60        assert_eq!(Encoding::Brotli.token(), "br");
61        assert_eq!(Encoding::Zstd.token(), "zstd");
62        assert_eq!(Encoding::Gzip.token(), "gzip");
63    }
64
65    #[test]
66    fn accept_encoding_basics() {
67        assert!(accept_encoding_allows("br, gzip", "br"));
68        assert!(accept_encoding_allows("br, gzip", "gzip"));
69        assert!(!accept_encoding_allows("br, gzip", "zstd"));
70    }
71
72    #[test]
73    fn accept_encoding_q_values() {
74        assert!(accept_encoding_allows("br;q=0.5, gzip", "br"));
75        assert!(!accept_encoding_allows("br;q=0, gzip", "br"));
76        assert!(accept_encoding_allows("br;q=0, gzip", "gzip"));
77    }
78
79    #[test]
80    fn accept_encoding_wildcard() {
81        assert!(accept_encoding_allows("*", "br"));
82        assert!(!accept_encoding_allows("*;q=0", "br"));
83        assert!(accept_encoding_allows("*, br;q=0", "gzip"));
84        assert!(!accept_encoding_allows("*, br;q=0", "br"));
85    }
86
87    #[test]
88    fn accept_encoding_case_insensitive() {
89        assert!(accept_encoding_allows("BR, GZIP", "br"));
90        assert!(accept_encoding_allows("Br", "br"));
91    }
92}