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}