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. Unlike the
186    /// `http` crate, which accepts only visible ASCII, non-ASCII UTF-8 such as
187    /// `café` succeeds. Check [`str::is_ascii`] on the result if a value must be
188    /// ASCII.
189    ///
190    /// # Examples
191    ///
192    /// ```
193    /// # use ntex_http::header::HeaderValue;
194    /// let val = HeaderValue::from_static("hello");
195    /// assert_eq!(val.to_str().unwrap(), "hello");
196    ///
197    /// let val = HeaderValue::from_bytes(b"caf\xe9").unwrap();
198    /// assert!(val.to_str().is_err());
199    /// ```
200    pub fn to_str(&self) -> Result<&str, ToStrError> {
201        simdutf8::basic::from_utf8(self.as_ref()).map_err(|_| ToStrError { _priv: () })
202    }
203
204    /// Returns the length of `self`.
205    ///
206    /// This length is in bytes.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # use ntex_http::header::HeaderValue;
212    /// let val = HeaderValue::from_static("hello");
213    /// assert_eq!(val.len(), 5);
214    /// ```
215    #[inline]
216    pub fn len(&self) -> usize {
217        self.as_ref().len()
218    }
219
220    /// Returns `true` if the header value is empty.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// # use ntex_http::header::HeaderValue;
226    /// let val = HeaderValue::from_static("");
227    /// assert!(val.is_empty());
228    ///
229    /// let val = HeaderValue::from_static("hello");
230    /// assert!(!val.is_empty());
231    /// ```
232    #[inline]
233    pub fn is_empty(&self) -> bool {
234        self.len() == 0
235    }
236
237    /// Returns the header value as a byte slice.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// # use ntex_http::header::HeaderValue;
243    /// let val = HeaderValue::from_static("hello");
244    /// assert_eq!(val.as_bytes(), b"hello");
245    /// ```
246    #[inline]
247    pub fn as_bytes(&self) -> &[u8] {
248        self.as_ref()
249    }
250
251    /// Returns the underlying shared byte buffer.
252    ///
253    /// # Examples
254    ///
255    /// ```
256    /// # use ntex_http::header::HeaderValue;
257    /// # use ntex_bytes::Bytes;
258    /// let val = HeaderValue::from_static("hello");
259    /// assert_eq!(val.as_shared(), &Bytes::from_static(b"hello"));
260    /// ```
261    #[inline]
262    pub fn as_shared(&self) -> &Bytes {
263        &self.inner
264    }
265
266    /// Marks whether the header value contains sensitive information.
267    ///
268    /// # Examples
269    ///
270    /// ```
271    /// # use ntex_http::header::HeaderValue;
272    /// let mut val = HeaderValue::from_static("my secret");
273    ///
274    /// val.set_sensitive(true);
275    /// assert!(val.is_sensitive());
276    ///
277    /// val.set_sensitive(false);
278    /// assert!(!val.is_sensitive());
279    /// ```
280    #[inline]
281    pub fn set_sensitive(&mut self, val: bool) {
282        self.is_sensitive = val;
283    }
284
285    /// Returns `true` if the value represents sensitive data.
286    ///
287    /// Sensitive data could represent passwords or other data that should not
288    /// be stored on disk or in memory. By marking header values as sensitive,
289    /// components using this crate can be instructed to treat them with special
290    /// care for security reasons. For example, caches can avoid storing
291    /// sensitive values, and HPACK encoders used by HTTP/2.0 implementations
292    /// can choose not to compress them.
293    ///
294    /// Additionally, sensitive values will be masked by the `Debug`
295    /// implementation of `HeaderValue`.
296    ///
297    /// Note that sensitivity is not factored into equality or ordering.
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// # use ntex_http::header::HeaderValue;
303    /// let mut val = HeaderValue::from_static("my secret");
304    ///
305    /// val.set_sensitive(true);
306    /// assert!(val.is_sensitive());
307    ///
308    /// val.set_sensitive(false);
309    /// assert!(!val.is_sensitive());
310    /// ```
311    #[inline]
312    pub fn is_sensitive(&self) -> bool {
313        self.is_sensitive
314    }
315}
316
317impl AsRef<[u8]> for HeaderValue {
318    #[inline]
319    fn as_ref(&self) -> &[u8] {
320        self.inner.as_ref()
321    }
322}
323
324impl fmt::Debug for HeaderValue {
325    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326        if self.is_sensitive {
327            f.write_str("Sensitive")
328        } else {
329            f.write_str("\"")?;
330            let mut from = 0;
331            let bytes = self.as_bytes();
332            for (i, &b) in bytes.iter().enumerate() {
333                if !is_visible_ascii(b) || b == b'"' {
334                    if from != i {
335                        f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
336                    }
337                    if b == b'"' {
338                        f.write_str("\\\"")?;
339                    } else {
340                        write!(f, "\\x{b:x}")?;
341                    }
342                    from = i + 1;
343                }
344            }
345
346            f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
347            f.write_str("\"")
348        }
349    }
350}
351
352impl FromStr for HeaderValue {
353    type Err = InvalidHeaderValue;
354
355    #[inline]
356    fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
357        HeaderValue::from_str(s)
358    }
359}
360
361impl<'a> From<&'a HeaderValue> for HeaderValue {
362    #[inline]
363    fn from(t: &'a HeaderValue) -> Self {
364        t.clone()
365    }
366}
367
368impl From<http::header::HeaderValue> for HeaderValue {
369    #[inline]
370    fn from(t: http::header::HeaderValue) -> Self {
371        let inner = Bytes::copy_from_slice(t.as_ref());
372        HeaderValue {
373            inner,
374            is_sensitive: t.is_sensitive(),
375        }
376    }
377}
378
379impl<'a> From<&'a http::header::HeaderValue> for HeaderValue {
380    #[inline]
381    fn from(t: &'a http::header::HeaderValue) -> Self {
382        let inner = Bytes::copy_from_slice(t.as_ref());
383        HeaderValue {
384            inner,
385            is_sensitive: t.is_sensitive(),
386        }
387    }
388}
389
390impl<'a> TryFrom<&'a str> for HeaderValue {
391    type Error = InvalidHeaderValue;
392
393    #[inline]
394    fn try_from(t: &'a str) -> Result<Self, Self::Error> {
395        t.parse()
396    }
397}
398
399impl<'a> TryFrom<&'a String> for HeaderValue {
400    type Error = InvalidHeaderValue;
401    #[inline]
402    fn try_from(s: &'a String) -> Result<Self, Self::Error> {
403        Self::from_bytes(s.as_bytes())
404    }
405}
406
407impl<'a> TryFrom<&'a ByteString> for HeaderValue {
408    type Error = InvalidHeaderValue;
409    #[inline]
410    fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> {
411        Self::from_shared(s.as_bytes().clone())
412    }
413}
414
415impl<'a> TryFrom<&'a [u8]> for HeaderValue {
416    type Error = InvalidHeaderValue;
417
418    #[inline]
419    fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
420        HeaderValue::from_bytes(t)
421    }
422}
423
424impl TryFrom<String> for HeaderValue {
425    type Error = InvalidHeaderValue;
426
427    #[inline]
428    fn try_from(s: String) -> Result<Self, Self::Error> {
429        HeaderValue::from_shared(s)
430    }
431}
432
433impl TryFrom<ByteString> for HeaderValue {
434    type Error = InvalidHeaderValue;
435    #[inline]
436    fn try_from(s: ByteString) -> Result<Self, Self::Error> {
437        Self::from_shared(s.into_bytes())
438    }
439}
440
441impl TryFrom<Vec<u8>> for HeaderValue {
442    type Error = InvalidHeaderValue;
443
444    #[inline]
445    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
446        HeaderValue::from_shared(vec)
447    }
448}
449
450impl TryFrom<Bytes> for HeaderValue {
451    type Error = InvalidHeaderValue;
452
453    #[inline]
454    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
455        HeaderValue::from_shared(bytes)
456    }
457}
458
459impl TryFrom<BytesMut> for HeaderValue {
460    type Error = InvalidHeaderValue;
461
462    #[inline]
463    fn try_from(bytes: BytesMut) -> Result<Self, Self::Error> {
464        HeaderValue::from_shared(bytes)
465    }
466}
467
468impl From<HeaderValue> for http::header::HeaderValue {
469    #[inline]
470    fn from(t: HeaderValue) -> Self {
471        let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
472        hdr.set_sensitive(t.is_sensitive());
473        hdr
474    }
475}
476
477impl<'a> From<&'a HeaderValue> for http::header::HeaderValue {
478    #[inline]
479    fn from(t: &'a HeaderValue) -> Self {
480        let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
481        hdr.set_sensitive(t.is_sensitive());
482        hdr
483    }
484}
485
486const fn is_visible_ascii(b: u8) -> bool {
487    b >= 32 && b < 127 || b == b'\t'
488}
489
490#[inline]
491fn is_valid(b: u8) -> bool {
492    b >= 32 && b != 127 || b == b'\t'
493}
494
495// ===== Hash / PartialEq / PartialOrd =====
496
497// Must agree with `PartialEq`, which ignores the sensitive flag
498impl hash::Hash for HeaderValue {
499    #[inline]
500    fn hash<H: hash::Hasher>(&self, state: &mut H) {
501        self.inner.hash(state);
502    }
503}
504
505impl PartialEq for HeaderValue {
506    #[inline]
507    fn eq(&self, other: &HeaderValue) -> bool {
508        self.inner == other.inner
509    }
510}
511
512impl PartialOrd for HeaderValue {
513    #[inline]
514    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
515        Some(self.cmp(other))
516    }
517}
518
519impl Ord for HeaderValue {
520    #[inline]
521    fn cmp(&self, other: &Self) -> cmp::Ordering {
522        self.inner.cmp(&other.inner)
523    }
524}
525
526impl PartialEq<str> for HeaderValue {
527    #[inline]
528    fn eq(&self, other: &str) -> bool {
529        self.inner == other.as_bytes()
530    }
531}
532
533impl PartialEq<[u8]> for HeaderValue {
534    #[inline]
535    fn eq(&self, other: &[u8]) -> bool {
536        self.inner == other
537    }
538}
539
540impl PartialOrd<str> for HeaderValue {
541    #[inline]
542    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
543        (*self.inner).partial_cmp(other.as_bytes())
544    }
545}
546
547impl PartialOrd<[u8]> for HeaderValue {
548    #[inline]
549    fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
550        (*self.inner).partial_cmp(other)
551    }
552}
553
554impl PartialEq<HeaderValue> for str {
555    #[inline]
556    fn eq(&self, other: &HeaderValue) -> bool {
557        *other == *self
558    }
559}
560
561impl PartialEq<HeaderValue> for [u8] {
562    #[inline]
563    fn eq(&self, other: &HeaderValue) -> bool {
564        *other == *self
565    }
566}
567
568impl PartialOrd<HeaderValue> for str {
569    #[inline]
570    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
571        self.as_bytes().partial_cmp(other.as_bytes())
572    }
573}
574
575impl PartialOrd<HeaderValue> for [u8] {
576    #[inline]
577    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
578        self.partial_cmp(other.as_bytes())
579    }
580}
581
582impl PartialEq<String> for HeaderValue {
583    #[inline]
584    fn eq(&self, other: &String) -> bool {
585        *self == other[..]
586    }
587}
588
589impl PartialOrd<String> for HeaderValue {
590    #[inline]
591    fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
592        self.inner.partial_cmp(other.as_bytes())
593    }
594}
595
596impl PartialEq<HeaderValue> for String {
597    #[inline]
598    fn eq(&self, other: &HeaderValue) -> bool {
599        *other == *self
600    }
601}
602
603impl PartialOrd<HeaderValue> for String {
604    #[inline]
605    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
606        self.as_bytes().partial_cmp(other.as_bytes())
607    }
608}
609
610impl PartialEq<HeaderValue> for &HeaderValue {
611    #[inline]
612    fn eq(&self, other: &HeaderValue) -> bool {
613        **self == *other
614    }
615}
616
617impl PartialOrd<HeaderValue> for &HeaderValue {
618    #[inline]
619    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
620        (**self).partial_cmp(other)
621    }
622}
623
624impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
625where
626    HeaderValue: PartialEq<T>,
627{
628    #[inline]
629    fn eq(&self, other: &&'a T) -> bool {
630        *self == **other
631    }
632}
633
634impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
635where
636    HeaderValue: PartialOrd<T>,
637{
638    #[inline]
639    fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
640        self.partial_cmp(*other)
641    }
642}
643
644impl PartialEq<HeaderValue> for &str {
645    #[inline]
646    fn eq(&self, other: &HeaderValue) -> bool {
647        *other == *self
648    }
649}
650
651impl PartialOrd<HeaderValue> for &str {
652    #[inline]
653    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
654        self.as_bytes().partial_cmp(other.as_bytes())
655    }
656}
657
658macro_rules! from_integers {
659    ($($name:ident: $t:ident => $max_len:expr),*) => {$(
660        impl From<$t> for HeaderValue {
661            fn from(num: $t) -> HeaderValue {
662                let mut b = itoa::Buffer::new();
663                let inner = Bytes::copy_from_slice(b.format(num).as_ref());
664                HeaderValue {
665                    inner,
666                    is_sensitive: false,
667                }
668            }
669        }
670
671        #[test]
672        fn $name() {
673            let n: $t = 55;
674            let val = HeaderValue::from(n);
675            assert_eq!(val, &n.to_string());
676
677            let n = $t::MAX;
678            let val = HeaderValue::from(n);
679            assert_eq!(val, &n.to_string());
680        }
681    )*};
682}
683
684from_integers! {
685    // integer type => maximum decimal length
686
687    // u8 purposely left off... HeaderValue::from(b'3') could be confusing
688    from_u16: u16 => 5,
689    from_i16: i16 => 6,
690    from_u32: u32 => 10,
691    from_i32: i32 => 11,
692    from_u64: u64 => 20,
693    from_i64: i64 => 20
694}
695
696#[cfg(target_pointer_width = "16")]
697from_integers! {
698    from_usize: usize => 5,
699    from_isize: isize => 6
700}
701
702#[cfg(target_pointer_width = "32")]
703from_integers! {
704    from_usize: usize => 10,
705    from_isize: isize => 11
706}
707
708#[cfg(target_pointer_width = "64")]
709from_integers! {
710    from_usize: usize => 20,
711    from_isize: isize => 20
712}
713
714#[cfg(test)]
715mod tests {
716    use super::*;
717
718    #[test]
719    fn test_hash_ignores_sensitive() {
720        use std::hash::{BuildHasher, RandomState};
721
722        let state = RandomState::new();
723        let hdr = HeaderValue::from_static("secret");
724        let mut sensitive = hdr.clone();
725        sensitive.set_sensitive(true);
726        assert_eq!(hdr, sensitive);
727        assert_eq!(state.hash_one(&hdr), state.hash_one(&sensitive));
728    }
729
730    #[test]
731    #[allow(clippy::op_ref, clippy::cmp_owned)]
732    fn test_basics() {
733        assert!(HeaderValue::from_str("").unwrap().is_empty());
734
735        let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
736        let hdr2 = HeaderValue::from(&hdr);
737        assert_eq!(hdr, hdr2);
738
739        let hdr3 = HeaderValue::from_shared(Bytes::from_static(b"upgrade")).unwrap();
740        assert_eq!(hdr, hdr3);
741
742        let hdr = http::header::HeaderValue::from_bytes(b"upgrade").unwrap();
743        let hdr2 = HeaderValue::from(&hdr);
744        assert_eq!(hdr2.as_bytes(), b"upgrade");
745        assert_eq!(hdr2.as_shared(), &Bytes::from_static(b"upgrade"));
746        let hdr2 = HeaderValue::from(hdr);
747        assert_eq!(hdr2.as_bytes(), b"upgrade");
748
749        let hdr = HeaderValue::try_from("upgrade".to_string()).unwrap();
750        assert_eq!(hdr.as_bytes(), b"upgrade");
751        let hdr = HeaderValue::try_from(&("upgrade".to_string())).unwrap();
752        assert_eq!(hdr.as_bytes(), b"upgrade");
753        let hdr = HeaderValue::try_from(ByteString::from("upgrade")).unwrap();
754        assert_eq!(hdr.as_bytes(), b"upgrade");
755        let hdr = HeaderValue::try_from(&ByteString::from("upgrade")).unwrap();
756        assert_eq!(hdr.as_bytes(), b"upgrade");
757        let hdr2 = HeaderValue::try_from(&ByteString::from("upgrade2")).unwrap();
758
759        assert_eq!(hdr, hdr);
760        assert_eq!(hdr, &hdr);
761        assert_eq!(hdr, "upgrade");
762        assert_eq!(hdr, "upgrade".to_string());
763        assert_eq!(hdr, b"upgrade".as_ref());
764        assert_eq!("upgrade", hdr);
765        assert_eq!("upgrade", &hdr);
766        assert_eq!("upgrade".to_string(), hdr);
767        assert_eq!(b"upgrade".as_ref(), &hdr);
768        assert!(hdr < hdr2);
769        assert!(hdr < &hdr2);
770        assert!(&hdr < &hdr2);
771        assert!(hdr2 > hdr);
772        assert!(&hdr2 > hdr);
773        assert!(&hdr < "upgrade2");
774        assert!(hdr < "upgrade2");
775        assert!(hdr < "upgrade2".to_string());
776        assert!(hdr < &b"upgrade2"[..]);
777        assert!(hdr < b"upgrade2"[..]);
778        assert_ne!(hdr, &b"upgrade2"[..]);
779        assert_ne!(hdr, b"upgrade2"[..]);
780        assert!("upgrade2" > hdr);
781        assert!("upgrade2" > &hdr);
782        assert!("upgrade2".to_string() > hdr);
783        assert!(b"upgrade2"[..] > hdr);
784        assert_ne!("upgrade2"[..], hdr);
785    }
786
787    #[test]
788    fn test_try_from() {
789        HeaderValue::try_from(vec![127]).unwrap_err();
790
791        let hdr = HeaderValue::try_from(Bytes::from_static(b"upgrade")).unwrap();
792        assert_eq!(hdr, "upgrade");
793        HeaderValue::try_from(Bytes::from_static(b"\n")).unwrap_err();
794        let hdr = HeaderValue::try_from(BytesMut::copy_from_slice(b"upgrade")).unwrap();
795        assert_eq!(hdr, "upgrade");
796        HeaderValue::try_from(BytesMut::copy_from_slice(b"\n")).unwrap_err();
797    }
798
799    #[test]
800    fn it_converts_using_try_from() {
801        assert!(HeaderValue::from_bytes(b"upgrade").is_ok());
802    }
803
804    #[test]
805    fn into_http_value() {
806        let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
807        let _ = http::header::HeaderValue::from(&hdr);
808        let _ = http::header::HeaderValue::from(hdr);
809    }
810
811    #[test]
812    fn test_fmt() {
813        let cases = &[
814            ("hello", "\"hello\""),
815            ("hello \"world\"", "\"hello \\\"world\\\"\""),
816            ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
817        ];
818
819        for &(value, expected) in cases {
820            let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
821            let actual = format!("{val:?}");
822            assert_eq!(expected, actual);
823        }
824
825        let mut sensitive = HeaderValue::from_static("password");
826        sensitive.set_sensitive(true);
827        assert_eq!("Sensitive", format!("{sensitive:?}"));
828
829        let s = format!("{}", InvalidHeaderValue { _priv: {} });
830        assert_eq!(s, "Invalid HTTP header value");
831
832        let s = format!("{}", ToStrError { _priv: {} });
833        assert_eq!(s, "failed to convert header to a str");
834    }
835
836    #[test]
837    #[should_panic(expected = "invalid header value")]
838    fn from_static_invalid() {
839        let src: &'static str = std::hint::black_box("bad\nvalue");
840        let _ = HeaderValue::from_static(src);
841    }
842}