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