feat(header): introduce header::Raw (#869)

The Raw type repesents the raw bytes of a header-value.

Having a special type allows a couple of benefits:

- The exact representation has become private, allowing "uglier"
internals. Specifically, since the common case is for a header to only
have 1 line of bytes, an enum is used to skip allocating a Vec for only
1 line. Additionally, a Cow<'static, [u8]> is used, so static bytes
don't require a copy. Finally, since we can use static bytes, when
parsing, we can compare the incoming bytes against a couple of the most
common header-values, and possibly remove another copy.

- As its own type, the `Headers.set_raw` method can be generic over
`Into<Raw>`, which allows for more ergnomic method calls.

BREAKING CHANGE: `Header::parse_header` now receives `&Raw`, instead of
  a `&[Vec<u8>]`. `Raw` provides several methods to ease using it, but
  may require some changes to existing code.
This commit is contained in:
Sean McArthur
2016-07-23 12:54:16 -07:00
committed by GitHub
parent d67dbc6028
commit 50ccdaa7e7
27 changed files with 489 additions and 245 deletions

View File

@@ -1,7 +1,7 @@
use std::fmt::{self, Display};
use std::str;
use unicase::UniCase;
use header::{Header};
use header::{Header, Raw};
/// `Access-Control-Allow-Credentials` header, part of
/// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)
@@ -46,16 +46,15 @@ impl Header for AccessControlAllowCredentials {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> {
if raw.len() == 1 {
fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowCredentials> {
if let Some(line) = raw.one() {
let text = unsafe {
// safe because:
// 1. we just checked raw.len == 1
// 2. we don't actually care if it's utf8, we just want to
// 1. we don't actually care if it's utf8, we just want to
// compare the bytes with the "case" normalized. If it's not
// utf8, then the byte comparison will fail, and we'll return
// None. No big deal.
str::from_utf8_unchecked(raw.get_unchecked(0))
str::from_utf8_unchecked(line)
};
if UniCase(text) == ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE {
return Ok(AccessControlAllowCredentials);

View File

@@ -1,6 +1,7 @@
use std::fmt::{self, Display};
use std::str;
use header::{Header};
use header::{Header, Raw};
/// The `Access-Control-Allow-Origin` response header,
/// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
@@ -59,16 +60,16 @@ impl Header for AccessControlAllowOrigin {
"Access-Control-Allow-Origin"
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> {
if raw.len() != 1 {
return Err(::Error::Header)
fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowOrigin> {
if let Some(line) = raw.one() {
Ok(match line {
b"*" => AccessControlAllowOrigin::Any,
b"null" => AccessControlAllowOrigin::Null,
_ => AccessControlAllowOrigin::Value(try!(str::from_utf8(line)).into())
})
} else {
Err(::Error::Header)
}
let value = unsafe { raw.get_unchecked(0) };
Ok(match &value[..] {
b"*" => AccessControlAllowOrigin::Any,
b"null" => AccessControlAllowOrigin::Null,
_ => AccessControlAllowOrigin::Value(try!(String::from_utf8(value.clone())))
})
}
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@@ -3,7 +3,7 @@ use std::fmt::{self, Display};
use std::str::{FromStr, from_utf8};
use std::ops::{Deref, DerefMut};
use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline};
use header::{Header};
use header::{Header, Raw};
/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
///
@@ -77,25 +77,26 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> {
if raw.len() != 1 {
return Err(::Error::Header);
}
let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] }));
if let Some(scheme) = <S as Scheme>::scheme() {
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
fn parse_header(raw: &Raw) -> ::Result<Authorization<S>> {
if let Some(line) = raw.one() {
let header = try!(from_utf8(line));
if let Some(scheme) = <S as Scheme>::scheme() {
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
Ok(h) => Ok(h),
Err(_) => Err(::Error::Header)
}
} else {
Err(::Error::Header)
}
} else {
match header.parse::<S>().map(Authorization) {
Ok(h) => Ok(h),
Err(_) => Err(::Error::Header)
}
} else {
Err(::Error::Header)
}
} else {
match header.parse::<S>().map(Authorization) {
Ok(h) => Ok(h),
Err(_) => Err(::Error::Header)
}
Err(::Error::Header)
}
}
@@ -231,8 +232,7 @@ mod tests {
#[test]
fn test_raw_auth_parse() {
let header: Authorization<String> = Header::parse_header(
&[b"foo bar baz".to_vec()]).unwrap();
let header: Authorization<String> = Header::parse_header(&b"foo bar baz".as_ref().into()).unwrap();
assert_eq!(header.0, "foo bar baz");
}
@@ -256,7 +256,7 @@ mod tests {
#[test]
fn test_basic_auth_parse() {
let auth: Authorization<Basic> = Header::parse_header(
&[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap();
&b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".as_ref().into()).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("open sesame".to_owned()));
}
@@ -264,7 +264,7 @@ mod tests {
#[test]
fn test_basic_auth_parse_no_password() {
let auth: Authorization<Basic> = Header::parse_header(
&[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap();
&b"Basic QWxhZGRpbjo=".as_ref().into()).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("".to_owned()));
}
@@ -282,7 +282,7 @@ mod tests {
#[test]
fn test_bearer_auth_parse() {
let auth: Authorization<Bearer> = Header::parse_header(
&[b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()]).unwrap();
&b"Bearer fpKL54jvWmEGVoRdCNjG".as_ref().into()).unwrap();
assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG");
}
}

View File

@@ -1,6 +1,6 @@
use std::fmt;
use std::str::FromStr;
use header::Header;
use header::{Header, Raw};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
@@ -55,7 +55,7 @@ impl Header for CacheControl {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> {
fn parse_header(raw: &Raw) -> ::Result<CacheControl> {
let directives = try!(from_comma_delimited(raw));
if !directives.is_empty() {
Ok(CacheControl(directives))
@@ -167,27 +167,27 @@ mod tests {
#[test]
fn test_parse_multiple_headers() {
let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]);
let cache = Header::parse_header(&vec![b"no-cache".to_vec(), b"private".to_vec()].into());
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache,
CacheDirective::Private])))
}
#[test]
fn test_parse_argument() {
let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]);
let cache = Header::parse_header(&vec![b"max-age=100, private".to_vec()].into());
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100),
CacheDirective::Private])))
}
#[test]
fn test_parse_quote_form() {
let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]);
let cache = Header::parse_header(&vec![b"max-age=\"200\"".to_vec()].into());
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
}
#[test]
fn test_parse_extension() {
let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
let cache = Header::parse_header(&vec![b"foo, bar=baz".to_vec()].into());
assert_eq!(cache.ok(), Some(CacheControl(vec![
CacheDirective::Extension("foo".to_owned(), None),
CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))])))
@@ -195,7 +195,7 @@ mod tests {
#[test]
fn test_parse_bad_syntax() {
let cache: ::Result<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
let cache: ::Result<CacheControl> = Header::parse_header(&vec![b"foo=".to_vec()].into());
assert_eq!(cache.ok(), None)
}
}

View File

@@ -127,8 +127,8 @@ mod tests {
use unicase::UniCase;
fn parse_option(header: Vec<u8>) -> Connection {
let val = vec![header];
let connection: Connection = Header::parse_header(&val[..]).unwrap();
let val = header.into();
let connection: Connection = Header::parse_header(&val).unwrap();
connection
}

View File

@@ -11,7 +11,7 @@ use std::fmt;
use unicase::UniCase;
use url::percent_encoding;
use header::{Header, parsing};
use header::{Header, Raw, parsing};
use header::parsing::{parse_extended_value, HTTP_VALUE};
use header::shared::Charset;
@@ -94,7 +94,7 @@ impl Header for ContentDisposition {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentDisposition> {
fn parse_header(raw: &Raw) -> ::Result<ContentDisposition> {
parsing::from_one_raw_str(raw).and_then(|s: String| {
let mut sections = s.split(';');
let disposition = match sections.next() {
@@ -201,10 +201,10 @@ mod tests {
#[test]
fn test_parse_header() {
assert!(ContentDisposition::parse_header([b"".to_vec()].as_ref()).is_err());
assert!(ContentDisposition::parse_header(&"".into()).is_err());
let a = [b"form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".to_vec()];
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let a = "form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let b = ContentDisposition {
disposition: DispositionType::Ext("form-data".to_owned()),
parameters: vec![
@@ -217,8 +217,8 @@ mod tests {
};
assert_eq!(a, b);
let a = [b"attachment; filename=\"image.jpg\"".to_vec()];
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let a = "attachment; filename=\"image.jpg\"".into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let b = ContentDisposition {
disposition: DispositionType::Attachment,
parameters: vec![
@@ -229,8 +229,8 @@ mod tests {
};
assert_eq!(a, b);
let a = [b"attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".to_vec()];
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let a = "attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let b = ContentDisposition {
disposition: DispositionType::Attachment,
parameters: vec![
@@ -245,19 +245,19 @@ mod tests {
#[test]
fn test_display() {
let a = [b"attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates".to_vec()];
let as_string = ::std::str::from_utf8(&(a[0])).unwrap();
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let as_string = "attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates";
let a = as_string.into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let display_rendered = format!("{}",a);
assert_eq!(as_string, display_rendered);
let a = [b"attachment; filename*=UTF-8''black%20and%20white.csv".to_vec()];
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let a = "attachment; filename*=UTF-8''black%20and%20white.csv".into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let display_rendered = format!("{}",a);
assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered);
let a = [b"attachment; filename=colourful.csv".to_vec()];
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
let a = "attachment; filename=colourful.csv".into();
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
let display_rendered = format!("{}",a);
assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered);
}

View File

@@ -1,6 +1,6 @@
use std::fmt;
use header::{Header, parsing};
use header::{Header, Raw, parsing};
/// `Content-Length` header, defined in
/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2)
@@ -40,12 +40,11 @@ impl Header for ContentLength {
static NAME: &'static str = "Content-Length";
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentLength> {
fn parse_header(raw: &Raw) -> ::Result<ContentLength> {
// If multiple Content-Length headers were sent, everything can still
// be alright if they all contain the same value, and all parse
// correctly. If not, then it's an error.
raw.iter()
.map(::std::ops::Deref::deref)
.map(parsing::from_raw_str)
.fold(None, |prev, x| {
match (prev, x) {
@@ -84,8 +83,8 @@ __hyper__tm!(ContentLength, tests {
// Can't use the test_header macro because "5, 5" gets cleaned to "5".
#[test]
fn test_duplicates() {
let parsed = HeaderField::parse_header(&[b"5"[..].into(),
b"5"[..].into()]).unwrap();
let parsed = HeaderField::parse_header(&vec![b"5".to_vec(),
b"5".to_vec()].into()).unwrap();
assert_eq!(parsed, HeaderField(5));
assert_eq!(format!("{}", parsed), "5");
}

View File

@@ -1,4 +1,4 @@
use header::{Header, CookiePair, CookieJar};
use header::{Header, Raw, CookiePair, CookieJar};
use std::fmt::{self, Display};
use std::str::from_utf8;
@@ -43,7 +43,7 @@ impl Header for Cookie {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> {
fn parse_header(raw: &Raw) -> ::Result<Cookie> {
let mut cookies = Vec::with_capacity(raw.len());
for cookies_raw in raw.iter() {
let cookies_str = try!(from_utf8(&cookies_raw[..]));
@@ -96,7 +96,7 @@ impl Cookie {
#[test]
fn test_parse() {
let h = Header::parse_header(&[b"foo=bar; baz=quux".to_vec()][..]);
let h = Header::parse_header(&b"foo=bar; baz=quux".as_ref().into());
let c1 = CookiePair::new("foo".to_owned(), "bar".to_owned());
let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned());
assert_eq!(h.ok(), Some(Cookie(vec![c1, c2])));

View File

@@ -3,7 +3,7 @@ use std::str;
use unicase::UniCase;
use header::{Header};
use header::{Header, Raw};
/// The `Expect` header.
///
@@ -34,16 +34,15 @@ impl Header for Expect {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> {
if raw.len() == 1 {
fn parse_header(raw: &Raw) -> ::Result<Expect> {
if let Some(line) = raw.one() {
let text = unsafe {
// safe because:
// 1. we just checked raw.len == 1
// 2. we don't actually care if it's utf8, we just want to
// 1. we don't actually care if it's utf8, we just want to
// compare the bytes with the "case" normalized. If it's not
// utf8, then the byte comparison will fail, and we'll return
// None. No big deal.
str::from_utf8_unchecked(raw.get_unchecked(0))
str::from_utf8_unchecked(line)
};
if UniCase(text) == EXPECT_CONTINUE {
Ok(Expect::Continue)

View File

@@ -1,4 +1,4 @@
use header::{Header};
use header::{Header, Raw};
use std::fmt;
use std::str::FromStr;
use header::parsing::from_one_raw_str;
@@ -49,7 +49,7 @@ impl Header for Host {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> {
fn parse_header(raw: &Raw) -> ::Result<Host> {
from_one_raw_str(raw)
}
@@ -98,14 +98,14 @@ mod tests {
#[test]
fn test_host() {
let host = Header::parse_header([b"foo.com".to_vec()].as_ref());
let host = Header::parse_header(&vec![b"foo.com".to_vec()].into());
assert_eq!(host.ok(), Some(Host {
hostname: "foo.com".to_owned(),
port: None
}));
let host = Header::parse_header([b"foo.com:8080".to_vec()].as_ref());
let host = Header::parse_header(&vec![b"foo.com:8080".to_vec()].into());
assert_eq!(host.ok(), Some(Host {
hostname: "foo.com".to_owned(),
port: Some(8080)

View File

@@ -67,10 +67,10 @@ mod tests {
fn test_if_none_match() {
let mut if_none_match: ::Result<IfNoneMatch>;
if_none_match = Header::parse_header([b"*".to_vec()].as_ref());
if_none_match = Header::parse_header(&b"*".as_ref().into());
assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Any));
if_none_match = Header::parse_header([b"\"foobar\", W/\"weak-etag\"".to_vec()].as_ref());
if_none_match = Header::parse_header(&b"\"foobar\", W/\"weak-etag\"".as_ref().into());
let mut entities: Vec<EntityTag> = Vec::new();
let foobar_etag = EntityTag::new(false, "foobar".to_owned());
let weak_etag = EntityTag::new(true, "weak-etag".to_owned());

View File

@@ -1,5 +1,5 @@
use std::fmt::{self, Display};
use header::{self, Header, EntityTag, HttpDate};
use header::{self, Header, Raw, EntityTag, HttpDate};
/// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2)
///
@@ -58,7 +58,7 @@ impl Header for IfRange {
static NAME: &'static str = "If-Range";
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
fn parse_header(raw: &Raw) -> ::Result<IfRange> {
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
if let Ok(etag) = etag {
return Ok(IfRange::EntityTag(etag));

View File

@@ -72,15 +72,16 @@ macro_rules! bench_header(
#[bench]
fn bench_parse(b: &mut Bencher) {
let val = $value;
let val = $value.into();
b.iter(|| {
let _: $ty = Header::parse_header(&val[..]).unwrap();
let _: $ty = Header::parse_header(&val).unwrap();
});
}
#[bench]
fn bench_format(b: &mut Bencher) {
let val: $ty = Header::parse_header(&$value[..]).unwrap();
let raw = $value.into();
let val: $ty = Header::parse_header(&raw).unwrap();
let fmt = ::header::HeaderFormatter(&val);
b.iter(|| {
format!("{}", fmt);
@@ -137,7 +138,8 @@ macro_rules! test_header {
use std::ascii::AsciiExt;
let raw = $raw;
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
let value = HeaderField::parse_header(&a[..]);
let a = a.into();
let value = HeaderField::parse_header(&a);
let result = format!("{}", value.unwrap());
let expected = String::from_utf8(raw[0].to_vec()).unwrap();
let result_cmp: Vec<String> = result
@@ -157,7 +159,8 @@ macro_rules! test_header {
#[test]
fn $id() {
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
let val = HeaderField::parse_header(&a[..]);
let a = a.into();
let val = HeaderField::parse_header(&a);
let typed: Option<HeaderField> = $typed;
// Test parsing
assert_eq!(val.ok(), typed);
@@ -195,9 +198,8 @@ macro_rules! __hyper_generate_header_serialization {
where D: ::serde::Deserializer {
let string_representation: String =
try!(::serde::Deserialize::deserialize(deserializer));
Ok($crate::header::Header::parse_header(&[
string_representation.into_bytes()
]).unwrap())
let raw = string_representation.into_bytes().into();
Ok($crate::header::Header::parse_header(&raw).unwrap())
}
}
}
@@ -221,7 +223,7 @@ macro_rules! header {
static NAME: &'static str = $n;
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id)
}
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
@@ -248,7 +250,7 @@ macro_rules! header {
static NAME: &'static str = $n;
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id)
}
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
@@ -274,7 +276,7 @@ macro_rules! header {
static NAME: &'static str = $n;
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_one_raw_str(raw).map($id)
}
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
@@ -303,10 +305,10 @@ macro_rules! header {
static NAME: &'static str = $n;
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
// FIXME: Return None if no item is in $id::Only
if raw.len() == 1 {
if raw[0] == b"*" {
if &raw[0] == b"*" {
return Ok($id::Any)
}
}

View File

@@ -1,4 +1,4 @@
use header::{Header, Host};
use header::{Header, Raw, Host};
use std::fmt;
use std::str::FromStr;
use header::parsing::from_one_raw_str;
@@ -57,7 +57,7 @@ impl Header for Origin {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Origin> {
fn parse_header(raw: &Raw) -> ::Result<Origin> {
from_one_raw_str(raw)
}
@@ -100,10 +100,10 @@ mod tests {
#[test]
fn test_origin() {
let origin = Header::parse_header([b"http://foo.com".to_vec()].as_ref());
let origin = Header::parse_header(&vec![b"http://foo.com".to_vec()].into());
assert_eq!(origin.ok(), Some(Origin::new("http", "foo.com", None)));
let origin = Header::parse_header([b"https://foo.com:443".to_vec()].as_ref());
let origin = Header::parse_header(&vec![b"https://foo.com:443".to_vec()].into());
assert_eq!(origin.ok(), Some(Origin::new("https", "foo.com", Some(443))));
}
}

View File

@@ -1,7 +1,7 @@
use std::fmt;
use std::ascii::AsciiExt;
use header::{Header, parsing};
use header::{Header, Raw, parsing};
/// The `Pragma` header defined by HTTP/1.0.
///
@@ -44,7 +44,7 @@ impl Header for Pragma {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Pragma> {
fn parse_header(raw: &Raw) -> ::Result<Pragma> {
parsing::from_one_raw_str(raw).and_then(|s: String| {
let slice = &s.to_ascii_lowercase()[..];
match slice {
@@ -64,12 +64,12 @@ impl Header for Pragma {
#[test]
fn test_parse_header() {
let a: Pragma = Header::parse_header([b"no-cache".to_vec()].as_ref()).unwrap();
let a: Pragma = Header::parse_header(&"no-cache".into()).unwrap();
let b = Pragma::NoCache;
assert_eq!(a, b);
let c: Pragma = Header::parse_header([b"FoObar".to_vec()].as_ref()).unwrap();
let c: Pragma = Header::parse_header(&"FoObar".into()).unwrap();
let d = Pragma::Ext("FoObar".to_owned());
assert_eq!(c, d);
let e: ::Result<Pragma> = Header::parse_header([b"".to_vec()].as_ref());
let e: ::Result<Pragma> = Header::parse_header(&"".into());
assert_eq!(e.ok(), None);
}

View File

@@ -1,6 +1,6 @@
use std::fmt;
use std::str::FromStr;
use header::{Header};
use header::{Header, Raw};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
@@ -56,7 +56,7 @@ impl Header for Prefer {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> {
fn parse_header(raw: &Raw) -> ::Result<Prefer> {
let preferences = try!(from_comma_delimited(raw));
if !preferences.is_empty() {
Ok(Prefer(preferences))
@@ -159,14 +159,14 @@ mod tests {
#[test]
fn test_parse_multiple_headers() {
let prefer = Header::parse_header(&[b"respond-async, return=representation".to_vec()]);
let prefer = Header::parse_header(&"respond-async, return=representation".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync,
Preference::ReturnRepresentation])))
}
#[test]
fn test_parse_argument() {
let prefer = Header::parse_header(&[b"wait=100, handling=leniant, respond-async".to_vec()]);
let prefer = Header::parse_header(&"wait=100, handling=leniant, respond-async".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100),
Preference::HandlingLeniant,
Preference::RespondAsync])))
@@ -174,14 +174,14 @@ mod tests {
#[test]
fn test_parse_quote_form() {
let prefer = Header::parse_header(&[b"wait=\"200\", handling=\"strict\"".to_vec()]);
let prefer = Header::parse_header(&"wait=\"200\", handling=\"strict\"".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200),
Preference::HandlingStrict])))
}
#[test]
fn test_parse_extension() {
let prefer = Header::parse_header(&[b"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".to_vec()]);
let prefer = Header::parse_header(&"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![
Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
@@ -192,7 +192,7 @@ mod tests {
#[test]
fn test_fail_with_args() {
let prefer: ::Result<Prefer> = Header::parse_header(&[b"respond-async; foo=bar".to_vec()]);
let prefer: ::Result<Prefer> = Header::parse_header(&"respond-async; foo=bar".into());
assert_eq!(prefer.ok(), None);
}
}

View File

@@ -1,5 +1,5 @@
use std::fmt;
use header::{Header, Preference};
use header::{Header, Raw, Preference};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
@@ -54,7 +54,7 @@ impl Header for PreferenceApplied {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> {
fn parse_header(raw: &Raw) -> ::Result<PreferenceApplied> {
let preferences = try!(from_comma_delimited(raw));
if !preferences.is_empty() {
Ok(PreferenceApplied(preferences))

View File

@@ -1,8 +1,8 @@
use std::fmt::{self, Display};
use std::str::FromStr;
use header::Header;
use header::parsing::{from_one_raw_str, from_comma_delimited};
use header::{Header, Raw};
use header::parsing::{from_one_raw_str};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
///
@@ -130,15 +130,11 @@ impl FromStr for Range {
match (iter.next(), iter.next()) {
(Some("bytes"), Some(ranges)) => {
match from_comma_delimited(&[ranges]) {
Ok(ranges) => {
if ranges.is_empty() {
return Err(::Error::Header);
}
Ok(Range::Bytes(ranges))
},
Err(_) => Err(::Error::Header)
let ranges = from_comma_delimited(ranges);
if ranges.is_empty() {
return Err(::Error::Header);
}
Ok(Range::Bytes(ranges))
}
(Some(unit), Some(range_str)) if unit != "" && range_str != "" => {
Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned()))
@@ -173,6 +169,16 @@ impl FromStr for ByteRangeSpec {
}
}
fn from_comma_delimited<T: FromStr>(s: &str) -> Vec<T> {
s.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y)
})
.filter_map(|x| x.parse().ok())
.collect()
}
impl Header for Range {
fn header_name() -> &'static str {
@@ -180,7 +186,7 @@ impl Header for Range {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {
fn parse_header(raw: &Raw) -> ::Result<Range> {
from_one_raw_str(raw)
}
@@ -192,29 +198,29 @@ impl Header for Range {
#[test]
fn test_parse_bytes_range_valid() {
let r: Range = Header::parse_header(&[b"bytes=1-100".to_vec()]).unwrap();
let r2: Range = Header::parse_header(&[b"bytes=1-100,-".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap();
let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap();
let r3 = Range::bytes(1, 100);
assert_eq!(r, r2);
assert_eq!(r2, r3);
let r: Range = Header::parse_header(&[b"bytes=1-100,200-".to_vec()]).unwrap();
let r2: Range = Header::parse_header(&[b"bytes= 1-100 , 101-xxx, 200- ".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap();
let r2: Range = Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap();
let r3 = Range::Bytes(
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)]
);
assert_eq!(r, r2);
assert_eq!(r2, r3);
let r: Range = Header::parse_header(&[b"bytes=1-100,-100".to_vec()]).unwrap();
let r2: Range = Header::parse_header(&[b"bytes=1-100, ,,-100".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap();
let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap();
let r3 = Range::Bytes(
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)]
);
assert_eq!(r, r2);
assert_eq!(r2, r3);
let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap();
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
assert_eq!(r, r2);
@@ -222,40 +228,40 @@ fn test_parse_bytes_range_valid() {
#[test]
fn test_parse_unregistered_range_valid() {
let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap();
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
assert_eq!(r, r2);
let r: Range = Header::parse_header(&[b"custom=abcd".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap();
let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned());
assert_eq!(r, r2);
let r: Range = Header::parse_header(&[b"custom=xxx-yyy".to_vec()]).unwrap();
let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap();
let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned());
assert_eq!(r, r2);
}
#[test]
fn test_parse_invalid() {
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-a,-".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"bytes=1-a,-".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-2-3".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"bytes=1-2-3".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"abc".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"abc".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-100=".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"bytes=1-100=".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"bytes=".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"bytes=".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"custom=".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"custom=".into());
assert_eq!(r.ok(), None);
let r: ::Result<Range> = Header::parse_header(&[b"=1-100".to_vec()]);
let r: ::Result<Range> = Header::parse_header(&"=1-100".into());
assert_eq!(r.ok(), None);
}

View File

@@ -1,7 +1,7 @@
use std::fmt;
use std::ascii::AsciiExt;
use header::{Header, parsing};
use header::{Header, Raw, parsing};
/// `Referrer-Policy` header, part of
/// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header)
@@ -52,7 +52,7 @@ impl Header for ReferrerPolicy {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ReferrerPolicy> {
fn parse_header(raw: &Raw) -> ::Result<ReferrerPolicy> {
use self::ReferrerPolicy::*;
parsing::from_one_raw_str(raw).and_then(|s: String| {
let slice = &s.to_ascii_lowercase()[..];
@@ -84,9 +84,9 @@ impl Header for ReferrerPolicy {
#[test]
fn test_parse_header() {
let a: ReferrerPolicy = Header::parse_header([b"origin".to_vec()].as_ref()).unwrap();
let a: ReferrerPolicy = Header::parse_header(&"origin".into()).unwrap();
let b = ReferrerPolicy::Origin;
assert_eq!(a, b);
let e: ::Result<ReferrerPolicy> = Header::parse_header([b"foobar".to_vec()].as_ref());
let e: ::Result<ReferrerPolicy> = Header::parse_header(&"foobar".into());
assert!(e.is_err());
}

View File

@@ -1,4 +1,4 @@
use header::{Header, CookiePair, CookieJar};
use header::{Header, Raw, CookiePair, CookieJar};
use std::fmt::{self, Display};
use std::str::from_utf8;
@@ -88,7 +88,7 @@ impl Header for SetCookie {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<SetCookie> {
fn parse_header(raw: &Raw) -> ::Result<SetCookie> {
let mut set_cookies = Vec::with_capacity(raw.len());
for set_cookies_raw in raw {
if let Ok(s) = from_utf8(&set_cookies_raw[..]) {
@@ -136,7 +136,7 @@ impl SetCookie {
#[test]
fn test_parse() {
let h = Header::parse_header(&[b"foo=bar; HttpOnly".to_vec()][..]);
let h = Header::parse_header(&"foo=bar; HttpOnly".into());
let mut c1 = CookiePair::new("foo".to_owned(), "bar".to_owned());
c1.httponly = true;

View File

@@ -3,7 +3,7 @@ use std::str::{self, FromStr};
use unicase::UniCase;
use header::{Header, parsing};
use header::{Header, Raw, parsing};
/// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797)
///
@@ -125,7 +125,7 @@ impl Header for StrictTransportSecurity {
NAME
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> {
fn parse_header(raw: &Raw) -> ::Result<StrictTransportSecurity> {
parsing::from_one_raw_str(raw)
}
@@ -145,49 +145,49 @@ mod tests {
#[test]
fn test_parse_max_age() {
let h = Header::parse_header(&[b"max-age=31536000".to_vec()][..]);
let h = Header::parse_header(&"max-age=31536000".into());
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
}
#[test]
fn test_parse_max_age_no_value() {
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"max-age".to_vec()][..]);
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age".into());
assert!(h.is_err());
}
#[test]
fn test_parse_quoted_max_age() {
let h = Header::parse_header(&[b"max-age=\"31536000\"".to_vec()][..]);
let h = Header::parse_header(&"max-age=\"31536000\"".into());
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
}
#[test]
fn test_parse_spaces_max_age() {
let h = Header::parse_header(&[b"max-age = 31536000".to_vec()][..]);
let h = Header::parse_header(&"max-age = 31536000".into());
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
}
#[test]
fn test_parse_include_subdomains() {
let h = Header::parse_header(&[b"max-age=15768000 ; includeSubDomains".to_vec()][..]);
let h = Header::parse_header(&"max-age=15768000 ; includeSubDomains".into());
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: true, max_age: 15768000u64 }));
}
#[test]
fn test_parse_no_max_age() {
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"includeSubDomains".to_vec()][..]);
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"includeSubDomains".into());
assert!(h.is_err());
}
#[test]
fn test_parse_max_age_nan() {
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"max-age = derp".to_vec()][..]);
let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age = derp".into());
assert!(h.is_err());
}
#[test]
fn test_parse_duplicate_directives() {
assert!(StrictTransportSecurity::parse_header(&[b"max-age=100; max-age=5; max-age=0".to_vec()][..]).is_err());
assert!(StrictTransportSecurity::parse_header(&"max-age=100; max-age=5; max-age=0".into()).is_err());
}
}

View File

@@ -68,7 +68,7 @@ header! {
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
#[test]
fn test3() {
let x: ::Result<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
let x: ::Result<Upgrade> = Header::parse_header(&"WEbSOCKet".into());
assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
}
}

View File

@@ -54,10 +54,10 @@ header! {
fn test2() {
let mut vary: ::Result<Vary>;
vary = Header::parse_header([b"*".to_vec()].as_ref());
vary = Header::parse_header(&"*".into());
assert_eq!(vary.ok(), Some(Vary::Any));
vary = Header::parse_header([b"etag,cookie,allow".to_vec()].as_ref());
vary = Header::parse_header(&"etag,cookie,allow".into());
assert_eq!(vary.ok(), Some(Vary::Items(vec!["eTag".parse().unwrap(),
"cookIE".parse().unwrap(),
"AlLOw".parse().unwrap(),])));