http/header/
value.rs

1use bytes::{Bytes, BytesMut};
2
3use std::convert::TryFrom;
4use std::error::Error;
5use std::str::FromStr;
6use std::{cmp, fmt, mem, str};
7
8use crate::header::name::HeaderName;
9
10/// Represents an HTTP header field value.
11///
12/// In practice, HTTP header field values are usually valid ASCII. However, the
13/// HTTP spec allows for a header value to contain opaque bytes as well. In this
14/// case, the header field value is not able to be represented as a string.
15///
16/// To handle this, the `HeaderValue` is useable as a type and can be compared
17/// with strings and implements `Debug`. A `to_str` fn is provided that returns
18/// an `Err` if the header value contains non visible ascii characters.
19#[derive(Clone, Hash)]
20pub struct HeaderValue {
21    inner: Bytes,
22    is_sensitive: bool,
23}
24
25/// A possible error when converting a `HeaderValue` from a string or byte
26/// slice.
27pub struct InvalidHeaderValue {
28    _priv: (),
29}
30
31/// A possible error when converting a `HeaderValue` to a string representation.
32///
33/// Header field values may contain opaque bytes, in which case it is not
34/// possible to represent the value as a string.
35#[derive(Debug)]
36pub struct ToStrError {
37    _priv: (),
38}
39
40impl HeaderValue {
41    /// Convert a static string to a `HeaderValue`.
42    ///
43    /// This function will not perform any copying, however the string is
44    /// checked to ensure that no invalid characters are present. Only visible
45    /// ASCII characters (32-127) are permitted.
46    ///
47    /// # Panics
48    ///
49    /// This function panics if the argument contains invalid header value
50    /// characters.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// # use http::header::HeaderValue;
56    /// let val = HeaderValue::from_static("hello");
57    /// assert_eq!(val, "hello");
58    /// ```
59    #[inline]
60    pub fn from_static(src: &'static str) -> HeaderValue {
61        let bytes = src.as_bytes();
62        for &b in bytes {
63            if !is_visible_ascii(b) {
64                panic!("invalid header value");
65            }
66        }
67
68        HeaderValue {
69            inner: Bytes::from_static(bytes),
70            is_sensitive: false,
71        }
72    }
73
74    /// Attempt to convert a string to a `HeaderValue`.
75    ///
76    /// If the argument contains invalid header value characters, an error is
77    /// returned. Only visible ASCII characters (32-127) are permitted. Use
78    /// `from_bytes` to create a `HeaderValue` that includes opaque octets
79    /// (128-255).
80    ///
81    /// This function is intended to be replaced in the future by a `TryFrom`
82    /// implementation once the trait is stabilized in std.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// # use http::header::HeaderValue;
88    /// let val = HeaderValue::from_str("hello").unwrap();
89    /// assert_eq!(val, "hello");
90    /// ```
91    ///
92    /// An invalid value
93    ///
94    /// ```
95    /// # use http::header::HeaderValue;
96    /// let val = HeaderValue::from_str("\n");
97    /// assert!(val.is_err());
98    /// ```
99    #[inline]
100    pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
101        HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
102    }
103
104    /// Converts a HeaderName into a HeaderValue
105    ///
106    /// Since every valid HeaderName is a valid HeaderValue this is done infallibly.
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// # use http::header::{HeaderValue, HeaderName};
112    /// # use http::header::ACCEPT;
113    /// let val = HeaderValue::from_name(ACCEPT);
114    /// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap());
115    /// ```
116    #[inline]
117    pub fn from_name(name: HeaderName) -> HeaderValue {
118        name.into()
119    }
120
121    /// Attempt to convert a byte slice to a `HeaderValue`.
122    ///
123    /// If the argument contains invalid header value bytes, an error is
124    /// returned. Only byte values between 32 and 255 (inclusive) are permitted,
125    /// excluding byte 127 (DEL).
126    ///
127    /// This function is intended to be replaced in the future by a `TryFrom`
128    /// implementation once the trait is stabilized in std.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// # use http::header::HeaderValue;
134    /// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
135    /// assert_eq!(val, &b"hello\xfa"[..]);
136    /// ```
137    ///
138    /// An invalid value
139    ///
140    /// ```
141    /// # use http::header::HeaderValue;
142    /// let val = HeaderValue::from_bytes(b"\n");
143    /// assert!(val.is_err());
144    /// ```
145    #[inline]
146    pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
147        HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
148    }
149
150    /// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
151    ///
152    /// This will try to prevent a copy if the type passed is the type used
153    /// internally, and will copy the data if it is not.
154    pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
155    where
156        T: AsRef<[u8]> + 'static,
157    {
158        if_downcast_into!(T, Bytes, src, {
159            return HeaderValue::from_shared(src);
160        });
161
162        HeaderValue::from_bytes(src.as_ref())
163    }
164
165    /// Convert a `Bytes` directly into a `HeaderValue` without validating.
166    ///
167    /// This function does NOT validate that illegal bytes are not contained
168    /// within the buffer.
169    pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue
170    where
171        T: AsRef<[u8]> + 'static,
172    {
173        if cfg!(debug_assertions) {
174            match HeaderValue::from_maybe_shared(src) {
175                Ok(val) => val,
176                Err(_err) => {
177                    panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes");
178                }
179            }
180        } else {
181
182            if_downcast_into!(T, Bytes, src, {
183                return HeaderValue {
184                    inner: src,
185                    is_sensitive: false,
186                };
187            });
188
189            let src = Bytes::copy_from_slice(src.as_ref());
190            HeaderValue {
191                inner: src,
192                is_sensitive: false,
193            }
194        }
195    }
196
197    fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
198        HeaderValue::try_from_generic(src, std::convert::identity)
199    }
200
201    fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(src: T, into: F) -> Result<HeaderValue, InvalidHeaderValue> {
202        for &b in src.as_ref() {
203            if !is_valid(b) {
204                return Err(InvalidHeaderValue { _priv: () });
205            }
206        }
207        Ok(HeaderValue {
208            inner: into(src),
209            is_sensitive: false,
210        })
211    }
212
213    /// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
214    /// chars.
215    ///
216    /// This function will perform a scan of the header value, checking all the
217    /// characters.
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// # use http::header::HeaderValue;
223    /// let val = HeaderValue::from_static("hello");
224    /// assert_eq!(val.to_str().unwrap(), "hello");
225    /// ```
226    pub fn to_str(&self) -> Result<&str, ToStrError> {
227        let bytes = self.as_ref();
228
229        for &b in bytes {
230            if !is_visible_ascii(b) {
231                return Err(ToStrError { _priv: () });
232            }
233        }
234
235        unsafe { Ok(str::from_utf8_unchecked(bytes)) }
236    }
237
238    /// Returns the length of `self`.
239    ///
240    /// This length is in bytes.
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// # use http::header::HeaderValue;
246    /// let val = HeaderValue::from_static("hello");
247    /// assert_eq!(val.len(), 5);
248    /// ```
249    #[inline]
250    pub fn len(&self) -> usize {
251        self.as_ref().len()
252    }
253
254    /// Returns true if the `HeaderValue` has a length of zero bytes.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// # use http::header::HeaderValue;
260    /// let val = HeaderValue::from_static("");
261    /// assert!(val.is_empty());
262    ///
263    /// let val = HeaderValue::from_static("hello");
264    /// assert!(!val.is_empty());
265    /// ```
266    #[inline]
267    pub fn is_empty(&self) -> bool {
268        self.len() == 0
269    }
270
271    /// Converts a `HeaderValue` to a byte slice.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// # use http::header::HeaderValue;
277    /// let val = HeaderValue::from_static("hello");
278    /// assert_eq!(val.as_bytes(), b"hello");
279    /// ```
280    #[inline]
281    pub fn as_bytes(&self) -> &[u8] {
282        self.as_ref()
283    }
284
285    /// Mark that the header value represents sensitive information.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// # use http::header::HeaderValue;
291    /// let mut val = HeaderValue::from_static("my secret");
292    ///
293    /// val.set_sensitive(true);
294    /// assert!(val.is_sensitive());
295    ///
296    /// val.set_sensitive(false);
297    /// assert!(!val.is_sensitive());
298    /// ```
299    #[inline]
300    pub fn set_sensitive(&mut self, val: bool) {
301        self.is_sensitive = val;
302    }
303
304    /// Returns `true` if the value represents sensitive data.
305    ///
306    /// Sensitive data could represent passwords or other data that should not
307    /// be stored on disk or in memory. By marking header values as sensitive,
308    /// components using this crate can be instructed to treat them with special
309    /// care for security reasons. For example, caches can avoid storing
310    /// sensitive values, and HPACK encoders used by HTTP/2.0 implementations
311    /// can choose not to compress them.
312    ///
313    /// Additionally, sensitive values will be masked by the `Debug`
314    /// implementation of `HeaderValue`.
315    ///
316    /// Note that sensitivity is not factored into equality or ordering.
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// # use http::header::HeaderValue;
322    /// let mut val = HeaderValue::from_static("my secret");
323    ///
324    /// val.set_sensitive(true);
325    /// assert!(val.is_sensitive());
326    ///
327    /// val.set_sensitive(false);
328    /// assert!(!val.is_sensitive());
329    /// ```
330    #[inline]
331    pub fn is_sensitive(&self) -> bool {
332        self.is_sensitive
333    }
334}
335
336impl AsRef<[u8]> for HeaderValue {
337    #[inline]
338    fn as_ref(&self) -> &[u8] {
339        self.inner.as_ref()
340    }
341}
342
343impl fmt::Debug for HeaderValue {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        if self.is_sensitive {
346            f.write_str("Sensitive")
347        } else {
348            f.write_str("\"")?;
349            let mut from = 0;
350            let bytes = self.as_bytes();
351            for (i, &b) in bytes.iter().enumerate() {
352                if !is_visible_ascii(b) || b == b'"' {
353                    if from != i {
354                        f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
355                    }
356                    if b == b'"' {
357                        f.write_str("\\\"")?;
358                    } else {
359                        write!(f, "\\x{:x}", b)?;
360                    }
361                    from = i + 1;
362                }
363            }
364
365            f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
366            f.write_str("\"")
367        }
368    }
369}
370
371impl From<HeaderName> for HeaderValue {
372    #[inline]
373    fn from(h: HeaderName) -> HeaderValue {
374        HeaderValue {
375            inner: h.into_bytes(),
376            is_sensitive: false,
377        }
378    }
379}
380
381macro_rules! from_integers {
382    ($($name:ident: $t:ident => $max_len:expr),*) => {$(
383        impl From<$t> for HeaderValue {
384            fn from(num: $t) -> HeaderValue {
385                let mut buf = if mem::size_of::<BytesMut>() - 1 < $max_len {
386                    // On 32bit platforms, BytesMut max inline size
387                    // is 15 bytes, but the $max_len could be bigger.
388                    //
389                    // The likelihood of the number *actually* being
390                    // that big is very small, so only allocate
391                    // if the number needs that space.
392                    //
393                    // The largest decimal number in 15 digits:
394                    // It wold be 10.pow(15) - 1, but this is a constant
395                    // version.
396                    if num as u64 > 999_999_999_999_999_999 {
397                        BytesMut::with_capacity($max_len)
398                    } else {
399                        // fits inline...
400                        BytesMut::new()
401                    }
402                } else {
403                    // full value fits inline, so don't allocate!
404                    BytesMut::new()
405                };
406                let _ = ::itoa::fmt(&mut buf, 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<'a> From<&'a HeaderValue> for HeaderValue {
494    #[inline]
495    fn from(t: &'a HeaderValue) -> Self {
496        t.clone()
497    }
498}
499
500impl<'a> TryFrom<&'a str> for HeaderValue {
501    type Error = InvalidHeaderValue;
502
503    #[inline]
504    fn try_from(t: &'a str) -> Result<Self, Self::Error> {
505        t.parse()
506    }
507}
508
509impl<'a> TryFrom<&'a String> for HeaderValue {
510    type Error = InvalidHeaderValue;
511    #[inline]
512    fn try_from(s: &'a String) -> Result<Self, Self::Error> {
513        Self::from_bytes(s.as_bytes())
514    }
515}
516
517impl<'a> TryFrom<&'a [u8]> for HeaderValue {
518    type Error = InvalidHeaderValue;
519
520    #[inline]
521    fn try_from(t: &'a [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
558fn 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 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        self.inner.partial_cmp(&other.inner)
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}