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