perf(header): use MemSlice when parsing headers
This commit is contained in:
committed by
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
|
||||||
@@ -51,7 +52,7 @@ impl Header for ContentLength {
|
|||||||
(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,9 +664,12 @@ mod tests {
|
|||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
|
macro_rules! raw {
|
||||||
|
($($line:expr),*) => ({
|
||||||
|
[$({
|
||||||
// Slice.position_elem was unstable
|
// Slice.position_elem was unstable
|
||||||
fn index_of(slice: &[u8], byte: u8) -> Option<usize> {
|
fn index_of(slice: &MemSlice, byte: u8) -> Option<usize> {
|
||||||
for (index, &b) in slice.iter().enumerate() {
|
for (index, &b) in slice.as_ref().iter().enumerate() {
|
||||||
if b == byte {
|
if b == byte {
|
||||||
return Some(index);
|
return Some(index);
|
||||||
}
|
}
|
||||||
@@ -673,14 +677,10 @@ mod tests {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! raw {
|
let pos = index_of(&$line, b':').expect("raw splits on ':', not found");
|
||||||
($($line:expr),*) => ({
|
|
||||||
[$({
|
|
||||||
let line = $line;
|
|
||||||
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 {
|
||||||
@@ -217,9 +228,88 @@ 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