Skip to main content

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