http_types_2/transfer/
te.rs

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