http_types/transfer/
te.rs

1use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING};
2use crate::transfer::{Encoding, EncodingProposal, TransferEncoding};
3use crate::utils::sort_by_weight;
4use crate::{Error, StatusCode};
5
6use std::fmt::{self, Debug, Write};
7use std::option;
8use std::slice;
9
10/// Client header advertising the transfer encodings the user agent is willing to
11/// accept.
12///
13/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE)
14///
15/// # Specifications
16///
17/// - [RFC 7230, section 4.3: TE](https://tools.ietf.org/html/rfc7230#section-4.3)
18///
19/// # Examples
20///
21/// ```
22/// # fn main() -> http_types::Result<()> {
23/// #
24/// use http_types::transfer::{TE, TransferEncoding, Encoding, EncodingProposal};
25/// use http_types::Response;
26///
27/// let mut te = TE::new();
28/// te.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
29/// te.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
30/// te.push(EncodingProposal::new(Encoding::Identity, None)?);
31///
32/// let mut res = Response::new(200);
33/// let encoding = te.negotiate(&[Encoding::Brotli, Encoding::Gzip])?;
34/// encoding.apply(&mut res);
35///
36/// assert_eq!(res["Content-Encoding"], "br");
37/// #
38/// # Ok(()) }
39/// ```
40#[allow(clippy::upper_case_acronyms)]
41pub struct TE {
42    wildcard: bool,
43    entries: Vec<EncodingProposal>,
44}
45
46impl TE {
47    /// Create a new instance of `TE`.
48    pub fn new() -> Self {
49        Self {
50            entries: vec![],
51            wildcard: false,
52        }
53    }
54
55    /// Create an instance of `TE` from a `Headers` instance.
56    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
57        let mut entries = vec![];
58        let headers = match headers.as_ref().get(ACCEPT_ENCODING) {
59            Some(headers) => headers,
60            None => return Ok(None),
61        };
62
63        let mut wildcard = false;
64
65        for value in headers {
66            for part in value.as_str().trim().split(',') {
67                let part = part.trim();
68
69                // Handle empty strings, and wildcard directives.
70                if part.is_empty() {
71                    continue;
72                } else if part == "*" {
73                    wildcard = true;
74                    continue;
75                }
76
77                // Try and parse a directive from a str. If the directive is
78                // unkown we skip it.
79                if let Some(entry) = EncodingProposal::from_str(part)? {
80                    entries.push(entry);
81                }
82            }
83        }
84
85        Ok(Some(Self { entries, wildcard }))
86    }
87
88    /// Push a directive into the list of entries.
89    pub fn push(&mut self, prop: impl Into<EncodingProposal>) {
90        self.entries.push(prop.into());
91    }
92
93    /// Returns `true` if a wildcard directive was passed.
94    pub fn wildcard(&self) -> bool {
95        self.wildcard
96    }
97
98    /// Set the wildcard directive.
99    pub fn set_wildcard(&mut self, wildcard: bool) {
100        self.wildcard = wildcard
101    }
102
103    /// Sort the header directives by weight.
104    ///
105    /// Headers with a higher `q=` value will be returned first. If two
106    /// directives have the same weight, the directive that was declared later
107    /// will be returned first.
108    pub fn sort(&mut self) {
109        sort_by_weight(&mut self.entries);
110    }
111
112    /// Determine the most suitable `Transfer-Encoding` encoding.
113    ///
114    /// # Errors
115    ///
116    /// If no suitable encoding is found, an error with the status of `406` will be returned.
117    pub fn negotiate(&mut self, available: &[Encoding]) -> crate::Result<TransferEncoding> {
118        // Start by ordering the encodings.
119        self.sort();
120
121        // Try and find the first encoding that matches.
122        for encoding in &self.entries {
123            if available.contains(encoding) {
124                return Ok(encoding.into());
125            }
126        }
127
128        // If no encoding matches and wildcard is set, send whichever encoding we got.
129        if self.wildcard {
130            if let Some(encoding) = available.iter().next() {
131                return Ok(encoding.into());
132            }
133        }
134
135        let mut err = Error::new_adhoc("No suitable Transfer-Encoding found");
136        err.set_status(StatusCode::NotAcceptable);
137        Err(err)
138    }
139
140    /// Sets the `Accept-Encoding` header.
141    pub fn apply(&self, mut headers: impl AsMut<Headers>) {
142        headers.as_mut().insert(ACCEPT_ENCODING, self.value());
143    }
144
145    /// Get the `HeaderName`.
146    pub fn name(&self) -> HeaderName {
147        ACCEPT_ENCODING
148    }
149
150    /// Get the `HeaderValue`.
151    pub fn value(&self) -> HeaderValue {
152        let mut output = String::new();
153        for (n, directive) in self.entries.iter().enumerate() {
154            let directive: HeaderValue = (*directive).into();
155            match n {
156                0 => write!(output, "{}", directive).unwrap(),
157                _ => write!(output, ", {}", directive).unwrap(),
158            };
159        }
160
161        if self.wildcard {
162            match output.len() {
163                0 => write!(output, "*").unwrap(),
164                _ => write!(output, ", *").unwrap(),
165            }
166        }
167
168        // SAFETY: the internal string is validated to be ASCII.
169        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
170    }
171
172    /// An iterator visiting all entries.
173    pub fn iter(&self) -> Iter<'_> {
174        Iter {
175            inner: self.entries.iter(),
176        }
177    }
178
179    /// An iterator visiting all entries.
180    pub fn iter_mut(&mut self) -> IterMut<'_> {
181        IterMut {
182            inner: self.entries.iter_mut(),
183        }
184    }
185}
186
187impl IntoIterator for TE {
188    type Item = EncodingProposal;
189    type IntoIter = IntoIter;
190
191    #[inline]
192    fn into_iter(self) -> Self::IntoIter {
193        IntoIter {
194            inner: self.entries.into_iter(),
195        }
196    }
197}
198
199impl<'a> IntoIterator for &'a TE {
200    type Item = &'a EncodingProposal;
201    type IntoIter = Iter<'a>;
202
203    #[inline]
204    fn into_iter(self) -> Self::IntoIter {
205        self.iter()
206    }
207}
208
209impl<'a> IntoIterator for &'a mut TE {
210    type Item = &'a mut EncodingProposal;
211    type IntoIter = IterMut<'a>;
212
213    #[inline]
214    fn into_iter(self) -> Self::IntoIter {
215        self.iter_mut()
216    }
217}
218
219/// A borrowing iterator over entries in `TE`.
220#[derive(Debug)]
221pub struct IntoIter {
222    inner: std::vec::IntoIter<EncodingProposal>,
223}
224
225impl Iterator for IntoIter {
226    type Item = EncodingProposal;
227
228    fn next(&mut self) -> Option<Self::Item> {
229        self.inner.next()
230    }
231
232    #[inline]
233    fn size_hint(&self) -> (usize, Option<usize>) {
234        self.inner.size_hint()
235    }
236}
237
238/// A lending iterator over entries in `TE`.
239#[derive(Debug)]
240pub struct Iter<'a> {
241    inner: slice::Iter<'a, EncodingProposal>,
242}
243
244impl<'a> Iterator for Iter<'a> {
245    type Item = &'a EncodingProposal;
246
247    fn next(&mut self) -> Option<Self::Item> {
248        self.inner.next()
249    }
250
251    #[inline]
252    fn size_hint(&self) -> (usize, Option<usize>) {
253        self.inner.size_hint()
254    }
255}
256
257/// A mutable iterator over entries in `TE`.
258#[derive(Debug)]
259pub struct IterMut<'a> {
260    inner: slice::IterMut<'a, EncodingProposal>,
261}
262
263impl<'a> Iterator for IterMut<'a> {
264    type Item = &'a mut EncodingProposal;
265
266    fn next(&mut self) -> Option<Self::Item> {
267        self.inner.next()
268    }
269
270    #[inline]
271    fn size_hint(&self) -> (usize, Option<usize>) {
272        self.inner.size_hint()
273    }
274}
275
276impl ToHeaderValues for TE {
277    type Iter = option::IntoIter<HeaderValue>;
278    fn to_header_values(&self) -> crate::Result<Self::Iter> {
279        // A HeaderValue will always convert into itself.
280        Ok(self.value().to_header_values().unwrap())
281    }
282}
283
284impl Debug for TE {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        let mut list = f.debug_list();
287        for directive in &self.entries {
288            list.entry(directive);
289        }
290        list.finish()
291    }
292}
293
294#[cfg(test)]
295mod test {
296    use super::*;
297    use crate::transfer::Encoding;
298    use crate::Response;
299
300    #[test]
301    fn smoke() -> crate::Result<()> {
302        let mut accept = TE::new();
303        accept.push(Encoding::Gzip);
304
305        let mut headers = Response::new(200);
306        accept.apply(&mut headers);
307
308        let accept = TE::from_headers(headers)?.unwrap();
309        assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
310        Ok(())
311    }
312
313    #[test]
314    fn wildcard() -> crate::Result<()> {
315        let mut accept = TE::new();
316        accept.set_wildcard(true);
317
318        let mut headers = Response::new(200);
319        accept.apply(&mut headers);
320
321        let accept = TE::from_headers(headers)?.unwrap();
322        assert!(accept.wildcard());
323        Ok(())
324    }
325
326    #[test]
327    fn wildcard_and_header() -> crate::Result<()> {
328        let mut accept = TE::new();
329        accept.push(Encoding::Gzip);
330        accept.set_wildcard(true);
331
332        let mut headers = Response::new(200);
333        accept.apply(&mut headers);
334
335        let accept = TE::from_headers(headers)?.unwrap();
336        assert!(accept.wildcard());
337        assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
338        Ok(())
339    }
340
341    #[test]
342    fn iter() -> crate::Result<()> {
343        let mut accept = TE::new();
344        accept.push(Encoding::Gzip);
345        accept.push(Encoding::Brotli);
346
347        let mut headers = Response::new(200);
348        accept.apply(&mut headers);
349
350        let accept = TE::from_headers(headers)?.unwrap();
351        let mut accept = accept.iter();
352        assert_eq!(accept.next().unwrap(), Encoding::Gzip);
353        assert_eq!(accept.next().unwrap(), Encoding::Brotli);
354        Ok(())
355    }
356
357    #[test]
358    fn reorder_based_on_weight() -> crate::Result<()> {
359        let mut accept = TE::new();
360        accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
361        accept.push(EncodingProposal::new(Encoding::Identity, None)?);
362        accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
363
364        let mut headers = Response::new(200);
365        accept.apply(&mut headers);
366
367        let mut accept = TE::from_headers(headers)?.unwrap();
368        accept.sort();
369        let mut accept = accept.iter();
370        assert_eq!(accept.next().unwrap(), Encoding::Brotli);
371        assert_eq!(accept.next().unwrap(), Encoding::Gzip);
372        assert_eq!(accept.next().unwrap(), Encoding::Identity);
373        Ok(())
374    }
375
376    #[test]
377    fn reorder_based_on_weight_and_location() -> crate::Result<()> {
378        let mut accept = TE::new();
379        accept.push(EncodingProposal::new(Encoding::Identity, None)?);
380        accept.push(EncodingProposal::new(Encoding::Gzip, None)?);
381        accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
382
383        let mut res = Response::new(200);
384        accept.apply(&mut res);
385
386        let mut accept = TE::from_headers(res)?.unwrap();
387        accept.sort();
388        let mut accept = accept.iter();
389        assert_eq!(accept.next().unwrap(), Encoding::Brotli);
390        assert_eq!(accept.next().unwrap(), Encoding::Gzip);
391        assert_eq!(accept.next().unwrap(), Encoding::Identity);
392        Ok(())
393    }
394
395    #[test]
396    fn negotiate() -> crate::Result<()> {
397        let mut accept = TE::new();
398        accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
399        accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
400        accept.push(EncodingProposal::new(Encoding::Identity, None)?);
401
402        assert_eq!(
403            accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?,
404            Encoding::Brotli,
405        );
406        Ok(())
407    }
408
409    #[test]
410    fn negotiate_not_acceptable() -> crate::Result<()> {
411        let mut accept = TE::new();
412        let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
413        assert_eq!(err.status(), 406);
414
415        let mut accept = TE::new();
416        accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
417        let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
418        assert_eq!(err.status(), 406);
419        Ok(())
420    }
421
422    #[test]
423    fn negotiate_wildcard() -> crate::Result<()> {
424        let mut accept = TE::new();
425        accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
426        accept.set_wildcard(true);
427
428        assert_eq!(accept.negotiate(&[Encoding::Gzip])?, Encoding::Gzip);
429        Ok(())
430    }
431}