perf(header): use MemSlice when parsing headers
This commit is contained in:
		
				
					committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							5c890321ee
						
					
				
				
					commit
					1b556389c0
				
			| @@ -40,6 +40,7 @@ impl Header for ContentLength { | |||||||
|         static NAME: &'static str = "Content-Length"; |         static NAME: &'static str = "Content-Length"; | ||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &Raw) -> ::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 | ||||||
| @@ -49,9 +50,9 @@ impl Header for ContentLength { | |||||||
|             .fold(None, |prev, x| { |             .fold(None, |prev, x| { | ||||||
|                 match (prev, x) { |                 match (prev, x) { | ||||||
|                     (None, x) => Some(x), |                     (None, x) => Some(x), | ||||||
|                     (e@Some(Err(_)), _ ) => e, |                     (e @ Some(Err(_)), _ ) => e, | ||||||
|                     (Some(Ok(prev)), Ok(x)) if prev == x => Some(Ok(prev)), |                     (Some(Ok(prev)), Ok(x)) if prev == x => Some(Ok(prev)), | ||||||
|                     _ => Some(Err(::Error::Header)) |                     _ => Some(Err(::Error::Header)), | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|             .unwrap_or(Err(::Error::Header)) |             .unwrap_or(Err(::Error::Header)) | ||||||
|   | |||||||
| @@ -179,14 +179,14 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn hyper_headers_from_raw_delay() { |     fn hyper_headers_from_raw_delay() { | ||||||
|         let headers = Headers::from_raw(&[httparse::Header { name: "Retry-After", value: b"300" }]).unwrap(); |         let headers = make_header!(b"Retry-After", b"300"); | ||||||
|         let retry_after = headers.get::<RetryAfter>().unwrap(); |         let retry_after = headers.get::<RetryAfter>().unwrap(); | ||||||
|         assert_eq!(retry_after, &RetryAfter::Delay(Duration::seconds(300))); |         assert_eq!(retry_after, &RetryAfter::Delay(Duration::seconds(300))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn hyper_headers_from_raw_datetime() { |     fn hyper_headers_from_raw_datetime() { | ||||||
|         let headers = Headers::from_raw(&[httparse::Header { name: "Retry-After", value: b"Sun, 06 Nov 1994 08:49:37 GMT" }]).unwrap(); |         let headers = make_header!(b"Retry-After", b"Sun, 06 Nov 1994 08:49:37 GMT"); | ||||||
|         let retry_after = headers.get::<RetryAfter>().unwrap(); |         let retry_after = headers.get::<RetryAfter>().unwrap(); | ||||||
|         let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap(); |         let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -88,6 +88,7 @@ use self::internals::{Item, VecMap, Entry}; | |||||||
| pub use self::shared::*; | pub use self::shared::*; | ||||||
| pub use self::common::*; | pub use self::common::*; | ||||||
| pub use self::raw::Raw; | pub use self::raw::Raw; | ||||||
|  | use http::buf::MemSlice; | ||||||
|  |  | ||||||
| mod common; | mod common; | ||||||
| mod internals; | mod internals; | ||||||
| @@ -353,20 +354,20 @@ impl Headers { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[doc(hidden)] |     #[doc(hidden)] | ||||||
|     pub fn from_raw(raw: &[httparse::Header]) -> ::Result<Headers> { |     pub fn from_raw(raw: &[httparse::Header], buf: MemSlice) -> ::Result<Headers> { | ||||||
|         let mut headers = Headers::new(); |         let mut headers = Headers::new(); | ||||||
|         for header in raw { |         for header in raw { | ||||||
|             trace!("raw header: {:?}={:?}", header.name, &header.value[..]); |  | ||||||
|             let name = HeaderName(UniCase(maybe_literal(header.name))); |             let name = HeaderName(UniCase(maybe_literal(header.name))); | ||||||
|             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_start = header.value.as_ptr() as usize - buf.get().as_ptr() as usize; | ||||||
|  |             let value_end = value_start + header.value.len() - trim; | ||||||
|  |  | ||||||
|             match headers.data.entry(name) { |             match headers.data.entry(name) { | ||||||
|                 Entry::Vacant(entry) => { |                 Entry::Vacant(entry) => { | ||||||
|                     entry.insert(Item::new_raw(self::raw::parsed(value))); |                     entry.insert(Item::new_raw(self::raw::parsed(buf.slice(value_start..value_end)))); | ||||||
|                 } |                 } | ||||||
|                 Entry::Occupied(entry) => { |                 Entry::Occupied(entry) => { | ||||||
|                     entry.into_mut().mut_raw().push(value); |                     entry.into_mut().mut_raw().push_slice(buf.slice(value_start..value_end)); | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
| @@ -663,24 +664,23 @@ mod tests { | |||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|     use test::Bencher; |     use test::Bencher; | ||||||
|  |  | ||||||
|     // Slice.position_elem was unstable |  | ||||||
|     fn index_of(slice: &[u8], byte: u8) -> Option<usize> { |  | ||||||
|         for (index, &b) in slice.iter().enumerate() { |  | ||||||
|             if b == byte { |  | ||||||
|                 return Some(index); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         None |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     macro_rules! raw { |     macro_rules! raw { | ||||||
|         ($($line:expr),*) => ({ |         ($($line:expr),*) => ({ | ||||||
|             [$({ |             [$({ | ||||||
|                 let line = $line; |                 // Slice.position_elem was unstable | ||||||
|                 let pos = index_of(line, b':').expect("raw splits on ':', not found"); |                 fn index_of(slice: &MemSlice, byte: u8) -> Option<usize> { | ||||||
|  |                     for (index, &b) in slice.as_ref().iter().enumerate() { | ||||||
|  |                         if b == byte { | ||||||
|  |                             return Some(index); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     None | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 let pos = index_of(&$line, b':').expect("raw splits on ':', not found"); | ||||||
|                 httparse::Header { |                 httparse::Header { | ||||||
|                     name: ::std::str::from_utf8(&line[..pos]).unwrap(), |                     name: ::std::str::from_utf8(&$line[..pos]).unwrap(), | ||||||
|                     value: &line[pos + 2..] |                     value: &$line[pos + 2..] | ||||||
|                 } |                 } | ||||||
|             }),*] |             }),*] | ||||||
|         }) |         }) | ||||||
| @@ -688,7 +688,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_from_raw() { |     fn test_from_raw() { | ||||||
|         let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap(); |         let headers = make_header!(b"Content-Length", b"10"); | ||||||
|         assert_eq!(headers.get(), Some(&ContentLength(10))); |         assert_eq!(headers.get(), Some(&ContentLength(10))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -738,20 +738,20 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_different_structs_for_same_header() { |     fn test_different_structs_for_same_header() { | ||||||
|         let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap(); |         let headers = make_header!(b"Content-Length: 10"); | ||||||
|         assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10))); |         assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10))); | ||||||
|         assert_eq!(headers.get::<CrazyLength>(), Some(&CrazyLength(Some(false), 10))); |         assert_eq!(headers.get::<CrazyLength>(), Some(&CrazyLength(Some(false), 10))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_trailing_whitespace() { |     fn test_trailing_whitespace() { | ||||||
|         let headers = Headers::from_raw(&raw!(b"Content-Length: 10   ")).unwrap(); |         let headers = make_header!(b"Content-Length: 10   "); | ||||||
|         assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10))); |         assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_multiple_reads() { |     fn test_multiple_reads() { | ||||||
|         let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap(); |         let headers = make_header!(b"Content-Length: 10"); | ||||||
|         let ContentLength(one) = *headers.get::<ContentLength>().unwrap(); |         let ContentLength(one) = *headers.get::<ContentLength>().unwrap(); | ||||||
|         let ContentLength(two) = *headers.get::<ContentLength>().unwrap(); |         let ContentLength(two) = *headers.get::<ContentLength>().unwrap(); | ||||||
|         assert_eq!(one, two); |         assert_eq!(one, two); | ||||||
| @@ -759,15 +759,14 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_different_reads() { |     fn test_different_reads() { | ||||||
|         let headers = Headers::from_raw( |         let headers = make_header!(b"Content-Length: 10\r\nContent-Type: text/plain"); | ||||||
|             &raw!(b"Content-Length: 10", b"Content-Type: text/plain")).unwrap(); |  | ||||||
|         let ContentLength(_) = *headers.get::<ContentLength>().unwrap(); |         let ContentLength(_) = *headers.get::<ContentLength>().unwrap(); | ||||||
|         let ContentType(_) = *headers.get::<ContentType>().unwrap(); |         let ContentType(_) = *headers.get::<ContentType>().unwrap(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_get_mutable() { |     fn test_get_mutable() { | ||||||
|         let mut headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap(); |         let mut headers = make_header!(b"Content-Length: 10"); | ||||||
|         *headers.get_mut::<ContentLength>().unwrap() = ContentLength(20); |         *headers.get_mut::<ContentLength>().unwrap() = ContentLength(20); | ||||||
|         assert_eq!(headers.get_raw("content-length").unwrap(), &[b"20".to_vec()][..]); |         assert_eq!(headers.get_raw("content-length").unwrap(), &[b"20".to_vec()][..]); | ||||||
|         assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20)); |         assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20)); | ||||||
| @@ -786,7 +785,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_headers_to_string_raw() { |     fn test_headers_to_string_raw() { | ||||||
|         let mut headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap(); |         let mut headers = make_header!(b"Content-Length: 10"); | ||||||
|         headers.set_raw("x-foo", vec![b"foo".to_vec(), b"bar".to_vec()]); |         headers.set_raw("x-foo", vec![b"foo".to_vec(), b"bar".to_vec()]); | ||||||
|         let s = headers.to_string(); |         let s = headers.to_string(); | ||||||
|         assert_eq!(s, "Content-Length: 10\r\nx-foo: foo\r\nx-foo: bar\r\n"); |         assert_eq!(s, "Content-Length: 10\r\nx-foo: foo\r\nx-foo: bar\r\n"); | ||||||
| @@ -903,8 +902,12 @@ mod tests { | |||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|     #[bench] |     #[bench] | ||||||
|     fn bench_headers_from_raw(b: &mut Bencher) { |     fn bench_headers_from_raw(b: &mut Bencher) { | ||||||
|         let raw = raw!(b"Content-Length: 10"); |         use ::http::buf::MemSlice; | ||||||
|         b.iter(|| Headers::from_raw(&raw).unwrap()) |  | ||||||
|  |         let buf = MemSlice::from(b"Content-Length: 10" as &[u8]); | ||||||
|  |         let buf_clone = buf.clone(); | ||||||
|  |         let raw = raw!(buf_clone); | ||||||
|  |         b.iter(|| Headers::from_raw(&raw, buf.clone()).unwrap()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| use std::borrow::Cow; | use std::borrow::Cow; | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  | use http::buf::MemSlice; | ||||||
|  |  | ||||||
| /// A raw header value. | /// A raw header value. | ||||||
| #[derive(Clone, PartialEq, Eq)] | #[derive(Clone, PartialEq, Eq)] | ||||||
| @@ -19,8 +20,8 @@ impl Raw { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn one(&self) -> Option<&[u8]> { |     pub fn one(&self) -> Option<&[u8]> { | ||||||
|         match self.0 { |         match self.0 { | ||||||
|             Lines::One(ref line) => Some(line), |             Lines::One(ref line) => Some(line.as_ref()), | ||||||
|             Lines::Many(ref lines) if lines.len() == 1 => Some(&lines[0]), |             Lines::Many(ref lines) if lines.len() == 1 => Some(lines[0].as_ref()), | ||||||
|             _ => None |             _ => None | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -29,12 +30,8 @@ impl Raw { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn iter(&self) -> RawLines { |     pub fn iter(&self) -> RawLines { | ||||||
|         RawLines { |         RawLines { | ||||||
|             inner: match self.0 { |             inner: &self.0, | ||||||
|                 Lines::One(ref line) => unsafe { |             pos: 0, | ||||||
|                     ::std::slice::from_raw_parts(line, 1) |  | ||||||
|                 }.iter(), |  | ||||||
|                 Lines::Many(ref lines) => lines.iter() |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -51,15 +48,34 @@ impl Raw { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     pub fn push_slice(&mut self, val: MemSlice) { | ||||||
|  |         let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new())); | ||||||
|  |         match lines { | ||||||
|  |             Lines::One(line) => { | ||||||
|  |                 self.0 = Lines::Many(vec![line, Line::Shared(val)]); | ||||||
|  |             } | ||||||
|  |             Lines::Many(mut lines) => { | ||||||
|  |                 lines.push(Line::Shared(val)); | ||||||
|  |                 self.0 = Lines::Many(lines); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, PartialEq, Eq)] | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
| enum Lines { | enum Lines { | ||||||
|     One(Line), |     One(Line), | ||||||
|     Many(Vec<Line>) |     Many(Vec<Line>), | ||||||
| } | } | ||||||
|  |  | ||||||
| type Line = Cow<'static, [u8]>; | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | enum Line { | ||||||
|  |     Static(&'static [u8]), | ||||||
|  |     Owned(Vec<u8>), | ||||||
|  |     Shared(MemSlice), | ||||||
|  | } | ||||||
|  |  | ||||||
| fn eq<A: AsRef<[u8]>, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { | fn eq<A: AsRef<[u8]>, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { | ||||||
|     if a.len() != b.len() { |     if a.len() != b.len() { | ||||||
| @@ -123,24 +139,55 @@ impl From<String> for Raw { | |||||||
| impl From<Vec<u8>> for Raw { | impl From<Vec<u8>> for Raw { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn from(val: Vec<u8>) -> Raw { |     fn from(val: Vec<u8>) -> Raw { | ||||||
|         Raw(Lines::One(Cow::Owned(val))) |         Raw(Lines::One(Line::from(val))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<&'static str> for Raw { | impl From<&'static str> for Raw { | ||||||
|     fn from(val: &'static str) -> Raw { |     fn from(val: &'static str) -> Raw { | ||||||
|         Raw(Lines::One(Cow::Borrowed(val.as_bytes()))) |         Raw(Lines::One(Line::Static(val.as_bytes()))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<&'static [u8]> for Raw { | impl From<&'static [u8]> for Raw { | ||||||
|     fn from(val: &'static [u8]) -> Raw { |     fn from(val: &'static [u8]) -> Raw { | ||||||
|         Raw(Lines::One(Cow::Borrowed(val))) |         Raw(Lines::One(Line::Static(val))) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn parsed(val: &[u8]) -> Raw { | impl From<MemSlice> for Raw { | ||||||
|     Raw(Lines::One(maybe_literal(val.into()))) |     #[inline] | ||||||
|  |     fn from(val: MemSlice) -> Raw { | ||||||
|  |         Raw(Lines::One(Line::Shared(val))) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Vec<u8>> for Line { | ||||||
|  |     #[inline] | ||||||
|  |     fn from(val: Vec<u8>) -> Line { | ||||||
|  |         Line::Owned(val) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<MemSlice> for Line { | ||||||
|  |     #[inline] | ||||||
|  |     fn from(val: MemSlice) -> Line { | ||||||
|  |         Line::Shared(val) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl AsRef<[u8]> for Line { | ||||||
|  |     fn as_ref(&self) -> &[u8] { | ||||||
|  |         match *self { | ||||||
|  |             Line::Static(ref s) => s, | ||||||
|  |             Line::Owned(ref v) => v.as_ref(), | ||||||
|  |             Line::Shared(ref m) => m.as_ref(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn parsed(val: MemSlice) -> Raw { | ||||||
|  |     Raw(Lines::One(From::from(val))) | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Raw { | impl fmt::Debug for Raw { | ||||||
| @@ -154,6 +201,7 @@ impl fmt::Debug for Raw { | |||||||
|  |  | ||||||
| impl ::std::ops::Index<usize> for Raw { | impl ::std::ops::Index<usize> for Raw { | ||||||
|     type Output = [u8]; |     type Output = [u8]; | ||||||
|  |  | ||||||
|     fn index(&self, idx: usize) -> &[u8] { |     fn index(&self, idx: usize) -> &[u8] { | ||||||
|         match self.0 { |         match self.0 { | ||||||
|             Lines::One(ref line) => if idx == 0 { |             Lines::One(ref line) => if idx == 0 { | ||||||
| @@ -168,12 +216,12 @@ impl ::std::ops::Index<usize> for Raw { | |||||||
|  |  | ||||||
| macro_rules! literals { | macro_rules! literals { | ||||||
|     ($($len:expr => $($value:expr),+;)+) => ( |     ($($len:expr => $($value:expr),+;)+) => ( | ||||||
|         fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Cow<'static, [u8]> { |         fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Line { | ||||||
|             match s.len() { |             match s.len() { | ||||||
|                 $($len => { |                 $($len => { | ||||||
|                     $( |                     $( | ||||||
|                     if s.as_ref() == $value { |                     if s.as_ref() == $value { | ||||||
|                         return Cow::Borrowed($value); |                         return Line::Static($value); | ||||||
|                     } |                     } | ||||||
|                     )+ |                     )+ | ||||||
|                 })+ |                 })+ | ||||||
| @@ -181,7 +229,7 @@ macro_rules! literals { | |||||||
|                 _ => () |                 _ => () | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             Cow::Owned(s.into_owned()) |             Line::from(s.into_owned()) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         #[test] |         #[test] | ||||||
| @@ -216,7 +264,8 @@ impl<'a> IntoIterator for &'a Raw { | |||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct RawLines<'a> { | pub struct RawLines<'a> { | ||||||
|     inner: ::std::slice::Iter<'a, Cow<'static, [u8]>> |     inner: &'a Lines, | ||||||
|  |     pos: usize, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> Iterator for RawLines<'a> { | impl<'a> Iterator for RawLines<'a> { | ||||||
| @@ -224,6 +273,17 @@ impl<'a> Iterator for RawLines<'a> { | |||||||
|  |  | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn next(&mut self) -> Option<&'a [u8]> { |     fn next(&mut self) -> Option<&'a [u8]> { | ||||||
|         self.inner.next().map(AsRef::as_ref) |         let current_pos = self.pos; | ||||||
|  |         self.pos += 1; | ||||||
|  |         match *self.inner { | ||||||
|  |             Lines::One(ref line) => { | ||||||
|  |                 if current_pos == 0 { | ||||||
|  |                     Some(line.as_ref()) | ||||||
|  |                 } else { | ||||||
|  |                     None | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Lines::Many(ref lines) => lines.get(current_pos).map(|l| l.as_ref()), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | use std::borrow::Cow; | ||||||
| use std::cell::UnsafeCell; | use std::cell::UnsafeCell; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io::{self, Read}; | use std::io::{self, Read}; | ||||||
| @@ -195,6 +196,11 @@ pub struct MemSlice { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl MemSlice { | impl MemSlice { | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     pub fn get(&self) -> &[u8] { | ||||||
|  |         unsafe { &(*self.buf.get())[self.start..self.end] } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn empty() -> MemSlice { |     pub fn empty() -> MemSlice { | ||||||
|         MemSlice { |         MemSlice { | ||||||
|             buf: Arc::new(UnsafeCell::new(Vec::new())), |             buf: Arc::new(UnsafeCell::new(Vec::new())), | ||||||
| @@ -208,6 +214,11 @@ impl MemSlice { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl AsRef<[u8]> for MemSlice { | ||||||
|  |     fn as_ref(&self) -> &[u8] { | ||||||
|  |         self.get() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl fmt::Debug for MemSlice { | impl fmt::Debug for MemSlice { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
| @@ -215,11 +226,90 @@ impl fmt::Debug for MemSlice { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Deref for  MemSlice { | impl Deref for MemSlice { | ||||||
|     type Target = [u8]; |     type Target = [u8]; | ||||||
|  |  | ||||||
|     fn deref(&self) -> &[u8] { |     fn deref(&self) -> &[u8] { | ||||||
|         unsafe { |         self.get() | ||||||
|             &(*self.buf.get())[self.start..self.end] |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> From<&'a [u8]> for MemSlice { | ||||||
|  |     fn from(v: &'a [u8]) -> MemSlice { | ||||||
|  |         MemSlice { | ||||||
|  |             buf: Arc::new(UnsafeCell::new(v.to_vec())), | ||||||
|  |             start: 0, | ||||||
|  |             end: v.len(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Vec<u8>> for MemSlice { | ||||||
|  |     fn from(v: Vec<u8>) -> MemSlice { | ||||||
|  |         let len = v.len(); | ||||||
|  |         MemSlice { | ||||||
|  |             buf: Arc::new(UnsafeCell::new(v)), | ||||||
|  |             start: 0, | ||||||
|  |             end: len, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> From<&'a str> for MemSlice { | ||||||
|  |     fn from(v: &'a str) -> MemSlice { | ||||||
|  |         let v = v.as_bytes(); | ||||||
|  |         MemSlice { | ||||||
|  |             buf: Arc::new(UnsafeCell::new(v.to_vec())), | ||||||
|  |             start: 0, | ||||||
|  |             end: v.len(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> From<Cow<'a, [u8]>> for MemSlice { | ||||||
|  |     fn from(v: Cow<'a, [u8]>) -> MemSlice { | ||||||
|  |         let v = v.into_owned(); | ||||||
|  |         let len = v.len(); | ||||||
|  |         MemSlice { | ||||||
|  |             buf: Arc::new(UnsafeCell::new(v)), | ||||||
|  |             start: 0, | ||||||
|  |             end: len, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq for MemSlice { | ||||||
|  |     fn eq(&self, other: &MemSlice) -> bool { | ||||||
|  |         self.get() == other.get() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<[u8]> for MemSlice { | ||||||
|  |     fn eq(&self, other: &[u8]) -> bool { | ||||||
|  |         self.get() == other | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<str> for MemSlice { | ||||||
|  |     fn eq(&self, other: &str) -> bool { | ||||||
|  |         self.get() == other.as_bytes() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<Vec<u8>> for MemSlice { | ||||||
|  |     fn eq(&self, other: &Vec<u8>) -> bool { | ||||||
|  |         self.get() == other.as_slice() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Eq for MemSlice {} | ||||||
|  |  | ||||||
|  | impl Clone for MemSlice { | ||||||
|  |     fn clone(&self) -> MemSlice { | ||||||
|  |         MemSlice { | ||||||
|  |             buf: self.buf.clone(), | ||||||
|  |             start: self.start, | ||||||
|  |             end: self.end, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ use httparse; | |||||||
| use header::{self, Headers, ContentLength, TransferEncoding}; | use header::{self, Headers, ContentLength, TransferEncoding}; | ||||||
| use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine}; | use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine}; | ||||||
| use http::h1::{Encoder, Decoder}; | use http::h1::{Encoder, Decoder}; | ||||||
|  | use http::buf::MemSlice; | ||||||
| use method::Method; | use method::Method; | ||||||
| use status::StatusCode; | use status::StatusCode; | ||||||
| use version::HttpVersion::{Http10, Http11}; | use version::HttpVersion::{Http10, Http11}; | ||||||
| @@ -13,7 +14,7 @@ use version::HttpVersion::{Http10, Http11}; | |||||||
| const MAX_HEADERS: usize = 100; | const MAX_HEADERS: usize = 100; | ||||||
| const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific | const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific | ||||||
|  |  | ||||||
| pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> { | pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: MemSlice) -> ParseResult<I> { | ||||||
|     if buf.len() == 0 { |     if buf.len() == 0 { | ||||||
|         return Ok(None); |         return Ok(None); | ||||||
|     } |     } | ||||||
| @@ -25,11 +26,11 @@ impl Http1Transaction for ServerTransaction { | |||||||
|     type Incoming = RequestLine; |     type Incoming = RequestLine; | ||||||
|     type Outgoing = StatusCode; |     type Outgoing = StatusCode; | ||||||
|  |  | ||||||
|     fn parse(buf: &[u8]) -> ParseResult<RequestLine> { |     fn parse(buf: MemSlice) -> ParseResult<RequestLine> { | ||||||
|         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; |         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||||
|         trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); |         trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||||
|         let mut req = httparse::Request::new(&mut headers); |         let mut req = httparse::Request::new(&mut headers); | ||||||
|         Ok(match try!(req.parse(buf)) { |         Ok(match try!(req.parse(buf.clone().get())) { | ||||||
|             httparse::Status::Complete(len) => { |             httparse::Status::Complete(len) => { | ||||||
|                 trace!("Request.parse Complete({})", len); |                 trace!("Request.parse Complete({})", len); | ||||||
|                 Some((MessageHead { |                 Some((MessageHead { | ||||||
| @@ -38,10 +39,10 @@ impl Http1Transaction for ServerTransaction { | |||||||
|                         try!(req.method.unwrap().parse()), |                         try!(req.method.unwrap().parse()), | ||||||
|                         try!(req.path.unwrap().parse()) |                         try!(req.path.unwrap().parse()) | ||||||
|                     ), |                     ), | ||||||
|                     headers: try!(Headers::from_raw(req.headers)) |                     headers: try!(Headers::from_raw(req.headers, buf)) | ||||||
|                 }, len)) |                 }, len)) | ||||||
|             }, |             } | ||||||
|             httparse::Status::Partial => None |             httparse::Status::Partial => None, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -112,11 +113,11 @@ impl Http1Transaction for ClientTransaction { | |||||||
|     type Incoming = RawStatus; |     type Incoming = RawStatus; | ||||||
|     type Outgoing = RequestLine; |     type Outgoing = RequestLine; | ||||||
|  |  | ||||||
|     fn parse(buf: &[u8]) -> ParseResult<RawStatus> { |     fn parse(buf: MemSlice) -> ParseResult<RawStatus> { | ||||||
|         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; |         let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; | ||||||
|         trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); |         trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); | ||||||
|         let mut res = httparse::Response::new(&mut headers); |         let mut res = httparse::Response::new(&mut headers); | ||||||
|         Ok(match try!(res.parse(buf)) { |         Ok(match try!(res.parse(buf.clone().get())) { | ||||||
|             httparse::Status::Complete(len) => { |             httparse::Status::Complete(len) => { | ||||||
|                 trace!("Response.try_parse Complete({})", len); |                 trace!("Response.try_parse Complete({})", len); | ||||||
|                 let code = res.code.unwrap(); |                 let code = res.code.unwrap(); | ||||||
| @@ -127,7 +128,7 @@ impl Http1Transaction for ClientTransaction { | |||||||
|                 Some((MessageHead { |                 Some((MessageHead { | ||||||
|                     version: if res.version.unwrap() == 1 { Http11 } else { Http10 }, |                     version: if res.version.unwrap() == 1 { Http11 } else { Http10 }, | ||||||
|                     subject: RawStatus(code, reason), |                     subject: RawStatus(code, reason), | ||||||
|                     headers: try!(Headers::from_raw(res.headers)) |                     headers: try!(Headers::from_raw(res.headers, buf)) | ||||||
|                 }, len)) |                 }, len)) | ||||||
|             }, |             }, | ||||||
|             httparse::Status::Partial => None |             httparse::Status::Partial => None | ||||||
| @@ -244,21 +245,22 @@ fn extend(dst: &mut Vec<u8>, data: &[u8]) { | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use http; |     use http; | ||||||
|  |     use http::buf::MemSlice; | ||||||
|     use super::{parse}; |     use super::{parse}; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_request() { |     fn test_parse_request() { | ||||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; |         let raw = MemSlice::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n" as &[u8]); | ||||||
|         parse::<http::ServerTransaction, _>(raw).unwrap(); |         parse::<http::ServerTransaction, _>(raw).unwrap(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_raw_status() { |     fn test_parse_raw_status() { | ||||||
|         let raw = b"HTTP/1.1 200 OK\r\n\r\n"; |         let raw = MemSlice::from(b"HTTP/1.1 200 OK\r\n\r\n" as &[u8]); | ||||||
|         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); |         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); | ||||||
|         assert_eq!(res.subject.1, "OK"); |         assert_eq!(res.subject.1, "OK"); | ||||||
|  |  | ||||||
|         let raw = b"HTTP/1.1 200 Howdy\r\n\r\n"; |         let raw = MemSlice::from(b"HTTP/1.1 200 Howdy\r\n\r\n" as &[u8]); | ||||||
|         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); |         let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap(); | ||||||
|         assert_eq!(res.subject.1, "Howdy"); |         assert_eq!(res.subject.1, "Howdy"); | ||||||
|     } |     } | ||||||
| @@ -269,9 +271,25 @@ mod tests { | |||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|     #[bench] |     #[bench] | ||||||
|     fn bench_parse_incoming(b: &mut Bencher) { |     fn bench_parse_incoming(b: &mut Bencher) { | ||||||
|         let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"; |         let raw = MemSlice::from(b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ | ||||||
|  |                                   I_wonder/Hard_to_write_in_an_uri_after_all/you_have_to_make\ | ||||||
|  |                                   _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\ | ||||||
|  |                                   foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1\r\nHost: \ | ||||||
|  |                                   hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \ | ||||||
|  |                                   utf8\r\nAccept-Encoding: *\r\nAccess-Control-Allow-\ | ||||||
|  |                                   Credentials: None\r\nAccess-Control-Allow-Origin: None\r\n\ | ||||||
|  |                                   Access-Control-Allow-Methods: None\r\nAccess-Control-Allow-\ | ||||||
|  |                                   Headers: None\r\nContent-Encoding: utf8\r\nContent-Security-\ | ||||||
|  |                                   Policy: None\r\nContent-Type: text/html\r\nOrigin: hyper\ | ||||||
|  |                                   \r\nSec-Websocket-Extensions: It looks super important!\r\n\ | ||||||
|  |                                   Sec-Websocket-Origin: hyper\r\nSec-Websocket-Version: 4.3\r\ | ||||||
|  |                                   \nStrict-Transport-Security: None\r\nUser-Agent: hyper\r\n\ | ||||||
|  |                                   X-Content-Duration: None\r\nX-Content-Security-Policy: None\ | ||||||
|  |                                   \r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \ | ||||||
|  |                                   Something important obviously\r\nX-Requested-With: Nothing\ | ||||||
|  |                                   \r\n\r\n" as &[u8]); | ||||||
|         b.iter(|| { |         b.iter(|| { | ||||||
|             parse::<http::ServerTransaction, _>(raw).unwrap() |             parse::<http::ServerTransaction, _>(raw.clone()).unwrap() | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ impl<T: Io> Buffered<T> { | |||||||
|                 _ => return Err(e.into()) |                 _ => return Err(e.into()) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         match try!(parse::<S, _>(self.read_buf.bytes())) { |         match try!(parse::<S, _>(MemSlice::from(self.read_buf.bytes()))) { | ||||||
|             Some((head, len)) => { |             Some((head, len)) => { | ||||||
|                 trace!("parsed {} bytes out of {}", len, self.read_buf.len()); |                 trace!("parsed {} bytes out of {}", len, self.read_buf.len()); | ||||||
|                 self.read_buf.slice(len); |                 self.read_buf.slice(len); | ||||||
| @@ -140,7 +140,7 @@ impl<T: Write> Write for Buffered<T> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> { | fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: MemSlice) -> ParseResult<I> { | ||||||
|     h1::parse::<T, I>(rdr) |     h1::parse::<T, I>(rdr) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,9 +13,11 @@ use version::HttpVersion::{Http10, Http11}; | |||||||
| pub use self::conn::{Conn, KeepAlive, KA}; | pub use self::conn::{Conn, KeepAlive, KA}; | ||||||
| pub use self::body::{Body, TokioBody}; | pub use self::body::{Body, TokioBody}; | ||||||
| pub use self::chunk::Chunk; | pub use self::chunk::Chunk; | ||||||
|  | use self::buf::MemSlice; | ||||||
|  |  | ||||||
| mod body; | mod body; | ||||||
| mod buf; | #[doc(hidden)] | ||||||
|  | pub mod buf; | ||||||
| mod chunk; | mod chunk; | ||||||
| mod conn; | mod conn; | ||||||
| mod io; | mod io; | ||||||
| @@ -123,7 +125,7 @@ pub trait Http1Transaction { | |||||||
|     type Incoming; |     type Incoming; | ||||||
|     type Outgoing: Default; |     type Outgoing: Default; | ||||||
|     //type KeepAlive: KeepAlive; |     //type KeepAlive: KeepAlive; | ||||||
|     fn parse(bytes: &[u8]) -> ParseResult<Self::Incoming>; |     fn parse(bytes: MemSlice) -> ParseResult<Self::Incoming>; | ||||||
|     fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>; |     fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>; | ||||||
|     fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder; |     fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder; | ||||||
|     fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool; |     fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool; | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| #![doc(html_root_url = "https://hyperium.github.io/hyper/")] | #![doc(html_root_url = "https://hyperium.github.io/hyper/")] | ||||||
| #![deny(missing_docs)] | #![deny(missing_docs)] | ||||||
| #![deny(warnings)] | //#![deny(warnings)] | ||||||
| #![deny(missing_debug_implementations)] | #![deny(missing_debug_implementations)] | ||||||
| #![cfg_attr(all(test, feature = "nightly"), feature(test))] | #![cfg_attr(all(test, feature = "nightly"), feature(test))] | ||||||
|  |  | ||||||
| @@ -55,6 +55,47 @@ macro_rules! unimplemented { | |||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | macro_rules! make_header { | ||||||
|  |     ($name:expr, $value:expr) => {{ | ||||||
|  |         use ::http::buf::MemSlice; | ||||||
|  |  | ||||||
|  |         let mut headers = [httparse::EMPTY_HEADER; 100]; | ||||||
|  |         let mut req = httparse::Request::new(&mut headers); | ||||||
|  |  | ||||||
|  |         let mut v: Vec<u8> = Vec::with_capacity($name.len() + $value.len() + 32); | ||||||
|  |         v.extend_from_slice(b"GET /404 HTTP/1.1\r\n" as &[u8]); | ||||||
|  |         v.extend_from_slice($name as &[u8]); | ||||||
|  |         v.extend_from_slice(b": " as &[u8]); | ||||||
|  |         v.extend_from_slice($value as &[u8]); | ||||||
|  |         v.extend_from_slice(b"\r\n\r\n" as &[u8]); | ||||||
|  |         let buf = MemSlice::from(v); | ||||||
|  |         match req.parse(buf.clone().get()).expect("parse failed") { | ||||||
|  |             _ => { | ||||||
|  |                 Headers::from_raw(req.headers, buf).expect("from_raw failed") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }}; | ||||||
|  |     ($name:expr) => {{ | ||||||
|  |         use ::http::buf::MemSlice; | ||||||
|  |  | ||||||
|  |         let mut headers = [httparse::EMPTY_HEADER; 100]; | ||||||
|  |         let mut req = httparse::Request::new(&mut headers); | ||||||
|  |  | ||||||
|  |         let mut v: Vec<u8> = Vec::with_capacity($name.len() + 25); | ||||||
|  |         v.extend_from_slice(b"GET /404 HTTP/1.1\r\n" as &[u8]); | ||||||
|  |         v.extend_from_slice($name as &[u8]); | ||||||
|  |         v.extend_from_slice(b"\r\n\r\n" as &[u8]); | ||||||
|  |         let buf = MemSlice::from(v); | ||||||
|  |         match req.parse(buf.clone().get()).expect("parse failed") { | ||||||
|  |             httparse::Status::Complete(_) => { | ||||||
|  |                 Headers::from_raw(req.headers, buf).expect("from_raw failed") | ||||||
|  |             } | ||||||
|  |             _ => panic!("got unexpected value"), | ||||||
|  |         } | ||||||
|  |     }} | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod mock; | mod mock; | ||||||
| pub mod client; | pub mod client; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user