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