rocket_http/header/
header.rs

1use std::borrow::{Borrow, Cow};
2use std::fmt;
3
4use indexmap::IndexMap;
5
6use crate::uncased::{Uncased, UncasedStr};
7
8/// Simple representation of an HTTP header.
9#[derive(Debug, Clone, Hash, PartialEq, Eq)]
10pub struct Header<'h> {
11    /// The name of the header.
12    pub name: Uncased<'h>,
13    /// The value of the header.
14    pub value: Cow<'h, str>,
15}
16
17impl<'h> Header<'h> {
18    /// Constructs a new header. This method should be used rarely and only for
19    /// non-standard headers. Instead, prefer to use the `Into<Header>`
20    /// implementations of many types, including
21    /// [`ContentType`](crate::ContentType) and all of the headers in
22    /// [`http::hyper::header`](hyper::header).
23    ///
24    /// # Examples
25    ///
26    /// Create a custom header with name `X-Custom-Header` and value `custom
27    /// value`.
28    ///
29    /// ```rust
30    /// # extern crate rocket;
31    /// use rocket::http::Header;
32    ///
33    /// let header = Header::new("X-Custom-Header", "custom value");
34    /// assert_eq!(header.to_string(), "X-Custom-Header: custom value");
35    /// ```
36    ///
37    /// Use a `String` as a value to do the same.
38    ///
39    /// ```rust
40    /// # extern crate rocket;
41    /// use rocket::http::Header;
42    ///
43    /// let value = format!("{} value", "custom");
44    /// let header = Header::new("X-Custom-Header", value);
45    /// assert_eq!(header.to_string(), "X-Custom-Header: custom value");
46    /// ```
47    #[inline(always)]
48    pub fn new<'a: 'h, 'b: 'h, N, V>(name: N, value: V) -> Header<'h>
49        where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
50    {
51        Header {
52            name: Uncased::new(name),
53            value: value.into()
54        }
55    }
56
57    /// Returns `true` if `name` is a valid header name.
58    ///
59    /// This implements a simple (i.e, correct but not particularly performant)
60    /// header "field-name" checker as defined in RFC 7230.
61    ///
62    /// ```text
63    ///     header-field   = field-name ":" OWS field-value OWS
64    ///     field-name     = token
65    ///     token          = 1*tchar
66    ///     tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
67    ///                    / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
68    ///                    / DIGIT / ALPHA
69    ///                    ; any VCHAR, except delimiters
70    /// ```
71    ///
72    /// # Example
73    ///
74    /// ```rust
75    /// # extern crate rocket;
76    /// use rocket::http::Header;
77    ///
78    /// assert!(!Header::is_valid_name(""));
79    /// assert!(!Header::is_valid_name("some header"));
80    /// assert!(!Header::is_valid_name("some()"));
81    /// assert!(!Header::is_valid_name("[SomeHeader]"));
82    /// assert!(!Header::is_valid_name("<"));
83    /// assert!(!Header::is_valid_name(""));
84    /// assert!(!Header::is_valid_name("header,here"));
85    ///
86    /// assert!(Header::is_valid_name("Some#Header"));
87    /// assert!(Header::is_valid_name("Some-Header"));
88    /// assert!(Header::is_valid_name("This-Is_A~Header"));
89    /// ```
90    #[doc(hidden)]
91    pub const fn is_valid_name(name: &str) -> bool {
92        const fn is_tchar(b: &u8) -> bool {
93            b.is_ascii_alphanumeric() || match *b {
94                b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' |
95                    b'.' | b'^' | b'_' | b'`' | b'|' | b'~' => true,
96                _ => false
97            }
98        }
99
100        let mut i = 0;
101        let bytes = name.as_bytes();
102        while i < bytes.len() {
103            if !is_tchar(&bytes[i]) {
104                return false
105            }
106
107            i += 1;
108        }
109
110        i > 0
111    }
112
113    /// Returns `true` if `val` is a valid header value.
114    ///
115    /// If `allow_empty` is `true`, this function returns `true` for empty
116    /// values. Otherwise, this function returns `false` for empty values.
117    ///
118    /// This implements a simple (i.e, correct but not particularly performant)
119    /// header "field-content" checker as defined in RFC 7230 without support
120    /// for obsolete (`obs-`) syntax:
121    ///
122    ///   field-value    = *(field-content)
123    ///   field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
124    ///   field-vchar    = VCHAR
125    ///   VCHAR          = %x21-7E ; visible (printing) characters
126    ///
127    /// Note that this is a generic checker. Specific headers may have stricter
128    /// requirements. For example, the `Server` header does not allow delimiters
129    /// in its values.
130    ///
131    /// # Example
132    ///
133    /// ```rust
134    /// # extern crate rocket;
135    /// use rocket::http::Header;
136    ///
137    /// assert!(!Header::is_valid_value("", false));
138    /// assert!(!Header::is_valid_value(" " , false));
139    /// assert!(!Header::is_valid_value(" hi", false));
140    /// assert!(!Header::is_valid_value("a\nbc", false));
141    /// assert!(!Header::is_valid_value("\nbc", false));
142    /// assert!(!Header::is_valid_value("\n", false));
143    /// assert!(!Header::is_valid_value("\t", false));
144    /// assert!(!Header::is_valid_value("\r", false));
145    /// assert!(!Header::is_valid_value("a\nb\nc", false));
146    /// assert!(!Header::is_valid_value("a\rb\rc", false));
147    ///
148    /// assert!(Header::is_valid_value("", true));
149    /// assert!(Header::is_valid_value("a", false));
150    /// assert!(Header::is_valid_value("a", true));
151    /// assert!(Header::is_valid_value("abc", false));
152    /// assert!(Header::is_valid_value("abc", true));
153    /// assert!(Header::is_valid_value("a b c", false));
154    /// assert!(Header::is_valid_value("a b c", true));
155    /// ```
156    #[doc(hidden)]
157    pub const fn is_valid_value(val: &str, allow_empty: bool) -> bool {
158        const fn is_valid_start(b: &u8) -> bool {
159            b.is_ascii_graphic()
160        }
161
162        const fn is_valid_continue(b: &u8) -> bool {
163            is_valid_start(b) || *b == b' ' || *b == b'\t'
164        }
165
166        let mut i = 0;
167        let bytes = val.as_bytes();
168        while i < bytes.len() {
169            match i {
170                0 if !is_valid_start(&bytes[i]) => return false,
171                _ if i > 0 && !is_valid_continue(&bytes[i]) => return false,
172                _ => { /* ok */ }
173            };
174
175            i += 1;
176        }
177
178        allow_empty || i > 0
179    }
180
181    /// Returns the name of this header.
182    ///
183    /// # Example
184    ///
185    /// A case-sensitive equality check:
186    ///
187    /// ```rust
188    /// # extern crate rocket;
189    /// use rocket::http::Header;
190    ///
191    /// let value = format!("{} value", "custom");
192    /// let header = Header::new("X-Custom-Header", value);
193    /// assert_eq!(header.name().as_str(), "X-Custom-Header");
194    /// assert_ne!(header.name().as_str(), "X-CUSTOM-HEADER");
195    /// ```
196    ///
197    /// A case-insensitive equality check:
198    ///
199    /// ```rust
200    /// # extern crate rocket;
201    /// use rocket::http::Header;
202    ///
203    /// let header = Header::new("X-Custom-Header", "custom value");
204    /// assert_eq!(header.name(), "X-Custom-Header");
205    /// assert_eq!(header.name(), "X-CUSTOM-HEADER");
206    /// ```
207    #[inline(always)]
208    pub fn name(&self) -> &UncasedStr {
209        &self.name
210    }
211
212    /// Returns the value of this header.
213    ///
214    /// # Example
215    ///
216    /// A case-sensitive equality check:
217    ///
218    /// ```rust
219    /// # extern crate rocket;
220    /// use rocket::http::Header;
221    ///
222    /// let header = Header::new("X-Custom-Header", "custom value");
223    /// assert_eq!(header.value(), "custom value");
224    /// ```
225    #[inline(always)]
226    pub fn value(&self) -> &str {
227        &self.value
228    }
229}
230
231impl fmt::Display for Header<'_> {
232    #[inline(always)]
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        write!(f, "{}: {}", self.name, self.value)
235    }
236}
237
238/// A collection of headers, mapping a header name to its many ordered values.
239///
240/// # Case-Insensitivity
241///
242/// All header names, including those passed in to `HeaderMap` methods and those
243/// stored in an existing `HeaderMap`, are treated case-insensitively. This
244/// means that, for instance, a look for a header by the name of "aBC" will
245/// returns values for headers of names "AbC", "ABC", "abc", and so on.
246#[derive(Clone, Debug, PartialEq, Default)]
247pub struct HeaderMap<'h> {
248    headers: IndexMap<Uncased<'h>, Vec<Cow<'h, str>>>
249}
250
251impl<'h> HeaderMap<'h> {
252    /// Returns an empty header collection.
253    ///
254    /// # Example
255    ///
256    /// ```rust
257    /// # extern crate rocket;
258    /// use rocket::http::HeaderMap;
259    ///
260    /// let map = HeaderMap::new();
261    /// ```
262    #[inline(always)]
263    pub fn new() -> HeaderMap<'h> {
264        HeaderMap { headers: IndexMap::new() }
265    }
266
267    /// Returns true if `self` contains a header with the name `name`.
268    ///
269    /// # Example
270    ///
271    /// ```rust
272    /// # extern crate rocket;
273    /// use rocket::http::{HeaderMap, ContentType};
274    ///
275    /// let mut map = HeaderMap::new();
276    /// map.add(ContentType::HTML);
277    ///
278    /// assert!(map.contains("Content-Type"));
279    /// assert!(!map.contains("Accepts"));
280    /// ```
281    #[inline]
282    pub fn contains<N: AsRef<str>>(&self, name: N) -> bool {
283        self.headers.get(UncasedStr::new(name.as_ref())).is_some()
284    }
285
286    /// Returns the number of _values_ stored in the map.
287    ///
288    /// # Example
289    ///
290    /// ```rust
291    /// # extern crate rocket;
292    /// use rocket::http::HeaderMap;
293    ///
294    /// let mut map = HeaderMap::new();
295    /// assert_eq!(map.len(), 0);
296    ///
297    /// map.add_raw("X-Custom", "value_1");
298    /// assert_eq!(map.len(), 1);
299    ///
300    /// map.replace_raw("X-Custom", "value_2");
301    /// assert_eq!(map.len(), 1);
302    ///
303    /// map.add_raw("X-Custom", "value_1");
304    /// assert_eq!(map.len(), 2);
305    /// ```
306    #[inline]
307    pub fn len(&self) -> usize {
308        self.headers.iter().flat_map(|(_, values)| values.iter()).count()
309    }
310
311    /// Returns `true` if there are no headers stored in the map. Otherwise
312    /// returns `false`.
313    ///
314    /// # Example
315    ///
316    /// ```rust
317    /// # extern crate rocket;
318    /// use rocket::http::HeaderMap;
319    ///
320    /// let map = HeaderMap::new();
321    /// assert!(map.is_empty());
322    /// ```
323    #[inline]
324    pub fn is_empty(&self) -> bool {
325        self.headers.is_empty()
326    }
327
328    /// Returns an iterator over all of the values stored in `self` for the
329    /// header with name `name`. The headers are returned in FIFO order.
330    ///
331    /// # Example
332    ///
333    /// ```rust
334    /// # extern crate rocket;
335    /// use rocket::http::HeaderMap;
336    ///
337    /// let mut map = HeaderMap::new();
338    /// map.add_raw("X-Custom", "value_1");
339    /// map.add_raw("X-Custom", "value_2");
340    ///
341    /// assert_eq!(map.len(), 2);
342    ///
343    /// let mut values = map.get("X-Custom");
344    /// assert_eq!(values.next(), Some("value_1"));
345    /// assert_eq!(values.next(), Some("value_2"));
346    /// assert_eq!(values.next(), None);
347    /// ```
348    #[inline]
349    pub fn get(&self, name: &str) -> impl Iterator<Item=&str> {
350        self.headers
351            .get(UncasedStr::new(name))
352            .into_iter()
353            .flat_map(|values| values.iter().map(|val| val.borrow()))
354    }
355
356    /// Returns the _first_ value stored for the header with name `name` if
357    /// there is one.
358    ///
359    /// # Examples
360    ///
361    /// Retrieve the first value when one exists:
362    ///
363    /// ```rust
364    /// # extern crate rocket;
365    /// use rocket::http::HeaderMap;
366    ///
367    /// let mut map = HeaderMap::new();
368    /// map.add_raw("X-Custom", "value_1");
369    /// map.add_raw("X-Custom", "value_2");
370    ///
371    /// assert_eq!(map.len(), 2);
372    ///
373    /// let first_value = map.get_one("X-Custom");
374    /// assert_eq!(first_value, Some("value_1"));
375    /// ```
376    ///
377    /// Attempt to retrieve a value that doesn't exist:
378    ///
379    /// ```rust
380    /// # extern crate rocket;
381    /// use rocket::http::HeaderMap;
382    ///
383    /// let mut map = HeaderMap::new();
384    /// map.add_raw("X-Custom", "value_1");
385    ///
386    /// let first_value = map.get_one("X-Other");
387    /// assert_eq!(first_value, None);
388    /// ```
389    #[inline]
390    pub fn get_one<'a>(&'a self, name: &str) -> Option<&'a str> {
391        self.headers.get(UncasedStr::new(name))
392            .and_then(|values| {
393                if !values.is_empty() { Some(values[0].borrow()) }
394                else { None }
395            })
396    }
397
398    /// Replace any header that matches the name of `header.name` with `header`.
399    /// If there is no such header in `self`, add `header`. If the matching
400    /// header had multiple values, all of the values are removed, and only the
401    /// value in `header` will remain.
402    ///
403    /// # Example
404    ///
405    /// Replace a header that doesn't yet exist:
406    ///
407    /// ```rust
408    /// # extern crate rocket;
409    /// use rocket::http::{HeaderMap, ContentType};
410    ///
411    /// let mut map = HeaderMap::new();
412    /// map.replace(ContentType::JSON);
413    ///
414    /// assert!(map.get_one("Content-Type").is_some());
415    /// ```
416    ///
417    /// Replace a header that already exists:
418    ///
419    /// ```rust
420    /// # extern crate rocket;
421    /// use rocket::http::{HeaderMap, ContentType};
422    ///
423    /// let mut map = HeaderMap::new();
424    ///
425    /// map.replace(ContentType::JSON);
426    /// assert_eq!(map.get_one("Content-Type"), Some("application/json"));
427    ///
428    /// map.replace(ContentType::GIF);
429    /// assert_eq!(map.get_one("Content-Type"), Some("image/gif"));
430    /// assert_eq!(map.len(), 1);
431    /// ```
432    ///
433    /// An example of case-insensitivity.
434    ///
435    /// ```rust
436    /// # extern crate rocket;
437    /// use rocket::http::{HeaderMap, Header, ContentType};
438    ///
439    /// let mut map = HeaderMap::new();
440    ///
441    /// map.replace(ContentType::JSON);
442    /// assert_eq!(map.get_one("Content-Type"), Some("application/json"));
443    ///
444    /// map.replace(Header::new("CONTENT-type", "image/gif"));
445    /// assert_eq!(map.get_one("Content-Type"), Some("image/gif"));
446    /// assert_eq!(map.len(), 1);
447    /// ```
448    #[inline(always)]
449    pub fn replace<'p: 'h, H: Into<Header<'p>>>(&mut self, header: H) -> bool {
450        let header = header.into();
451        self.headers.insert(header.name, vec![header.value]).is_some()
452    }
453
454    /// A convenience method to replace a header using a raw name and value.
455    /// Aliases `replace(Header::new(name, value))`. Should be used rarely.
456    ///
457    /// # Example
458    ///
459    /// ```rust
460    /// # extern crate rocket;
461    /// use rocket::http::HeaderMap;
462    ///
463    /// let mut map = HeaderMap::new();
464    ///
465    /// map.replace_raw("X-Custom", "value_1");
466    /// assert_eq!(map.get_one("X-Custom"), Some("value_1"));
467    ///
468    /// map.replace_raw("X-Custom", "value_2");
469    /// assert_eq!(map.get_one("X-Custom"), Some("value_2"));
470    /// assert_eq!(map.len(), 1);
471    /// ```
472    #[inline(always)]
473    pub fn replace_raw<'a: 'h, 'b: 'h, N, V>(&mut self, name: N, value: V) -> bool
474        where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
475    {
476        self.replace(Header::new(name, value))
477    }
478
479    /// Replaces all of the values for a header with name `name` with `values`.
480    /// This a low-level method and should rarely be used.
481    ///
482    ///
483    /// # Example
484    ///
485    /// ```rust
486    /// # extern crate rocket;
487    /// use rocket::http::HeaderMap;
488    ///
489    /// let mut map = HeaderMap::new();
490    /// map.add_raw("X-Custom", "value_1");
491    /// map.add_raw("X-Custom", "value_2");
492    ///
493    /// let vals: Vec<_> = map.get("X-Custom").map(|s| s.to_string()).collect();
494    /// assert_eq!(vals, vec!["value_1", "value_2"]);
495    ///
496    /// map.replace_all("X-Custom", vec!["value_3".into(), "value_4".into()]);
497    /// let vals: Vec<_> = map.get("X-Custom").collect();
498    /// assert_eq!(vals, vec!["value_3", "value_4"]);
499    /// ```
500    #[inline(always)]
501    pub fn replace_all<'n, 'v: 'h, H>(&mut self, name: H, values: Vec<Cow<'v, str>>)
502        where 'n: 'h, H: Into<Cow<'n, str>>
503    {
504        self.headers.insert(Uncased::new(name), values);
505    }
506
507    /// Adds `header` into the map. If a header with `header.name` was
508    /// previously added, that header will have one more value.
509    ///
510    /// ```rust
511    /// # extern crate rocket;
512    /// use rocket::http::{Cookie, HeaderMap};
513    ///
514    /// let mut map = HeaderMap::new();
515    ///
516    /// map.add(&Cookie::new("a", "b"));
517    /// assert_eq!(map.get("Set-Cookie").count(), 1);
518    ///
519    /// map.add(&Cookie::new("c", "d"));
520    /// assert_eq!(map.get("Set-Cookie").count(), 2);
521    /// ```
522    #[inline(always)]
523    pub fn add<'p: 'h, H: Into<Header<'p>>>(&mut self, header: H) {
524        let header = header.into();
525        self.headers.entry(header.name).or_insert(vec![]).push(header.value);
526    }
527
528    /// A convenience method to add a header using a raw name and value.
529    /// Aliases `add(Header::new(name, value))`. Should be used rarely.
530    ///
531    /// # Example
532    ///
533    /// ```rust
534    /// # extern crate rocket;
535    /// use rocket::http::HeaderMap;
536    ///
537    /// let mut map = HeaderMap::new();
538    ///
539    /// map.add_raw("X-Custom", "value_1");
540    /// assert_eq!(map.get("X-Custom").count(), 1);
541    ///
542    /// map.add_raw("X-Custom", "value_2");
543    /// let values: Vec<_> = map.get("X-Custom").collect();
544    /// assert_eq!(values, vec!["value_1", "value_2"]);
545    /// ```
546    #[inline(always)]
547    pub fn add_raw<'a: 'h, 'b: 'h, N, V>(&mut self, name: N, value: V)
548        where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
549    {
550        self.add(Header::new(name, value))
551    }
552
553    /// Adds all of the values to a header with name `name`. This a low-level
554    /// method and should rarely be used. `values` will be empty when this
555    /// method returns.
556    ///
557    /// # Example
558    ///
559    /// ```rust
560    /// # extern crate rocket;
561    /// use rocket::http::HeaderMap;
562    ///
563    /// let mut map = HeaderMap::new();
564    ///
565    /// let mut values = vec!["value_1".into(), "value_2".into()];
566    /// map.add_all("X-Custom", &mut values);
567    /// assert_eq!(map.get("X-Custom").count(), 2);
568    /// assert_eq!(values.len(), 0);
569    ///
570    /// let mut values = vec!["value_3".into(), "value_4".into()];
571    /// map.add_all("X-Custom", &mut values);
572    /// assert_eq!(map.get("X-Custom").count(), 4);
573    /// assert_eq!(values.len(), 0);
574    ///
575    /// let values: Vec<_> = map.get("X-Custom").collect();
576    /// assert_eq!(values, vec!["value_1", "value_2", "value_3", "value_4"]);
577    /// ```
578    #[inline(always)]
579    pub fn add_all<'n, H>(&mut self, name: H, values: &mut Vec<Cow<'h, str>>)
580        where 'n:'h, H: Into<Cow<'n, str>>
581    {
582        self.headers.entry(Uncased::new(name))
583            .or_insert(vec![])
584            .append(values)
585    }
586
587    /// Remove all of the values for header with name `name`.
588    ///
589    /// # Example
590    ///
591    /// ```rust
592    /// # extern crate rocket;
593    /// use rocket::http::HeaderMap;
594    ///
595    /// let mut map = HeaderMap::new();
596    /// map.add_raw("X-Custom", "value_1");
597    /// map.add_raw("X-Custom", "value_2");
598    /// map.add_raw("X-Other", "other");
599    ///
600    /// assert_eq!(map.len(), 3);
601    ///
602    /// map.remove("X-Custom");
603    /// assert_eq!(map.len(), 1);
604    #[inline(always)]
605    pub fn remove(&mut self, name: &str) {
606        self.headers.swap_remove(UncasedStr::new(name));
607    }
608
609    /// Removes all of the headers stored in this map and returns a vector
610    /// containing them. Header names are returned in no specific order, but all
611    /// values for a given header name are grouped together, and values are in
612    /// FIFO order.
613    ///
614    /// # Example
615    ///
616    /// ```rust
617    /// # extern crate rocket;
618    /// use rocket::http::{HeaderMap, Header};
619    /// use std::collections::HashSet;
620    ///
621    /// // The headers we'll be storing.
622    /// let all_headers = vec![
623    ///     Header::new("X-Custom", "value_1"),
624    ///     Header::new("X-Custom", "value_2"),
625    ///     Header::new("X-Other", "other")
626    /// ];
627    ///
628    /// // Create a map, store all of the headers.
629    /// let mut map = HeaderMap::new();
630    /// for header in all_headers.clone() {
631    ///     map.add(header)
632    /// }
633    ///
634    /// assert_eq!(map.len(), 3);
635    ///
636    /// // Now remove them all, ensure the map is empty.
637    /// let removed_headers = map.remove_all();
638    /// assert!(map.is_empty());
639    ///
640    /// // Create two sets: what we expect and got. Ensure they're equal.
641    /// let expected_set: HashSet<_> = all_headers.into_iter().collect();
642    /// let actual_set: HashSet<_> = removed_headers.into_iter().collect();
643    /// assert_eq!(expected_set, actual_set);
644    /// ```
645    #[inline(always)]
646    pub fn remove_all(&mut self) -> Vec<Header<'h>> {
647        let old_map = std::mem::replace(self, HeaderMap::new());
648        old_map.into_iter().collect()
649    }
650
651    /// Returns an iterator over all of the `Header`s stored in the map. Header
652    /// names are returned in no specific order, but all values for a given
653    /// header name are grouped together, and values are in FIFO order.
654    ///
655    /// # Example
656    ///
657    /// ```rust
658    /// # extern crate rocket;
659    /// use rocket::http::{HeaderMap, Header};
660    ///
661    /// // The headers we'll be storing.
662    /// let all_headers = vec![
663    ///     Header::new("X-Custom", "value_1"),
664    ///     Header::new("X-Other", "other"),
665    ///     Header::new("X-Third", "third"),
666    /// ];
667    ///
668    /// // Create a map, store all of the headers.
669    /// let mut map = HeaderMap::new();
670    /// for header in all_headers {
671    ///     map.add(header)
672    /// }
673    ///
674    /// // Ensure there are three headers via the iterator.
675    /// assert_eq!(map.iter().count(), 3);
676    ///
677    /// // Actually iterate through them.
678    /// for header in map.iter() {
679    ///     match header.name().as_str() {
680    ///         "X-Custom" => assert_eq!(header.value(), "value_1"),
681    ///         "X-Other" => assert_eq!(header.value(), "other"),
682    ///         "X-Third" => assert_eq!(header.value(), "third"),
683    ///         _ => unreachable!("there are only three headers")
684    ///     }
685    /// }
686    /// ```
687    pub fn iter(&self) -> impl Iterator<Item=Header<'_>> {
688        self.headers.iter().flat_map(|(key, values)| {
689            values.iter().map(move |val| {
690                Header::new(key.as_str(), &**val)
691            })
692        })
693    }
694
695    /// Consumes `self` and returns an iterator over all of the `Header`s stored
696    /// in the map. Header names are returned in no specific order, but all
697    /// values for a given header name are grouped together, and values are in
698    /// FIFO order.
699    ///
700    /// # Example
701    ///
702    /// ```rust
703    /// # extern crate rocket;
704    /// use rocket::http::{HeaderMap, Header};
705    ///
706    /// // The headers we'll be storing.
707    /// let all_headers = vec![
708    ///     Header::new("X-Custom", "value_1"),
709    ///     Header::new("X-Other", "other"),
710    ///     Header::new("X-Third", "third"),
711    /// ];
712    ///
713    /// // Create a map, store all of the headers.
714    /// let mut map = HeaderMap::new();
715    /// for header in all_headers {
716    ///     map.add(header)
717    /// }
718    ///
719    /// // Ensure there are three headers via the iterator.
720    /// assert_eq!(map.iter().count(), 3);
721    ///
722    /// // Actually iterate through them.
723    /// for header in map.into_iter() {
724    ///     match header.name().as_str() {
725    ///         "X-Custom" => assert_eq!(header.value(), "value_1"),
726    ///         "X-Other" => assert_eq!(header.value(), "other"),
727    ///         "X-Third" => assert_eq!(header.value(), "third"),
728    ///         _ => unreachable!("there are only three headers")
729    ///     }
730    /// }
731    /// ```
732    // TODO: Implement IntoIterator.
733    #[inline(always)]
734    pub fn into_iter(self) -> impl Iterator<Item=Header<'h>> {
735        self.headers.into_iter().flat_map(|(name, value)| {
736            value.into_iter().map(move |value| {
737                Header { name: name.clone(), value }
738            })
739        })
740    }
741
742    /// Consumes `self` and returns an iterator over all of the headers stored
743    /// in the map in the way they are stored. This is a low-level mechanism and
744    /// should likely not be used.
745    /// WARNING: This is unstable! Do not use this method outside of Rocket!
746    #[doc(hidden)]
747    #[inline]
748    pub fn into_iter_raw(self)
749            -> impl Iterator<Item=(Uncased<'h>, Vec<Cow<'h, str>>)> {
750        self.headers.into_iter()
751    }
752}
753
754impl From<cookie::Cookie<'_>> for Header<'static> {
755    fn from(cookie: cookie::Cookie<'_>) -> Header<'static> {
756        Header::new("Set-Cookie", cookie.encoded().to_string())
757    }
758}
759
760impl From<&cookie::Cookie<'_>> for Header<'static> {
761    fn from(cookie: &cookie::Cookie<'_>) -> Header<'static> {
762        Header::new("Set-Cookie", cookie.encoded().to_string())
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use super::HeaderMap;
769
770    #[test]
771    fn case_insensitive_add_get() {
772        let mut map = HeaderMap::new();
773        map.add_raw("content-type", "application/json");
774
775        let ct = map.get_one("Content-Type");
776        assert_eq!(ct, Some("application/json"));
777
778        let ct2 = map.get_one("CONTENT-TYPE");
779        assert_eq!(ct2, Some("application/json"))
780    }
781
782    #[test]
783    fn case_insensitive_multiadd() {
784        let mut map = HeaderMap::new();
785        map.add_raw("x-custom", "a");
786        map.add_raw("X-Custom", "b");
787        map.add_raw("x-CUSTOM", "c");
788
789        let vals: Vec<_> = map.get("x-CuStOm").collect();
790        assert_eq!(vals, vec!["a", "b", "c"]);
791    }
792}