Files
third_party_rust_http/src/header/value.rs
T
Carl Lerche 2b25e25fb7 Add sensitivity flag to HeaderValue (#28)
This allows marking a header value as sensitive. HTTP infrastructure is
able to special case such headers. For example, caches can avoid storing
them and HPACK can set them to "never index".
2017-06-01 15:34:15 -07:00

418 lines
11 KiB
Rust

use bytes::Bytes;
use std::{char, cmp, fmt, str};
/// Represents an HTTP header field value.
///
/// In practice, HTTP header field values are usually valid ASCII. However, the
/// HTTP spec allows for a header value to contain opaque bytes as well. In this
/// case, the header field value is not able to be represented as a string.
///
/// To handle this, the `HeaderValue` is useable as a type and can be compared
/// with strings and implements `Debug`. A `to_str` fn is provided that returns
/// an `Err` if the header value contains non visible ascii characters.
#[derive(Clone, Hash)]
pub struct HeaderValue {
inner: Bytes,
is_sensitive: bool,
}
/// A possible error when converting a `HeaderValue` from a string or byte
/// slice.
#[derive(Debug)]
pub struct InvalidValueError {
_priv: (),
}
/// A possible error when converting a `HeaderValue` to a string representation.
///
/// Header field values may contain opaque bytes, in which case it is not
/// possible to represent the value as a string.
#[derive(Debug)]
pub struct ToStrError {
_priv: (),
}
impl HeaderValue {
/// Convert a static string to a `HeaderValue`
///
/// This function will not perform any copying, however the string is
/// checked to ensure that no invalid characters are present. Only visible
/// ASCII characters (32-127) are permitted.
///
/// # Panics
///
/// This function panics if the argument contains invalid header value
/// characters.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::from_static("hello");
/// assert_eq!(val, "hello");
/// ```
pub fn from_static(src: &'static str) -> HeaderValue {
let bytes = src.as_bytes();
for &b in bytes {
if !is_visible_ascii(b) {
panic!("invalid header value");
}
}
HeaderValue {
inner: Bytes::from_static(bytes),
is_sensitive: false,
}
}
/// Attempt to convert a string to a `HeaderValue`.
///
/// If the argument contains invalid header value characters, an error is
/// returned. Only visible ASCII characters (32-127) are permitted. Use
/// `try_from_bytes` to create a `HeaderValue` that includes opaque octets
/// (128-255).
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::try_from_str("hello").unwrap();
/// assert_eq!(val, "hello");
/// ```
///
/// An invalid value
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::try_from_str("\n");
/// assert!(val.is_err());
/// ```
pub fn try_from_str(src: &str) -> Result<HeaderValue, InvalidValueError> {
let bytes = src.as_bytes();
for &b in bytes {
if !is_visible_ascii(b) {
return Err(InvalidValueError {
_priv: (),
});
}
}
Ok(HeaderValue {
inner: Bytes::from(bytes),
is_sensitive: false,
})
}
/// Attempt to convert a byte slice to a `HeaderValue`.
///
/// If the argument contains invalid header value bytes, an error is
/// returned. Only byte values between 32 and 255 (inclusive) are permitted.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::try_from_slice(b"hello\xfa").unwrap();
/// assert_eq!(val, &b"hello\xfa"[..]);
/// ```
///
/// An invalid value
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::try_from_slice(b"\n");
/// assert!(val.is_err());
/// ```
pub fn try_from_slice(src: &[u8]) -> Result<HeaderValue, InvalidValueError> {
for &b in src {
if !is_valid(b) {
return Err(InvalidValueError {
_priv: (),
});
}
}
Ok(HeaderValue {
inner: Bytes::from(src),
is_sensitive: false,
})
}
/// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
/// chars.
///
/// This function will perform a scan of the header value, checking all the
/// characters.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::from_static("hello");
/// assert_eq!(val.to_str().unwrap(), "hello");
/// ```
pub fn to_str(&self) -> Result<&str, ToStrError> {
let bytes = self.as_ref();
for &b in bytes {
if !is_visible_ascii(b) {
return Err(ToStrError { _priv: () });
}
}
unsafe { Ok(str::from_utf8_unchecked(bytes)) }
}
/// Returns the length of `self`.
///
/// This length is in bytes.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::from_static("hello");
/// assert_eq!(val.len(), 5);
/// ```
pub fn len(&self) -> usize {
self.as_ref().len()
}
/// Returns true if the `HeaderValue` has a length of zero bytes.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::from_static("");
/// assert!(val.is_empty());
///
/// let val = HeaderValue::from_static("hello");
/// assert!(!val.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Converts a `HeaderValue` to a byte slice.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let val = HeaderValue::from_static("hello");
/// assert_eq!(val.as_bytes(), b"hello");
/// ```
pub fn as_bytes(&self) -> &[u8] {
self.as_ref()
}
/// Mark that the header value represents sensitive information.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let mut val = HeaderValue::from_static("my secret");
///
/// val.set_sensitive(true);
/// assert!(val.is_sensitive());
///
/// val.set_sensitive(false);
/// assert!(!val.is_sensitive());
/// ```
pub fn set_sensitive(&mut self, val: bool) {
self.is_sensitive = val;
}
/// Returns `true` if the value represents sensitive data.
///
/// Sensitive data could represent passwords or other data that should not
/// be stored on disk or in memory. This setting can be used by components
/// like caches to avoid storing the value. HPACK encoders must set the
/// header field to never index when `is_sensitive` returns true.
///
/// Note that sensitivity is not factored into equality or ordering.
///
/// # Examples
///
/// ```
/// # use http::header::HeaderValue;
/// let mut val = HeaderValue::from_static("my secret");
///
/// val.set_sensitive(true);
/// assert!(val.is_sensitive());
///
/// val.set_sensitive(false);
/// assert!(!val.is_sensitive());
/// ```
pub fn is_sensitive(&self) -> bool {
self.is_sensitive
}
}
impl AsRef<[u8]> for HeaderValue {
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
impl fmt::Debug for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("HeaderValue")
.field("value", &EscapeBytes(self.as_ref()))
.field("is_sensitive", &self.is_sensitive)
.finish()
}
}
struct EscapeBytes<'a>(&'a [u8]);
impl<'a> fmt::Debug for EscapeBytes<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &b in self.0 {
if is_visible_ascii(b) {
let ch = unsafe { char::from_u32_unchecked(b as u32) };
try!(write!(f, "{}", ch));
} else {
try!(write!(f, "\\x{:x}", b));
}
}
Ok(())
}
}
fn is_visible_ascii(b: u8) -> bool {
is_valid(b) && b < 127
}
fn is_valid(b: u8) -> bool {
b >= 32
}
// ===== PartialEq / PartialOrd =====
impl PartialEq for HeaderValue {
fn eq(&self, other: &HeaderValue) -> bool {
self.inner == other.inner
}
}
impl Eq for HeaderValue {}
impl PartialOrd for HeaderValue {
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl Ord for HeaderValue {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl PartialEq<str> for HeaderValue {
fn eq(&self, other: &str) -> bool {
self.inner == other.as_bytes()
}
}
impl PartialEq<[u8]> for HeaderValue {
fn eq(&self, other: &[u8]) -> bool {
self.inner == other
}
}
impl PartialOrd<str> for HeaderValue {
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
(*self.inner).partial_cmp(other.as_bytes())
}
}
impl PartialOrd<[u8]> for HeaderValue {
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
(*self.inner).partial_cmp(other)
}
}
impl PartialEq<HeaderValue> for str {
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialEq<HeaderValue> for [u8] {
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialOrd<HeaderValue> for str {
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
impl PartialOrd<HeaderValue> for [u8] {
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.partial_cmp(other.as_bytes())
}
}
impl PartialEq<String> for HeaderValue {
fn eq(&self, other: &String) -> bool {
*self == &other[..]
}
}
impl PartialOrd<String> for HeaderValue {
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
self.inner.partial_cmp(other.as_bytes())
}
}
impl PartialEq<HeaderValue> for String {
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialOrd<HeaderValue> for String {
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
where HeaderValue: PartialEq<T>
{
fn eq(&self, other: &&'a T) -> bool {
*self == **other
}
}
impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
where HeaderValue: PartialOrd<T>
{
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl<'a> PartialEq<HeaderValue> for &'a str {
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl<'a> PartialOrd<HeaderValue> for &'a str {
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}