http_encoding/
coding.rs

1use http::header::{HeaderMap, ACCEPT_ENCODING};
2
3use super::error::FeatureError;
4
5/// Represents a supported content encoding.
6#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
7pub enum ContentEncoding {
8    /// A format using the Brotli algorithm.
9    Br,
10    /// A format using the zlib structure with deflate algorithm.
11    Deflate,
12    /// Gzip algorithm.
13    Gzip,
14    /// Indicates no operation is done with encoding.
15    #[default]
16    NoOp,
17}
18
19impl ContentEncoding {
20    pub fn from_headers(headers: &HeaderMap) -> Self {
21        let mut prefer = ContentEncodingWithQValue::default();
22
23        for encoding in Self::_from_headers(headers) {
24            prefer.try_update(encoding);
25        }
26
27        prefer.enc
28    }
29
30    fn _from_headers(headers: &HeaderMap) -> impl Iterator<Item = ContentEncodingWithQValue> + '_ {
31        headers
32            .get_all(ACCEPT_ENCODING)
33            .iter()
34            .filter_map(|hval| hval.to_str().ok())
35            .flat_map(|s| s.split(','))
36            .filter_map(|v| {
37                let mut v = v.splitn(2, ';');
38                Self::try_parse(v.next().unwrap().trim()).ok().map(|enc| {
39                    let val = v
40                        .next()
41                        .and_then(|v| QValue::parse(v.trim()))
42                        .unwrap_or_else(QValue::one);
43                    ContentEncodingWithQValue { enc, val }
44                })
45            })
46    }
47
48    pub(super) fn try_parse(s: &str) -> Result<Self, FeatureError> {
49        if s.eq_ignore_ascii_case("gzip") {
50            Ok(Self::Gzip)
51        } else if s.eq_ignore_ascii_case("deflate") {
52            Ok(Self::Deflate)
53        } else if s.eq_ignore_ascii_case("br") {
54            Ok(Self::Br)
55        } else if s.eq_ignore_ascii_case("identity") {
56            Ok(Self::NoOp)
57        } else {
58            Err(FeatureError::Unknown(s.to_string().into_boxed_str()))
59        }
60    }
61}
62
63struct ContentEncodingWithQValue {
64    enc: ContentEncoding,
65    val: QValue,
66}
67
68impl Default for ContentEncodingWithQValue {
69    fn default() -> Self {
70        Self {
71            enc: ContentEncoding::NoOp,
72            val: QValue::zero(),
73        }
74    }
75}
76
77impl ContentEncodingWithQValue {
78    fn try_update(&mut self, other: Self) {
79        if other.val > self.val {
80            match other.enc {
81                #[cfg(not(feature = "br"))]
82                ContentEncoding::Br => return,
83                #[cfg(not(feature = "de"))]
84                ContentEncoding::Deflate => return,
85                #[cfg(not(feature = "gz"))]
86                ContentEncoding::Gzip => return,
87                _ => {}
88            };
89            *self = other;
90        }
91    }
92}
93
94#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
95struct QValue(u16);
96
97impl QValue {
98    const fn zero() -> Self {
99        Self(0)
100    }
101
102    const fn one() -> Self {
103        Self(1000)
104    }
105
106    // Parse a q-value as specified in RFC 7231 section 5.3.1.
107    fn parse(s: &str) -> Option<Self> {
108        let mut c = s.chars();
109        // Parse "q=" (case-insensitively).
110        match c.next() {
111            Some('q') | Some('Q') => (),
112            _ => return None,
113        };
114        match c.next() {
115            Some('=') => (),
116            _ => return None,
117        };
118
119        // Parse leading digit. Since valid q-values are between 0.000 and 1.000, only "0" and "1"
120        // are allowed.
121        let mut value = match c.next() {
122            Some('0') => 0,
123            Some('1') => 1000,
124            _ => return None,
125        };
126
127        // Parse optional decimal point.
128        match c.next() {
129            Some('.') => (),
130            None => return Some(Self(value)),
131            _ => return None,
132        };
133
134        // Parse optional fractional digits. The value of each digit is multiplied by `factor`.
135        // Since the q-value is represented as an integer between 0 and 1000, `factor` is `100` for
136        // the first digit, `10` for the next, and `1` for the digit after that.
137        let mut factor = 100;
138        loop {
139            match c.next() {
140                Some(n @ '0'..='9') => {
141                    // If `factor` is less than `1`, three digits have already been parsed. A
142                    // q-value having more than 3 fractional digits is invalid.
143                    if factor < 1 {
144                        return None;
145                    }
146                    // Add the digit's value multiplied by `factor` to `value`.
147                    value += factor * (n as u16 - '0' as u16);
148                }
149                None => {
150                    // No more characters to parse. Check that the value representing the q-value is
151                    // in the valid range.
152                    return if value <= 1000 { Some(Self(value)) } else { None };
153                }
154                _ => return None,
155            };
156            factor /= 10;
157        }
158    }
159}