Skip to main content

encoding_negotiator/
impl.rs

1use super::*;
2
3impl Default for CompressionPolicy {
4    /// Builds the policy that is used when none is given.
5    ///
6    /// # Returns
7    ///
8    /// - `Self` - The default policy.
9    fn default() -> Self {
10        Self {
11            min_size: DEFAULT_MIN_SIZE,
12            encodings: vec![ENCODING_GZIP.to_owned(), ENCODING_BROTLI.to_owned()],
13        }
14    }
15}
16
17impl CompressionPolicy {
18    /// The encoding the client asked for, when one of the offered encodings
19    /// is accepted.
20    ///
21    /// The offered encodings are tried in the order they are preferred, so the
22    /// first one the client accepts is the one that is used.
23    ///
24    /// # Arguments
25    ///
26    /// - `&str` - The `accept-encoding` header of the request.
27    ///
28    /// # Returns
29    ///
30    /// - `Option<String>` - The encoding to answer with, when the client
31    ///   accepts one of the offered encodings.
32    pub fn pick(&self, accept_encoding: &str) -> Option<String> {
33        let tokens: Vec<String> = self.accepted(accept_encoding);
34        for encoding in self.get_encodings().iter() {
35            if encoding.is_empty() {
36                continue;
37            }
38            if tokens
39                .iter()
40                .any(|token: &String| token == TOKEN_ANY || token == encoding)
41            {
42                return Some(encoding.clone());
43            }
44        }
45        None
46    }
47
48    /// The encodings the client accepts, without the ones it refused.
49    ///
50    /// An encoding that is refused with a quality of zero is dropped, and the
51    /// tokens are read in the case the client wrote them in.
52    ///
53    /// # Arguments
54    ///
55    /// - `&str` - The `accept-encoding` header of the request.
56    ///
57    /// # Returns
58    ///
59    /// - `Vec<String>` - The tokens the client accepts.
60    pub fn accepted(&self, accept_encoding: &str) -> Vec<String> {
61        let mut tokens: Vec<String> = Vec::new();
62        for part in accept_encoding.split(SEPARATOR_COMMA) {
63            let token: String = part.split(';').next().unwrap_or("").trim().to_lowercase();
64            if token.is_empty() {
65                continue;
66            }
67            let refused: bool = part.split(';').skip(1).any(|parameter: &str| {
68                let quality: Option<&str> = parameter.trim().strip_prefix(PARAMETER_QUALITY);
69                match quality {
70                    Some(quality) => quality
71                        .parse::<f32>()
72                        .map(|value: f32| value <= 0.0)
73                        .unwrap_or(false),
74                    None => false,
75                }
76            });
77            if !refused {
78                tokens.push(token);
79            }
80        }
81        tokens
82    }
83
84    /// Whether a body of that content type is worth compressing.
85    ///
86    /// A body that is already compressed is sent as it is, and the stream of
87    /// events is never compressed. The rule does not depend on the encodings
88    /// that are offered, so it is asked of the policy itself.
89    ///
90    /// # Arguments
91    ///
92    /// - `&str` - The content type of the answer.
93    ///
94    /// # Returns
95    ///
96    /// - `bool` - Whether the body may be compressed.
97    pub fn is_compressible(content_type: &str) -> bool {
98        let content_type: String = content_type.trim().to_lowercase();
99        if content_type.is_empty() {
100            return true;
101        }
102        if content_type == TEXT_EVENT_STREAM {
103            return false;
104        }
105        if CONTENT_TYPES_COMPRESSED.contains(&content_type.as_str()) {
106            return false;
107        }
108        !CONTENT_TYPE_PREFIXES_COMPRESSED
109            .iter()
110            .any(|prefix: &&str| content_type.starts_with(*prefix))
111    }
112}