1 use util::FlatCsv;
2 use 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;
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)]
42 pub struct TransferEncoding(FlatCsv);
43 
44 derive_header! {
45     TransferEncoding(_),
46     name: TRANSFER_ENCODING
47 }
48 
49 impl TransferEncoding {
50     /// Constructor for the most common Transfer-Encoding, `chunked`.
chunked() -> TransferEncoding51     pub fn chunked() -> TransferEncoding {
52         TransferEncoding(HeaderValue::from_static("chunked").into())
53     }
54 
55     /// Returns whether this ends with the `chunked` encoding.
is_chunked(&self) -> bool56     pub fn is_chunked(&self) -> bool {
57         self.0
58             .value
59             //TODO(perf): use split and trim (not an actual method) on &[u8]
60             .to_str()
61             .map(|s| {
62                 s.split(',')
63                     .next_back()
64                     .map(|encoding| encoding.trim() == "chunked")
65                     .expect("split always has at least 1 item")
66             })
67             .unwrap_or(false)
68     }
69 }
70 
71 #[cfg(test)]
72 mod tests {
73     use super::super::test_decode;
74     use super::TransferEncoding;
75 
76     #[test]
chunked_is_chunked()77     fn chunked_is_chunked() {
78         assert!(TransferEncoding::chunked().is_chunked());
79     }
80 
81     #[test]
decode_gzip_chunked_is_chunked()82     fn decode_gzip_chunked_is_chunked() {
83         let te = test_decode::<TransferEncoding>(&["gzip, chunked"]).unwrap();
84         assert!(te.is_chunked());
85     }
86 
87     #[test]
decode_chunked_gzip_is_not_chunked()88     fn decode_chunked_gzip_is_not_chunked() {
89         let te = test_decode::<TransferEncoding>(&["chunked, gzip"]).unwrap();
90         assert!(!te.is_chunked());
91     }
92 
93     #[test]
decode_notchunked_is_not_chunked()94     fn decode_notchunked_is_not_chunked() {
95         let te = test_decode::<TransferEncoding>(&["notchunked"]).unwrap();
96         assert!(!te.is_chunked());
97     }
98 
99     #[test]
decode_multiple_is_chunked()100     fn decode_multiple_is_chunked() {
101         let te = test_decode::<TransferEncoding>(&["gzip", "chunked"]).unwrap();
102         assert!(te.is_chunked());
103     }
104 }
105