Files
h2/src/hpack/header.rs
2017-06-07 21:54:04 -07:00

236 lines
7.0 KiB
Rust

use super::DecoderError;
use util::byte_str::{ByteStr, FromUtf8Error};
use http::{Method, StatusCode};
use http::header::{HeaderName, HeaderValue};
use bytes::Bytes;
/// HTTP/2.0 Header
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Header {
Field {
name: HeaderName,
value: HeaderValue,
},
Authority(ByteStr),
Method(Method),
Scheme(ByteStr),
Path(ByteStr),
Status(StatusCode),
}
/// The header field name
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Name<'a> {
Field(&'a HeaderName),
Authority,
Method,
Scheme,
Path,
Status,
}
pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
let n: &str = name.as_ref();
32 + n.len() + value.len()
}
impl Header {
pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
if name[0] == b':' {
match &name[1..] {
b"authority" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Header::Authority(value))
}
b"method" => {
let method = try!(Method::from_bytes(&value));
Ok(Header::Method(method))
}
b"scheme" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Header::Scheme(value))
}
b"path" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Header::Path(value))
}
b"status" => {
let status = try!(StatusCode::from_bytes(&value));
Ok(Header::Status(status))
}
_ => {
Err(DecoderError::InvalidPseudoheader)
}
}
} else {
let name = try!(HeaderName::from_bytes(&name));
let value = try!(HeaderValue::try_from_bytes(&value));
Ok(Header::Field { name: name, value: value })
}
}
pub fn len(&self) -> usize {
match *self {
Header::Field { ref name, ref value } => {
len(name, value)
}
Header::Authority(ref v) => {
32 + 10 + v.len()
}
Header::Method(ref v) => {
32 + 7 + v.as_ref().len()
}
Header::Scheme(ref v) => {
32 + 7 + v.len()
}
Header::Path(ref v) => {
32 + 5 + v.len()
}
Header::Status(ref v) => {
32 + 7 + 3
}
}
}
/// Returns the header name
pub fn name(&self) -> Name {
match *self {
Header::Field { ref name, .. } => Name::Field(name),
Header::Authority(..) => Name::Authority,
Header::Method(..) => Name::Method,
Header::Scheme(..) => Name::Scheme,
Header::Path(..) => Name::Path,
Header::Status(..) => Name::Status,
}
}
pub fn value_slice(&self) -> &[u8] {
match *self {
Header::Field { ref value, .. } => value.as_ref(),
Header::Authority(ref v) => v.as_ref(),
Header::Method(ref v) => v.as_ref().as_ref(),
Header::Scheme(ref v) => v.as_ref(),
Header::Path(ref v) => v.as_ref(),
Header::Status(ref v) => v.as_str().as_ref(),
}
}
pub fn value_eq(&self, other: &Header) -> bool {
match *self {
Header::Field { ref value, .. } => {
let a = value;
match *other {
Header::Field { ref value, .. } => a == value,
_ => false,
}
}
Header::Authority(ref a) => {
match *other {
Header::Authority(ref b) => a == b,
_ => false,
}
}
Header::Method(ref a) => {
match *other {
Header::Method(ref b) => a == b,
_ => false,
}
}
Header::Scheme(ref a) => {
match *other {
Header::Scheme(ref b) => a == b,
_ => false,
}
}
Header::Path(ref a) => {
match *other {
Header::Path(ref b) => a == b,
_ => false,
}
}
Header::Status(ref a) => {
match *other {
Header::Status(ref b) => a == b,
_ => false,
}
}
}
}
pub fn is_sensitive(&self) -> bool {
match *self {
Header::Field { ref value, .. } => value.is_sensitive(),
// TODO: Technically these other header values can be sensitive too.
_ => false,
}
}
pub fn skip_value_index(&self) -> bool {
use http::header;
match *self {
Header::Field { ref name, .. } => {
match *name {
header::AGE |
header::AUTHORIZATION |
header::CONTENT_LENGTH |
header::ETAG |
header::IF_MODIFIED_SINCE |
header::IF_NONE_MATCH |
header::LOCATION |
header::COOKIE |
header::SET_COOKIE => true,
_ => false,
}
}
Header::Path(..) => true,
_ => false,
}
}
}
impl<'a> Name<'a> {
pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
match self {
Name::Field(name) => {
Ok(Header::Field {
name: name.clone(),
value: try!(HeaderValue::try_from_bytes(&*value)),
})
}
Name::Authority => {
Ok(Header::Authority(try!(ByteStr::from_utf8(value))))
}
Name::Method => {
Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
}
Name::Scheme => {
Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
}
Name::Path => {
Ok(Header::Path(try!(ByteStr::from_utf8(value))))
}
Name::Status => {
match StatusCode::from_bytes(&value) {
Ok(status) => Ok(Header::Status(status)),
// TODO: better error handling
Err(_) => Err(DecoderError::InvalidStatusCode),
}
}
}
}
pub fn as_slice(&self) -> &[u8] {
match *self {
Name::Field(ref name) => name.as_ref(),
Name::Authority => b":authority",
Name::Method => b":method",
Name::Scheme => b":scheme",
Name::Path => b":path",
Name::Status => b":status",
}
}
}