perf(header): improve on MemSlice usage in headers

This commit is contained in:
Sean McArthur
2017-01-26 23:49:44 -08:00
parent 1b556389c0
commit 61364d245b
14 changed files with 190 additions and 195 deletions

View File

@@ -141,9 +141,7 @@ impl Header for RetryAfter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate httparse; use header::Header;
use header::{Header, Headers};
use header::shared::HttpDate; use header::shared::HttpDate;
use time::{Duration}; use time::{Duration};
@@ -179,17 +177,15 @@ mod tests {
#[test] #[test]
fn hyper_headers_from_raw_delay() { fn hyper_headers_from_raw_delay() {
let headers = make_header!(b"Retry-After", b"300"); let retry_after = RetryAfter::parse_header(&b"300".to_vec().into()).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 = make_header!(b"Retry-After", b"Sun, 06 Nov 1994 08:49:37 GMT"); let retry_after = RetryAfter::parse_header(&b"Sun, 06 Nov 1994 08:49:37 GMT".to_vec().into()).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();
assert_eq!(retry_after, &RetryAfter::DateTime(expected.0)); assert_eq!(retry_after, RetryAfter::DateTime(expected.0));
} }
} }

View File

@@ -4,9 +4,9 @@ pub struct VecMap<K, V> {
} }
impl<K: PartialEq, V> VecMap<K, V> { impl<K: PartialEq, V> VecMap<K, V> {
pub fn new() -> VecMap<K, V> { pub fn with_capacity(cap: usize) -> VecMap<K, V> {
VecMap { VecMap {
vec: Vec::new() vec: Vec::with_capacity(cap)
} }
} }
@@ -43,6 +43,7 @@ impl<K: PartialEq, V> VecMap<K, V> {
} }
pub fn len(&self) -> usize { self.vec.len() } pub fn len(&self) -> usize { self.vec.len() }
pub fn iter(&self) -> ::std::slice::Iter<(K, V)> { pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
self.vec.iter() self.vec.iter()
} }

View File

@@ -80,7 +80,6 @@ use std::borrow::{Cow, ToOwned};
use std::iter::{FromIterator, IntoIterator}; use std::iter::{FromIterator, IntoIterator};
use std::{mem, fmt}; use std::{mem, fmt};
use httparse;
use unicase::UniCase; use unicase::UniCase;
use self::internals::{Item, VecMap, Entry}; use self::internals::{Item, VecMap, Entry};
@@ -347,31 +346,17 @@ literals! {
impl Headers { impl Headers {
/// Creates a new, empty headers map. /// Creates a new, empty headers map.
#[inline]
pub fn new() -> Headers { pub fn new() -> Headers {
Headers { Headers::with_capacity(0)
data: VecMap::new()
}
} }
#[doc(hidden)] /// Creates a new `Headers` struct with space reserved for `len` headers.
pub fn from_raw(raw: &[httparse::Header], buf: MemSlice) -> ::Result<Headers> { #[inline]
let mut headers = Headers::new(); pub fn with_capacity(len: usize) -> Headers {
for header in raw { Headers {
let name = HeaderName(UniCase(maybe_literal(header.name))); data: VecMap::with_capacity(len)
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));
}
};
} }
Ok(headers)
} }
/// Set a header field to the corresponding value. /// Set a header field to the corresponding value.
@@ -587,6 +572,24 @@ impl<'a> Extend<HeaderView<'a>> for Headers {
} }
} }
impl<'a> Extend<(&'a str, MemSlice)> for Headers {
fn extend<I: IntoIterator<Item=(&'a str, MemSlice)>>(&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<HeaderView<'a>> for Headers { impl<'a> FromIterator<HeaderView<'a>> for Headers {
fn from_iter<I: IntoIterator<Item=HeaderView<'a>>>(iter: I) -> Headers { fn from_iter<I: IntoIterator<Item=HeaderView<'a>>>(iter: I) -> Headers {
let mut headers = Headers::new(); let mut headers = Headers::new();
@@ -659,33 +662,22 @@ mod tests {
use mime::SubLevel::Plain; use mime::SubLevel::Plain;
use super::{Headers, Header, Raw, ContentLength, ContentType, use super::{Headers, Header, Raw, ContentLength, ContentType,
Accept, Host, qitem}; Accept, Host, qitem};
use httparse;
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use test::Bencher; use test::Bencher;
macro_rules! raw { macro_rules! make_header {
($($line:expr),*) => ({ ($name:expr, $value:expr) => ({
[$({ let mut headers = Headers::new();
// Slice.position_elem was unstable headers.set_raw(String::from_utf8($name.to_vec()).unwrap(), $value.to_vec());
fn index_of(slice: &MemSlice, byte: u8) -> Option<usize> { headers
for (index, &b) in slice.as_ref().iter().enumerate() { });
if b == byte { ($text:expr) => ({
return Some(index); let bytes = $text;
} let colon = bytes.iter().position(|&x| x == b':').unwrap();
} make_header!(&bytes[..colon], &bytes[colon + 2..])
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..]
}
}),*]
}) })
} }
#[test] #[test]
fn test_from_raw() { fn test_from_raw() {
let headers = make_header!(b"Content-Length", b"10"); let headers = make_header!(b"Content-Length", b"10");
@@ -759,7 +751,9 @@ mod tests {
#[test] #[test]
fn test_different_reads() { 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::<ContentLength>().unwrap(); let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
let ContentType(_) = *headers.get::<ContentType>().unwrap(); let ContentType(_) = *headers.get::<ContentType>().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")] #[cfg(feature = "nightly")]
#[bench] #[bench]
fn bench_headers_get(b: &mut Bencher) { fn bench_headers_get(b: &mut Bencher) {

View File

@@ -21,7 +21,7 @@ pub fn from_one_raw_str<T: str::FromStr>(raw: &Raw) -> ::Result<T> {
/// Reads a raw string into a value. /// Reads a raw string into a value.
pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> { pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> {
let s = try!(str::from_utf8(raw)); let s = try!(str::from_utf8(raw)).trim();
T::from_str(s).or(Err(::Error::Header)) T::from_str(s).or(Err(::Error::Header))
} }
@@ -36,7 +36,7 @@ pub fn from_comma_delimited<T: str::FromStr>(raw: &Raw) -> ::Result<Vec<T>> {
"" => None, "" => None,
y => Some(y) y => Some(y)
}) })
.filter_map(|x| x.parse().ok())) .filter_map(|x| x.trim().parse().ok()))
} }
Ok(result) Ok(result)
} }

View File

@@ -37,27 +37,17 @@ impl Raw {
/// Append a line to this `Raw` header value. /// Append a line to this `Raw` header value.
pub fn push(&mut self, val: &[u8]) { pub fn push(&mut self, val: &[u8]) {
let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new())); self.push_line(maybe_literal(val.into()));
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);
}
}
} }
#[doc(hidden)] fn push_line(&mut self, line: Line) {
pub fn push_slice(&mut self, val: MemSlice) {
let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new())); let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new()));
match lines { match lines {
Lines::One(line) => { Lines::One(one) => {
self.0 = Lines::Many(vec![line, Line::Shared(val)]); self.0 = Lines::Many(vec![one, line]);
} }
Lines::Many(mut lines) => { Lines::Many(mut lines) => {
lines.push(Line::Shared(val)); lines.push(line);
self.0 = Lines::Many(lines); self.0 = Lines::Many(lines);
} }
} }
@@ -190,6 +180,10 @@ pub fn parsed(val: MemSlice) -> Raw {
Raw(Lines::One(From::from(val))) 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 { impl fmt::Debug for Raw {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 { match self.0 {

View File

@@ -1,14 +1,14 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::UnsafeCell; use std::cell::{Cell, UnsafeCell};
use std::fmt; use std::fmt;
use std::io::{self, Read}; 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::ptr;
use std::sync::Arc; use std::sync::Arc;
pub struct MemBuf { pub struct MemBuf {
buf: Arc<UnsafeCell<Vec<u8>>>, buf: Arc<UnsafeCell<Vec<u8>>>,
start: usize, start: Cell<usize>,
end: usize, end: usize,
} }
@@ -20,13 +20,13 @@ impl MemBuf {
pub fn with_capacity(cap: usize) -> MemBuf { pub fn with_capacity(cap: usize) -> MemBuf {
MemBuf { MemBuf {
buf: Arc::new(UnsafeCell::new(vec![0; cap])), buf: Arc::new(UnsafeCell::new(vec![0; cap])),
start: 0, start: Cell::new(0),
end: 0, end: 0,
} }
} }
pub fn bytes(&self) -> &[u8] { pub fn bytes(&self) -> &[u8] {
&self.buf()[self.start..self.end] &self.buf()[self.start.get()..self.end]
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
@@ -34,7 +34,7 @@ impl MemBuf {
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.end - self.start self.end - self.start.get()
} }
pub fn capacity(&self) -> usize { pub fn capacity(&self) -> usize {
@@ -42,20 +42,21 @@ impl MemBuf {
} }
pub fn read_from<R: Read>(&mut self, io: &mut R) -> io::Result<usize> { pub fn read_from<R: Read>(&mut self, io: &mut R) -> io::Result<usize> {
let start = self.end - self.start; let start = self.end - self.start.get();
let n = try!(io.read(&mut self.buf_mut()[start..])); let n = try!(io.read(&mut self.buf_mut()[start..]));
self.end += n; self.end += n;
Ok(n) Ok(n)
} }
pub fn slice(&mut self, len: usize) -> MemSlice { pub fn slice(&self, len: usize) -> MemSlice {
assert!(self.end - self.start >= len); assert!(self.end - self.start.get() >= len);
let start = self.start; let start = self.start.get();
self.start += len; let end = start + len;
self.start.set(end);
MemSlice { MemSlice {
buf: self.buf.clone(), buf: self.buf.clone(),
start: start, start: start,
end: self.start, end: end,
} }
} }
@@ -68,18 +69,18 @@ impl MemBuf {
} }
let is_unique = Arc::get_mut(&mut self.buf).is_some(); let is_unique = Arc::get_mut(&mut self.buf).is_some();
trace!("MemBuf::reserve {} access", if is_unique { "unique" } else { "shared" }); 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 // we have unique access, we can mutate this vector
trace!("MemBuf::reserve unique access, shifting"); trace!("MemBuf::reserve unique access, shifting");
unsafe { unsafe {
let mut buf = &mut *self.buf.get(); let mut buf = &mut *self.buf.get();
let len = self.len(); let len = self.len();
ptr::copy( ptr::copy(
buf.as_ptr().offset(self.start as isize), buf.as_ptr().offset(self.start.get() as isize),
buf.as_mut_ptr(), buf.as_mut_ptr(),
len len
); );
self.start = 0; self.start.set(0);
self.end = len; self.end = len;
} }
} else if is_unique { } else if is_unique {
@@ -110,7 +111,7 @@ impl MemBuf {
match Arc::get_mut(&mut self.buf) { match Arc::get_mut(&mut self.buf) {
Some(_) => { Some(_) => {
trace!("MemBuf::reset was unique, re-using"); trace!("MemBuf::reset was unique, re-using");
self.start = 0; self.start.set(0);
self.end = 0; self.end = 0;
}, },
None => { 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] { fn buf_mut(&mut self) -> &mut [u8] {
// The contract here is that we NEVER have a MemSlice that exists // The contract here is that we NEVER have a MemSlice that exists
// with slice.end > self.start. // with slice.end > self.start.
// In other words, we should *ALWAYS* be the only instance that can // In other words, we should *ALWAYS* be the only instance that can
// look at the bytes on the right side of self.start. // look at the bytes on the right side of self.start.
unsafe { 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 { impl fmt::Debug for MemBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MemBuf") f.debug_struct("MemBuf")
.field("start", &self.start) .field("start", &self.start.get())
.field("end", &self.end) .field("end", &self.end)
.field("buf", &&self.buf()[self.start..self.end]) .field("buf", &&self.buf()[self.start.get()..self.end])
.finish() .finish()
} }
} }
@@ -183,7 +190,7 @@ impl From<Vec<u8>> for MemBuf {
vec.shrink_to_fit(); vec.shrink_to_fit();
MemBuf { MemBuf {
buf: Arc::new(UnsafeCell::new(vec)), buf: Arc::new(UnsafeCell::new(vec)),
start: 0, start: Cell::new(0),
end: end, end: end,
} }
} }
@@ -196,11 +203,6 @@ 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())),
@@ -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<S: Slice>(&self, range: S) -> MemSlice { pub fn slice<S: Slice>(&self, range: S) -> MemSlice {
range.slice(self) range.slice(self)
} }
fn get(&self) -> &[u8] {
unsafe { &(*self.buf.get())[self.start..self.end] }
}
} }
impl AsRef<[u8]> for MemSlice { impl AsRef<[u8]> for MemSlice {
@@ -222,15 +236,14 @@ impl AsRef<[u8]> for MemSlice {
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 {
fmt::Debug::fmt(&**self, f) fmt::Debug::fmt(&self.get(), f)
} }
} }
impl Deref for MemSlice { impl Index<usize> for MemSlice {
type Target = [u8]; type Output = u8;
fn index(&self, i: usize) -> &u8 {
fn deref(&self) -> &[u8] { &self.get()[i]
self.get()
} }
} }
@@ -386,18 +399,18 @@ mod tests {
#[test] #[test]
fn test_mem_slice_slice() { 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 len = buf.len();
let full = buf.slice(len); let full = buf.slice(len);
assert_eq!(&*full, b"Hello World"); assert_eq!(full.as_ref(), b"Hello World");
assert_eq!(&*full.slice(6..), b"World"); assert_eq!(full.slice(6..).as_ref(), b"World");
assert_eq!(&*full.slice(..5), b"Hello"); assert_eq!(full.slice(..5).as_ref(), b"Hello");
assert_eq!(&*full.slice(..), b"Hello World"); assert_eq!(full.slice(..).as_ref(), b"Hello World");
for a in 0..len { for a in 0..len {
for b in a..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);
} }
} }
} }

View File

@@ -69,7 +69,7 @@ impl AsRef<[u8]> for Chunk {
match self.0 { match self.0 {
Inner::Owned(ref vec) => vec, Inner::Owned(ref vec) => vec,
Inner::Referenced(ref vec) => vec, Inner::Referenced(ref vec) => vec,
Inner::Mem(ref slice) => slice, Inner::Mem(ref slice) => slice.as_ref(),
Inner::Static(slice) => slice, Inner::Static(slice) => slice,
} }
} }

View File

@@ -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] #[test]
fn test_conn_closed_read() { fn test_conn_closed_read() {
let io = AsyncIo::new_buf(vec![], 0); let io = AsyncIo::new_buf(vec![], 0);

View File

@@ -88,7 +88,7 @@ impl Decoder {
} else { } else {
let to_read = *remaining as usize; let to_read = *remaining as usize;
let buf = try!(body.read_mem(to_read)); 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); trace!("Length read: {}", num);
if num > *remaining { if num > *remaining {
*remaining = 0; *remaining = 0;
@@ -399,7 +399,7 @@ mod tests {
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..];
let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode"); let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode");
assert_eq!(16, buf.len()); 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); assert_eq!("1234567890abcdef", &result);
} }
@@ -411,7 +411,7 @@ mod tests {
// normal read // normal read
let buf = decoder.decode(&mut mock_buf).expect("decode"); let buf = decoder.decode(&mut mock_buf).expect("decode");
assert_eq!(16, buf.len()); 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); assert_eq!("1234567890abcdef", &result);
// eof read // eof read
@@ -438,7 +438,7 @@ mod tests {
if buf.is_empty() { if buf.is_empty() {
break; // eof break; // eof
} }
outs.write(&buf).expect("write buffer"); outs.write(buf.as_ref()).expect("write buffer");
} }
Err(e) => match e.kind() { Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => { io::ErrorKind::WouldBlock => {

View File

@@ -6,7 +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 http::buf::{MemBuf, MemSlice};
use method::Method; use method::Method;
use status::StatusCode; use status::StatusCode;
use version::HttpVersion::{Http10, Http11}; use version::HttpVersion::{Http10, Http11};
@@ -14,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: MemSlice) -> ParseResult<I> { pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: &MemBuf) -> ParseResult<I> {
if buf.len() == 0 { if buf.len() == 0 {
return Ok(None); return Ok(None);
} }
@@ -26,20 +26,26 @@ impl Http1Transaction for ServerTransaction {
type Incoming = RequestLine; type Incoming = RequestLine;
type Outgoing = StatusCode; type Outgoing = StatusCode;
fn parse(buf: MemSlice) -> ParseResult<RequestLine> { fn parse(buf: &MemBuf) -> 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.clone().get())) { Ok(match try!(req.parse(buf.bytes())) {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
trace!("Request.parse 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 { Some((MessageHead {
version: if req.version.unwrap() == 1 { Http11 } else { Http10 }, version: if req.version.unwrap() == 1 { Http11 } else { Http10 },
subject: RequestLine( subject: RequestLine(
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, buf)) headers: headers,
}, len)) }, len))
} }
httparse::Status::Partial => None, httparse::Status::Partial => None,
@@ -113,11 +119,11 @@ impl Http1Transaction for ClientTransaction {
type Incoming = RawStatus; type Incoming = RawStatus;
type Outgoing = RequestLine; type Outgoing = RequestLine;
fn parse(buf: MemSlice) -> ParseResult<RawStatus> { fn parse(buf: &MemBuf) -> 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.clone().get())) { Ok(match try!(res.parse(buf.bytes())) {
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();
@@ -125,10 +131,16 @@ impl Http1Transaction for ClientTransaction {
Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason), Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason),
_ => Cow::Owned(res.reason.unwrap().to_owned()) _ => 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 { 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, buf)) headers: headers,
}, len)) }, len))
}, },
httparse::Status::Partial => None 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::Item> {
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<u8>); struct FastWrite<'a>(&'a mut Vec<u8>);
impl<'a> fmt::Write for FastWrite<'a> { impl<'a> fmt::Write for FastWrite<'a> {
@@ -245,23 +273,23 @@ 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 http::buf::MemBuf;
use super::{parse}; use super::{parse};
#[test] #[test]
fn test_parse_request() { fn test_parse_request() {
let raw = MemSlice::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n" as &[u8]); let raw = MemBuf::from(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n".to_vec());
parse::<http::ServerTransaction, _>(raw).unwrap(); parse::<http::ServerTransaction, _>(&raw).unwrap();
} }
#[test] #[test]
fn test_parse_raw_status() { fn test_parse_raw_status() {
let raw = MemSlice::from(b"HTTP/1.1 200 OK\r\n\r\n" as &[u8]); let raw = MemBuf::from(b"HTTP/1.1 200 OK\r\n\r\n".to_vec());
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 = MemSlice::from(b"HTTP/1.1 200 Howdy\r\n\r\n" as &[u8]); let raw = MemBuf::from(b"HTTP/1.1 200 Howdy\r\n\r\n".to_vec());
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");
} }
@@ -271,7 +299,7 @@ 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 = 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\ 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=\ _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\
foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1\r\nHost: \ 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\ X-Content-Duration: None\r\nX-Content-Security-Policy: None\
\r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \ \r\nX-DNSPrefetch-Control: None\r\nX-Frame-Options: \
Something important obviously\r\nX-Requested-With: Nothing\ Something important obviously\r\nX-Requested-With: Nothing\
\r\n\r\n" as &[u8]); \r\n\r\n".to_vec());
b.iter(|| { b.iter(|| {
parse::<http::ServerTransaction, _>(raw.clone()).unwrap() parse::<http::ServerTransaction, _>(&raw).unwrap();
raw.restart();
}); });
} }

View File

@@ -70,11 +70,11 @@ impl<T: Io> Buffered<T> {
_ => return Err(e.into()) _ => return Err(e.into())
} }
} }
match try!(parse::<S, _>(MemSlice::from(self.read_buf.bytes()))) { match try!(parse::<S, _>(&self.read_buf)) {
Some((head, len)) => { Some(head) => {
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);
Ok(Some(head)) Ok(Some(head.0))
}, },
None => { None => {
if self.read_buf.capacity() >= MAX_BUFFER_SIZE { if self.read_buf.capacity() >= MAX_BUFFER_SIZE {
@@ -140,7 +140,7 @@ impl<T: Write> Write for Buffered<T> {
} }
} }
fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: MemSlice) -> ParseResult<I> { fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: &MemBuf) -> ParseResult<I> {
h1::parse::<T, I>(rdr) h1::parse::<T, I>(rdr)
} }

View File

@@ -13,7 +13,7 @@ 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; use self::buf::MemBuf;
mod body; mod body;
#[doc(hidden)] #[doc(hidden)]
@@ -125,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: MemSlice) -> ParseResult<Self::Incoming>; fn parse(bytes: &MemBuf) -> 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;

View File

@@ -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,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<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;

View File

@@ -6,7 +6,8 @@ use tokio::io::Io;
#[derive(Debug)] #[derive(Debug)]
pub struct Buf { pub struct Buf {
vec: Vec<u8> vec: Vec<u8>,
pos: usize,
} }
impl Buf { impl Buf {
@@ -17,6 +18,7 @@ impl Buf {
pub fn wrap(vec: Vec<u8>) -> Buf { pub fn wrap(vec: Vec<u8>) -> Buf {
Buf { Buf {
vec: vec, vec: vec,
pos: 0,
} }
} }
} }
@@ -48,7 +50,10 @@ impl Write for Buf {
impl Read for Buf { impl Read for Buf {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&*self.vec).read(buf) (&self.vec[self.pos..]).read(buf).map(|n| {
self.pos += n;
n
})
} }
} }