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