1use std::collections::HashMap;
4use std::hash::{Hash, Hasher};
5use std::iter::Peekable;
6use std::str::Chars;
7
8use itertools::Itertools;
9
10const SEPARATORS: [char; 10] = ['(', ')', '<', '>', '@', ',', ';', '=', '{', '}'];
11const VALUE_SEPARATORS: [char; 9] = ['(', ')', '<', '>', '@', ',', ';', '{', '}'];
12
13fn batch(values: &[String]) -> Vec<(String, String)> {
14 values.into_iter().batching(|it| {
15 match it.next() {
16 None => None,
17 Some(x) => match it.next() {
18 None => Some((x.to_string(), "".to_string())),
19 Some(y) => Some((x.to_string(), y.to_string())),
20 }
21 }
22 }).collect()
23}
24
25fn header_value(chars: &mut Peekable<Chars>, seperators: &[char]) -> String {
27 let mut value = String::new();
28 skip_whitespace(chars);
29 if chars.peek().is_some() && chars.peek().unwrap() == &'"' {
30 chars.next();
31 while chars.peek().is_some() && chars.peek().unwrap() != &'"' {
32 let ch = chars.next().unwrap();
33 match ch {
34 '\\' => {
35 if chars.peek().is_some() {
36 value.push(chars.next().unwrap());
37 } else {
38 value.push(ch);
39 }
40 },
41 _ => value.push(ch)
42 }
43 }
44 if chars.peek().is_some() {
45 chars.next();
46 }
47 } else {
48 while chars.peek().is_some() && !seperators.contains(chars.peek().unwrap()) {
49 value.push(chars.next().unwrap())
50 }
51 }
52 value.trim().to_string()
53}
54
55fn parse_header(s: &str) -> Vec<String> {
57 let mut chars = s.chars().peekable();
58 let header_value = header_value(&mut chars, &VALUE_SEPARATORS);
59 let mut values = vec![header_value];
60 if chars.peek().is_some() && chars.peek().unwrap() == &';' {
61 chars.next();
62 parse_header_parameters(&mut chars, &mut values);
63 }
64 values
65}
66
67fn parse_header_parameters(chars: &mut Peekable<Chars>, values: &mut Vec<String>) {
69 parse_header_parameter(chars, values);
70 if chars.peek().is_some() && chars.peek().unwrap() == &';' {
71 chars.next();
72 parse_header_parameters(chars, values);
73 }
74}
75
76fn parse_header_parameter(chars: &mut Peekable<Chars>, values: &mut Vec<String>) {
78 values.push(header_value(chars, &SEPARATORS));
79 if chars.peek().is_some() && chars.peek().unwrap() == &'=' {
80 chars.next();
81 parse_header_parameter_value(chars, values);
82 }
83}
84
85fn parse_header_parameter_value(chars: &mut Peekable<Chars>, values: &mut Vec<String>) {
87 skip_whitespace(chars);
88 if chars.peek().is_some() && chars.peek().unwrap() == &'"' {
89 chars.next();
90 let mut value = String::new();
91 while chars.peek().is_some() && chars.peek().unwrap() != &'"' {
92 let ch = chars.next().unwrap();
93 match ch {
94 '\\' => {
95 if chars.peek().is_some() {
96 value.push(chars.next().unwrap());
97 } else {
98 value.push(ch);
99 }
100 },
101 _ => value.push(ch)
102 }
103 }
104 if chars.peek().is_some() {
105 chars.next();
106 }
107 values.push(value.to_string());
108 } else {
109 values.push(header_value(chars, &[';']));
110 }
111}
112
113fn skip_whitespace(chars: &mut Peekable<Chars>) {
114 while chars.peek().is_some() && chars.peek().unwrap().is_whitespace() {
115 chars.next();
116 }
117}
118
119
120#[derive(Debug, Clone, Eq, Default)]
122pub struct HeaderValue {
123 pub value: String,
125 pub params: HashMap<String, String>,
127 pub quote: bool
129}
130
131impl HeaderValue {
132 pub fn parse_string(s: &str) -> HeaderValue {
134 let values = parse_header(s);
135 let (first, second) = values.split_first().unwrap();
136 if second.is_empty() {
137 HeaderValue::basic(first.as_str())
138 } else {
139 HeaderValue {
140 value: first.clone(),
141 params: batch(second).iter()
142 .fold(HashMap::new(), |mut map, params| {
143 if !params.0.is_empty() {
144 map.insert(params.0.clone(), params.1.clone());
145 }
146 map
147 }),
148 quote: false
149 }
150 }
151 }
152
153 pub fn basic<S: Into<String>>(s: S) -> HeaderValue {
155 HeaderValue {
156 value: s.into(),
157 params: HashMap::new(),
158 quote: false
159 }
160 }
161
162 pub fn to_string(&self) -> String {
164 let sparams = self.params.iter()
165 .map(|(k, v)| format!("{}={}", k, v))
166 .join("; ");
167 if self.quote {
168 if sparams.is_empty() {
169 format!("\"{}\"", self.value)
170 } else {
171 format!("\"{}\"; {}", self.value, sparams)
172 }
173 } else {
174 if self.params.is_empty() {
175 self.value.clone()
176 } else {
177 format!("{}; {}", self.value, sparams)
178 }
179 }
180 }
181
182 pub fn weak_etag(&self) -> Option<String> {
185 if self.value.starts_with("W/") {
186 Some(parse_header(&self.value[2..])[0].clone())
187 } else {
188 None
189 }
190 }
191
192 pub fn quote(mut self) -> HeaderValue {
194 self.quote = true;
195 self
196 }
197
198 pub fn json() -> HeaderValue {
200 HeaderValue {
201 value: "application/json".to_string(),
202 params: Default::default(),
203 quote: false
204 }
205 }
206}
207
208impl PartialEq<HeaderValue> for HeaderValue {
209 fn eq(&self, other: &HeaderValue) -> bool {
210 self.value == other.value && self.params == other.params
211 }
212}
213
214impl PartialEq<&HeaderValue> for HeaderValue {
215 fn eq(&self, other: &&HeaderValue) -> bool {
216 self == *other
217 }
218}
219
220
221impl PartialEq<String> for HeaderValue {
222 fn eq(&self, other: &String) -> bool {
223 self.value == *other
224 }
225}
226
227impl PartialEq<str> for HeaderValue {
228 fn eq(&self, other: &str) -> bool {
229 self.value == *other
230 }
231}
232
233impl Hash for HeaderValue {
234 fn hash<H: Hasher>(&self, state: &mut H) {
235 self.value.hash(state);
236 for (k, v) in self.params.clone() {
237 k.hash(state);
238 v.hash(state);
239 }
240 }
241}
242
243#[macro_export]
245macro_rules! h {
246 ($e:expr) => (HeaderValue::parse_string($e.into()))
247}
248
249#[cfg(test)]
250mod tests {
251 use expectest::prelude::*;
252 use maplit::hashmap;
253
254 use super::*;
255
256 #[test]
257 fn parse_header_value_test() {
258 expect!(HeaderValue::parse_string("")).to(be_equal_to("".to_string()));
259 expect!(HeaderValue::parse_string("A B")).to(be_equal_to("A B".to_string()));
260 expect!(HeaderValue::parse_string("A; B")).to(be_equal_to(HeaderValue {
261 value: "A".to_string(),
262 params: hashmap!{ "B".to_string() => "".to_string() },
263 quote: false
264 }));
265 expect!(HeaderValue::parse_string("text/html;charset=utf-8")).to(be_equal_to(HeaderValue {
266 value: "text/html".to_string(),
267 params: hashmap!{ "charset".to_string() => "utf-8".to_string() },
268 quote: false
269 }));
270 expect!(HeaderValue::parse_string("text/html;charset=UTF-8")).to(be_equal_to(HeaderValue {
271 value: "text/html".to_string(),
272 params: hashmap!{ "charset".to_string() => "UTF-8".to_string() },
273 quote: false
274 }));
275 expect!(HeaderValue::parse_string("Text/HTML;Charset= \"utf-8\"")).to(be_equal_to(HeaderValue {
276 value: "Text/HTML".to_string(),
277 params: hashmap!{ "Charset".to_string() => "utf-8".to_string() },
278 quote: false
279 }));
280 expect!(HeaderValue::parse_string("text/html; charset = \" utf-8 \"")).to(be_equal_to(HeaderValue {
281 value: "text/html".to_string(),
282 params: hashmap!{ "charset".to_string() => " utf-8 ".to_string() },
283 quote: false
284 }));
285 expect!(HeaderValue::parse_string(";")).to(be_equal_to(HeaderValue {
286 value: "".to_string(),
287 params: hashmap!{},
288 quote: false
289 }));
290 expect!(HeaderValue::parse_string("A;b=c=d")).to(be_equal_to(HeaderValue {
291 value: "A".to_string(),
292 params: hashmap!{ "b".to_string() => "c=d".to_string() },
293 quote: false
294 }));
295 expect!(HeaderValue::parse_string("A;b=\"c;d\"")).to(be_equal_to(HeaderValue {
296 value: "A".to_string(),
297 params: hashmap!{ "b".to_string() => "c;d".to_string() },
298 quote: false
299 }));
300 expect!(HeaderValue::parse_string("A;b=\"c\\\"d\"")).to(be_equal_to(HeaderValue {
301 value: "A".to_string(),
302 params: hashmap!{ "b".to_string() => "c\"d".to_string() },
303 quote: false
304 }));
305 expect!(HeaderValue::parse_string("A;b=\"c,d\"")).to(be_equal_to(HeaderValue {
306 value: "A".to_string(),
307 params: hashmap!{ "b".to_string() => "c,d".to_string() },
308 quote: false
309 }));
310 expect!(HeaderValue::parse_string("en;q=0.0")).to(be_equal_to(HeaderValue {
311 value: "en".to_string(),
312 params: hashmap!{ "q".to_string() => "0.0".to_string() },
313 quote: false
314 }));
315 }
316
317 #[test]
318 fn parse_qouted_header_value_test() {
319 expect!(HeaderValue::parse_string("\"*\"")).to(be_equal_to(HeaderValue {
320 value: "*".to_string(),
321 params: hashmap!{},
322 quote: false
323 }));
324 expect!(HeaderValue::parse_string(" \"quoted; value\"")).to(be_equal_to(HeaderValue {
325 value: "quoted; value".to_string(),
326 params: hashmap!{},
327 quote: false
328 }));
329 }
330
331 #[test]
332 fn parse_etag_header_value_test() {
333 let etag = "\"1234567890\"";
334 let weak_etag = "W/\"1234567890\"";
335
336 let header = HeaderValue::parse_string(etag);
337 expect!(header.clone()).to(be_equal_to(HeaderValue {
338 value: "1234567890".to_string(),
339 params: hashmap!{},
340 quote: false
341 }));
342 expect!(header.weak_etag()).to(be_none());
343
344 let weak_etag_value = HeaderValue::parse_string(weak_etag);
345 expect!(weak_etag_value.clone()).to(be_equal_to(HeaderValue {
346 value: weak_etag.to_string(),
347 params: hashmap!{},
348 quote: false
349 }));
350 expect!(weak_etag_value.weak_etag()).to(be_some().value("1234567890"));
351 }
352}