1use bytes::{Bytes, BytesMut};
2
3use std::convert::TryFrom;
4use std::error::Error;
5use std::fmt::Write;
6use std::str::FromStr;
7use std::{cmp, fmt, mem, str};
8
9use crate::header::name::HeaderName;
10
11/// Represents an HTTP header field value.
12///
13/// In practice, HTTP header field values are usually valid ASCII. However, the
14/// HTTP spec allows for a header value to contain opaque bytes as well. In this
15/// case, the header field value is not able to be represented as a string.
16///
17/// To handle this, the `HeaderValue` is useable as a type and can be compared
18/// with strings and implements `Debug`. A `to_str` fn is provided that returns
19/// an `Err` if the header value contains non visible ascii characters.
20#[derive(Clone, Hash)]
21pub struct HeaderValue {
22 inner: Bytes,
23 is_sensitive: bool,
24}
25
26/// A possible error when converting a `HeaderValue` from a string or byte
27/// slice.
28pub struct InvalidHeaderValue {
29 _priv: (),
30}
31
32/// A possible error when converting a `HeaderValue` to a string representation.
33///
34/// Header field values may contain opaque bytes, in which case it is not
35/// possible to represent the value as a string.
36#[derive(Debug)]
37pub struct ToStrError {
38 _priv: (),
39}
40
41impl HeaderValue {
42 /// Convert a static string to a `HeaderValue`.
43 ///
44 /// This function will not perform any copying, however the string is
45 /// checked to ensure that no invalid characters are present. Only visible
46 /// ASCII characters (32-127) are permitted.
47 ///
48 /// # Panics
49 ///
50 /// This function panics if the argument contains invalid header value
51 /// characters.
52 ///
53 /// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345)
54 /// makes its way into stable, the panic message at compile-time is
55 /// going to look cryptic, but should at least point at your header value:
56 ///
57 /// ```text
58 /// error: any use of this value will cause an error
59 /// --> http/src/header/value.rs:67:17
60 /// |
61 /// 67 | ([] as [u8; 0])[0]; // Invalid header value
62 /// | ^^^^^^^^^^^^^^^^^^
63 /// | |
64 /// | index out of bounds: the length is 0 but the index is 0
65 /// | inside `HeaderValue::from_static` at http/src/header/value.rs:67:17
66 /// | inside `INVALID_HEADER` at src/main.rs:73:33
67 /// |
68 /// ::: src/main.rs:73:1
69 /// |
70 /// 73 | const INVALID_HEADER: HeaderValue = HeaderValue::from_static("жsome value");
71 /// | ----------------------------------------------------------------------------
72 /// ```
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// # use http::header::HeaderValue;
78 /// let val = HeaderValue::from_static("hello");
79 /// assert_eq!(val, "hello");
80 /// ```
81 #[inline]
82 #[allow(unconditional_panic)] // required for the panic circumvention
83 pub const fn from_static(src: &'static str) -> HeaderValue {
84 let bytes = src.as_bytes();
85 let mut i = 0;
86 while i < bytes.len() {
87 if !is_visible_ascii(bytes[i]) {
88 ([] as [u8; 0])[0]; // Invalid header value
89 }
90 i += 1;
91 }
92
93 HeaderValue {
94 inner: Bytes::from_static(bytes),
95 is_sensitive: false,
96 }
97 }
98
99 /// Attempt to convert a string to a `HeaderValue`.
100 ///
101 /// If the argument contains invalid header value characters, an error is
102 /// returned. Only visible ASCII characters (32-127) are permitted. Use
103 /// `from_bytes` to create a `HeaderValue` that includes opaque octets
104 /// (128-255).
105 ///
106 /// This function is intended to be replaced in the future by a `TryFrom`
107 /// implementation once the trait is stabilized in std.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// # use http::header::HeaderValue;
113 /// let val = HeaderValue::from_str("hello").unwrap();
114 /// assert_eq!(val, "hello");
115 /// ```
116 ///
117 /// An invalid value
118 ///
119 /// ```
120 /// # use http::header::HeaderValue;
121 /// let val = HeaderValue::from_str("\n");
122 /// assert!(val.is_err());
123 /// ```
124 #[inline]
125 pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
126 HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
127 }
128
129 /// Converts a HeaderName into a HeaderValue
130 ///
131 /// Since every valid HeaderName is a valid HeaderValue this is done infallibly.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// # use http::header::{HeaderValue, HeaderName};
137 /// # use http::header::ACCEPT;
138 /// let val = HeaderValue::from_name(ACCEPT);
139 /// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap());
140 /// ```
141 #[inline]
142 pub fn from_name(name: HeaderName) -> HeaderValue {
143 name.into()
144 }
145
146 /// Attempt to convert a byte slice to a `HeaderValue`.
147 ///
148 /// If the argument contains invalid header value bytes, an error is
149 /// returned. Only byte values between 32 and 255 (inclusive) are permitted,
150 /// excluding byte 127 (DEL).
151 ///
152 /// This function is intended to be replaced in the future by a `TryFrom`
153 /// implementation once the trait is stabilized in std.
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// # use http::header::HeaderValue;
159 /// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
160 /// assert_eq!(val, &b"hello\xfa"[..]);
161 /// ```
162 ///
163 /// An invalid value
164 ///
165 /// ```
166 /// # use http::header::HeaderValue;
167 /// let val = HeaderValue::from_bytes(b"\n");
168 /// assert!(val.is_err());
169 /// ```
170 #[inline]
171 pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
172 HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
173 }
174
175 /// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
176 ///
177 /// This will try to prevent a copy if the type passed is the type used
178 /// internally, and will copy the data if it is not.
179 pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
180 where
181 T: AsRef<[u8]> + 'static,
182 {
183 if_downcast_into!(T, Bytes, src, {
184 return HeaderValue::from_shared(src);
185 });
186
187 HeaderValue::from_bytes(src.as_ref())
188 }
189
190 /// Convert a `Bytes` directly into a `HeaderValue` without validating.
191 ///
192 /// This function does NOT validate that illegal bytes are not contained
193 /// within the buffer.
194 pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue
195 where
196 T: AsRef<[u8]> + 'static,
197 {
198 if cfg!(debug_assertions) {
199 match HeaderValue::from_maybe_shared(src) {
200 Ok(val) => val,
201 Err(_err) => {
202 panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes");
203 }
204 }
205 } else {
206
207 if_downcast_into!(T, Bytes, src, {
208 return HeaderValue {
209 inner: src,
210 is_sensitive: false,
211 };
212 });
213
214 let src = Bytes::copy_from_slice(src.as_ref());
215 HeaderValue {
216 inner: src,
217 is_sensitive: false,
218 }
219 }
220 }
221
222 fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
223 HeaderValue::try_from_generic(src, std::convert::identity)
224 }
225
226 fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(src: T, into: F) -> Result<HeaderValue, InvalidHeaderValue> {
227 for &b in src.as_ref() {
228 if !is_valid(b) {
229 return Err(InvalidHeaderValue { _priv: () });
230 }
231 }
232 Ok(HeaderValue {
233 inner: into(src),
234 is_sensitive: false,
235 })
236 }
237
238 /// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
239 /// chars.
240 ///
241 /// This function will perform a scan of the header value, checking all the
242 /// characters.
243 ///
244 /// # Examples
245 ///
246 /// ```
247 /// # use http::header::HeaderValue;
248 /// let val = HeaderValue::from_static("hello");
249 /// assert_eq!(val.to_str().unwrap(), "hello");
250 /// ```
251 pub fn to_str(&self) -> Result<&str, ToStrError> {
252 let bytes = self.as_ref();
253
254 for &b in bytes {
255 if !is_visible_ascii(b) {
256 return Err(ToStrError { _priv: () });
257 }
258 }
259
260 unsafe { Ok(str::from_utf8_unchecked(bytes)) }
261 }
262
263 /// Returns the length of `self`.
264 ///
265 /// This length is in bytes.
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// # use http::header::HeaderValue;
271 /// let val = HeaderValue::from_static("hello");
272 /// assert_eq!(val.len(), 5);
273 /// ```
274 #[inline]
275 pub fn len(&self) -> usize {
276 self.as_ref().len()
277 }
278
279 /// Returns true if the `HeaderValue` has a length of zero bytes.
280 ///
281 /// # Examples
282 ///
283 /// ```
284 /// # use http::header::HeaderValue;
285 /// let val = HeaderValue::from_static("");
286 /// assert!(val.is_empty());
287 ///
288 /// let val = HeaderValue::from_static("hello");
289 /// assert!(!val.is_empty());
290 /// ```
291 #[inline]
292 pub fn is_empty(&self) -> bool {
293 self.len() == 0
294 }
295
296 /// Converts a `HeaderValue` to a byte slice.
297 ///
298 /// # Examples
299 ///
300 /// ```
301 /// # use http::header::HeaderValue;
302 /// let val = HeaderValue::from_static("hello");
303 /// assert_eq!(val.as_bytes(), b"hello");
304 /// ```
305 #[inline]
306 pub fn as_bytes(&self) -> &[u8] {
307 self.as_ref()
308 }
309
310 /// Mark that the header value represents sensitive information.
311 ///
312 /// # Examples
313 ///
314 /// ```
315 /// # use http::header::HeaderValue;
316 /// let mut val = HeaderValue::from_static("my secret");
317 ///
318 /// val.set_sensitive(true);
319 /// assert!(val.is_sensitive());
320 ///
321 /// val.set_sensitive(false);
322 /// assert!(!val.is_sensitive());
323 /// ```
324 #[inline]
325 pub fn set_sensitive(&mut self, val: bool) {
326 self.is_sensitive = val;
327 }
328
329 /// Returns `true` if the value represents sensitive data.
330 ///
331 /// Sensitive data could represent passwords or other data that should not
332 /// be stored on disk or in memory. By marking header values as sensitive,
333 /// components using this crate can be instructed to treat them with special
334 /// care for security reasons. For example, caches can avoid storing
335 /// sensitive values, and HPACK encoders used by HTTP/2.0 implementations
336 /// can choose not to compress them.
337 ///
338 /// Additionally, sensitive values will be masked by the `Debug`
339 /// implementation of `HeaderValue`.
340 ///
341 /// Note that sensitivity is not factored into equality or ordering.
342 ///
343 /// # Examples
344 ///
345 /// ```
346 /// # use http::header::HeaderValue;
347 /// let mut val = HeaderValue::from_static("my secret");
348 ///
349 /// val.set_sensitive(true);
350 /// assert!(val.is_sensitive());
351 ///
352 /// val.set_sensitive(false);
353 /// assert!(!val.is_sensitive());
354 /// ```
355 #[inline]
356 pub fn is_sensitive(&self) -> bool {
357 self.is_sensitive
358 }
359}
360
361impl AsRef<[u8]> for HeaderValue {
362 #[inline]
363 fn as_ref(&self) -> &[u8] {
364 self.inner.as_ref()
365 }
366}
367
368impl fmt::Debug for HeaderValue {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 if self.is_sensitive {
371 f.write_str("Sensitive")
372 } else {
373 f.write_str("\"")?;
374 let mut from = 0;
375 let bytes = self.as_bytes();
376 for (i, &b) in bytes.iter().enumerate() {
377 if !is_visible_ascii(b) || b == b'"' {
378 if from != i {
379 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
380 }
381 if b == b'"' {
382 f.write_str("\\\"")?;
383 } else {
384 write!(f, "\\x{:x}", b)?;
385 }
386 from = i + 1;
387 }
388 }
389
390 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
391 f.write_str("\"")
392 }
393 }
394}
395
396impl From<HeaderName> for HeaderValue {
397 #[inline]
398 fn from(h: HeaderName) -> HeaderValue {
399 HeaderValue {
400 inner: h.into_bytes(),
401 is_sensitive: false,
402 }
403 }
404}
405
406macro_rules! from_integers {
407 ($($name:ident: $t:ident => $max_len:expr),*) => {$(
408 impl From<$t> for HeaderValue {
409 fn from(num: $t) -> HeaderValue {
410 let mut buf = if mem::size_of::<BytesMut>() - 1 < $max_len {
411 // On 32bit platforms, BytesMut max inline size
412 // is 15 bytes, but the $max_len could be bigger.
413 //
414 // The likelihood of the number *actually* being
415 // that big is very small, so only allocate
416 // if the number needs that space.
417 //
418 // The largest decimal number in 15 digits:
419 // It wold be 10.pow(15) - 1, but this is a constant
420 // version.
421 if num as u64 > 999_999_999_999_999_999 {
422 BytesMut::with_capacity($max_len)
423 } else {
424 // fits inline...
425 BytesMut::new()
426 }
427 } else {
428 // full value fits inline, so don't allocate!
429 BytesMut::new()
430 };
431 let _ = buf.write_str(::itoa::Buffer::new().format(num));
432 HeaderValue {
433 inner: buf.freeze(),
434 is_sensitive: false,
435 }
436 }
437 }
438
439 #[test]
440 fn $name() {
441 let n: $t = 55;
442 let val = HeaderValue::from(n);
443 assert_eq!(val, &n.to_string());
444
445 let n = ::std::$t::MAX;
446 let val = HeaderValue::from(n);
447 assert_eq!(val, &n.to_string());
448 }
449 )*};
450}
451
452from_integers! {
453 // integer type => maximum decimal length
454
455 // u8 purposely left off... HeaderValue::from(b'3') could be confusing
456 from_u16: u16 => 5,
457 from_i16: i16 => 6,
458 from_u32: u32 => 10,
459 from_i32: i32 => 11,
460 from_u64: u64 => 20,
461 from_i64: i64 => 20
462}
463
464#[cfg(target_pointer_width = "16")]
465from_integers! {
466 from_usize: usize => 5,
467 from_isize: isize => 6
468}
469
470#[cfg(target_pointer_width = "32")]
471from_integers! {
472 from_usize: usize => 10,
473 from_isize: isize => 11
474}
475
476#[cfg(target_pointer_width = "64")]
477from_integers! {
478 from_usize: usize => 20,
479 from_isize: isize => 20
480}
481
482#[cfg(test)]
483mod from_header_name_tests {
484 use super::*;
485 use crate::header::map::HeaderMap;
486 use crate::header::name;
487
488 #[test]
489 fn it_can_insert_header_name_as_header_value() {
490 let mut map = HeaderMap::new();
491 map.insert(name::UPGRADE, name::SEC_WEBSOCKET_PROTOCOL.into());
492 map.insert(
493 name::ACCEPT,
494 name::HeaderName::from_bytes(b"hello-world").unwrap().into(),
495 );
496
497 assert_eq!(
498 map.get(name::UPGRADE).unwrap(),
499 HeaderValue::from_bytes(b"sec-websocket-protocol").unwrap()
500 );
501
502 assert_eq!(
503 map.get(name::ACCEPT).unwrap(),
504 HeaderValue::from_bytes(b"hello-world").unwrap()
505 );
506 }
507}
508
509impl FromStr for HeaderValue {
510 type Err = InvalidHeaderValue;
511
512 #[inline]
513 fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
514 HeaderValue::from_str(s)
515 }
516}
517
518impl<'a> From<&'a HeaderValue> for HeaderValue {
519 #[inline]
520 fn from(t: &'a HeaderValue) -> Self {
521 t.clone()
522 }
523}
524
525impl<'a> TryFrom<&'a str> for HeaderValue {
526 type Error = InvalidHeaderValue;
527
528 #[inline]
529 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
530 t.parse()
531 }
532}
533
534impl<'a> TryFrom<&'a String> for HeaderValue {
535 type Error = InvalidHeaderValue;
536 #[inline]
537 fn try_from(s: &'a String) -> Result<Self, Self::Error> {
538 Self::from_bytes(s.as_bytes())
539 }
540}
541
542impl<'a> TryFrom<&'a [u8]> for HeaderValue {
543 type Error = InvalidHeaderValue;
544
545 #[inline]
546 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
547 HeaderValue::from_bytes(t)
548 }
549}
550
551impl TryFrom<String> for HeaderValue {
552 type Error = InvalidHeaderValue;
553
554 #[inline]
555 fn try_from(t: String) -> Result<Self, Self::Error> {
556 HeaderValue::from_shared(t.into())
557 }
558}
559
560impl TryFrom<Vec<u8>> for HeaderValue {
561 type Error = InvalidHeaderValue;
562
563 #[inline]
564 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
565 HeaderValue::from_shared(vec.into())
566 }
567}
568
569#[cfg(test)]
570mod try_from_header_name_tests {
571 use super::*;
572 use crate::header::name;
573
574 #[test]
575 fn it_converts_using_try_from() {
576 assert_eq!(
577 HeaderValue::try_from(name::UPGRADE).unwrap(),
578 HeaderValue::from_bytes(b"upgrade").unwrap()
579 );
580 }
581}
582
583const fn is_visible_ascii(b: u8) -> bool {
584 b >= 32 && b < 127 || b == b'\t'
585}
586
587#[inline]
588fn is_valid(b: u8) -> bool {
589 b >= 32 && b != 127 || b == b'\t'
590}
591
592impl fmt::Debug for InvalidHeaderValue {
593 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
594 f.debug_struct("InvalidHeaderValue")
595 // skip _priv noise
596 .finish()
597 }
598}
599
600impl fmt::Display for InvalidHeaderValue {
601 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
602 f.write_str("failed to parse header value")
603 }
604}
605
606impl Error for InvalidHeaderValue {}
607
608impl fmt::Display for ToStrError {
609 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610 f.write_str("failed to convert header to a str")
611 }
612}
613
614impl Error for ToStrError {}
615
616// ===== PartialEq / PartialOrd =====
617
618impl PartialEq for HeaderValue {
619 #[inline]
620 fn eq(&self, other: &HeaderValue) -> bool {
621 self.inner == other.inner
622 }
623}
624
625impl Eq for HeaderValue {}
626
627impl PartialOrd for HeaderValue {
628 #[inline]
629 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
630 self.inner.partial_cmp(&other.inner)
631 }
632}
633
634impl Ord for HeaderValue {
635 #[inline]
636 fn cmp(&self, other: &Self) -> cmp::Ordering {
637 self.inner.cmp(&other.inner)
638 }
639}
640
641impl PartialEq<str> for HeaderValue {
642 #[inline]
643 fn eq(&self, other: &str) -> bool {
644 self.inner == other.as_bytes()
645 }
646}
647
648impl PartialEq<[u8]> for HeaderValue {
649 #[inline]
650 fn eq(&self, other: &[u8]) -> bool {
651 self.inner == other
652 }
653}
654
655impl PartialOrd<str> for HeaderValue {
656 #[inline]
657 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
658 (*self.inner).partial_cmp(other.as_bytes())
659 }
660}
661
662impl PartialOrd<[u8]> for HeaderValue {
663 #[inline]
664 fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
665 (*self.inner).partial_cmp(other)
666 }
667}
668
669impl PartialEq<HeaderValue> for str {
670 #[inline]
671 fn eq(&self, other: &HeaderValue) -> bool {
672 *other == *self
673 }
674}
675
676impl PartialEq<HeaderValue> for [u8] {
677 #[inline]
678 fn eq(&self, other: &HeaderValue) -> bool {
679 *other == *self
680 }
681}
682
683impl PartialOrd<HeaderValue> for str {
684 #[inline]
685 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
686 self.as_bytes().partial_cmp(other.as_bytes())
687 }
688}
689
690impl PartialOrd<HeaderValue> for [u8] {
691 #[inline]
692 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
693 self.partial_cmp(other.as_bytes())
694 }
695}
696
697impl PartialEq<String> for HeaderValue {
698 #[inline]
699 fn eq(&self, other: &String) -> bool {
700 *self == &other[..]
701 }
702}
703
704impl PartialOrd<String> for HeaderValue {
705 #[inline]
706 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
707 self.inner.partial_cmp(other.as_bytes())
708 }
709}
710
711impl PartialEq<HeaderValue> for String {
712 #[inline]
713 fn eq(&self, other: &HeaderValue) -> bool {
714 *other == *self
715 }
716}
717
718impl PartialOrd<HeaderValue> for String {
719 #[inline]
720 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
721 self.as_bytes().partial_cmp(other.as_bytes())
722 }
723}
724
725impl<'a> PartialEq<HeaderValue> for &'a HeaderValue {
726 #[inline]
727 fn eq(&self, other: &HeaderValue) -> bool {
728 **self == *other
729 }
730}
731
732impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue {
733 #[inline]
734 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
735 (**self).partial_cmp(other)
736 }
737}
738
739impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
740where
741 HeaderValue: PartialEq<T>,
742{
743 #[inline]
744 fn eq(&self, other: &&'a T) -> bool {
745 *self == **other
746 }
747}
748
749impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
750where
751 HeaderValue: PartialOrd<T>,
752{
753 #[inline]
754 fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
755 self.partial_cmp(*other)
756 }
757}
758
759impl<'a> PartialEq<HeaderValue> for &'a str {
760 #[inline]
761 fn eq(&self, other: &HeaderValue) -> bool {
762 *other == *self
763 }
764}
765
766impl<'a> PartialOrd<HeaderValue> for &'a str {
767 #[inline]
768 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
769 self.as_bytes().partial_cmp(other.as_bytes())
770 }
771}
772
773#[test]
774fn test_try_from() {
775 HeaderValue::try_from(vec![127]).unwrap_err();
776}
777
778#[test]
779fn test_debug() {
780 let cases = &[
781 ("hello", "\"hello\""),
782 ("hello \"world\"", "\"hello \\\"world\\\"\""),
783 ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
784 ];
785
786 for &(value, expected) in cases {
787 let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
788 let actual = format!("{:?}", val);
789 assert_eq!(expected, actual);
790 }
791
792 let mut sensitive = HeaderValue::from_static("password");
793 sensitive.set_sensitive(true);
794 assert_eq!("Sensitive", format!("{:?}", sensitive));
795}
796