Skip to main content

ntex_http/
value.rs

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