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}