http/header/
value.rs

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}