hyperx/header/
compat.rs

1//! Implementation module for various compatibility features with the _http_
2//! crate.
3
4use std::fmt::Display;
5
6use http::header::{GetAll, HeaderMap, HeaderValue, ValueIter};
7
8use ::Result;
9use super::{Header, RawLike};
10
11#[cfg(feature = "headers")]
12use std::convert::From;
13
14#[cfg(feature = "headers")]
15use http;
16
17#[cfg(feature = "headers")]
18use super::{Headers};
19
20/// A trait for the "standard" headers that have an associated `HeaderName`
21/// constant in the _http_ crate.
22pub trait StandardHeader: Header + Sized {
23    /// The `HeaderName` from the _http_ crate for this header.
24    fn http_header_name() -> ::http::header::HeaderName;
25}
26
27/// Extension trait for `decode` (parsing) and `encode` (serialization) of
28/// typed headers from/to a collection of headers such as `http::HeaderMap`.
29pub trait TypedHeaders {
30    /// Decode and return `Header` type H or `Error::Header`.
31    ///
32    /// `Error::Header` is returned on failed parse, or for a single-valued
33    /// Header type, if no values or multiple values are found in the
34    /// collection.  Multi-valued header types such as `ContentEncoding` will
35    /// instead return an empty list value if no values are found.  To
36    /// distinguish the not found case, use `try_decode` instead.
37    fn decode<H>(&self) -> Result<H>
38        where H: StandardHeader;
39
40    /// Decode and return `Header` type H or `Error::Header` if found, or
41    /// return `None` if not found.
42    ///
43    /// This variant will return `Option::None` if no header with the
44    /// associated key (`HeaderName`) is found in the collection. If the
45    /// collection does contain such a key, it will return the header type H or
46    /// `Error::Header`.
47    fn try_decode<H>(&self) -> Option<Result<H>>
48        where H: StandardHeader;
49
50    /// Encode and write the specified typed header value in the collection.
51    ///
52    /// Uses the `Display` format of the provided header value to write a single
53    /// header. This will overwrite any preexisting values with the same
54    /// key (`HeaderName`). Use `encode_append` instead to avoid this.
55    fn encode<H>(&mut self, value: &H)
56        where H: StandardHeader + Display;
57
58    /// Encode and append the specified typed header value into the collection.
59    ///
60    /// Uses the `Display` format of the provided header value to append a
61    /// single header. If the collection previously had a value for the same
62    /// key, the additional value is appended to the end.
63    fn encode_append<H>(&mut self, value: &H)
64        where H: StandardHeader + Display;
65}
66
67/// Iterator adaptor for HeaderValue
68#[derive(Debug)]
69pub struct ValueMapIter<'a>(ValueIter<'a, HeaderValue>);
70
71impl TypedHeaders for HeaderMap {
72    fn decode<H>(&self) -> Result<H>
73        where H: StandardHeader
74    {
75        let vals = self.get_all(H::http_header_name());
76        H::parse_header(&vals)
77    }
78
79    fn try_decode<H>(&self) -> Option<Result<H>>
80        where H: StandardHeader
81    {
82        let hname = H::http_header_name();
83        if self.contains_key(&hname) {
84            let vals = self.get_all(&hname);
85            Some(H::parse_header(&vals))
86        } else {
87            None
88        }
89    }
90
91    fn encode<H>(&mut self, val: &H)
92        where H: StandardHeader + Display
93    {
94        self.insert(
95            H::http_header_name(),
96            val.to_string().parse().expect("header value"));
97    }
98
99    fn encode_append<H>(&mut self, val: &H)
100        where H: StandardHeader + Display
101    {
102        self.append(
103            H::http_header_name(),
104            val.to_string().parse().expect("header value"));
105    }
106}
107
108#[cfg(feature = "headers")]
109impl From<http::HeaderMap> for Headers {
110    fn from(header_map: http::HeaderMap) -> Headers {
111        Headers::from(&header_map)
112    }
113}
114
115#[cfg(feature = "headers")]
116impl<'a> From<&'a http::HeaderMap> for Headers {
117    fn from(header_map: &'a http::HeaderMap) -> Headers {
118        let mut headers = Headers::new();
119        for (name, value) in header_map.iter() {
120            headers.append_raw_str(name.as_str(), value.as_bytes());
121        }
122        headers
123    }
124}
125
126#[cfg(feature = "headers")]
127impl From<Headers> for http::HeaderMap {
128    #[inline]
129    fn from(headers: Headers) -> http::HeaderMap {
130        http::HeaderMap::from(&headers)
131    }
132}
133
134#[cfg(feature = "headers")]
135impl<'a> From<&'a Headers> for http::HeaderMap {
136    fn from(headers: &'a Headers) -> http::HeaderMap {
137        let mut hmap = http::HeaderMap::new();
138        for header in headers.iter() {
139            let name: http::header::HeaderName = header.name().parse()
140                .expect("convert invalid header name");
141            let entry = hmap.entry(name);
142            let mut value_iter = header.raw().iter().map(|line| {
143                http::header::HeaderValue::from_bytes(line)
144                    .expect("convert invalid header value")
145            });
146            match entry {
147                http::header::Entry::Occupied(mut  occupied) => {
148                    for value in value_iter {
149                        occupied.append(value);
150                    }
151                },
152                http::header::Entry::Vacant(vacant) => {
153                    if let Some(first_value) = value_iter.next() {
154                        let mut occupied = vacant.insert_entry(first_value);
155                        for value in value_iter {
156                            occupied.append(value);
157                        }
158                    }
159                }
160            }
161        }
162        hmap
163    }
164}
165
166impl<'a> Iterator for ValueMapIter<'a> {
167    type Item = &'a [u8];
168
169    fn next(&mut self) -> Option<Self::Item> {
170        self.0.next().map(HeaderValue::as_bytes)
171    }
172}
173
174impl<'a> RawLike<'a> for GetAll<'a, HeaderValue> {
175    type IntoIter = ValueMapIter<'a>;
176
177    fn len(&'a self) -> usize {
178        self.iter().count()
179    }
180
181    fn one(&'a self) -> Option<&'a [u8]> {
182        let mut iter = self.iter();
183        if let Some(v) = iter.next() {
184            if iter.next().is_none() {
185                return Some(v.as_bytes());
186            }
187        }
188        None
189    }
190
191    fn iter(&'a self) -> ValueMapIter<'a> {
192        ValueMapIter(self.iter())
193    }
194}
195
196impl<'a> RawLike<'a> for &'a HeaderValue {
197    type IntoIter = ::std::iter::Once<&'a [u8]>;
198
199    fn len(&'a self) -> usize {
200        1
201    }
202
203    fn one(&'a self) -> Option<&'a [u8]> {
204        Some(self.as_bytes())
205    }
206
207    fn iter(&'a self) -> Self::IntoIter {
208        ::std::iter::once(self.as_bytes())
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use http;
215    use ::header::{
216        ContentEncoding, ContentLength, Encoding, ETag,
217        Header, Te, TypedHeaders};
218
219    #[cfg(feature = "headers")]
220    use ::header::{Headers, Host};
221
222    #[cfg(feature = "nightly")]
223    use test::Bencher;
224
225    #[cfg(feature = "nightly")]
226    use ::header::EntityTag;
227
228    #[test]
229    fn test_empty_decode() {
230        let hmap = http::HeaderMap::new();
231        let len = hmap.decode::<ContentLength>();
232        assert!(len.is_err());
233    }
234
235    #[test]
236    fn test_empty_decode_etag() {
237        let hmap = http::HeaderMap::new();
238        let len = hmap.decode::<ETag>();
239        assert!(len.is_err());
240    }
241
242    #[test]
243    fn test_empty_decode_te() {
244        let hmap = http::HeaderMap::new();
245        let te = hmap.decode::<Te>().unwrap();
246        assert_eq!(te, Te(vec![]));
247    }
248
249    #[test]
250    fn test_empty_decode_content_encoding() {
251        let hmap = http::HeaderMap::new();
252        let ce = hmap.decode::<ContentEncoding>().unwrap();
253        assert_eq!(ce, ContentEncoding(vec![]));
254    }
255
256    #[test]
257    fn test_empty_try_decode() {
258        let hmap = http::HeaderMap::new();
259        let len = hmap.try_decode::<ContentLength>();
260        assert!(len.is_none());
261    }
262
263    #[test]
264    fn test_empty_try_decode_te() {
265        let hmap = http::HeaderMap::new();
266        let te = hmap.try_decode::<Te>();
267        assert!(te.is_none());
268    }
269
270    #[test]
271    fn test_decode() {
272        let mut hmap = http::HeaderMap::new();
273        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
274        let len: ContentLength = hmap.decode().unwrap();
275        assert_eq!(*len, 11);
276    }
277
278    #[test]
279    fn test_encode_decode() {
280        let mut hmap = http::HeaderMap::new();
281        hmap.encode(&ContentLength(11));
282        let len: ContentLength = hmap.decode().unwrap();
283        assert_eq!(*len, 11);
284    }
285
286    #[test]
287    fn test_empty_encode() {
288        let mut hmap = http::HeaderMap::new();
289        hmap.encode(&ContentEncoding(vec![]));
290        assert_eq!(hmap.len(), 1);
291        let ce: ContentEncoding = hmap.decode().unwrap();
292        assert_eq!(*ce, vec![]);
293    }
294
295    #[test]
296    fn test_empty_encode_2() {
297        let mut hmap = http::HeaderMap::new();
298        hmap.encode(&ContentEncoding(vec![]));
299        hmap.encode_append(&ContentEncoding(vec![]));
300        assert_eq!(hmap.len(), 2);
301        let ce: ContentEncoding = hmap.decode().unwrap();
302        assert_eq!(*ce, vec![]);
303    }
304
305    #[test]
306    fn test_encode_append() {
307        let mut hmap = http::HeaderMap::new();
308        hmap.encode_append(
309            &ContentEncoding(vec![Encoding::Identity]));
310        hmap.encode_append(
311            &ContentEncoding(vec![Encoding::Gzip, Encoding::Chunked]));
312        let ce: ContentEncoding = hmap.decode().unwrap();
313        assert_eq!(
314            *ce,
315            vec![Encoding::Identity, Encoding::Gzip, Encoding::Chunked]);
316    }
317
318    #[cfg(feature = "headers")]
319    fn raw_headers_sample() -> Headers {
320        let mut heads = Headers::new();
321
322        heads.set_raw("date", b"Thu, 02 May 2019 21:10:03 GMT".as_ref());
323        heads.set_raw("connection", b"Keep-Alive".as_ref());
324        heads.set_raw("accept-ranges", b"bytes".as_ref());
325        heads.set_raw("etag", b"\"1544639720\"".as_ref());
326        heads.set_raw("transfer-encoding", b"gzip".as_ref());
327        heads.append_raw("transfer-encoding", b"chunked".as_ref());
328        heads.set_raw("content-length", b"7050".as_ref());
329        heads.set_raw("content-type", b"text/css; charset=utf-8".as_ref());
330        heads.set_raw("last-modified", b"Wed, 12 Dec 2018 18:35:20 GMT".as_ref());
331        heads.set_raw("x-hello-human",
332                      b"Say hello back! @getBootstrapCDN on Twitter".as_ref());
333        heads.set_raw("access-control-allow-origin", b"*".as_ref());
334        heads.set_raw("vary", b"Accept-Encoding".as_ref());
335        heads.set_raw("x-cache", b"HIT".as_ref());
336        heads.set_raw("timing-allow-origin", b"*".as_ref());
337        heads.set_raw("cache-control", b"public, max-age=31536000".as_ref());
338
339        heads
340    }
341
342    #[cfg(feature = "headers")]
343    #[test]
344    fn test_convert_mixed() {
345        let mut headers = Headers::new();
346        headers.set(ContentLength(11));
347        headers.set(Host::new("foo.bar", None));
348        headers.append_raw("x-foo", b"bar".to_vec());
349        headers.append_raw("x-foo", b"quux".to_vec());
350
351        let mut hmap = http::HeaderMap::new();
352        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
353        hmap.insert(http::header::HOST, "foo.bar".parse().unwrap());
354        hmap.append("x-foo", "bar".parse().unwrap());
355        hmap.append("x-foo", "quux".parse().unwrap());
356
357        let headers2: Headers = hmap.clone().into();
358        let hmap2: http::HeaderMap = headers.clone().into();
359        assert_eq!(headers, headers2);
360        assert_eq!(headers2.len(), 3);
361        assert_eq!(hmap, hmap2);
362        assert_eq!(hmap2.len(), 4);
363    }
364
365    #[test]
366    #[cfg(feature = "headers")]
367    fn test_convert_sample() {
368        let headers = raw_headers_sample();
369        let hmap = http::HeaderMap::from(headers.clone());
370        let headers2 = Headers::from(hmap.clone());
371        let hmap2 = http::HeaderMap::from(headers2.clone());
372        assert_eq!(headers, headers2);
373        assert_eq!(headers2.len(), 14);
374        assert_eq!(hmap2, hmap);
375        assert_eq!(hmap2.len(), 15);
376    }
377
378    #[test]
379    #[cfg(feature = "headers")]
380    fn test_convert_by_ref() {
381        let headers = raw_headers_sample();
382        let hmap = http::HeaderMap::from(&headers);
383        let headers2 = Headers::from(&hmap);
384        let hmap2 = http::HeaderMap::from(&headers2);
385        assert_eq!(headers, headers2);
386        assert_eq!(headers2.len(), 14);
387        assert_eq!(hmap2, hmap);
388        assert_eq!(hmap2.len(), 15);
389    }
390
391    #[test]
392    fn test_value_parse() {
393        let mut hmap = http::HeaderMap::new();
394        hmap.insert(http::header::CONTENT_ENCODING,
395                    "chunked, gzip".parse().unwrap());
396        let val = hmap.get(http::header::CONTENT_ENCODING).unwrap();
397        let ce = ContentEncoding::parse_header(&val).unwrap();
398        assert_eq!(ce, ContentEncoding(vec![Encoding::Chunked, Encoding::Gzip]))
399    }
400
401    #[test]
402    fn test_multi_value_parse() {
403        let mut hmap = http::HeaderMap::new();
404        hmap.insert(http::header::CONTENT_ENCODING,
405                    "chunked, gzip".parse().unwrap());
406        hmap.append(http::header::CONTENT_ENCODING,
407                    "br".parse().unwrap());
408
409        let vals = hmap.get_all(http::header::CONTENT_ENCODING);
410        let ce = ContentEncoding::parse_header(&vals).unwrap();
411        assert_eq!(
412            ce,
413            ContentEncoding(vec![
414                Encoding::Chunked, Encoding::Gzip, Encoding::Brotli
415            ])
416        )
417    }
418
419    #[cfg(feature = "nightly")]
420    #[bench]
421    fn bench_0_value_parse(b: &mut Bencher) {
422        let mut hmap = http::HeaderMap::new();
423        hmap.insert(http::header::CONTENT_ENCODING,
424                    "chunked, gzip".parse().unwrap());
425        b.iter(|| {
426            let val = hmap.get(http::header::CONTENT_ENCODING).unwrap();
427            ContentEncoding::parse_header(&val).unwrap();
428        })
429    }
430
431    #[cfg(feature = "nightly")]
432    #[bench]
433    fn bench_0_value_parse_extra_str(b: &mut Bencher) {
434        use header::Raw;
435        let mut hmap = http::HeaderMap::new();
436        hmap.insert(http::header::CONTENT_ENCODING,
437                    "chunked, gzip".parse().unwrap());
438        b.iter(|| {
439            let val = hmap.get(http::header::CONTENT_ENCODING).unwrap();
440            let r: Raw = val.to_str().unwrap().into();
441            ContentEncoding::parse_header(&r).unwrap();
442        })
443    }
444
445    #[cfg(feature = "nightly")]
446    #[bench]
447    fn bench_0_value_parse_int(b: &mut Bencher) {
448        let mut hmap = http::HeaderMap::new();
449        hmap.insert(http::header::CONTENT_LENGTH, "1024".parse().unwrap());
450        b.iter(|| {
451            let val = hmap.get(http::header::CONTENT_LENGTH).unwrap();
452            ContentLength::parse_header(&val).unwrap();
453        })
454    }
455
456    #[cfg(feature = "nightly")]
457    #[bench]
458    fn bench_1_get_parse_int(b: &mut Bencher) {
459        let mut hmap = http::HeaderMap::new();
460        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
461        b.iter(|| {
462            let vals = hmap.get_all(http::header::CONTENT_LENGTH);
463            let len = ContentLength::parse_header(&vals).unwrap();
464            assert_eq!(*len, 11);
465        })
466    }
467
468    #[cfg(feature = "nightly")]
469    #[bench]
470    fn bench_1_get_parse_int_one(b: &mut Bencher) {
471        let mut hmap = http::HeaderMap::new();
472        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
473        b.iter(|| {
474            let val = hmap.get(http::header::CONTENT_LENGTH).unwrap();
475            let len = ContentLength::parse_header(&val).unwrap();
476            assert_eq!(*len, 11);
477        })
478    }
479
480    #[cfg(feature = "nightly")]
481    #[bench]
482    fn bench_2_decode_int(b: &mut Bencher) {
483        let mut hmap = http::HeaderMap::new();
484        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
485        b.iter(|| {
486            let len: ContentLength = hmap.decode().unwrap();
487            assert_eq!(*len, 11);
488        })
489    }
490
491    #[cfg(feature = "nightly")]
492    #[bench]
493    fn bench_2_try_decode_int(b: &mut Bencher) {
494        let mut hmap = http::HeaderMap::new();
495        hmap.insert(http::header::CONTENT_LENGTH, "11".parse().unwrap());
496        b.iter(|| {
497            let len: ContentLength = hmap.try_decode().unwrap().unwrap();
498            assert_eq!(*len, 11);
499        })
500    }
501
502    #[cfg(all(feature = "nightly", feature = "headers"))]
503    #[bench]
504    fn bench_3_get_orig_int(b: &mut Bencher) {
505        let mut hdrs = ::header::Headers::new();
506        hdrs.set_raw("content-length", "11");
507        b.iter(|| {
508            let len: &ContentLength = hdrs.get().unwrap();
509            assert_eq!(**len, 11);
510        })
511    }
512
513    #[cfg(feature = "nightly")]
514    #[bench]
515    fn bench_4_encode_int(b: &mut Bencher) {
516        b.iter(|| {
517            let mut hmap = http::HeaderMap::new();
518            hmap.encode(&ContentLength(11));
519            assert_eq!(hmap.len(), 1);
520        })
521    }
522
523    #[cfg(feature = "nightly")]
524    #[bench]
525    fn bench_4_encode_multi(b: &mut Bencher) {
526        b.iter(|| {
527            let mut hmap = http::HeaderMap::new();
528            hmap.encode(
529                &ContentEncoding(vec![Encoding::Identity]));
530            hmap.encode_append(
531                &ContentEncoding(vec![Encoding::Gzip, Encoding::Chunked]));
532            hmap.encode(&ContentLength(11));
533            hmap.encode(
534                &ETag(EntityTag::strong("pMMV3zmCrXr-n4ZZLR9".to_owned())));
535            assert_eq!(hmap.len(), 4);
536        })
537    }
538
539    #[cfg(all(feature = "nightly", feature = "headers"))]
540    #[bench]
541    fn bench_5_map_from_headers(b: &mut Bencher) {
542        let heads = raw_headers_sample();
543        b.iter(|| {
544            let hmap = http::HeaderMap::from(&heads);
545            assert_eq!(hmap.len(), 15);
546        })
547    }
548
549    #[cfg(all(feature = "nightly", feature = "headers"))]
550    #[bench]
551    fn bench_5_headers_from_map(b: &mut Bencher) {
552        let heads = raw_headers_sample();
553        let hmap: http::HeaderMap = heads.into();
554        b.iter(|| {
555            let heads = Headers::from(&hmap);
556            assert_eq!(heads.len(), 14);
557        })
558    }
559
560    #[cfg(all(feature = "nightly", feature = "headers"))]
561    #[bench]
562    fn bench_5_headers_from_map_by_value(b: &mut Bencher) {
563        let heads = raw_headers_sample();
564        let hmap: http::HeaderMap = heads.into();
565        b.iter(|| {
566            let heads = Headers::from(hmap.clone());
567            assert_eq!(heads.len(), 14);
568        })
569    }
570}