perf(header): use MemSlice when parsing headers

This commit is contained in:
Guillaume Gomez
2017-01-22 13:33:47 +01:00
committed by Sean McArthur
parent 5c890321ee
commit 1b556389c0
9 changed files with 291 additions and 76 deletions

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::cell::UnsafeCell;
use std::fmt;
use std::io::{self, Read};
@@ -195,6 +196,11 @@ 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())),
@@ -208,6 +214,11 @@ impl MemSlice {
}
}
impl AsRef<[u8]> for MemSlice {
fn as_ref(&self) -> &[u8] {
self.get()
}
}
impl fmt::Debug for MemSlice {
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];
fn deref(&self) -> &[u8] {
unsafe {
&(*self.buf.get())[self.start..self.end]
self.get()
}
}
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,
}
}
}

View File

@@ -6,6 +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 method::Method;
use status::StatusCode;
use version::HttpVersion::{Http10, Http11};
@@ -13,7 +14,7 @@ use version::HttpVersion::{Http10, Http11};
const MAX_HEADERS: usize = 100;
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 {
return Ok(None);
}
@@ -25,11 +26,11 @@ impl Http1Transaction for ServerTransaction {
type Incoming = RequestLine;
type Outgoing = StatusCode;
fn parse(buf: &[u8]) -> ParseResult<RequestLine> {
fn parse(buf: MemSlice) -> ParseResult<RequestLine> {
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)) {
Ok(match try!(req.parse(buf.clone().get())) {
httparse::Status::Complete(len) => {
trace!("Request.parse Complete({})", len);
Some((MessageHead {
@@ -38,10 +39,10 @@ impl Http1Transaction for ServerTransaction {
try!(req.method.unwrap().parse()),
try!(req.path.unwrap().parse())
),
headers: try!(Headers::from_raw(req.headers))
headers: try!(Headers::from_raw(req.headers, buf))
}, len))
},
httparse::Status::Partial => None
}
httparse::Status::Partial => None,
})
}
@@ -112,11 +113,11 @@ impl Http1Transaction for ClientTransaction {
type Incoming = RawStatus;
type Outgoing = RequestLine;
fn parse(buf: &[u8]) -> ParseResult<RawStatus> {
fn parse(buf: MemSlice) -> ParseResult<RawStatus> {
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)) {
Ok(match try!(res.parse(buf.clone().get())) {
httparse::Status::Complete(len) => {
trace!("Response.try_parse Complete({})", len);
let code = res.code.unwrap();
@@ -127,7 +128,7 @@ impl Http1Transaction for ClientTransaction {
Some((MessageHead {
version: if res.version.unwrap() == 1 { Http11 } else { Http10 },
subject: RawStatus(code, reason),
headers: try!(Headers::from_raw(res.headers))
headers: try!(Headers::from_raw(res.headers, buf))
}, len))
},
httparse::Status::Partial => None
@@ -244,21 +245,22 @@ fn extend(dst: &mut Vec<u8>, data: &[u8]) {
#[cfg(test)]
mod tests {
use http;
use http::buf::MemSlice;
use super::{parse};
#[test]
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();
}
#[test]
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();
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();
assert_eq!(res.subject.1, "Howdy");
}
@@ -269,9 +271,25 @@ mod tests {
#[cfg(feature = "nightly")]
#[bench]
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(|| {
parse::<http::ServerTransaction, _>(raw).unwrap()
parse::<http::ServerTransaction, _>(raw.clone()).unwrap()
});
}

View File

@@ -70,7 +70,7 @@ impl<T: Io> Buffered<T> {
_ => 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)) => {
trace!("parsed {} bytes out of {}", len, self.read_buf.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)
}

View File

@@ -13,9 +13,11 @@ 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;
mod body;
mod buf;
#[doc(hidden)]
pub mod buf;
mod chunk;
mod conn;
mod io;
@@ -123,7 +125,7 @@ pub trait Http1Transaction {
type Incoming;
type Outgoing: Default;
//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 encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder;
fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool;