http_types/content/
accept_encoding.rs
1use crate::content::{ContentEncoding, Encoding, EncodingProposal};
4use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING};
5use crate::utils::sort_by_weight;
6use crate::{Error, StatusCode};
7
8use std::fmt::{self, Debug, Write};
9use std::option;
10use std::slice;
11
12pub struct AcceptEncoding {
40 wildcard: bool,
41 entries: Vec<EncodingProposal>,
42}
43
44impl AcceptEncoding {
45 pub fn new() -> Self {
47 Self {
48 entries: vec![],
49 wildcard: false,
50 }
51 }
52
53 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
55 let mut entries = vec![];
56 let headers = match headers.as_ref().get(ACCEPT_ENCODING) {
57 Some(headers) => headers,
58 None => return Ok(None),
59 };
60
61 let mut wildcard = false;
62
63 for value in headers {
64 for part in value.as_str().trim().split(',') {
65 let part = part.trim();
66
67 if part.is_empty() {
69 continue;
70 } else if part == "*" {
71 wildcard = true;
72 continue;
73 }
74
75 if let Some(entry) = EncodingProposal::from_str(part)? {
78 entries.push(entry);
79 }
80 }
81 }
82
83 Ok(Some(Self { entries, wildcard }))
84 }
85
86 pub fn push(&mut self, prop: impl Into<EncodingProposal>) {
88 self.entries.push(prop.into());
89 }
90
91 pub fn wildcard(&self) -> bool {
93 self.wildcard
94 }
95
96 pub fn set_wildcard(&mut self, wildcard: bool) {
98 self.wildcard = wildcard
99 }
100
101 pub fn sort(&mut self) {
107 sort_by_weight(&mut self.entries);
108 }
109
110 pub fn negotiate(&mut self, available: &[Encoding]) -> crate::Result<ContentEncoding> {
116 self.sort();
118
119 for encoding in &self.entries {
121 if available.contains(encoding) {
122 return Ok(encoding.into());
123 }
124 }
125
126 if self.wildcard {
128 if let Some(encoding) = available.iter().next() {
129 return Ok(encoding.into());
130 }
131 }
132
133 let mut err = Error::new_adhoc("No suitable Content-Encoding found");
134 err.set_status(StatusCode::NotAcceptable);
135 Err(err)
136 }
137
138 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
140 headers.as_mut().insert(ACCEPT_ENCODING, self.value());
141 }
142
143 pub fn name(&self) -> HeaderName {
145 ACCEPT_ENCODING
146 }
147
148 pub fn value(&self) -> HeaderValue {
150 let mut output = String::new();
151 for (n, directive) in self.entries.iter().enumerate() {
152 let directive: HeaderValue = (*directive).into();
153 match n {
154 0 => write!(output, "{}", directive).unwrap(),
155 _ => write!(output, ", {}", directive).unwrap(),
156 };
157 }
158
159 if self.wildcard {
160 match output.len() {
161 0 => write!(output, "*").unwrap(),
162 _ => write!(output, ", *").unwrap(),
163 }
164 }
165
166 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
168 }
169
170 pub fn iter(&self) -> Iter<'_> {
172 Iter {
173 inner: self.entries.iter(),
174 }
175 }
176
177 pub fn iter_mut(&mut self) -> IterMut<'_> {
179 IterMut {
180 inner: self.entries.iter_mut(),
181 }
182 }
183}
184
185impl IntoIterator for AcceptEncoding {
186 type Item = EncodingProposal;
187 type IntoIter = IntoIter;
188
189 #[inline]
190 fn into_iter(self) -> Self::IntoIter {
191 IntoIter {
192 inner: self.entries.into_iter(),
193 }
194 }
195}
196
197impl<'a> IntoIterator for &'a AcceptEncoding {
198 type Item = &'a EncodingProposal;
199 type IntoIter = Iter<'a>;
200
201 #[inline]
202 fn into_iter(self) -> Self::IntoIter {
203 self.iter()
204 }
205}
206
207impl<'a> IntoIterator for &'a mut AcceptEncoding {
208 type Item = &'a mut EncodingProposal;
209 type IntoIter = IterMut<'a>;
210
211 #[inline]
212 fn into_iter(self) -> Self::IntoIter {
213 self.iter_mut()
214 }
215}
216
217#[derive(Debug)]
219pub struct IntoIter {
220 inner: std::vec::IntoIter<EncodingProposal>,
221}
222
223impl Iterator for IntoIter {
224 type Item = EncodingProposal;
225
226 fn next(&mut self) -> Option<Self::Item> {
227 self.inner.next()
228 }
229
230 #[inline]
231 fn size_hint(&self) -> (usize, Option<usize>) {
232 self.inner.size_hint()
233 }
234}
235
236#[derive(Debug)]
238pub struct Iter<'a> {
239 inner: slice::Iter<'a, EncodingProposal>,
240}
241
242impl<'a> Iterator for Iter<'a> {
243 type Item = &'a EncodingProposal;
244
245 fn next(&mut self) -> Option<Self::Item> {
246 self.inner.next()
247 }
248
249 #[inline]
250 fn size_hint(&self) -> (usize, Option<usize>) {
251 self.inner.size_hint()
252 }
253}
254
255#[derive(Debug)]
257pub struct IterMut<'a> {
258 inner: slice::IterMut<'a, EncodingProposal>,
259}
260
261impl<'a> Iterator for IterMut<'a> {
262 type Item = &'a mut EncodingProposal;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 self.inner.next()
266 }
267
268 #[inline]
269 fn size_hint(&self) -> (usize, Option<usize>) {
270 self.inner.size_hint()
271 }
272}
273
274impl ToHeaderValues for AcceptEncoding {
275 type Iter = option::IntoIter<HeaderValue>;
276 fn to_header_values(&self) -> crate::Result<Self::Iter> {
277 Ok(self.value().to_header_values().unwrap())
279 }
280}
281
282impl Debug for AcceptEncoding {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 let mut list = f.debug_list();
285 for directive in &self.entries {
286 list.entry(directive);
287 }
288 list.finish()
289 }
290}
291
292#[cfg(test)]
293mod test {
294 use super::*;
295 use crate::content::Encoding;
296 use crate::Response;
297
298 #[test]
299 fn smoke() -> crate::Result<()> {
300 let mut accept = AcceptEncoding::new();
301 accept.push(Encoding::Gzip);
302
303 let mut headers = Response::new(200);
304 accept.apply(&mut headers);
305
306 let accept = AcceptEncoding::from_headers(headers)?.unwrap();
307 assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
308 Ok(())
309 }
310
311 #[test]
312 fn wildcard() -> crate::Result<()> {
313 let mut accept = AcceptEncoding::new();
314 accept.set_wildcard(true);
315
316 let mut headers = Response::new(200);
317 accept.apply(&mut headers);
318
319 let accept = AcceptEncoding::from_headers(headers)?.unwrap();
320 assert!(accept.wildcard());
321 Ok(())
322 }
323
324 #[test]
325 fn wildcard_and_header() -> crate::Result<()> {
326 let mut accept = AcceptEncoding::new();
327 accept.push(Encoding::Gzip);
328 accept.set_wildcard(true);
329
330 let mut headers = Response::new(200);
331 accept.apply(&mut headers);
332
333 let accept = AcceptEncoding::from_headers(headers)?.unwrap();
334 assert!(accept.wildcard());
335 assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
336 Ok(())
337 }
338
339 #[test]
340 fn iter() -> crate::Result<()> {
341 let mut accept = AcceptEncoding::new();
342 accept.push(Encoding::Gzip);
343 accept.push(Encoding::Brotli);
344
345 let mut headers = Response::new(200);
346 accept.apply(&mut headers);
347
348 let accept = AcceptEncoding::from_headers(headers)?.unwrap();
349 let mut accept = accept.iter();
350 assert_eq!(accept.next().unwrap(), Encoding::Gzip);
351 assert_eq!(accept.next().unwrap(), Encoding::Brotli);
352 Ok(())
353 }
354
355 #[test]
356 fn reorder_based_on_weight() -> crate::Result<()> {
357 let mut accept = AcceptEncoding::new();
358 accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
359 accept.push(EncodingProposal::new(Encoding::Identity, None)?);
360 accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
361
362 let mut headers = Response::new(200);
363 accept.apply(&mut headers);
364
365 let mut accept = AcceptEncoding::from_headers(headers)?.unwrap();
366 accept.sort();
367 let mut accept = accept.iter();
368 assert_eq!(accept.next().unwrap(), Encoding::Brotli);
369 assert_eq!(accept.next().unwrap(), Encoding::Gzip);
370 assert_eq!(accept.next().unwrap(), Encoding::Identity);
371 Ok(())
372 }
373
374 #[test]
375 fn reorder_based_on_weight_and_location() -> crate::Result<()> {
376 let mut accept = AcceptEncoding::new();
377 accept.push(EncodingProposal::new(Encoding::Identity, None)?);
378 accept.push(EncodingProposal::new(Encoding::Gzip, None)?);
379 accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
380
381 let mut res = Response::new(200);
382 accept.apply(&mut res);
383
384 let mut accept = AcceptEncoding::from_headers(res)?.unwrap();
385 accept.sort();
386 let mut accept = accept.iter();
387 assert_eq!(accept.next().unwrap(), Encoding::Brotli);
388 assert_eq!(accept.next().unwrap(), Encoding::Gzip);
389 assert_eq!(accept.next().unwrap(), Encoding::Identity);
390 Ok(())
391 }
392
393 #[test]
394 fn negotiate() -> crate::Result<()> {
395 let mut accept = AcceptEncoding::new();
396 accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
397 accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
398 accept.push(EncodingProposal::new(Encoding::Identity, None)?);
399
400 assert_eq!(
401 accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?,
402 Encoding::Brotli,
403 );
404 Ok(())
405 }
406
407 #[test]
408 fn negotiate_not_acceptable() -> crate::Result<()> {
409 let mut accept = AcceptEncoding::new();
410 let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
411 assert_eq!(err.status(), 406);
412
413 let mut accept = AcceptEncoding::new();
414 accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
415 let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
416 assert_eq!(err.status(), 406);
417 Ok(())
418 }
419
420 #[test]
421 fn negotiate_wildcard() -> crate::Result<()> {
422 let mut accept = AcceptEncoding::new();
423 accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
424 accept.set_wildcard(true);
425
426 assert_eq!(accept.negotiate(&[Encoding::Gzip])?, Encoding::Gzip);
427 Ok(())
428 }
429}