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}