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#[derive(Debug, Clone, Eq, Default)]
121pub struct HeaderValue {
122 pub value: String,
124 pub params: HashMap<String, String>,
126 pub quote: bool
128}
129
130impl HeaderValue {
131 pub fn parse_string(s: &str) -> HeaderValue {
133 let values = parse_header(s);
134 let (first, second) = values.split_first().unwrap();
135 if second.is_empty() {
136 HeaderValue::basic(first.as_str())
137 } else {
138 HeaderValue {
139 value: first.clone(),
140 params: batch(second).iter()
141 .fold(HashMap::new(), |mut map, params| {
142 if !params.0.is_empty() {
143 map.insert(params.0.clone(), params.1.clone());
144 }
145 map
146 }),
147 quote: false
148 }
149 }
150 }
151
152 pub fn basic<S: Into<String>>(s: S) -> HeaderValue {
154 HeaderValue {
155 value: s.into(),
156 params: HashMap::new(),
157 quote: false
158 }
159 }
160
161 pub fn to_string(&self) -> String {
163 let sparams = self.params.iter()
164 .map(|(k, v)| format!("{}={}", k, v))
165 .join("; ");
166 if self.quote {
167 if sparams.is_empty() {
168 format!("\"{}\"", self.value)
169 } else {
170 format!("\"{}\"; {}", self.value, sparams)
171 }
172 } else {
173 if self.params.is_empty() {
174 self.value.clone()
175 } else {
176 format!("{}; {}", self.value, sparams)
177 }
178 }
179 }
180
181 pub fn weak_etag(&self) -> Option<String> {
184 if self.value.starts_with("W/") {
185 Some(parse_header(&self.value[2..])[0].clone())
186 } else {
187 None
188 }
189 }
190
191 pub fn quote(mut self) -> HeaderValue {
193 self.quote = true;
194 self
195 }
196
197 pub fn json() -> HeaderValue {
199 HeaderValue {
200 value: "application/json".to_string(),
201 params: Default::default(),
202 quote: false
203 }
204 }
205}
206
207impl PartialEq<HeaderValue> for HeaderValue {
208 fn eq(&self, other: &HeaderValue) -> bool {
209 self.value == other.value && self.params == other.params
210 }
211}
212
213impl PartialEq<&HeaderValue> for HeaderValue {
214 fn eq(&self, other: &&HeaderValue) -> bool {
215 self == *other
216 }
217}
218
219
220impl PartialEq<String> for HeaderValue {
221 fn eq(&self, other: &String) -> bool {
222 self.value == *other
223 }
224}
225
226impl PartialEq<str> for HeaderValue {
227 fn eq(&self, other: &str) -> bool {
228 self.value == *other
229 }
230}
231
232impl Hash for HeaderValue {
233 fn hash<H: Hasher>(&self, state: &mut H) {
234 self.value.hash(state);
235 for (k, v) in self.params.clone() {
236 k.hash(state);
237 v.hash(state);
238 }
239 }
240}
241
242#[macro_export]
244macro_rules! h {
245 ($e:expr) => (HeaderValue::parse_string($e.into()))
246}
247
248#[cfg(test)]
249mod tests {
250 use expectest::prelude::*;
251 use maplit::hashmap;
252
253 use super::*;
254
255 #[test]
256 fn parse_header_value_test() {
257 expect!(HeaderValue::parse_string("")).to(be_equal_to("".to_string()));
258 expect!(HeaderValue::parse_string("A B")).to(be_equal_to("A B".to_string()));
259 expect!(HeaderValue::parse_string("A; B")).to(be_equal_to(HeaderValue {
260 value: "A".to_string(),
261 params: hashmap!{ "B".to_string() => "".to_string() },
262 quote: false
263 }));
264 expect!(HeaderValue::parse_string("text/html;charset=utf-8")).to(be_equal_to(HeaderValue {
265 value: "text/html".to_string(),
266 params: hashmap!{ "charset".to_string() => "utf-8".to_string() },
267 quote: false
268 }));
269 expect!(HeaderValue::parse_string("text/html;charset=UTF-8")).to(be_equal_to(HeaderValue {
270 value: "text/html".to_string(),
271 params: hashmap!{ "charset".to_string() => "UTF-8".to_string() },
272 quote: false
273 }));
274 expect!(HeaderValue::parse_string("Text/HTML;Charset= \"utf-8\"")).to(be_equal_to(HeaderValue {
275 value: "Text/HTML".to_string(),
276 params: hashmap!{ "Charset".to_string() => "utf-8".to_string() },
277 quote: false
278 }));
279 expect!(HeaderValue::parse_string("text/html; charset = \" utf-8 \"")).to(be_equal_to(HeaderValue {
280 value: "text/html".to_string(),
281 params: hashmap!{ "charset".to_string() => " utf-8 ".to_string() },
282 quote: false
283 }));
284 expect!(HeaderValue::parse_string(";")).to(be_equal_to(HeaderValue {
285 value: "".to_string(),
286 params: hashmap!{},
287 quote: false
288 }));
289 expect!(HeaderValue::parse_string("A;b=c=d")).to(be_equal_to(HeaderValue {
290 value: "A".to_string(),
291 params: hashmap!{ "b".to_string() => "c=d".to_string() },
292 quote: false
293 }));
294 expect!(HeaderValue::parse_string("A;b=\"c;d\"")).to(be_equal_to(HeaderValue {
295 value: "A".to_string(),
296 params: hashmap!{ "b".to_string() => "c;d".to_string() },
297 quote: false
298 }));
299 expect!(HeaderValue::parse_string("A;b=\"c\\\"d\"")).to(be_equal_to(HeaderValue {
300 value: "A".to_string(),
301 params: hashmap!{ "b".to_string() => "c\"d".to_string() },
302 quote: false
303 }));
304 expect!(HeaderValue::parse_string("A;b=\"c,d\"")).to(be_equal_to(HeaderValue {
305 value: "A".to_string(),
306 params: hashmap!{ "b".to_string() => "c,d".to_string() },
307 quote: false
308 }));
309 expect!(HeaderValue::parse_string("en;q=0.0")).to(be_equal_to(HeaderValue {
310 value: "en".to_string(),
311 params: hashmap!{ "q".to_string() => "0.0".to_string() },
312 quote: false
313 }));
314 }
315
316 #[test]
317 fn parse_qouted_header_value_test() {
318 expect!(HeaderValue::parse_string("\"*\"")).to(be_equal_to(HeaderValue {
319 value: "*".to_string(),
320 params: hashmap!{},
321 quote: false
322 }));
323 expect!(HeaderValue::parse_string(" \"quoted; value\"")).to(be_equal_to(HeaderValue {
324 value: "quoted; value".to_string(),
325 params: hashmap!{},
326 quote: false
327 }));
328 }
329
330 #[test]
331 fn parse_etag_header_value_test() {
332 let etag = "\"1234567890\"";
333 let weak_etag = "W/\"1234567890\"";
334
335 let header = HeaderValue::parse_string(etag);
336 expect!(header.clone()).to(be_equal_to(HeaderValue {
337 value: "1234567890".to_string(),
338 params: hashmap!{},
339 quote: false
340 }));
341 expect!(header.weak_etag()).to(be_none());
342
343 let weak_etag_value = HeaderValue::parse_string(weak_etag);
344 expect!(weak_etag_value.clone()).to(be_equal_to(HeaderValue {
345 value: weak_etag.to_string(),
346 params: hashmap!{},
347 quote: false
348 }));
349 expect!(weak_etag_value.weak_etag()).to(be_some().value("1234567890"));
350 }
351}