diff --git a/src/header/common/content_length.rs b/src/header/common/content_length.rs index dd2536b1..bc023b50 100644 --- a/src/header/common/content_length.rs +++ b/src/header/common/content_length.rs @@ -40,6 +40,7 @@ impl Header for ContentLength { static NAME: &'static str = "Content-Length"; NAME } + fn parse_header(raw: &Raw) -> ::Result { // If multiple Content-Length headers were sent, everything can still // be alright if they all contain the same value, and all parse @@ -49,9 +50,9 @@ impl Header for ContentLength { .fold(None, |prev, x| { match (prev, 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(Err(::Error::Header)) + _ => Some(Err(::Error::Header)), } }) .unwrap_or(Err(::Error::Header)) diff --git a/src/header/common/retry_after.rs b/src/header/common/retry_after.rs index 2722d680..e12f951d 100644 --- a/src/header/common/retry_after.rs +++ b/src/header/common/retry_after.rs @@ -179,14 +179,14 @@ mod tests { #[test] 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::().unwrap(); assert_eq!(retry_after, &RetryAfter::Delay(Duration::seconds(300))); } #[test] 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::().unwrap(); let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::().unwrap(); diff --git a/src/header/mod.rs b/src/header/mod.rs index 1238e87b..b3e8881c 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -88,6 +88,7 @@ use self::internals::{Item, VecMap, Entry}; pub use self::shared::*; pub use self::common::*; pub use self::raw::Raw; +use http::buf::MemSlice; mod common; mod internals; @@ -353,20 +354,20 @@ impl Headers { } #[doc(hidden)] - pub fn from_raw(raw: &[httparse::Header]) -> ::Result { + pub fn from_raw(raw: &[httparse::Header], buf: MemSlice) -> ::Result { let mut headers = Headers::new(); for header in raw { - trace!("raw header: {:?}={:?}", header.name, &header.value[..]); let name = HeaderName(UniCase(maybe_literal(header.name))); 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) { 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.into_mut().mut_raw().push(value); + entry.into_mut().mut_raw().push_slice(buf.slice(value_start..value_end)); } }; } @@ -663,24 +664,23 @@ mod tests { #[cfg(feature = "nightly")] use test::Bencher; - // Slice.position_elem was unstable - fn index_of(slice: &[u8], byte: u8) -> Option { - for (index, &b) in slice.iter().enumerate() { - if b == byte { - return Some(index); - } - } - None - } - macro_rules! raw { ($($line:expr),*) => ({ [$({ - let line = $line; - let pos = index_of(line, b':').expect("raw splits on ':', not found"); + // 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..] + name: ::std::str::from_utf8(&$line[..pos]).unwrap(), + value: &$line[pos + 2..] } }),*] }) @@ -688,7 +688,7 @@ mod tests { #[test] 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))); } @@ -738,20 +738,20 @@ mod tests { #[test] 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::(), Some(&ContentLength(10))); assert_eq!(headers.get::(), Some(&CrazyLength(Some(false), 10))); } #[test] 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::(), Some(&ContentLength(10))); } #[test] 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::().unwrap(); let ContentLength(two) = *headers.get::().unwrap(); assert_eq!(one, two); @@ -759,15 +759,14 @@ mod tests { #[test] fn test_different_reads() { - let headers = Headers::from_raw( - &raw!(b"Content-Length: 10", b"Content-Type: text/plain")).unwrap(); + let headers = make_header!(b"Content-Length: 10\r\nContent-Type: text/plain"); let ContentLength(_) = *headers.get::().unwrap(); let ContentType(_) = *headers.get::().unwrap(); } #[test] 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::().unwrap() = ContentLength(20); assert_eq!(headers.get_raw("content-length").unwrap(), &[b"20".to_vec()][..]); assert_eq!(*headers.get::().unwrap(), ContentLength(20)); @@ -786,7 +785,7 @@ mod tests { #[test] 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()]); let s = headers.to_string(); 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")] #[bench] fn bench_headers_from_raw(b: &mut Bencher) { - let raw = raw!(b"Content-Length: 10"); - b.iter(|| Headers::from_raw(&raw).unwrap()) + 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")] diff --git a/src/header/raw.rs b/src/header/raw.rs index 39735a2f..b165dd1d 100644 --- a/src/header/raw.rs +++ b/src/header/raw.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::fmt; +use http::buf::MemSlice; /// A raw header value. #[derive(Clone, PartialEq, Eq)] @@ -19,8 +20,8 @@ impl Raw { #[inline] pub fn one(&self) -> Option<&[u8]> { match self.0 { - Lines::One(ref line) => Some(line), - Lines::Many(ref lines) if lines.len() == 1 => Some(&lines[0]), + Lines::One(ref line) => Some(line.as_ref()), + Lines::Many(ref lines) if lines.len() == 1 => Some(lines[0].as_ref()), _ => None } } @@ -29,12 +30,8 @@ impl Raw { #[inline] pub fn iter(&self) -> RawLines { RawLines { - inner: match self.0 { - Lines::One(ref line) => unsafe { - ::std::slice::from_raw_parts(line, 1) - }.iter(), - Lines::Many(ref lines) => lines.iter() - } + inner: &self.0, + pos: 0, } } @@ -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 { One(Line), - Many(Vec) + Many(Vec), } -type Line = Cow<'static, [u8]>; +#[derive(Debug, Clone, PartialEq, Eq)] +enum Line { + Static(&'static [u8]), + Owned(Vec), + Shared(MemSlice), +} fn eq, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { if a.len() != b.len() { @@ -123,24 +139,55 @@ impl From for Raw { impl From> for Raw { #[inline] fn from(val: Vec) -> Raw { - Raw(Lines::One(Cow::Owned(val))) + Raw(Lines::One(Line::from(val))) } } impl From<&'static str> for 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 { fn from(val: &'static [u8]) -> Raw { - Raw(Lines::One(Cow::Borrowed(val))) + Raw(Lines::One(Line::Static(val))) } } -pub fn parsed(val: &[u8]) -> Raw { - Raw(Lines::One(maybe_literal(val.into()))) +impl From for Raw { + #[inline] + fn from(val: MemSlice) -> Raw { + Raw(Lines::One(Line::Shared(val))) + } +} + +impl From> for Line { + #[inline] + fn from(val: Vec) -> Line { + Line::Owned(val) + } +} + +impl From 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 { @@ -154,6 +201,7 @@ impl fmt::Debug for Raw { impl ::std::ops::Index for Raw { type Output = [u8]; + fn index(&self, idx: usize) -> &[u8] { match self.0 { Lines::One(ref line) => if idx == 0 { @@ -168,12 +216,12 @@ impl ::std::ops::Index for Raw { macro_rules! literals { ($($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() { $($len => { $( 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] @@ -216,7 +264,8 @@ impl<'a> IntoIterator for &'a Raw { #[derive(Debug)] pub struct RawLines<'a> { - inner: ::std::slice::Iter<'a, Cow<'static, [u8]>> + inner: &'a Lines, + pos: usize, } impl<'a> Iterator for RawLines<'a> { @@ -224,6 +273,17 @@ impl<'a> Iterator for RawLines<'a> { #[inline] 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()), + } } } diff --git a/src/http/buf.rs b/src/http/buf.rs index 295f7a5f..0425a29d 100644 --- a/src/http/buf.rs +++ b/src/http/buf.rs @@ -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> for MemSlice { + fn from(v: Vec) -> 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> 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 for MemSlice { + fn eq(&self, other: &str) -> bool { + self.get() == other.as_bytes() + } +} + +impl PartialEq> for MemSlice { + fn eq(&self, other: &Vec) -> 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, } } } diff --git a/src/http/h1/parse.rs b/src/http/h1/parse.rs index 559bd006..99cd2b51 100644 --- a/src/http/h1/parse.rs +++ b/src/http/h1/parse.rs @@ -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, I>(buf: &[u8]) -> ParseResult { +pub fn parse, I>(buf: MemSlice) -> ParseResult { 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 { + fn parse(buf: MemSlice) -> 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)) { + 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 { + fn parse(buf: MemSlice) -> 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)) { + 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, 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::(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::(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::(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::(raw).unwrap() + parse::(raw.clone()).unwrap() }); } diff --git a/src/http/io.rs b/src/http/io.rs index 95853ca6..e4d9621a 100644 --- a/src/http/io.rs +++ b/src/http/io.rs @@ -70,7 +70,7 @@ impl Buffered { _ => return Err(e.into()) } } - match try!(parse::(self.read_buf.bytes())) { + 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); @@ -140,7 +140,7 @@ impl Write for Buffered { } } -fn parse, I>(rdr: &[u8]) -> ParseResult { +fn parse, I>(rdr: MemSlice) -> ParseResult { h1::parse::(rdr) } diff --git a/src/http/mod.rs b/src/http/mod.rs index cdad710e..eb0ef982 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -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; + fn parse(bytes: MemSlice) -> 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 05e5b191..80015291 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,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 = 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;