headers_ext/common/
transfer_encoding.rs

1use util::FlatCsv;
2use ::HeaderValue;
3
4/// `Transfer-Encoding` header, defined in
5/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.1)
6///
7/// The `Transfer-Encoding` header field lists the transfer coding names
8/// corresponding to the sequence of transfer codings that have been (or
9/// will be) applied to the payload body in order to form the message
10/// body.
11///
12/// Note that setting this header will *remove* any previously set
13/// `Content-Length` header, in accordance with
14/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2):
15///
16/// > A sender MUST NOT send a Content-Length header field in any message
17/// > that contains a Transfer-Encoding header field.
18///
19/// # ABNF
20///
21/// ```text
22/// Transfer-Encoding = 1#transfer-coding
23/// ```
24///
25/// # Example values
26///
27/// * `chunked`
28/// * `gzip, chunked`
29///
30/// # Example
31///
32/// ```
33/// # extern crate headers_ext as headers;
34/// use headers::TransferEncoding;
35///
36/// let transfer = TransferEncoding::chunked();
37/// ```
38// This currently is just a `HeaderValue`, instead of a `Vec<Encoding>`, since
39// the most common by far instance is simply the string `chunked`. It'd be a
40// waste to need to allocate just for that.
41#[derive(Clone, Debug, Header)]
42pub struct TransferEncoding(FlatCsv);
43
44impl TransferEncoding {
45    /// Constructor for the most common Transfer-Encoding, `chunked`.
46    pub fn chunked() -> TransferEncoding {
47        TransferEncoding(HeaderValue::from_static("chunked").into())
48    }
49
50    /// Returns whether this ends with the `chunked` encoding.
51    pub fn is_chunked(&self) -> bool {
52        self
53            .0
54            .value
55            //TODO(perf): use split and trim (not an actual method) on &[u8]
56            .to_str()
57            .map(|s| s
58                .split(',')
59                .next_back()
60                .map(|encoding| {
61                    encoding.trim() == "chunked"
62                })
63                .expect("split always has at least 1 item")
64            )
65            .unwrap_or(false)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::TransferEncoding;
72    use super::super::test_decode;
73
74    #[test]
75    fn chunked_is_chunked() {
76        assert!(TransferEncoding::chunked().is_chunked());
77    }
78
79    #[test]
80    fn decode_gzip_chunked_is_chunked() {
81        let te = test_decode::<TransferEncoding>(&["gzip, chunked"]).unwrap();
82        assert!(te.is_chunked());
83    }
84
85    #[test]
86    fn decode_chunked_gzip_is_not_chunked() {
87        let te = test_decode::<TransferEncoding>(&["chunked, gzip"]).unwrap();
88        assert!(!te.is_chunked());
89    }
90
91    #[test]
92    fn decode_notchunked_is_not_chunked() {
93        let te = test_decode::<TransferEncoding>(&["notchunked"]).unwrap();
94        assert!(!te.is_chunked());
95    }
96
97    #[test]
98    fn decode_multiple_is_chunked() {
99        let te = test_decode::<TransferEncoding>(&["gzip", "chunked"]).unwrap();
100        assert!(te.is_chunked());
101    }
102}