From 61364d245bd24b10ffb70e98bc8682cbb09ce34b Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 26 Jan 2017 23:49:44 -0800 Subject: [PATCH] perf(header): improve on MemSlice usage in headers --- src/header/common/retry_after.rs | 14 ++--- src/header/internals/vec_map.rs | 5 +- src/header/mod.rs | 93 +++++++++++++------------------- src/header/parsing.rs | 4 +- src/header/raw.rs | 24 ++++----- src/http/buf.rs | 87 +++++++++++++++++------------- src/http/chunk.rs | 2 +- src/http/conn.rs | 15 ++++++ src/http/h1/decode.rs | 8 +-- src/http/h1/parse.rs | 65 +++++++++++++++------- src/http/io.rs | 12 ++--- src/http/mod.rs | 4 +- src/lib.rs | 43 +-------------- src/mock.rs | 9 +++- 14 files changed, 190 insertions(+), 195 deletions(-) diff --git a/src/header/common/retry_after.rs b/src/header/common/retry_after.rs index e12f951d..23c4bf6c 100644 --- a/src/header/common/retry_after.rs +++ b/src/header/common/retry_after.rs @@ -141,9 +141,7 @@ impl Header for RetryAfter { #[cfg(test)] mod tests { - extern crate httparse; - - use header::{Header, Headers}; + use header::Header; use header::shared::HttpDate; use time::{Duration}; @@ -179,17 +177,15 @@ mod tests { #[test] fn hyper_headers_from_raw_delay() { - let headers = make_header!(b"Retry-After", b"300"); - let retry_after = headers.get::().unwrap(); - assert_eq!(retry_after, &RetryAfter::Delay(Duration::seconds(300))); + let retry_after = RetryAfter::parse_header(&b"300".to_vec().into()).unwrap(); + assert_eq!(retry_after, RetryAfter::Delay(Duration::seconds(300))); } #[test] fn hyper_headers_from_raw_datetime() { - let headers = make_header!(b"Retry-After", b"Sun, 06 Nov 1994 08:49:37 GMT"); - let retry_after = headers.get::().unwrap(); + let retry_after = RetryAfter::parse_header(&b"Sun, 06 Nov 1994 08:49:37 GMT".to_vec().into()).unwrap(); let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::().unwrap(); - assert_eq!(retry_after, &RetryAfter::DateTime(expected.0)); + assert_eq!(retry_after, RetryAfter::DateTime(expected.0)); } } diff --git a/src/header/internals/vec_map.rs b/src/header/internals/vec_map.rs index 70b30d17..ecdaa17b 100644 --- a/src/header/internals/vec_map.rs +++ b/src/header/internals/vec_map.rs @@ -4,9 +4,9 @@ pub struct VecMap { } impl VecMap { - pub fn new() -> VecMap { + pub fn with_capacity(cap: usize) -> VecMap { VecMap { - vec: Vec::new() + vec: Vec::with_capacity(cap) } } @@ -43,6 +43,7 @@ impl VecMap { } pub fn len(&self) -> usize { self.vec.len() } + pub fn iter(&self) -> ::std::slice::Iter<(K, V)> { self.vec.iter() } diff --git a/src/header/mod.rs b/src/header/mod.rs index b3e8881c..d56716f1 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -80,7 +80,6 @@ use std::borrow::{Cow, ToOwned}; use std::iter::{FromIterator, IntoIterator}; use std::{mem, fmt}; -use httparse; use unicase::UniCase; use self::internals::{Item, VecMap, Entry}; @@ -347,31 +346,17 @@ literals! { impl Headers { /// Creates a new, empty headers map. + #[inline] pub fn new() -> Headers { - Headers { - data: VecMap::new() - } + Headers::with_capacity(0) } - #[doc(hidden)] - pub fn from_raw(raw: &[httparse::Header], buf: MemSlice) -> ::Result { - let mut headers = Headers::new(); - for header in raw { - let name = HeaderName(UniCase(maybe_literal(header.name))); - let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count(); - 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) { - Entry::Vacant(entry) => { - entry.insert(Item::new_raw(self::raw::parsed(buf.slice(value_start..value_end)))); - } - Entry::Occupied(entry) => { - entry.into_mut().mut_raw().push_slice(buf.slice(value_start..value_end)); - } - }; + /// Creates a new `Headers` struct with space reserved for `len` headers. + #[inline] + pub fn with_capacity(len: usize) -> Headers { + Headers { + data: VecMap::with_capacity(len) } - Ok(headers) } /// Set a header field to the corresponding value. @@ -587,6 +572,24 @@ impl<'a> Extend> for Headers { } } +impl<'a> Extend<(&'a str, MemSlice)> for Headers { + fn extend>(&mut self, iter: I) { + for (name, value) in iter { + let name = HeaderName(UniCase(maybe_literal(name))); + //let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count(); + + match self.data.entry(name) { + Entry::Vacant(entry) => { + entry.insert(Item::new_raw(self::raw::parsed(value))); + } + Entry::Occupied(entry) => { + self::raw::push(entry.into_mut().mut_raw(), value); + } + }; + } + } +} + impl<'a> FromIterator> for Headers { fn from_iter>>(iter: I) -> Headers { let mut headers = Headers::new(); @@ -659,33 +662,22 @@ mod tests { use mime::SubLevel::Plain; use super::{Headers, Header, Raw, ContentLength, ContentType, Accept, Host, qitem}; - use httparse; #[cfg(feature = "nightly")] use test::Bencher; - macro_rules! raw { - ($($line:expr),*) => ({ - [$({ - // Slice.position_elem was unstable - fn index_of(slice: &MemSlice, byte: u8) -> Option { - 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 { - name: ::std::str::from_utf8(&$line[..pos]).unwrap(), - value: &$line[pos + 2..] - } - }),*] + macro_rules! make_header { + ($name:expr, $value:expr) => ({ + let mut headers = Headers::new(); + headers.set_raw(String::from_utf8($name.to_vec()).unwrap(), $value.to_vec()); + headers + }); + ($text:expr) => ({ + let bytes = $text; + let colon = bytes.iter().position(|&x| x == b':').unwrap(); + make_header!(&bytes[..colon], &bytes[colon + 2..]) }) } - #[test] fn test_from_raw() { let headers = make_header!(b"Content-Length", b"10"); @@ -759,7 +751,9 @@ mod tests { #[test] fn test_different_reads() { - let headers = make_header!(b"Content-Length: 10\r\nContent-Type: text/plain"); + let mut headers = Headers::new(); + headers.set_raw("Content-Length", "10"); + headers.set_raw("Content-Type", "text/plain"); let ContentLength(_) = *headers.get::().unwrap(); let ContentType(_) = *headers.get::().unwrap(); } @@ -899,17 +893,6 @@ mod tests { }) } - #[cfg(feature = "nightly")] - #[bench] - fn bench_headers_from_raw(b: &mut Bencher) { - use ::http::buf::MemSlice; - - 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")] #[bench] fn bench_headers_get(b: &mut Bencher) { diff --git a/src/header/parsing.rs b/src/header/parsing.rs index e3c4c070..8caecf78 100644 --- a/src/header/parsing.rs +++ b/src/header/parsing.rs @@ -21,7 +21,7 @@ pub fn from_one_raw_str(raw: &Raw) -> ::Result { /// Reads a raw string into a value. pub fn from_raw_str(raw: &[u8]) -> ::Result { - let s = try!(str::from_utf8(raw)); + let s = try!(str::from_utf8(raw)).trim(); T::from_str(s).or(Err(::Error::Header)) } @@ -36,7 +36,7 @@ pub fn from_comma_delimited(raw: &Raw) -> ::Result> { "" => None, y => Some(y) }) - .filter_map(|x| x.parse().ok())) + .filter_map(|x| x.trim().parse().ok())) } Ok(result) } diff --git a/src/header/raw.rs b/src/header/raw.rs index b165dd1d..5e93d677 100644 --- a/src/header/raw.rs +++ b/src/header/raw.rs @@ -37,27 +37,17 @@ impl Raw { /// 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); - } - } + self.push_line(maybe_literal(val.into())); } - #[doc(hidden)] - pub fn push_slice(&mut self, val: MemSlice) { + fn push_line(&mut self, line: Line) { 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::One(one) => { + self.0 = Lines::Many(vec![one, line]); } Lines::Many(mut lines) => { - lines.push(Line::Shared(val)); + lines.push(line); self.0 = Lines::Many(lines); } } @@ -190,6 +180,10 @@ pub fn parsed(val: MemSlice) -> Raw { Raw(Lines::One(From::from(val))) } +pub fn push(raw: &mut Raw, val: MemSlice) { + raw.push_line(Line::from(val)); +} + impl fmt::Debug for Raw { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { diff --git a/src/http/buf.rs b/src/http/buf.rs index 0425a29d..f31a4680 100644 --- a/src/http/buf.rs +++ b/src/http/buf.rs @@ -1,14 +1,14 @@ use std::borrow::Cow; -use std::cell::UnsafeCell; +use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::io::{self, Read}; -use std::ops::{Deref, Range, RangeFrom, RangeTo, RangeFull}; +use std::ops::{Index, Range, RangeFrom, RangeTo, RangeFull}; use std::ptr; use std::sync::Arc; pub struct MemBuf { buf: Arc>>, - start: usize, + start: Cell, end: usize, } @@ -20,13 +20,13 @@ impl MemBuf { pub fn with_capacity(cap: usize) -> MemBuf { MemBuf { buf: Arc::new(UnsafeCell::new(vec![0; cap])), - start: 0, + start: Cell::new(0), end: 0, } } pub fn bytes(&self) -> &[u8] { - &self.buf()[self.start..self.end] + &self.buf()[self.start.get()..self.end] } pub fn is_empty(&self) -> bool { @@ -34,7 +34,7 @@ impl MemBuf { } pub fn len(&self) -> usize { - self.end - self.start + self.end - self.start.get() } pub fn capacity(&self) -> usize { @@ -42,20 +42,21 @@ impl MemBuf { } pub fn read_from(&mut self, io: &mut R) -> io::Result { - let start = self.end - self.start; + let start = self.end - self.start.get(); let n = try!(io.read(&mut self.buf_mut()[start..])); self.end += n; Ok(n) } - pub fn slice(&mut self, len: usize) -> MemSlice { - assert!(self.end - self.start >= len); - let start = self.start; - self.start += len; + pub fn slice(&self, len: usize) -> MemSlice { + assert!(self.end - self.start.get() >= len); + let start = self.start.get(); + let end = start + len; + self.start.set(end); MemSlice { buf: self.buf.clone(), start: start, - end: self.start, + end: end, } } @@ -68,18 +69,18 @@ impl MemBuf { } let is_unique = Arc::get_mut(&mut self.buf).is_some(); trace!("MemBuf::reserve {} access", if is_unique { "unique" } else { "shared" }); - if is_unique && remaining + self.start >= needed { + if is_unique && remaining + self.start.get() >= needed { // we have unique access, we can mutate this vector trace!("MemBuf::reserve unique access, shifting"); unsafe { let mut buf = &mut *self.buf.get(); let len = self.len(); ptr::copy( - buf.as_ptr().offset(self.start as isize), + buf.as_ptr().offset(self.start.get() as isize), buf.as_mut_ptr(), len ); - self.start = 0; + self.start.set(0); self.end = len; } } else if is_unique { @@ -110,7 +111,7 @@ impl MemBuf { match Arc::get_mut(&mut self.buf) { Some(_) => { trace!("MemBuf::reset was unique, re-using"); - self.start = 0; + self.start.set(0); self.end = 0; }, None => { @@ -120,13 +121,19 @@ impl MemBuf { } } + #[cfg(all(feature = "nightly", test))] + pub fn restart(&mut self) { + Arc::get_mut(&mut self.buf).unwrap(); + self.start.set(0); + } + fn buf_mut(&mut self) -> &mut [u8] { // The contract here is that we NEVER have a MemSlice that exists // with slice.end > self.start. // In other words, we should *ALWAYS* be the only instance that can // look at the bytes on the right side of self.start. unsafe { - &mut (*self.buf.get())[self.start..] + &mut (*self.buf.get())[self.start.get()..] } } @@ -170,9 +177,9 @@ fn test_grow_zerofill() { impl fmt::Debug for MemBuf { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MemBuf") - .field("start", &self.start) + .field("start", &self.start.get()) .field("end", &self.end) - .field("buf", &&self.buf()[self.start..self.end]) + .field("buf", &&self.buf()[self.start.get()..self.end]) .finish() } } @@ -183,7 +190,7 @@ impl From> for MemBuf { vec.shrink_to_fit(); MemBuf { buf: Arc::new(UnsafeCell::new(vec)), - start: 0, + start: Cell::new(0), end: end, } } @@ -196,11 +203,6 @@ pub struct MemSlice { } impl MemSlice { - #[doc(hidden)] - pub fn get(&self) -> &[u8] { - unsafe { &(*self.buf.get())[self.start..self.end] } - } - pub fn empty() -> MemSlice { MemSlice { buf: Arc::new(UnsafeCell::new(Vec::new())), @@ -209,9 +211,21 @@ impl MemSlice { } } + pub fn len(&self) -> usize { + self.get().len() + } + + pub fn is_empty(&self) -> bool { + self.get().is_empty() + } + pub fn slice(&self, range: S) -> MemSlice { range.slice(self) } + + fn get(&self) -> &[u8] { + unsafe { &(*self.buf.get())[self.start..self.end] } + } } impl AsRef<[u8]> for MemSlice { @@ -222,15 +236,14 @@ impl AsRef<[u8]> for MemSlice { impl fmt::Debug for MemSlice { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) + fmt::Debug::fmt(&self.get(), f) } } -impl Deref for MemSlice { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.get() +impl Index for MemSlice { + type Output = u8; + fn index(&self, i: usize) -> &u8 { + &self.get()[i] } } @@ -386,18 +399,18 @@ mod tests { #[test] fn test_mem_slice_slice() { - let mut buf = MemBuf::from(b"Hello World".to_vec()); + let buf = MemBuf::from(b"Hello World".to_vec()); let len = buf.len(); let full = buf.slice(len); - assert_eq!(&*full, b"Hello World"); - assert_eq!(&*full.slice(6..), b"World"); - assert_eq!(&*full.slice(..5), b"Hello"); - assert_eq!(&*full.slice(..), b"Hello World"); + assert_eq!(full.as_ref(), b"Hello World"); + assert_eq!(full.slice(6..).as_ref(), b"World"); + assert_eq!(full.slice(..5).as_ref(), b"Hello"); + assert_eq!(full.slice(..).as_ref(), b"Hello World"); for a in 0..len { for b in a..len { - assert_eq!(&*full.slice(a..b), &b"Hello World"[a..b], "{}..{}", a, b); + assert_eq!(full.slice(a..b).as_ref(), &b"Hello World"[a..b], "{}..{}", a, b); } } } diff --git a/src/http/chunk.rs b/src/http/chunk.rs index 04e598fe..a8d6f408 100644 --- a/src/http/chunk.rs +++ b/src/http/chunk.rs @@ -69,7 +69,7 @@ impl AsRef<[u8]> for Chunk { match self.0 { Inner::Owned(ref vec) => vec, Inner::Referenced(ref vec) => vec, - Inner::Mem(ref slice) => slice, + Inner::Mem(ref slice) => slice.as_ref(), Inner::Static(slice) => slice, } } diff --git a/src/http/conn.rs b/src/http/conn.rs index 758b961a..7954fadb 100644 --- a/src/http/conn.rs +++ b/src/http/conn.rs @@ -595,6 +595,21 @@ mod tests { } } + #[test] + fn test_conn_parse_partial() { + let good_message = b"GET / HTTP/1.1\r\nHost: foo.bar\r\n\r\n".to_vec(); + let io = AsyncIo::new_buf(good_message, 10); + let mut conn = Conn::<_, ServerTransaction>::new(io, Default::default()); + assert!(conn.poll().unwrap().is_not_ready()); + conn.io.io_mut().block_in(50); + let async = conn.poll().unwrap(); + assert!(async.is_ready()); + match async { + Async::Ready(Some(Frame::Message { .. })) => (), + f => panic!("frame is not Message: {:?}", f), + } + } + #[test] fn test_conn_closed_read() { let io = AsyncIo::new_buf(vec![], 0); diff --git a/src/http/h1/decode.rs b/src/http/h1/decode.rs index aaef02ea..1c226cce 100644 --- a/src/http/h1/decode.rs +++ b/src/http/h1/decode.rs @@ -88,7 +88,7 @@ impl Decoder { } else { let to_read = *remaining as usize; let buf = try!(body.read_mem(to_read)); - let num = buf.len() as u64; + let num = buf.as_ref().len() as u64; trace!("Length read: {}", num); if num > *remaining { *remaining = 0; @@ -399,7 +399,7 @@ mod tests { let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode"); assert_eq!(16, buf.len()); - let result = String::from_utf8(buf.to_vec()).expect("decode String"); + let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); assert_eq!("1234567890abcdef", &result); } @@ -411,7 +411,7 @@ mod tests { // normal read let buf = decoder.decode(&mut mock_buf).expect("decode"); assert_eq!(16, buf.len()); - let result = String::from_utf8(buf.to_vec()).expect("decode String"); + let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); assert_eq!("1234567890abcdef", &result); // eof read @@ -438,7 +438,7 @@ mod tests { if buf.is_empty() { break; // eof } - outs.write(&buf).expect("write buffer"); + outs.write(buf.as_ref()).expect("write buffer"); } Err(e) => match e.kind() { io::ErrorKind::WouldBlock => { diff --git a/src/http/h1/parse.rs b/src/http/h1/parse.rs index 99cd2b51..814eab67 100644 --- a/src/http/h1/parse.rs +++ b/src/http/h1/parse.rs @@ -6,7 +6,7 @@ use httparse; use header::{self, Headers, ContentLength, TransferEncoding}; use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine}; use http::h1::{Encoder, Decoder}; -use http::buf::MemSlice; +use http::buf::{MemBuf, MemSlice}; use method::Method; use status::StatusCode; use version::HttpVersion::{Http10, Http11}; @@ -14,7 +14,7 @@ use version::HttpVersion::{Http10, Http11}; const MAX_HEADERS: usize = 100; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific -pub fn parse, I>(buf: MemSlice) -> ParseResult { +pub fn parse, I>(buf: &MemBuf) -> ParseResult { if buf.len() == 0 { return Ok(None); } @@ -26,20 +26,26 @@ impl Http1Transaction for ServerTransaction { type Incoming = RequestLine; type Outgoing = StatusCode; - fn parse(buf: MemSlice) -> ParseResult { + fn parse(buf: &MemBuf) -> ParseResult { let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); let mut req = httparse::Request::new(&mut headers); - Ok(match try!(req.parse(buf.clone().get())) { + Ok(match try!(req.parse(buf.bytes())) { httparse::Status::Complete(len) => { trace!("Request.parse Complete({})", len); + let mut headers = Headers::with_capacity(req.headers.len()); + let slice = buf.slice(len); + headers.extend(HeadersAsMemSliceIter { + headers: req.headers.iter(), + slice: slice, + }); Some((MessageHead { version: if req.version.unwrap() == 1 { Http11 } else { Http10 }, subject: RequestLine( try!(req.method.unwrap().parse()), try!(req.path.unwrap().parse()) ), - headers: try!(Headers::from_raw(req.headers, buf)) + headers: headers, }, len)) } httparse::Status::Partial => None, @@ -113,11 +119,11 @@ impl Http1Transaction for ClientTransaction { type Incoming = RawStatus; type Outgoing = RequestLine; - fn parse(buf: MemSlice) -> ParseResult { + fn parse(buf: &MemBuf) -> ParseResult { let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len()); let mut res = httparse::Response::new(&mut headers); - Ok(match try!(res.parse(buf.clone().get())) { + Ok(match try!(res.parse(buf.bytes())) { httparse::Status::Complete(len) => { trace!("Response.try_parse Complete({})", len); let code = res.code.unwrap(); @@ -125,10 +131,16 @@ impl Http1Transaction for ClientTransaction { Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason), _ => Cow::Owned(res.reason.unwrap().to_owned()) }; + let mut headers = Headers::with_capacity(res.headers.len()); + let slice = buf.slice(len); + headers.extend(HeadersAsMemSliceIter { + headers: res.headers.iter(), + slice: slice, + }); Some((MessageHead { version: if res.version.unwrap() == 1 { Http11 } else { Http10 }, subject: RawStatus(code, reason), - headers: try!(Headers::from_raw(res.headers, buf)) + headers: headers, }, len)) }, httparse::Status::Partial => None @@ -217,6 +229,22 @@ impl Http1Transaction for ClientTransaction { } } +struct HeadersAsMemSliceIter<'a> { + headers: ::std::slice::Iter<'a, httparse::Header<'a>>, + slice: MemSlice, +} + +impl<'a> Iterator for HeadersAsMemSliceIter<'a> { + type Item = (&'a str, MemSlice); + fn next(&mut self) -> Option { + self.headers.next().map(|header| { + let value_start = header.value.as_ptr() as usize - self.slice.as_ref().as_ptr() as usize; + let value_end = value_start + header.value.len(); + (header.name, self.slice.slice(value_start..value_end)) + }) + } +} + struct FastWrite<'a>(&'a mut Vec); impl<'a> fmt::Write for FastWrite<'a> { @@ -245,23 +273,23 @@ fn extend(dst: &mut Vec, data: &[u8]) { #[cfg(test)] mod tests { use http; - use http::buf::MemSlice; + use http::buf::MemBuf; use super::{parse}; #[test] fn test_parse_request() { - let raw = MemSlice::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n" as &[u8]); - parse::(raw).unwrap(); + let raw = MemBuf::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec()); + parse::(&raw).unwrap(); } #[test] fn test_parse_raw_status() { - let raw = MemSlice::from(b"HTTP/1.1 200 OK\r\n\r\n" as &[u8]); - let (res, _) = parse::(raw).unwrap().unwrap(); + let raw = MemBuf::from(b"HTTP/1.1 200 OK\r\n\r\n".to_vec()); + let (res, _) = parse::(&raw).unwrap().unwrap(); assert_eq!(res.subject.1, "OK"); - let raw = MemSlice::from(b"HTTP/1.1 200 Howdy\r\n\r\n" as &[u8]); - let (res, _) = parse::(raw).unwrap().unwrap(); + let raw = MemBuf::from(b"HTTP/1.1 200 Howdy\r\n\r\n".to_vec()); + let (res, _) = parse::(&raw).unwrap().unwrap(); assert_eq!(res.subject.1, "Howdy"); } @@ -271,7 +299,7 @@ mod tests { #[cfg(feature = "nightly")] #[bench] fn bench_parse_incoming(b: &mut Bencher) { - let raw = MemSlice::from(b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ + let mut raw = MemBuf::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: \ @@ -287,9 +315,10 @@ mod tests { 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]); + \r\n\r\n".to_vec()); b.iter(|| { - parse::(raw.clone()).unwrap() + parse::(&raw).unwrap(); + raw.restart(); }); } diff --git a/src/http/io.rs b/src/http/io.rs index e4d9621a..643b5cfb 100644 --- a/src/http/io.rs +++ b/src/http/io.rs @@ -70,11 +70,11 @@ impl Buffered { _ => return Err(e.into()) } } - match try!(parse::(MemSlice::from(self.read_buf.bytes()))) { - Some((head, len)) => { - trace!("parsed {} bytes out of {}", len, self.read_buf.len()); - self.read_buf.slice(len); - Ok(Some(head)) + match try!(parse::(&self.read_buf)) { + Some(head) => { + //trace!("parsed {} bytes out of {}", len, self.read_buf.len()); + //self.read_buf.slice(len); + Ok(Some(head.0)) }, None => { if self.read_buf.capacity() >= MAX_BUFFER_SIZE { @@ -140,7 +140,7 @@ impl Write for Buffered { } } -fn parse, I>(rdr: MemSlice) -> ParseResult { +fn parse, I>(rdr: &MemBuf) -> ParseResult { h1::parse::(rdr) } diff --git a/src/http/mod.rs b/src/http/mod.rs index eb0ef982..13c50119 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -13,7 +13,7 @@ use version::HttpVersion::{Http10, Http11}; pub use self::conn::{Conn, KeepAlive, KA}; pub use self::body::{Body, TokioBody}; pub use self::chunk::Chunk; -use self::buf::MemSlice; +use self::buf::MemBuf; mod body; #[doc(hidden)] @@ -125,7 +125,7 @@ pub trait Http1Transaction { type Incoming; type Outgoing: Default; //type KeepAlive: KeepAlive; - fn parse(bytes: MemSlice) -> ParseResult; + fn parse(bytes: &MemBuf) -> ParseResult; fn decoder(head: &MessageHead) -> ::Result; fn encode(head: &mut MessageHead, dst: &mut Vec) -> h1::Encoder; fn should_set_length(head: &MessageHead) -> bool; diff --git a/src/lib.rs b/src/lib.rs index 80015291..05e5b191 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![doc(html_root_url = "https://hyperium.github.io/hyper/")] #![deny(missing_docs)] -//#![deny(warnings)] +#![deny(warnings)] #![deny(missing_debug_implementations)] #![cfg_attr(all(test, feature = "nightly"), feature(test))] @@ -55,47 +55,6 @@ 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 = 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 = 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)] mod mock; pub mod client; diff --git a/src/mock.rs b/src/mock.rs index db969032..934732ad 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -6,7 +6,8 @@ use tokio::io::Io; #[derive(Debug)] pub struct Buf { - vec: Vec + vec: Vec, + pos: usize, } impl Buf { @@ -17,6 +18,7 @@ impl Buf { pub fn wrap(vec: Vec) -> Buf { Buf { vec: vec, + pos: 0, } } } @@ -48,7 +50,10 @@ impl Write for Buf { impl Read for Buf { fn read(&mut self, buf: &mut [u8]) -> io::Result { - (&*self.vec).read(buf) + (&self.vec[self.pos..]).read(buf).map(|n| { + self.pos += n; + n + }) } }