ntex_http/
value.rs

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