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