http/header/
value.rs

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