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:
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::str;
|
use std::str;
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
|
|
||||||
/// `Access-Control-Allow-Credentials` header, part of
|
/// `Access-Control-Allow-Credentials` header, part of
|
||||||
/// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)
|
/// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)
|
||||||
@@ -46,16 +46,15 @@ impl Header for AccessControlAllowCredentials {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> {
|
fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowCredentials> {
|
||||||
if raw.len() == 1 {
|
if let Some(line) = raw.one() {
|
||||||
let text = unsafe {
|
let text = unsafe {
|
||||||
// safe because:
|
// safe because:
|
||||||
// 1. we just checked raw.len == 1
|
// 1. we don't actually care if it's utf8, we just want to
|
||||||
// 2. we don't actually care if it's utf8, we just want to
|
|
||||||
// compare the bytes with the "case" normalized. If it's not
|
// compare the bytes with the "case" normalized. If it's not
|
||||||
// utf8, then the byte comparison will fail, and we'll return
|
// utf8, then the byte comparison will fail, and we'll return
|
||||||
// None. No big deal.
|
// 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 {
|
if UniCase(text) == ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE {
|
||||||
return Ok(AccessControlAllowCredentials);
|
return Ok(AccessControlAllowCredentials);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
|
|
||||||
/// The `Access-Control-Allow-Origin` response header,
|
/// The `Access-Control-Allow-Origin` response header,
|
||||||
/// part of [CORS](http://www.w3.org/TR/cors/#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"
|
"Access-Control-Allow-Origin"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> {
|
fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowOrigin> {
|
||||||
if raw.len() != 1 {
|
if let Some(line) = raw.one() {
|
||||||
return Err(::Error::Header)
|
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 {
|
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::fmt::{self, Display};
|
|||||||
use std::str::{FromStr, from_utf8};
|
use std::str::{FromStr, from_utf8};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline};
|
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)
|
/// `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
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> {
|
fn parse_header(raw: &Raw) -> ::Result<Authorization<S>> {
|
||||||
if raw.len() != 1 {
|
if let Some(line) = raw.one() {
|
||||||
return Err(::Error::Header);
|
let header = try!(from_utf8(line));
|
||||||
}
|
if let Some(scheme) = <S as Scheme>::scheme() {
|
||||||
let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] }));
|
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
|
||||||
if let Some(scheme) = <S as Scheme>::scheme() {
|
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
|
||||||
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
|
Ok(h) => Ok(h),
|
||||||
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
|
Err(_) => Err(::Error::Header)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(::Error::Header)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match header.parse::<S>().map(Authorization) {
|
||||||
Ok(h) => Ok(h),
|
Ok(h) => Ok(h),
|
||||||
Err(_) => Err(::Error::Header)
|
Err(_) => Err(::Error::Header)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Err(::Error::Header)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match header.parse::<S>().map(Authorization) {
|
Err(::Error::Header)
|
||||||
Ok(h) => Ok(h),
|
|
||||||
Err(_) => Err(::Error::Header)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +232,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_raw_auth_parse() {
|
fn test_raw_auth_parse() {
|
||||||
let header: Authorization<String> = Header::parse_header(
|
let header: Authorization<String> = Header::parse_header(&b"foo bar baz".as_ref().into()).unwrap();
|
||||||
&[b"foo bar baz".to_vec()]).unwrap();
|
|
||||||
assert_eq!(header.0, "foo bar baz");
|
assert_eq!(header.0, "foo bar baz");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_basic_auth_parse() {
|
fn test_basic_auth_parse() {
|
||||||
let auth: Authorization<Basic> = Header::parse_header(
|
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.username, "Aladdin");
|
||||||
assert_eq!(auth.0.password, Some("open sesame".to_owned()));
|
assert_eq!(auth.0.password, Some("open sesame".to_owned()));
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_basic_auth_parse_no_password() {
|
fn test_basic_auth_parse_no_password() {
|
||||||
let auth: Authorization<Basic> = Header::parse_header(
|
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.username, "Aladdin");
|
||||||
assert_eq!(auth.0.password, Some("".to_owned()));
|
assert_eq!(auth.0.password, Some("".to_owned()));
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_bearer_auth_parse() {
|
fn test_bearer_auth_parse() {
|
||||||
let auth: Authorization<Bearer> = Header::parse_header(
|
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");
|
assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use header::Header;
|
use header::{Header, Raw};
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
||||||
|
|
||||||
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
|
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
|
||||||
@@ -55,7 +55,7 @@ impl Header for CacheControl {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> {
|
fn parse_header(raw: &Raw) -> ::Result<CacheControl> {
|
||||||
let directives = try!(from_comma_delimited(raw));
|
let directives = try!(from_comma_delimited(raw));
|
||||||
if !directives.is_empty() {
|
if !directives.is_empty() {
|
||||||
Ok(CacheControl(directives))
|
Ok(CacheControl(directives))
|
||||||
@@ -167,27 +167,27 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_multiple_headers() {
|
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,
|
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache,
|
||||||
CacheDirective::Private])))
|
CacheDirective::Private])))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_argument() {
|
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),
|
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100),
|
||||||
CacheDirective::Private])))
|
CacheDirective::Private])))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_quote_form() {
|
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)])))
|
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_extension() {
|
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![
|
assert_eq!(cache.ok(), Some(CacheControl(vec![
|
||||||
CacheDirective::Extension("foo".to_owned(), None),
|
CacheDirective::Extension("foo".to_owned(), None),
|
||||||
CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))])))
|
CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))])))
|
||||||
@@ -195,7 +195,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_bad_syntax() {
|
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)
|
assert_eq!(cache.ok(), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ mod tests {
|
|||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
|
|
||||||
fn parse_option(header: Vec<u8>) -> Connection {
|
fn parse_option(header: Vec<u8>) -> Connection {
|
||||||
let val = vec![header];
|
let val = header.into();
|
||||||
let connection: Connection = Header::parse_header(&val[..]).unwrap();
|
let connection: Connection = Header::parse_header(&val).unwrap();
|
||||||
connection
|
connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::fmt;
|
|||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
use url::percent_encoding;
|
use url::percent_encoding;
|
||||||
|
|
||||||
use header::{Header, parsing};
|
use header::{Header, Raw, parsing};
|
||||||
use header::parsing::{parse_extended_value, HTTP_VALUE};
|
use header::parsing::{parse_extended_value, HTTP_VALUE};
|
||||||
use header::shared::Charset;
|
use header::shared::Charset;
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ impl Header for ContentDisposition {
|
|||||||
NAME
|
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| {
|
parsing::from_one_raw_str(raw).and_then(|s: String| {
|
||||||
let mut sections = s.split(';');
|
let mut sections = s.split(';');
|
||||||
let disposition = match sections.next() {
|
let disposition = match sections.next() {
|
||||||
@@ -201,10 +201,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_header() {
|
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 = "form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let b = ContentDisposition {
|
let b = ContentDisposition {
|
||||||
disposition: DispositionType::Ext("form-data".to_owned()),
|
disposition: DispositionType::Ext("form-data".to_owned()),
|
||||||
parameters: vec![
|
parameters: vec![
|
||||||
@@ -217,8 +217,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(a, b);
|
assert_eq!(a, b);
|
||||||
|
|
||||||
let a = [b"attachment; filename=\"image.jpg\"".to_vec()];
|
let a = "attachment; filename=\"image.jpg\"".into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let b = ContentDisposition {
|
let b = ContentDisposition {
|
||||||
disposition: DispositionType::Attachment,
|
disposition: DispositionType::Attachment,
|
||||||
parameters: vec![
|
parameters: vec![
|
||||||
@@ -229,8 +229,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(a, b);
|
assert_eq!(a, b);
|
||||||
|
|
||||||
let a = [b"attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".to_vec()];
|
let a = "attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let b = ContentDisposition {
|
let b = ContentDisposition {
|
||||||
disposition: DispositionType::Attachment,
|
disposition: DispositionType::Attachment,
|
||||||
parameters: vec![
|
parameters: vec![
|
||||||
@@ -245,19 +245,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_display() {
|
fn test_display() {
|
||||||
let a = [b"attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates".to_vec()];
|
let as_string = "attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates";
|
||||||
let as_string = ::std::str::from_utf8(&(a[0])).unwrap();
|
let a = as_string.into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let display_rendered = format!("{}",a);
|
let display_rendered = format!("{}",a);
|
||||||
assert_eq!(as_string, display_rendered);
|
assert_eq!(as_string, display_rendered);
|
||||||
|
|
||||||
let a = [b"attachment; filename*=UTF-8''black%20and%20white.csv".to_vec()];
|
let a = "attachment; filename*=UTF-8''black%20and%20white.csv".into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let display_rendered = format!("{}",a);
|
let display_rendered = format!("{}",a);
|
||||||
assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered);
|
assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered);
|
||||||
|
|
||||||
let a = [b"attachment; filename=colourful.csv".to_vec()];
|
let a = "attachment; filename=colourful.csv".into();
|
||||||
let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap();
|
let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap();
|
||||||
let display_rendered = format!("{}",a);
|
let display_rendered = format!("{}",a);
|
||||||
assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered);
|
assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use header::{Header, parsing};
|
use header::{Header, Raw, parsing};
|
||||||
|
|
||||||
/// `Content-Length` header, defined in
|
/// `Content-Length` header, defined in
|
||||||
/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2)
|
/// [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";
|
static NAME: &'static str = "Content-Length";
|
||||||
NAME
|
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
|
// If multiple Content-Length headers were sent, everything can still
|
||||||
// be alright if they all contain the same value, and all parse
|
// be alright if they all contain the same value, and all parse
|
||||||
// correctly. If not, then it's an error.
|
// correctly. If not, then it's an error.
|
||||||
raw.iter()
|
raw.iter()
|
||||||
.map(::std::ops::Deref::deref)
|
|
||||||
.map(parsing::from_raw_str)
|
.map(parsing::from_raw_str)
|
||||||
.fold(None, |prev, x| {
|
.fold(None, |prev, x| {
|
||||||
match (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".
|
// Can't use the test_header macro because "5, 5" gets cleaned to "5".
|
||||||
#[test]
|
#[test]
|
||||||
fn test_duplicates() {
|
fn test_duplicates() {
|
||||||
let parsed = HeaderField::parse_header(&[b"5"[..].into(),
|
let parsed = HeaderField::parse_header(&vec![b"5".to_vec(),
|
||||||
b"5"[..].into()]).unwrap();
|
b"5".to_vec()].into()).unwrap();
|
||||||
assert_eq!(parsed, HeaderField(5));
|
assert_eq!(parsed, HeaderField(5));
|
||||||
assert_eq!(format!("{}", parsed), "5");
|
assert_eq!(format!("{}", parsed), "5");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use header::{Header, CookiePair, CookieJar};
|
use header::{Header, Raw, CookiePair, CookieJar};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ impl Header for Cookie {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> {
|
fn parse_header(raw: &Raw) -> ::Result<Cookie> {
|
||||||
let mut cookies = Vec::with_capacity(raw.len());
|
let mut cookies = Vec::with_capacity(raw.len());
|
||||||
for cookies_raw in raw.iter() {
|
for cookies_raw in raw.iter() {
|
||||||
let cookies_str = try!(from_utf8(&cookies_raw[..]));
|
let cookies_str = try!(from_utf8(&cookies_raw[..]));
|
||||||
@@ -96,7 +96,7 @@ impl Cookie {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() {
|
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 c1 = CookiePair::new("foo".to_owned(), "bar".to_owned());
|
||||||
let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned());
|
let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned());
|
||||||
assert_eq!(h.ok(), Some(Cookie(vec![c1, c2])));
|
assert_eq!(h.ok(), Some(Cookie(vec![c1, c2])));
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::str;
|
|||||||
|
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
|
|
||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
|
|
||||||
/// The `Expect` header.
|
/// The `Expect` header.
|
||||||
///
|
///
|
||||||
@@ -34,16 +34,15 @@ impl Header for Expect {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> {
|
fn parse_header(raw: &Raw) -> ::Result<Expect> {
|
||||||
if raw.len() == 1 {
|
if let Some(line) = raw.one() {
|
||||||
let text = unsafe {
|
let text = unsafe {
|
||||||
// safe because:
|
// safe because:
|
||||||
// 1. we just checked raw.len == 1
|
// 1. we don't actually care if it's utf8, we just want to
|
||||||
// 2. we don't actually care if it's utf8, we just want to
|
|
||||||
// compare the bytes with the "case" normalized. If it's not
|
// compare the bytes with the "case" normalized. If it's not
|
||||||
// utf8, then the byte comparison will fail, and we'll return
|
// utf8, then the byte comparison will fail, and we'll return
|
||||||
// None. No big deal.
|
// None. No big deal.
|
||||||
str::from_utf8_unchecked(raw.get_unchecked(0))
|
str::from_utf8_unchecked(line)
|
||||||
};
|
};
|
||||||
if UniCase(text) == EXPECT_CONTINUE {
|
if UniCase(text) == EXPECT_CONTINUE {
|
||||||
Ok(Expect::Continue)
|
Ok(Expect::Continue)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use header::parsing::from_one_raw_str;
|
use header::parsing::from_one_raw_str;
|
||||||
@@ -49,7 +49,7 @@ impl Header for Host {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> {
|
fn parse_header(raw: &Raw) -> ::Result<Host> {
|
||||||
from_one_raw_str(raw)
|
from_one_raw_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,14 +98,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_host() {
|
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 {
|
assert_eq!(host.ok(), Some(Host {
|
||||||
hostname: "foo.com".to_owned(),
|
hostname: "foo.com".to_owned(),
|
||||||
port: None
|
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 {
|
assert_eq!(host.ok(), Some(Host {
|
||||||
hostname: "foo.com".to_owned(),
|
hostname: "foo.com".to_owned(),
|
||||||
port: Some(8080)
|
port: Some(8080)
|
||||||
|
|||||||
@@ -67,10 +67,10 @@ mod tests {
|
|||||||
fn test_if_none_match() {
|
fn test_if_none_match() {
|
||||||
let mut if_none_match: ::Result<IfNoneMatch>;
|
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));
|
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 mut entities: Vec<EntityTag> = Vec::new();
|
||||||
let foobar_etag = EntityTag::new(false, "foobar".to_owned());
|
let foobar_etag = EntityTag::new(false, "foobar".to_owned());
|
||||||
let weak_etag = EntityTag::new(true, "weak-etag".to_owned());
|
let weak_etag = EntityTag::new(true, "weak-etag".to_owned());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::fmt::{self, Display};
|
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)
|
/// `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";
|
static NAME: &'static str = "If-Range";
|
||||||
NAME
|
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);
|
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
|
||||||
if let Ok(etag) = etag {
|
if let Ok(etag) = etag {
|
||||||
return Ok(IfRange::EntityTag(etag));
|
return Ok(IfRange::EntityTag(etag));
|
||||||
|
|||||||
@@ -72,15 +72,16 @@ macro_rules! bench_header(
|
|||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_parse(b: &mut Bencher) {
|
fn bench_parse(b: &mut Bencher) {
|
||||||
let val = $value;
|
let val = $value.into();
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let _: $ty = Header::parse_header(&val[..]).unwrap();
|
let _: $ty = Header::parse_header(&val).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_format(b: &mut Bencher) {
|
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);
|
let fmt = ::header::HeaderFormatter(&val);
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
format!("{}", fmt);
|
format!("{}", fmt);
|
||||||
@@ -137,7 +138,8 @@ macro_rules! test_header {
|
|||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
let raw = $raw;
|
let raw = $raw;
|
||||||
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
|
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 result = format!("{}", value.unwrap());
|
||||||
let expected = String::from_utf8(raw[0].to_vec()).unwrap();
|
let expected = String::from_utf8(raw[0].to_vec()).unwrap();
|
||||||
let result_cmp: Vec<String> = result
|
let result_cmp: Vec<String> = result
|
||||||
@@ -157,7 +159,8 @@ macro_rules! test_header {
|
|||||||
#[test]
|
#[test]
|
||||||
fn $id() {
|
fn $id() {
|
||||||
let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect();
|
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;
|
let typed: Option<HeaderField> = $typed;
|
||||||
// Test parsing
|
// Test parsing
|
||||||
assert_eq!(val.ok(), typed);
|
assert_eq!(val.ok(), typed);
|
||||||
@@ -195,9 +198,8 @@ macro_rules! __hyper_generate_header_serialization {
|
|||||||
where D: ::serde::Deserializer {
|
where D: ::serde::Deserializer {
|
||||||
let string_representation: String =
|
let string_representation: String =
|
||||||
try!(::serde::Deserialize::deserialize(deserializer));
|
try!(::serde::Deserialize::deserialize(deserializer));
|
||||||
Ok($crate::header::Header::parse_header(&[
|
let raw = string_representation.into_bytes().into();
|
||||||
string_representation.into_bytes()
|
Ok($crate::header::Header::parse_header(&raw).unwrap())
|
||||||
]).unwrap())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +223,7 @@ macro_rules! header {
|
|||||||
static NAME: &'static str = $n;
|
static NAME: &'static str = $n;
|
||||||
NAME
|
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)
|
$crate::header::parsing::from_comma_delimited(raw).map($id)
|
||||||
}
|
}
|
||||||
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
@@ -248,7 +250,7 @@ macro_rules! header {
|
|||||||
static NAME: &'static str = $n;
|
static NAME: &'static str = $n;
|
||||||
NAME
|
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)
|
$crate::header::parsing::from_comma_delimited(raw).map($id)
|
||||||
}
|
}
|
||||||
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
@@ -274,7 +276,7 @@ macro_rules! header {
|
|||||||
static NAME: &'static str = $n;
|
static NAME: &'static str = $n;
|
||||||
NAME
|
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)
|
$crate::header::parsing::from_one_raw_str(raw).map($id)
|
||||||
}
|
}
|
||||||
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
@@ -303,10 +305,10 @@ macro_rules! header {
|
|||||||
static NAME: &'static str = $n;
|
static NAME: &'static str = $n;
|
||||||
NAME
|
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
|
// FIXME: Return None if no item is in $id::Only
|
||||||
if raw.len() == 1 {
|
if raw.len() == 1 {
|
||||||
if raw[0] == b"*" {
|
if &raw[0] == b"*" {
|
||||||
return Ok($id::Any)
|
return Ok($id::Any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use header::{Header, Host};
|
use header::{Header, Raw, Host};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use header::parsing::from_one_raw_str;
|
use header::parsing::from_one_raw_str;
|
||||||
@@ -57,7 +57,7 @@ impl Header for Origin {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Origin> {
|
fn parse_header(raw: &Raw) -> ::Result<Origin> {
|
||||||
from_one_raw_str(raw)
|
from_one_raw_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +100,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_origin() {
|
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)));
|
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))));
|
assert_eq!(origin.ok(), Some(Origin::new("https", "foo.com", Some(443))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
use header::{Header, parsing};
|
use header::{Header, Raw, parsing};
|
||||||
|
|
||||||
/// The `Pragma` header defined by HTTP/1.0.
|
/// The `Pragma` header defined by HTTP/1.0.
|
||||||
///
|
///
|
||||||
@@ -44,7 +44,7 @@ impl Header for Pragma {
|
|||||||
NAME
|
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| {
|
parsing::from_one_raw_str(raw).and_then(|s: String| {
|
||||||
let slice = &s.to_ascii_lowercase()[..];
|
let slice = &s.to_ascii_lowercase()[..];
|
||||||
match slice {
|
match slice {
|
||||||
@@ -64,12 +64,12 @@ impl Header for Pragma {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_header() {
|
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;
|
let b = Pragma::NoCache;
|
||||||
assert_eq!(a, b);
|
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());
|
let d = Pragma::Ext("FoObar".to_owned());
|
||||||
assert_eq!(c, d);
|
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);
|
assert_eq!(e.ok(), None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
||||||
|
|
||||||
/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
|
/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
|
||||||
@@ -56,7 +56,7 @@ impl Header for Prefer {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> {
|
fn parse_header(raw: &Raw) -> ::Result<Prefer> {
|
||||||
let preferences = try!(from_comma_delimited(raw));
|
let preferences = try!(from_comma_delimited(raw));
|
||||||
if !preferences.is_empty() {
|
if !preferences.is_empty() {
|
||||||
Ok(Prefer(preferences))
|
Ok(Prefer(preferences))
|
||||||
@@ -159,14 +159,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_multiple_headers() {
|
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,
|
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync,
|
||||||
Preference::ReturnRepresentation])))
|
Preference::ReturnRepresentation])))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_argument() {
|
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),
|
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100),
|
||||||
Preference::HandlingLeniant,
|
Preference::HandlingLeniant,
|
||||||
Preference::RespondAsync])))
|
Preference::RespondAsync])))
|
||||||
@@ -174,14 +174,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_quote_form() {
|
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),
|
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200),
|
||||||
Preference::HandlingStrict])))
|
Preference::HandlingStrict])))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_extension() {
|
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![
|
assert_eq!(prefer.ok(), Some(Prefer(vec![
|
||||||
Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
|
Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
|
||||||
Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
|
Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
|
||||||
@@ -192,7 +192,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fail_with_args() {
|
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);
|
assert_eq!(prefer.ok(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use header::{Header, Preference};
|
use header::{Header, Raw, Preference};
|
||||||
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
|
||||||
|
|
||||||
/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
|
/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
|
||||||
@@ -54,7 +54,7 @@ impl Header for PreferenceApplied {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> {
|
fn parse_header(raw: &Raw) -> ::Result<PreferenceApplied> {
|
||||||
let preferences = try!(from_comma_delimited(raw));
|
let preferences = try!(from_comma_delimited(raw));
|
||||||
if !preferences.is_empty() {
|
if !preferences.is_empty() {
|
||||||
Ok(PreferenceApplied(preferences))
|
Ok(PreferenceApplied(preferences))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use header::Header;
|
use header::{Header, Raw};
|
||||||
use header::parsing::{from_one_raw_str, from_comma_delimited};
|
use header::parsing::{from_one_raw_str};
|
||||||
|
|
||||||
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
|
/// `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()) {
|
match (iter.next(), iter.next()) {
|
||||||
(Some("bytes"), Some(ranges)) => {
|
(Some("bytes"), Some(ranges)) => {
|
||||||
match from_comma_delimited(&[ranges]) {
|
let ranges = from_comma_delimited(ranges);
|
||||||
Ok(ranges) => {
|
if ranges.is_empty() {
|
||||||
if ranges.is_empty() {
|
return Err(::Error::Header);
|
||||||
return Err(::Error::Header);
|
|
||||||
}
|
|
||||||
Ok(Range::Bytes(ranges))
|
|
||||||
},
|
|
||||||
Err(_) => Err(::Error::Header)
|
|
||||||
}
|
}
|
||||||
|
Ok(Range::Bytes(ranges))
|
||||||
}
|
}
|
||||||
(Some(unit), Some(range_str)) if unit != "" && range_str != "" => {
|
(Some(unit), Some(range_str)) if unit != "" && range_str != "" => {
|
||||||
Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned()))
|
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 {
|
impl Header for Range {
|
||||||
|
|
||||||
fn header_name() -> &'static str {
|
fn header_name() -> &'static str {
|
||||||
@@ -180,7 +186,7 @@ impl Header for Range {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {
|
fn parse_header(raw: &Raw) -> ::Result<Range> {
|
||||||
from_one_raw_str(raw)
|
from_one_raw_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,29 +198,29 @@ impl Header for Range {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_bytes_range_valid() {
|
fn test_parse_bytes_range_valid() {
|
||||||
let r: 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(&[b"bytes=1-100,-".to_vec()]).unwrap();
|
let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap();
|
||||||
let r3 = Range::bytes(1, 100);
|
let r3 = Range::bytes(1, 100);
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
assert_eq!(r2, r3);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&[b"bytes=1-100,200-".to_vec()]).unwrap();
|
let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap();
|
||||||
let r2: Range = Header::parse_header(&[b"bytes= 1-100 , 101-xxx, 200- ".to_vec()]).unwrap();
|
let r2: Range = Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap();
|
||||||
let r3 = Range::Bytes(
|
let r3 = Range::Bytes(
|
||||||
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)]
|
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)]
|
||||||
);
|
);
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
assert_eq!(r2, r3);
|
||||||
|
|
||||||
let r: 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(&[b"bytes=1-100, ,,-100".to_vec()]).unwrap();
|
let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap();
|
||||||
let r3 = Range::Bytes(
|
let r3 = Range::Bytes(
|
||||||
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)]
|
vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)]
|
||||||
);
|
);
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
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());
|
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
|
|
||||||
@@ -222,40 +228,40 @@ fn test_parse_bytes_range_valid() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unregistered_range_valid() {
|
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());
|
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
||||||
assert_eq!(r, r2);
|
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());
|
let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned());
|
||||||
assert_eq!(r, r2);
|
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());
|
let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_invalid() {
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
assert_eq!(r.ok(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
use header::{Header, parsing};
|
use header::{Header, Raw, parsing};
|
||||||
|
|
||||||
/// `Referrer-Policy` header, part of
|
/// `Referrer-Policy` header, part of
|
||||||
/// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header)
|
/// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header)
|
||||||
@@ -52,7 +52,7 @@ impl Header for ReferrerPolicy {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ReferrerPolicy> {
|
fn parse_header(raw: &Raw) -> ::Result<ReferrerPolicy> {
|
||||||
use self::ReferrerPolicy::*;
|
use self::ReferrerPolicy::*;
|
||||||
parsing::from_one_raw_str(raw).and_then(|s: String| {
|
parsing::from_one_raw_str(raw).and_then(|s: String| {
|
||||||
let slice = &s.to_ascii_lowercase()[..];
|
let slice = &s.to_ascii_lowercase()[..];
|
||||||
@@ -84,9 +84,9 @@ impl Header for ReferrerPolicy {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_header() {
|
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;
|
let b = ReferrerPolicy::Origin;
|
||||||
assert_eq!(a, b);
|
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());
|
assert!(e.is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use header::{Header, CookiePair, CookieJar};
|
use header::{Header, Raw, CookiePair, CookieJar};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ impl Header for SetCookie {
|
|||||||
NAME
|
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());
|
let mut set_cookies = Vec::with_capacity(raw.len());
|
||||||
for set_cookies_raw in raw {
|
for set_cookies_raw in raw {
|
||||||
if let Ok(s) = from_utf8(&set_cookies_raw[..]) {
|
if let Ok(s) = from_utf8(&set_cookies_raw[..]) {
|
||||||
@@ -136,7 +136,7 @@ impl SetCookie {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() {
|
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());
|
let mut c1 = CookiePair::new("foo".to_owned(), "bar".to_owned());
|
||||||
c1.httponly = true;
|
c1.httponly = true;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::str::{self, FromStr};
|
|||||||
|
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
|
|
||||||
use header::{Header, parsing};
|
use header::{Header, Raw, parsing};
|
||||||
|
|
||||||
/// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797)
|
/// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797)
|
||||||
///
|
///
|
||||||
@@ -125,7 +125,7 @@ impl Header for StrictTransportSecurity {
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> {
|
fn parse_header(raw: &Raw) -> ::Result<StrictTransportSecurity> {
|
||||||
parsing::from_one_raw_str(raw)
|
parsing::from_one_raw_str(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,49 +145,49 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_max_age() {
|
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 }));
|
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_max_age_no_value() {
|
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());
|
assert!(h.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_quoted_max_age() {
|
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 }));
|
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_spaces_max_age() {
|
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 }));
|
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_include_subdomains() {
|
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 }));
|
assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: true, max_age: 15768000u64 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_no_max_age() {
|
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());
|
assert!(h.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_max_age_nan() {
|
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());
|
assert!(h.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_duplicate_directives() {
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ header! {
|
|||||||
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||||
#[test]
|
#[test]
|
||||||
fn test3() {
|
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)])));
|
assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ header! {
|
|||||||
fn test2() {
|
fn test2() {
|
||||||
let mut vary: ::Result<Vary>;
|
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));
|
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(),
|
assert_eq!(vary.ok(), Some(Vary::Items(vec!["eTag".parse().unwrap(),
|
||||||
"cookIE".parse().unwrap(),
|
"cookIE".parse().unwrap(),
|
||||||
"AlLOw".parse().unwrap(),])));
|
"AlLOw".parse().unwrap(),])));
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ use std::fmt;
|
|||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
|
|
||||||
use super::cell::{OptCell, PtrMapCell};
|
use super::cell::{OptCell, PtrMapCell};
|
||||||
use header::{Header};
|
use header::{Header, Raw};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
raw: OptCell<Vec<Vec<u8>>>,
|
raw: OptCell<Raw>,
|
||||||
typed: PtrMapCell<Header + Send + Sync>
|
typed: PtrMapCell<Header + Send + Sync>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_raw(data: Vec<Vec<u8>>) -> Item {
|
pub fn new_raw(data: Raw) -> Item {
|
||||||
Item {
|
Item {
|
||||||
raw: OptCell::new(Some(data)),
|
raw: OptCell::new(Some(data)),
|
||||||
typed: PtrMapCell::new(),
|
typed: PtrMapCell::new(),
|
||||||
@@ -33,23 +33,22 @@ impl Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mut_raw(&mut self) -> &mut Vec<Vec<u8>> {
|
pub fn mut_raw(&mut self) -> &mut Raw {
|
||||||
self.typed = PtrMapCell::new();
|
self.typed = PtrMapCell::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
self.raw.get_mut()
|
self.raw.get_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> &[Vec<u8>] {
|
pub fn raw(&self) -> &Raw {
|
||||||
if let Some(ref raw) = *self.raw {
|
if let Some(ref raw) = *self.raw {
|
||||||
return &raw[..];
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
let raw = vec![unsafe { self.typed.one() }.to_string().into_bytes()];
|
let raw = unsafe { self.typed.one() }.to_string().into_bytes().into();
|
||||||
self.raw.set(raw);
|
self.raw.set(raw);
|
||||||
|
|
||||||
let raw = self.raw.as_ref().unwrap();
|
self.raw.as_ref().unwrap()
|
||||||
&raw[..]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn typed<H: Header + Any>(&self) -> Option<&H> {
|
pub fn typed<H: Header + Any>(&self) -> Option<&H> {
|
||||||
@@ -86,10 +85,8 @@ impl Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse<H: Header>(raw: &Vec<Vec<u8>>) ->
|
fn parse<H: Header>(raw: &Raw) -> ::Result<Box<Header + Send + Sync>> {
|
||||||
::Result<Box<Header + Send + Sync>> {
|
H::parse_header(raw).map(|h| {
|
||||||
Header::parse_header(&raw[..]).map(|h: H| {
|
|
||||||
// FIXME: Use Type ascription
|
|
||||||
let h: Box<Header + Send + Sync> = Box::new(h);
|
let h: Box<Header + Send + Sync> = Box::new(h);
|
||||||
h
|
h
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use std::fmt;
|
//! use std::fmt;
|
||||||
//! use hyper::header::Header;
|
//! use hyper::header::{Header, Raw};
|
||||||
//!
|
//!
|
||||||
//! #[derive(Debug, Clone, Copy)]
|
//! #[derive(Debug, Clone, Copy)]
|
||||||
//! struct Dnt(bool);
|
//! struct Dnt(bool);
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
//! "DNT"
|
//! "DNT"
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn parse_header(raw: &[Vec<u8>]) -> hyper::Result<Dnt> {
|
//! fn parse_header(raw: &Raw) -> hyper::Result<Dnt> {
|
||||||
//! if raw.len() == 1 {
|
//! if raw.len() == 1 {
|
||||||
//! let line = &raw[0];
|
//! let line = &raw[0];
|
||||||
//! if line.len() == 1 {
|
//! if line.len() == 1 {
|
||||||
@@ -94,9 +94,11 @@ use serde::ser;
|
|||||||
|
|
||||||
pub use self::shared::*;
|
pub use self::shared::*;
|
||||||
pub use self::common::*;
|
pub use self::common::*;
|
||||||
|
pub use self::raw::Raw;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
mod internals;
|
mod internals;
|
||||||
|
mod raw;
|
||||||
mod shared;
|
mod shared;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ pub trait Header: HeaderClone + Any + GetType + Send + Sync {
|
|||||||
/// it's not necessarily the case that a Header is *allowed* to have more
|
/// it's not necessarily the case that a Header is *allowed* to have more
|
||||||
/// than one field value. If that's the case, you **should** return `None`
|
/// than one field value. If that's the case, you **should** return `None`
|
||||||
/// if `raw.len() > 1`.
|
/// if `raw.len() > 1`.
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self> where Self: Sized;
|
fn parse_header(raw: &Raw) -> ::Result<Self> where Self: Sized;
|
||||||
/// Format a header to be output into a TcpStream.
|
/// Format a header to be output into a TcpStream.
|
||||||
///
|
///
|
||||||
/// This method is not allowed to introduce an Err not produced
|
/// This method is not allowed to introduce an Err not produced
|
||||||
@@ -199,7 +201,6 @@ fn header_name<T: Header>() -> &'static str {
|
|||||||
/// A map of header fields on requests and responses.
|
/// A map of header fields on requests and responses.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Headers {
|
pub struct Headers {
|
||||||
//data: HashMap<HeaderName, Item>
|
|
||||||
data: VecMap<HeaderName, Item>,
|
data: VecMap<HeaderName, Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,13 +271,17 @@ impl Headers {
|
|||||||
for header in raw {
|
for header in raw {
|
||||||
trace!("raw header: {:?}={:?}", header.name, &header.value[..]);
|
trace!("raw header: {:?}={:?}", header.name, &header.value[..]);
|
||||||
let name = HeaderName(UniCase(maybe_literal(header.name)));
|
let name = HeaderName(UniCase(maybe_literal(header.name)));
|
||||||
let mut item = match headers.data.entry(name) {
|
|
||||||
Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])),
|
|
||||||
Entry::Occupied(entry) => entry.into_mut()
|
|
||||||
};
|
|
||||||
let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count();
|
let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count();
|
||||||
let value = &header.value[.. header.value.len() - trim];
|
let value = &header.value[.. header.value.len() - trim];
|
||||||
item.mut_raw().push(value.to_vec());
|
|
||||||
|
match headers.data.entry(name) {
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(Item::new_raw(self::raw::parsed(value)));
|
||||||
|
}
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
entry.into_mut().mut_raw().push(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(headers)
|
Ok(headers)
|
||||||
}
|
}
|
||||||
@@ -290,46 +295,6 @@ impl Headers {
|
|||||||
Item::new_typed(Box::new(value)));
|
Item::new_typed(Box::new(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the raw value of a header.
|
|
||||||
///
|
|
||||||
/// Prefer to use the typed getters instead.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use hyper::header::Headers;
|
|
||||||
/// # let mut headers = Headers::new();
|
|
||||||
/// let raw_content_type = headers.get_raw("content-type");
|
|
||||||
/// ```
|
|
||||||
pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
|
|
||||||
self.data
|
|
||||||
.get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))))
|
|
||||||
.map(Item::raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the raw value of a header, bypassing any typed headers.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use hyper::header::Headers;
|
|
||||||
/// # let mut headers = Headers::new();
|
|
||||||
/// headers.set_raw("content-length", vec![b"5".to_vec()]);
|
|
||||||
/// ```
|
|
||||||
pub fn set_raw<K: Into<Cow<'static, str>> + fmt::Debug>(&mut self, name: K,
|
|
||||||
value: Vec<Vec<u8>>) {
|
|
||||||
trace!("Headers.set_raw( {:?}, {:?} )", name, value);
|
|
||||||
self.data.insert(HeaderName(UniCase(name.into())), Item::new_raw(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a header set by set_raw
|
|
||||||
pub fn remove_raw(&mut self, name: &str) {
|
|
||||||
trace!("Headers.remove_raw( {:?} )", name);
|
|
||||||
self.data.remove(
|
|
||||||
&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the header field's value, if it exists.
|
/// Get a reference to the header field's value, if it exists.
|
||||||
pub fn get<H: Header>(&self) -> Option<&H> {
|
pub fn get<H: Header>(&self) -> Option<&H> {
|
||||||
self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))))
|
self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))))
|
||||||
@@ -379,6 +344,54 @@ impl Headers {
|
|||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.data.clear()
|
self.data.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the raw value of a header.
|
||||||
|
///
|
||||||
|
/// Prefer to use the typed getters instead.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use hyper::header::Headers;
|
||||||
|
/// # let mut headers = Headers::new();
|
||||||
|
/// # headers.set_raw("content-type", "text/plain");
|
||||||
|
/// let raw = headers.get_raw("content-type").unwrap();
|
||||||
|
/// assert_eq!(raw, "text/plain");
|
||||||
|
/// ```
|
||||||
|
pub fn get_raw(&self, name: &str) -> Option<&Raw> {
|
||||||
|
self.data
|
||||||
|
.get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))))
|
||||||
|
.map(Item::raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the raw value of a header, bypassing any typed headers.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use hyper::header::Headers;
|
||||||
|
/// # let mut headers = Headers::new();
|
||||||
|
/// headers.set_raw("content-length", b"1".as_ref());
|
||||||
|
/// headers.set_raw("content-length", "2");
|
||||||
|
/// headers.set_raw("content-length", "3".to_string());
|
||||||
|
/// headers.set_raw("content-length", vec![vec![b'4']]);
|
||||||
|
/// ```
|
||||||
|
pub fn set_raw<K: Into<Cow<'static, str>>, V: Into<Raw>>(&mut self, name: K, value: V) {
|
||||||
|
let name = name.into();
|
||||||
|
let value = value.into();
|
||||||
|
trace!("Headers.set_raw( {:?}, {:?} )", name, value);
|
||||||
|
self.data.insert(HeaderName(UniCase(name)), Item::new_raw(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a header by name.
|
||||||
|
pub fn remove_raw(&mut self, name: &str) {
|
||||||
|
trace!("Headers.remove_raw( {:?} )", name);
|
||||||
|
self.data.remove(
|
||||||
|
&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Headers {
|
impl PartialEq for Headers {
|
||||||
@@ -584,7 +597,7 @@ mod tests {
|
|||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use mime::TopLevel::Text;
|
use mime::TopLevel::Text;
|
||||||
use mime::SubLevel::Plain;
|
use mime::SubLevel::Plain;
|
||||||
use super::{Headers, Header, ContentLength, ContentType,
|
use super::{Headers, Header, Raw, ContentLength, ContentType,
|
||||||
Accept, Host, qitem};
|
Accept, Host, qitem};
|
||||||
use httparse;
|
use httparse;
|
||||||
|
|
||||||
@@ -622,7 +635,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_content_type() {
|
fn test_content_type() {
|
||||||
let content_type = Header::parse_header([b"text/plain".to_vec()].as_ref());
|
let content_type = Header::parse_header(&b"text/plain".as_ref().into());
|
||||||
assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![]))));
|
assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![]))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,11 +644,11 @@ mod tests {
|
|||||||
let text_plain = qitem(Mime(Text, Plain, vec![]));
|
let text_plain = qitem(Mime(Text, Plain, vec![]));
|
||||||
let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap();
|
let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap();
|
||||||
|
|
||||||
let accept = Header::parse_header([b"text/plain".to_vec()].as_ref());
|
let accept = Header::parse_header(&b"text/plain".as_ref().into());
|
||||||
assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()])));
|
assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()])));
|
||||||
|
|
||||||
let bytevec = [b"application/vnd.github.v3.full+json; q=0.5, text/plain".to_vec()];
|
let bytevec = b"application/vnd.github.v3.full+json; q=0.5, text/plain".as_ref().into();
|
||||||
let accept = Header::parse_header(bytevec.as_ref());
|
let accept = Header::parse_header(&bytevec);
|
||||||
assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain])));
|
assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain])));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,20 +659,15 @@ mod tests {
|
|||||||
fn header_name() -> &'static str {
|
fn header_name() -> &'static str {
|
||||||
"content-length"
|
"content-length"
|
||||||
}
|
}
|
||||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CrazyLength> {
|
fn parse_header(raw: &Raw) -> ::Result<CrazyLength> {
|
||||||
use std::str::from_utf8;
|
use std::str::from_utf8;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
if raw.len() != 1 {
|
if let Some(line) = raw.one() {
|
||||||
return Err(::Error::Header);
|
let s = try!(from_utf8(line).map(|s| FromStr::from_str(s).map_err(|_| ::Error::Header)));
|
||||||
}
|
s.map(|u| CrazyLength(Some(false), u))
|
||||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
} else {
|
||||||
match match from_utf8(unsafe { &raw.get_unchecked(0)[..] }) {
|
Err(::Error::Header)
|
||||||
Ok(s) => FromStr::from_str(s).ok(),
|
|
||||||
Err(_) => None
|
|
||||||
}.map(|u| CrazyLength(Some(false), u)) {
|
|
||||||
Some(x) => Ok(x),
|
|
||||||
None => Err(::Error::Header),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ use std::str::FromStr;
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use url::percent_encoding;
|
use url::percent_encoding;
|
||||||
|
|
||||||
|
use header::Raw;
|
||||||
use header::shared::Charset;
|
use header::shared::Charset;
|
||||||
|
|
||||||
/// Reads a single raw string when parsing a header.
|
/// Reads a single raw string when parsing a header.
|
||||||
pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<T> {
|
pub fn from_one_raw_str<T: str::FromStr>(raw: &Raw) -> ::Result<T> {
|
||||||
if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) }
|
if let Some(line) = raw.one() {
|
||||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
if !line.is_empty() {
|
||||||
from_raw_str( unsafe { raw.get_unchecked(0) })
|
return from_raw_str(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(::Error::Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a raw string into a value.
|
/// Reads a raw string into a value.
|
||||||
@@ -23,7 +27,7 @@ pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> {
|
|||||||
|
|
||||||
/// Reads a comma-delimited raw header into a Vec.
|
/// Reads a comma-delimited raw header into a Vec.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_comma_delimited<T: str::FromStr, S: AsRef<[u8]>>(raw: &[S]) -> ::Result<Vec<T>> {
|
pub fn from_comma_delimited<T: str::FromStr>(raw: &Raw) -> ::Result<Vec<T>> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for s in raw {
|
for s in raw {
|
||||||
let s = try!(str::from_utf8(s.as_ref()));
|
let s = try!(str::from_utf8(s.as_ref()));
|
||||||
|
|||||||
229
src/header/raw.rs
Normal file
229
src/header/raw.rs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// A raw header value.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct Raw(Lines);
|
||||||
|
|
||||||
|
impl Raw {
|
||||||
|
/// Returns the amount of lines.
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(..) => 1,
|
||||||
|
Lines::Many(ref lines) => lines.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the line if there is only 1.
|
||||||
|
#[inline]
|
||||||
|
pub fn one(&self) -> Option<&[u8]> {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => Some(line),
|
||||||
|
Lines::Many(ref lines) if lines.len() == 1 => Some(&lines[0]),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate the lines of raw bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn iter(&self) -> RawLines {
|
||||||
|
RawLines {
|
||||||
|
inner: match self.0 {
|
||||||
|
Lines::One(ref line) => unsafe {
|
||||||
|
::std::slice::from_raw_parts(line, 1)
|
||||||
|
}.iter(),
|
||||||
|
Lines::Many(ref lines) => lines.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a line to this `Raw` header value.
|
||||||
|
pub fn push(&mut self, val: &[u8]) {
|
||||||
|
let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new()));
|
||||||
|
match lines {
|
||||||
|
Lines::One(line) => {
|
||||||
|
self.0 = Lines::Many(vec![line, maybe_literal(val.into())]);
|
||||||
|
}
|
||||||
|
Lines::Many(mut lines) => {
|
||||||
|
lines.push(maybe_literal(val.into()));
|
||||||
|
self.0 = Lines::Many(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
enum Lines {
|
||||||
|
One(Line),
|
||||||
|
Many(Vec<Line>)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Line = Cow<'static, [u8]>;
|
||||||
|
|
||||||
|
fn eq<A: AsRef<[u8]>, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool {
|
||||||
|
if a.len() != b.len() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
for (a, b) in a.iter().zip(b.iter()) {
|
||||||
|
if a.as_ref() != b.as_ref() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<[Vec<u8>]> for Raw {
|
||||||
|
fn eq(&self, bytes: &[Vec<u8>]) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => eq(&[line], bytes),
|
||||||
|
Lines::Many(ref lines) => eq(lines, bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<[u8]> for Raw {
|
||||||
|
fn eq(&self, bytes: &[u8]) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => line.as_ref() == bytes,
|
||||||
|
Lines::Many(..) => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<str> for Raw {
|
||||||
|
fn eq(&self, s: &str) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => line.as_ref() == s.as_bytes(),
|
||||||
|
Lines::Many(..) => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Vec<u8>>> for Raw {
|
||||||
|
#[inline]
|
||||||
|
fn from(val: Vec<Vec<u8>>) -> Raw {
|
||||||
|
Raw(Lines::Many(
|
||||||
|
val.into_iter()
|
||||||
|
.map(|vec| maybe_literal(vec.into()))
|
||||||
|
.collect()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Raw {
|
||||||
|
#[inline]
|
||||||
|
fn from(val: String) -> Raw {
|
||||||
|
let vec: Vec<u8> = val.into();
|
||||||
|
vec.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Raw {
|
||||||
|
#[inline]
|
||||||
|
fn from(val: Vec<u8>) -> Raw {
|
||||||
|
Raw(Lines::One(Cow::Owned(val)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Raw {
|
||||||
|
fn from(val: &'static str) -> Raw {
|
||||||
|
Raw(Lines::One(Cow::Borrowed(val.as_bytes())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static [u8]> for Raw {
|
||||||
|
fn from(val: &'static [u8]) -> Raw {
|
||||||
|
Raw(Lines::One(Cow::Borrowed(val)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parsed(val: &[u8]) -> Raw {
|
||||||
|
Raw(Lines::One(maybe_literal(val.into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Raw {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => fmt::Debug::fmt(&[line], f),
|
||||||
|
Lines::Many(ref lines) => fmt::Debug::fmt(lines, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::ops::Index<usize> for Raw {
|
||||||
|
type Output = [u8];
|
||||||
|
fn index(&self, idx: usize) -> &[u8] {
|
||||||
|
match self.0 {
|
||||||
|
Lines::One(ref line) => if idx == 0 {
|
||||||
|
line.as_ref()
|
||||||
|
} else {
|
||||||
|
panic!("index out of bounds: {}", idx)
|
||||||
|
},
|
||||||
|
Lines::Many(ref lines) => lines[idx].as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! literals {
|
||||||
|
($($len:expr => $($value:expr),+;)+) => (
|
||||||
|
fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Cow<'static, [u8]> {
|
||||||
|
match s.len() {
|
||||||
|
$($len => {
|
||||||
|
$(
|
||||||
|
if s.as_ref() == $value {
|
||||||
|
return Cow::Borrowed($value);
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
})+
|
||||||
|
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
Cow::Owned(s.into_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_literal_lens() {
|
||||||
|
$(
|
||||||
|
$({
|
||||||
|
let s = $value;
|
||||||
|
assert!(s.len() == $len, "{:?} has len of {}, listed as {}", s, s.len(), $len);
|
||||||
|
})+
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
literals! {
|
||||||
|
1 => b"*", b"0";
|
||||||
|
3 => b"*/*";
|
||||||
|
4 => b"gzip";
|
||||||
|
5 => b"close";
|
||||||
|
7 => b"chunked";
|
||||||
|
10 => b"keep-alive";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a Raw {
|
||||||
|
type IntoIter = RawLines<'a>;
|
||||||
|
type Item = &'a [u8];
|
||||||
|
|
||||||
|
fn into_iter(self) -> RawLines<'a> {
|
||||||
|
self.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RawLines<'a> {
|
||||||
|
inner: ::std::slice::Iter<'a, Cow<'static, [u8]>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for RawLines<'a> {
|
||||||
|
type Item = &'a [u8];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<&'a [u8]> {
|
||||||
|
self.inner.next().map(AsRef::as_ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user