Merge pull request #370 from hyperium/httparse
perf(http): changes http parsing to use httparse crate
This commit is contained in:
@@ -32,9 +32,9 @@ impl<S: Scheme + 'static> Header for Authorization<S> where <S as FromStr>::Err:
|
||||
match (from_utf8(unsafe { &raw.get_unchecked(0)[..] }), Scheme::scheme(None::<S>)) {
|
||||
(Ok(header), Some(scheme))
|
||||
if header.starts_with(scheme) && header.len() > scheme.len() + 1 => {
|
||||
header[scheme.len() + 1..].parse::<S>().map(|s| Authorization(s)).ok()
|
||||
header[scheme.len() + 1..].parse::<S>().map(Authorization).ok()
|
||||
},
|
||||
(Ok(header), None) => header.parse::<S>().map(|s| Authorization(s)).ok(),
|
||||
(Ok(header), None) => header.parse::<S>().map(Authorization).ok(),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
@@ -143,7 +143,7 @@ impl FromStr for Basic {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Authorization, Basic};
|
||||
use super::super::super::{Headers};
|
||||
use super::super::super::{Headers, Header};
|
||||
|
||||
#[test]
|
||||
fn test_raw_auth() {
|
||||
@@ -154,8 +154,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_raw_auth_parse() {
|
||||
let headers = Headers::from_raw(&mut b"Authorization: foo bar baz\r\n\r\n").unwrap();
|
||||
assert_eq!(&headers.get::<Authorization<String>>().unwrap().0[..], "foo bar baz");
|
||||
let header: Authorization<String> = Header::parse_header(&[b"foo bar baz".to_vec()]).unwrap();
|
||||
assert_eq!(header.0, "foo bar baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -174,17 +174,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_parse() {
|
||||
let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n").unwrap();
|
||||
let auth = headers.get::<Authorization<Basic>>().unwrap();
|
||||
assert_eq!(&auth.0.username[..], "Aladdin");
|
||||
let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap();
|
||||
assert_eq!(auth.0.username, "Aladdin");
|
||||
assert_eq!(auth.0.password, Some("open sesame".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_parse_no_password() {
|
||||
let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjo=\r\n\r\n").unwrap();
|
||||
let auth = headers.get::<Authorization<Basic>>().unwrap();
|
||||
assert_eq!(auth.0.username.as_slice(), "Aladdin");
|
||||
let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap();
|
||||
assert_eq!(auth.0.username, "Aladdin");
|
||||
assert_eq!(auth.0.password, Some("".to_string()));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
//! must implement the `Header` trait from this module. Several common headers
|
||||
//! are already provided, such as `Host`, `ContentType`, `UserAgent`, and others.
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow::{Borrowed, Owned};
|
||||
use std::borrow::Cow::{Borrowed};
|
||||
use std::borrow::ToOwned;
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use std::raw::TraitObject;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::{Iter, Entry};
|
||||
@@ -15,10 +15,11 @@ use std::iter::{FromIterator, IntoIterator};
|
||||
use std::borrow::{Cow, IntoCow};
|
||||
use std::{mem, raw};
|
||||
|
||||
use httparse;
|
||||
use unicase::UniCase;
|
||||
|
||||
use self::internals::Item;
|
||||
use {http, HttpResult, HttpError};
|
||||
use error::HttpResult;
|
||||
|
||||
pub use self::shared::{Charset, Encoding, EntityTag, Quality, QualityItem, qitem, q};
|
||||
pub use self::common::*;
|
||||
@@ -105,10 +106,6 @@ pub struct Headers {
|
||||
data: HashMap<HeaderName, Item>
|
||||
}
|
||||
|
||||
// To prevent DOS from a server sending a never ending header.
|
||||
// The value was copied from curl.
|
||||
const MAX_HEADERS_LENGTH: u32 = 100 * 1024;
|
||||
|
||||
impl Headers {
|
||||
|
||||
/// Creates a new, empty headers map.
|
||||
@@ -119,27 +116,18 @@ impl Headers {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn from_raw<R: Read>(rdr: &mut R) -> HttpResult<Headers> {
|
||||
pub fn from_raw<'a>(raw: &[httparse::Header<'a>]) -> HttpResult<Headers> {
|
||||
let mut headers = Headers::new();
|
||||
let mut count = 0u32;
|
||||
loop {
|
||||
match try!(http::read_header(rdr)) {
|
||||
Some((name, value)) => {
|
||||
debug!("raw header: {:?}={:?}", name, &value[..]);
|
||||
count += (name.len() + value.len()) as u32;
|
||||
if count > MAX_HEADERS_LENGTH {
|
||||
debug!("Max header size reached, aborting");
|
||||
return Err(HttpError::HttpHeaderError)
|
||||
}
|
||||
let name = UniCase(Owned(name));
|
||||
let mut item = match headers.data.entry(name) {
|
||||
Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])),
|
||||
Entry::Occupied(entry) => entry.into_mut()
|
||||
};
|
||||
item.mut_raw().push(value);
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
for header in raw {
|
||||
debug!("raw header: {:?}={:?}", header.name, &header.value[..]);
|
||||
let name = UniCase(header.name.to_owned().into_cow());
|
||||
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 value = &header.value[.. header.value.len() - trim];
|
||||
item.mut_raw().push(value.to_vec());
|
||||
}
|
||||
Ok(headers)
|
||||
}
|
||||
@@ -364,12 +352,26 @@ mod tests {
|
||||
use mime::SubLevel::Plain;
|
||||
use super::{Headers, Header, HeaderFormat, ContentLength, ContentType,
|
||||
Accept, Host, qitem};
|
||||
use httparse;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
macro_rules! raw {
|
||||
($($line:expr),*) => ({
|
||||
[$({
|
||||
let line = $line;
|
||||
let pos = line.position_elem(&b':').expect("raw splits on :, not found");
|
||||
httparse::Header {
|
||||
name: ::std::str::from_utf8(&line[..pos]).unwrap(),
|
||||
value: &line[pos + 2..]
|
||||
}
|
||||
}),*]
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_raw() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
|
||||
assert_eq!(headers.get(), Some(&ContentLength(10)));
|
||||
}
|
||||
|
||||
@@ -422,20 +424,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_different_structs_for_same_header() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
|
||||
assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10)));
|
||||
assert_eq!(headers.get::<CrazyLength>(), Some(&CrazyLength(Some(false), 10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailing_whitespace() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10 \r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10 ")).unwrap();
|
||||
assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_reads() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
|
||||
let ContentLength(one) = *headers.get::<ContentLength>().unwrap();
|
||||
let ContentLength(two) = *headers.get::<ContentLength>().unwrap();
|
||||
assert_eq!(one, two);
|
||||
@@ -443,14 +445,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_different_reads() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10", b"Content-Type: text/plain")).unwrap();
|
||||
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
|
||||
let ContentType(_) = *headers.get::<ContentType>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mutable() {
|
||||
let mut headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap();
|
||||
let mut headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
|
||||
*headers.get_mut::<ContentLength>().unwrap() = ContentLength(20);
|
||||
assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20));
|
||||
}
|
||||
@@ -471,7 +473,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_headers_show_raw() {
|
||||
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
|
||||
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
|
||||
let s = headers.to_string();
|
||||
assert_eq!(s, "Content-Length: 10\r\n");
|
||||
}
|
||||
@@ -538,7 +540,8 @@ mod tests {
|
||||
|
||||
#[bench]
|
||||
fn bench_headers_from_raw(b: &mut Bencher) {
|
||||
b.iter(|| Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap())
|
||||
let raw = raw!(b"Content-Length: 10");
|
||||
b.iter(|| Headers::from_raw(&raw).unwrap())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
||||
Reference in New Issue
Block a user