http_with_url/header/
value.rs

1use bytes::Bytes;
2
3use std::{cmp, fmt, str};
4use std::error::Error;
5use std::str::FromStr;
6
7use ::convert::HttpTryFrom;
8use 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.
27#[derive(Debug)]
28pub struct InvalidHeaderValue {
29    _priv: (),
30}
31
32/// A possible error when converting a `HeaderValue` from a string or byte
33/// slice.
34#[derive(Debug)]
35pub struct InvalidHeaderValueBytes(InvalidHeaderValue);
36
37/// A possible error when converting a `HeaderValue` to a string representation.
38///
39/// Header field values may contain opaque bytes, in which case it is not
40/// possible to represent the value as a string.
41#[derive(Debug)]
42pub struct ToStrError {
43    _priv: (),
44}
45
46impl HeaderValue {
47    /// Convert a static string to a `HeaderValue`.
48    ///
49    /// This function will not perform any copying, however the string is
50    /// checked to ensure that no invalid characters are present. Only visible
51    /// ASCII characters (32-127) are permitted.
52    ///
53    /// # Panics
54    ///
55    /// This function panics if the argument contains invalid header value
56    /// characters.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// # use http::header::HeaderValue;
62    /// let val = HeaderValue::from_static("hello");
63    /// assert_eq!(val, "hello");
64    /// ```
65    #[inline]
66    pub fn from_static(src: &'static str) -> HeaderValue {
67        let bytes = src.as_bytes();
68        for &b in bytes {
69            if !is_visible_ascii(b) {
70                panic!("invalid header value");
71            }
72        }
73
74        HeaderValue {
75            inner: Bytes::from_static(bytes),
76            is_sensitive: false,
77        }
78    }
79
80    /// Attempt to convert a string to a `HeaderValue`.
81    ///
82    /// If the argument contains invalid header value characters, an error is
83    /// returned. Only visible ASCII characters (32-127) are permitted. Use
84    /// `from_bytes` to create a `HeaderValue` that includes opaque octets
85    /// (128-255).
86    ///
87    /// This function is intended to be replaced in the future by a `TryFrom`
88    /// implementation once the trait is stabilized in std.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use http::header::HeaderValue;
94    /// let val = HeaderValue::from_str("hello").unwrap();
95    /// assert_eq!(val, "hello");
96    /// ```
97    ///
98    /// An invalid value
99    ///
100    /// ```
101    /// # use http::header::HeaderValue;
102    /// let val = HeaderValue::from_str("\n");
103    /// assert!(val.is_err());
104    /// ```
105    #[inline]
106    pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
107        HeaderValue::try_from(src)
108    }
109
110    /// Converts a HeaderName into a HeaderValue
111    ///
112    /// Since every valid HeaderName is a valid HeaderValue this is done infallibly.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// # use http::header::{HeaderValue, HeaderName};
118    /// # use http::header::ACCEPT;
119    /// let val = HeaderValue::from_name(ACCEPT);
120    /// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap());
121    /// ```
122    #[inline]
123    pub fn from_name(name: HeaderName) -> HeaderValue {
124        name.into()
125    }
126
127    /// Attempt to convert a byte slice to a `HeaderValue`.
128    ///
129    /// If the argument contains invalid header value bytes, an error is
130    /// returned. Only byte values between 32 and 255 (inclusive) are permitted,
131    /// excluding byte 127 (DEL).
132    ///
133    /// This function is intended to be replaced in the future by a `TryFrom`
134    /// implementation once the trait is stabilized in std.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// # use http::header::HeaderValue;
140    /// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
141    /// assert_eq!(val, &b"hello\xfa"[..]);
142    /// ```
143    ///
144    /// An invalid value
145    ///
146    /// ```
147    /// # use http::header::HeaderValue;
148    /// let val = HeaderValue::from_bytes(b"\n");
149    /// assert!(val.is_err());
150    /// ```
151    #[inline]
152    pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
153        HeaderValue::try_from(src)
154    }
155
156    /// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
157    ///
158    /// If the argument contains invalid header value bytes, an error is
159    /// returned. Only byte values between 32 and 255 (inclusive) are permitted,
160    /// excluding byte 127 (DEL).
161    ///
162    /// This function is intended to be replaced in the future by a `TryFrom`
163    /// implementation once the trait is stabilized in std.
164    #[inline]
165    pub fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValueBytes> {
166        HeaderValue::try_from(src).map_err(InvalidHeaderValueBytes)
167    }
168
169    /// Convert a `Bytes` directly into a `HeaderValue` without validating.
170    ///
171    /// This function does NOT validate that illegal bytes are not contained
172    /// within the buffer.
173    #[inline]
174    pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue {
175        HeaderValue {
176            inner: src,
177            is_sensitive: false,
178        }
179    }
180
181    fn try_from<T: AsRef<[u8]> + Into<Bytes>>(src: T) -> Result<HeaderValue, InvalidHeaderValue> {
182        for &b in src.as_ref() {
183            if !is_valid(b) {
184                return Err(InvalidHeaderValue {
185                    _priv: (),
186                });
187            }
188        }
189        Ok(HeaderValue {
190            inner: src.into(),
191            is_sensitive: false,
192        })
193    }
194
195    /// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
196    /// chars.
197    ///
198    /// This function will perform a scan of the header value, checking all the
199    /// characters.
200    ///
201    /// # Examples
202    ///
203    /// ```
204    /// # use http::header::HeaderValue;
205    /// let val = HeaderValue::from_static("hello");
206    /// assert_eq!(val.to_str().unwrap(), "hello");
207    /// ```
208    pub fn to_str(&self) -> Result<&str, ToStrError> {
209        let bytes = self.as_ref();
210
211        for &b in bytes {
212            if !is_visible_ascii(b) {
213                return Err(ToStrError { _priv: () });
214            }
215        }
216
217        unsafe { Ok(str::from_utf8_unchecked(bytes)) }
218    }
219
220    /// Returns the length of `self`.
221    ///
222    /// This length is in bytes.
223    ///
224    /// # Examples
225    ///
226    /// ```
227    /// # use http::header::HeaderValue;
228    /// let val = HeaderValue::from_static("hello");
229    /// assert_eq!(val.len(), 5);
230    /// ```
231    #[inline]
232    pub fn len(&self) -> usize {
233        self.as_ref().len()
234    }
235
236    /// Returns true if the `HeaderValue` has a length of zero bytes.
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// # use http::header::HeaderValue;
242    /// let val = HeaderValue::from_static("");
243    /// assert!(val.is_empty());
244    ///
245    /// let val = HeaderValue::from_static("hello");
246    /// assert!(!val.is_empty());
247    /// ```
248    #[inline]
249    pub fn is_empty(&self) -> bool {
250        self.len() == 0
251    }
252
253    /// Converts a `HeaderValue` to a byte slice.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use http::header::HeaderValue;
259    /// let val = HeaderValue::from_static("hello");
260    /// assert_eq!(val.as_bytes(), b"hello");
261    /// ```
262    #[inline]
263    pub fn as_bytes(&self) -> &[u8] {
264        self.as_ref()
265    }
266
267    /// Mark that the header value represents sensitive information.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// # use http::header::HeaderValue;
273    /// let mut val = HeaderValue::from_static("my secret");
274    ///
275    /// val.set_sensitive(true);
276    /// assert!(val.is_sensitive());
277    ///
278    /// val.set_sensitive(false);
279    /// assert!(!val.is_sensitive());
280    /// ```
281    #[inline]
282    pub fn set_sensitive(&mut self, val: bool) {
283        self.is_sensitive = val;
284    }
285
286    /// Returns `true` if the value represents sensitive data.
287    ///
288    /// Sensitive data could represent passwords or other data that should not
289    /// be stored on disk or in memory. This setting can be used by components
290    /// like caches to avoid storing the value. HPACK encoders must set the
291    /// header field to never index when `is_sensitive` returns true.
292    ///
293    /// Note that sensitivity is not factored into equality or ordering.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// # use http::header::HeaderValue;
299    /// let mut val = HeaderValue::from_static("my secret");
300    ///
301    /// val.set_sensitive(true);
302    /// assert!(val.is_sensitive());
303    ///
304    /// val.set_sensitive(false);
305    /// assert!(!val.is_sensitive());
306    /// ```
307    #[inline]
308    pub fn is_sensitive(&self) -> bool {
309        self.is_sensitive
310    }
311}
312
313impl AsRef<[u8]> for HeaderValue {
314    #[inline]
315    fn as_ref(&self) -> &[u8] {
316        self.inner.as_ref()
317    }
318}
319
320impl fmt::Debug for HeaderValue {
321    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322        if self.is_sensitive {
323            f.write_str("Sensitive")
324        } else {
325            f.write_str("\"")?;
326            let mut from = 0;
327            let bytes = self.as_bytes();
328            for (i, &b) in bytes.iter().enumerate() {
329                if !is_visible_ascii(b) || b == b'"' {
330                    if from != i {
331                        f.write_str(unsafe {
332                            str::from_utf8_unchecked(&bytes[from..i])
333                        })?;
334                    }
335                    if b == b'"' {
336                        f.write_str("\\\"")?;
337                    } else {
338                        write!(f, "\\x{:x}", b)?;
339                    }
340                    from = i + 1;
341                }
342            }
343
344            f.write_str(unsafe {
345                str::from_utf8_unchecked(&bytes[from..])
346            })?;
347            f.write_str("\"")
348        }
349    }
350}
351
352impl From<HeaderName> for HeaderValue {
353    #[inline]
354    fn from(h: HeaderName) -> HeaderValue {
355        HeaderValue {
356            inner: h.into(),
357            is_sensitive: false,
358        }
359    }
360}
361
362#[cfg(test)]
363mod from_header_name_tests {
364    use super::*;
365    use header::map::HeaderMap;
366    use header::name;
367
368    #[test]
369    fn it_can_insert_header_name_as_header_value() {
370        let mut map = HeaderMap::new();
371        map.insert(name::UPGRADE, name::SEC_WEBSOCKET_PROTOCOL.into());
372        map.insert(name::ACCEPT, name::HeaderName::from_bytes(b"hello-world").unwrap().into());
373
374        assert_eq!(
375            map.get(name::UPGRADE).unwrap(),
376            HeaderValue::from_bytes(b"sec-websocket-protocol").unwrap()
377        );
378
379        assert_eq!(
380            map.get(name::ACCEPT).unwrap(),
381            HeaderValue::from_bytes(b"hello-world").unwrap()
382        );
383    }
384}
385
386impl FromStr for HeaderValue {
387    type Err = InvalidHeaderValue;
388
389    #[inline]
390    fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
391        HeaderValue::from_str(s)
392    }
393}
394
395impl From<HeaderValue> for Bytes {
396    #[inline]
397    fn from(value: HeaderValue) -> Bytes {
398        value.inner
399    }
400}
401
402impl<'a> HttpTryFrom<&'a str> for HeaderValue {
403    type Error = InvalidHeaderValue;
404
405    #[inline]
406    fn try_from(t: &'a str) -> Result<Self, Self::Error> {
407        t.parse()
408    }
409}
410
411impl<'a> HttpTryFrom<&'a [u8]> for HeaderValue {
412    type Error = InvalidHeaderValue;
413
414    #[inline]
415    fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
416        HeaderValue::from_bytes(t)
417    }
418}
419
420impl HttpTryFrom<Bytes> for HeaderValue {
421    type Error = InvalidHeaderValueBytes;
422
423    #[inline]
424    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
425        HeaderValue::from_shared(bytes)
426    }
427}
428
429impl HttpTryFrom<HeaderName> for HeaderValue {
430    type Error = InvalidHeaderValue;
431
432    #[inline]
433    fn try_from(name: HeaderName) -> Result<Self, Self::Error> {
434        // Infallable as header names have the same validations
435        Ok(name.into())
436    }
437}
438
439#[cfg(test)]
440mod try_from_header_name_tests {
441    use super::*;
442    use header::name;
443
444    #[test]
445    fn it_converts_using_try_from() {
446        assert_eq!(
447            HeaderValue::try_from(name::UPGRADE).unwrap(),
448            HeaderValue::from_bytes(b"upgrade").unwrap()
449        );
450    }
451}
452
453fn is_visible_ascii(b: u8) -> bool {
454    b >= 32 && b < 127
455}
456
457#[inline]
458fn is_valid(b: u8) -> bool {
459    b >= 32 && b != 127
460}
461
462impl fmt::Display for InvalidHeaderValue {
463    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464        self.description().fmt(f)
465    }
466}
467
468impl Error for InvalidHeaderValue {
469    fn description(&self) -> &str {
470        "failed to parse header value"
471    }
472}
473
474impl fmt::Display for InvalidHeaderValueBytes {
475    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476        self.0.fmt(f)
477    }
478}
479
480impl Error for InvalidHeaderValueBytes {
481    fn description(&self) -> &str {
482        self.0.description()
483    }
484}
485
486impl fmt::Display for ToStrError {
487    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488        self.description().fmt(f)
489    }
490}
491
492impl Error for ToStrError {
493    fn description(&self) -> &str {
494        "failed to convert header to a str"
495    }
496}
497
498// ===== PartialEq / PartialOrd =====
499
500impl PartialEq for HeaderValue {
501    #[inline]
502    fn eq(&self, other: &HeaderValue) -> bool {
503        self.inner == other.inner
504    }
505}
506
507impl Eq for HeaderValue {}
508
509impl PartialOrd for HeaderValue {
510    #[inline]
511    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
512        self.inner.partial_cmp(&other.inner)
513    }
514}
515
516impl Ord for HeaderValue {
517    #[inline]
518    fn cmp(&self, other: &Self) -> cmp::Ordering {
519        self.inner.cmp(&other.inner)
520    }
521}
522
523impl PartialEq<str> for HeaderValue {
524    #[inline]
525    fn eq(&self, other: &str) -> bool {
526        self.inner == other.as_bytes()
527    }
528}
529
530impl PartialEq<[u8]> for HeaderValue {
531    #[inline]
532    fn eq(&self, other: &[u8]) -> bool {
533        self.inner == other
534    }
535}
536
537impl PartialOrd<str> for HeaderValue {
538    #[inline]
539    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
540        (*self.inner).partial_cmp(other.as_bytes())
541    }
542}
543
544impl PartialOrd<[u8]> for HeaderValue {
545    #[inline]
546    fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
547        (*self.inner).partial_cmp(other)
548    }
549}
550
551impl PartialEq<HeaderValue> for str {
552    #[inline]
553    fn eq(&self, other: &HeaderValue) -> bool {
554        *other == *self
555    }
556}
557
558impl PartialEq<HeaderValue> for [u8] {
559    #[inline]
560    fn eq(&self, other: &HeaderValue) -> bool {
561        *other == *self
562    }
563}
564
565impl PartialOrd<HeaderValue> for str {
566    #[inline]
567    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
568        self.as_bytes().partial_cmp(other.as_bytes())
569    }
570}
571
572impl PartialOrd<HeaderValue> for [u8] {
573    #[inline]
574    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
575        self.partial_cmp(other.as_bytes())
576    }
577}
578
579impl PartialEq<String> for HeaderValue {
580    #[inline]
581    fn eq(&self, other: &String) -> bool {
582        *self == &other[..]
583    }
584}
585
586impl PartialOrd<String> for HeaderValue {
587    #[inline]
588    fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
589        self.inner.partial_cmp(other.as_bytes())
590    }
591}
592
593impl PartialEq<HeaderValue> for String {
594    #[inline]
595    fn eq(&self, other: &HeaderValue) -> bool {
596        *other == *self
597    }
598}
599
600impl PartialOrd<HeaderValue> for String {
601    #[inline]
602    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
603        self.as_bytes().partial_cmp(other.as_bytes())
604    }
605}
606
607impl<'a> PartialEq<HeaderValue> for &'a HeaderValue {
608    #[inline]
609    fn eq(&self, other: &HeaderValue) -> bool {
610        **self == *other
611    }
612}
613
614impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue {
615    #[inline]
616    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
617        (**self).partial_cmp(other)
618    }
619}
620
621impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
622    where HeaderValue: PartialEq<T>
623{
624    #[inline]
625    fn eq(&self, other: &&'a T) -> bool {
626        *self == **other
627    }
628}
629
630impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
631    where HeaderValue: PartialOrd<T>
632{
633    #[inline]
634    fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
635        self.partial_cmp(*other)
636    }
637}
638
639impl<'a> PartialEq<HeaderValue> for &'a str {
640    #[inline]
641    fn eq(&self, other: &HeaderValue) -> bool {
642        *other == *self
643    }
644}
645
646impl<'a> PartialOrd<HeaderValue> for &'a str {
647    #[inline]
648    fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
649        self.as_bytes().partial_cmp(other.as_bytes())
650    }
651}
652
653#[test]
654fn test_try_from() {
655    HeaderValue::try_from(vec![127]).unwrap_err();
656}
657
658#[test]
659fn test_debug() {
660    let cases = &[
661        ("hello", "\"hello\""),
662        ("hello \"world\"", "\"hello \\\"world\\\"\""),
663        ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
664    ];
665
666    for &(value, expected) in cases {
667        let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
668        let actual = format!("{:?}", val);
669        assert_eq!(expected, actual);
670    }
671
672    let mut sensitive = HeaderValue::from_static("password");
673    sensitive.set_sensitive(true);
674    assert_eq!("Sensitive", format!("{:?}", sensitive));
675}