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

@@ -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");
}
}