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, Header)]
42 pub struct TransferEncoding(FlatCsv);
43 
44 impl TransferEncoding {
45     /// Constructor for the most common Transfer-Encoding, `chunked`.
chunked() -> TransferEncoding46     pub fn chunked() -> TransferEncoding {
47         TransferEncoding(HeaderValue::from_static("chunked").into())
48     }
49 
50     /// Returns whether this ends with the `chunked` encoding.
is_chunked(&self) -> bool51     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)]
70 mod tests {
71     use super::TransferEncoding;
72     use super::super::test_decode;
73 
74     #[test]
chunked_is_chunked()75     fn chunked_is_chunked() {
76         assert!(TransferEncoding::chunked().is_chunked());
77     }
78 
79     #[test]
decode_gzip_chunked_is_chunked()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]
decode_chunked_gzip_is_not_chunked()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]
decode_notchunked_is_not_chunked()92     fn decode_notchunked_is_not_chunked() {
93         let te = test_decode::<TransferEncoding>(&["notchunked"]).unwrap();
94         assert!(!te.is_chunked());
95     }
96 
97     #[test]
decode_multiple_is_chunked()98     fn decode_multiple_is_chunked() {
99         let te = test_decode::<TransferEncoding>(&["gzip", "chunked"]).unwrap();
100         assert!(te.is_chunked());
101     }
102 }
103