1 use bytes::BytesMut;
2 use http::HeaderMap;
3 use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
4 use http::header::{HeaderValue, OccupiedEntry, ValueIter};
5 
connection_keep_alive(value: &HeaderValue) -> bool6 pub fn connection_keep_alive(value: &HeaderValue) -> bool {
7     connection_has(value, "keep-alive")
8 }
9 
connection_close(value: &HeaderValue) -> bool10 pub fn connection_close(value: &HeaderValue) -> bool {
11     connection_has(value, "close")
12 }
13 
connection_has(value: &HeaderValue, needle: &str) -> bool14 fn connection_has(value: &HeaderValue, needle: &str) -> bool {
15     if let Ok(s) = value.to_str() {
16         for val in s.split(',') {
17             if val.trim().eq_ignore_ascii_case(needle) {
18                 return true;
19             }
20         }
21     }
22     false
23 }
24 
content_length_parse(value: &HeaderValue) -> Option<u64>25 pub fn content_length_parse(value: &HeaderValue) -> Option<u64> {
26     value
27         .to_str()
28         .ok()
29         .and_then(|s| s.parse().ok())
30 }
31 
content_length_parse_all(headers: &HeaderMap) -> Option<u64>32 pub fn content_length_parse_all(headers: &HeaderMap) -> Option<u64> {
33     content_length_parse_all_values(headers.get_all(CONTENT_LENGTH).into_iter())
34 }
35 
content_length_parse_all_values(values: ValueIter<HeaderValue>) -> Option<u64>36 pub fn content_length_parse_all_values(values: ValueIter<HeaderValue>) -> Option<u64> {
37     // If multiple Content-Length headers were sent, everything can still
38     // be alright if they all contain the same value, and all parse
39     // correctly. If not, then it's an error.
40 
41     let folded = values
42         .fold(None, |prev, line| match prev {
43             Some(Ok(prev)) => {
44                 Some(line
45                     .to_str()
46                     .map_err(|_| ())
47                     .and_then(|s| s.parse().map_err(|_| ()))
48                     .and_then(|n| if prev == n { Ok(n) } else { Err(()) }))
49             },
50             None => {
51                 Some(line
52                     .to_str()
53                     .map_err(|_| ())
54                     .and_then(|s| s.parse().map_err(|_| ())))
55             },
56             Some(Err(())) => Some(Err(())),
57         });
58 
59     if let Some(Ok(n)) = folded {
60         Some(n)
61     } else {
62         None
63     }
64 }
65 
set_content_length_if_missing(headers: &mut HeaderMap, len: u64)66 pub fn set_content_length_if_missing(headers: &mut HeaderMap, len: u64) {
67     headers
68         .entry(CONTENT_LENGTH)
69         .unwrap()
70         .or_insert_with(|| HeaderValue::from(len));
71 }
72 
transfer_encoding_is_chunked(headers: &HeaderMap) -> bool73 pub fn transfer_encoding_is_chunked(headers: &HeaderMap) -> bool {
74     is_chunked(headers.get_all(TRANSFER_ENCODING).into_iter())
75 }
76 
is_chunked(mut encodings: ValueIter<HeaderValue>) -> bool77 pub fn is_chunked(mut encodings: ValueIter<HeaderValue>) -> bool {
78     // chunked must always be the last encoding, according to spec
79     if let Some(line) = encodings.next_back() {
80         return is_chunked_(line);
81     }
82 
83     false
84 }
85 
is_chunked_(value: &HeaderValue) -> bool86 pub fn is_chunked_(value: &HeaderValue) -> bool {
87     // chunked must always be the last encoding, according to spec
88     if let Ok(s) = value.to_str() {
89         if let Some(encoding) = s.rsplit(',').next() {
90             return encoding.trim().eq_ignore_ascii_case("chunked");
91         }
92     }
93 
94     false
95 }
96 
add_chunked(mut entry: OccupiedEntry<HeaderValue>)97 pub fn add_chunked(mut entry: OccupiedEntry<HeaderValue>) {
98     const CHUNKED: &'static str = "chunked";
99 
100     if let Some(line) = entry.iter_mut().next_back() {
101         // + 2 for ", "
102         let new_cap = line.as_bytes().len() + CHUNKED.len() + 2;
103         let mut buf = BytesMut::with_capacity(new_cap);
104         buf.copy_from_slice(line.as_bytes());
105         buf.copy_from_slice(b", ");
106         buf.copy_from_slice(CHUNKED.as_bytes());
107 
108         *line = HeaderValue::from_shared(buf.freeze())
109             .expect("original header value plus ascii is valid");
110         return;
111     }
112 
113     entry.insert(HeaderValue::from_static(CHUNKED));
114 }
115