Merge pull request #401 from hyperium/packets
fix(http): read more before triggering TooLargeError
This commit is contained in:
95
src/buffer.rs
Normal file
95
src/buffer.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use std::cmp;
|
||||
use std::iter;
|
||||
use std::io::{self, Read, BufRead, Cursor};
|
||||
|
||||
pub struct BufReader<R> {
|
||||
buf: Cursor<Vec<u8>>,
|
||||
inner: R
|
||||
}
|
||||
|
||||
const INIT_BUFFER_SIZE: usize = 4096;
|
||||
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
|
||||
|
||||
impl<R: Read> BufReader<R> {
|
||||
pub fn new(rdr: R) -> BufReader<R> {
|
||||
BufReader::with_capacity(rdr, INIT_BUFFER_SIZE)
|
||||
}
|
||||
|
||||
pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> {
|
||||
BufReader {
|
||||
buf: Cursor::new(Vec::with_capacity(cap)),
|
||||
inner: rdr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ref(&self) -> &R { &self.inner }
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
|
||||
|
||||
pub fn get_buf(&self) -> &[u8] {
|
||||
self.buf.get_ref()
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> R { self.inner }
|
||||
|
||||
pub fn read_into_buf(&mut self) -> io::Result<usize> {
|
||||
let v = self.buf.get_mut();
|
||||
reserve(v);
|
||||
let inner = &mut self.inner;
|
||||
with_end_to_cap(v, |b| inner.read(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Read for BufReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
if self.buf.get_ref().len() == self.buf.position() as usize &&
|
||||
buf.len() >= self.buf.get_ref().capacity() {
|
||||
return self.inner.read(buf);
|
||||
}
|
||||
try!(self.fill_buf());
|
||||
self.buf.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
if self.buf.position() as usize == self.buf.get_ref().len() {
|
||||
self.buf.set_position(0);
|
||||
let v = self.buf.get_mut();
|
||||
v.truncate(0);
|
||||
let inner = &mut self.inner;
|
||||
try!(with_end_to_cap(v, |b| inner.read(b)));
|
||||
}
|
||||
self.buf.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.buf.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
fn with_end_to_cap<F>(v: &mut Vec<u8>, f: F) -> io::Result<usize>
|
||||
where F: FnOnce(&mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
let len = v.len();
|
||||
let new_area = v.capacity() - len;
|
||||
v.extend(iter::repeat(0).take(new_area));
|
||||
match f(&mut v[len..]) {
|
||||
Ok(n) => {
|
||||
v.truncate(len + n);
|
||||
Ok(n)
|
||||
}
|
||||
Err(e) => {
|
||||
v.truncate(len);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reserve(v: &mut Vec<u8>) {
|
||||
let cap = v.capacity();
|
||||
if v.len() == cap {
|
||||
v.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
//! Client Responses
|
||||
use std::io::{self, Read, BufReader};
|
||||
use std::io::{self, Read};
|
||||
use std::num::FromPrimitive;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use buffer::BufReader;
|
||||
use header;
|
||||
use header::{ContentLength, TransferEncoding};
|
||||
use header::Encoding::Chunked;
|
||||
@@ -103,9 +104,10 @@ impl Read for Response {
|
||||
mod tests {
|
||||
use std::borrow::Cow::Borrowed;
|
||||
use std::boxed::BoxAny;
|
||||
use std::io::{self, Read, BufReader};
|
||||
use std::io::{self, Read};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use buffer::BufReader;
|
||||
use header::Headers;
|
||||
use header::TransferEncoding;
|
||||
use header::Encoding;
|
||||
|
||||
118
src/http.rs
118
src/http.rs
@@ -5,12 +5,13 @@ use std::io::{self, Read, Write, BufRead};
|
||||
|
||||
use httparse;
|
||||
|
||||
use buffer::BufReader;
|
||||
use header::Headers;
|
||||
use method::Method;
|
||||
use uri::RequestUri;
|
||||
use version::HttpVersion::{self, Http10, Http11};
|
||||
use HttpError:: HttpTooLargeError;
|
||||
use HttpResult;
|
||||
use {HttpError, HttpResult};
|
||||
|
||||
use self::HttpReader::{SizedReader, ChunkedReader, EofReader, EmptyReader};
|
||||
use self::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter};
|
||||
@@ -307,56 +308,88 @@ impl<W: Write> Write for HttpWriter<W> {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_HEADERS: usize = 100;
|
||||
|
||||
/// Parses a request into an Incoming message head.
|
||||
pub fn parse_request<T: BufRead>(buf: &mut T) -> HttpResult<Incoming<(Method, RequestUri)>> {
|
||||
let (inc, len) = {
|
||||
let slice = try!(buf.fill_buf());
|
||||
let mut headers = [httparse::Header { name: "", value: b"" }; 64];
|
||||
let mut req = httparse::Request::new(&mut headers);
|
||||
match try!(req.parse(slice)) {
|
||||
#[inline]
|
||||
pub fn parse_request<R: Read>(buf: &mut BufReader<R>) -> HttpResult<Incoming<(Method, RequestUri)>> {
|
||||
parse::<R, httparse::Request, (Method, RequestUri)>(buf)
|
||||
}
|
||||
|
||||
/// Parses a response into an Incoming message head.
|
||||
#[inline]
|
||||
pub fn parse_response<R: Read>(buf: &mut BufReader<R>) -> HttpResult<Incoming<RawStatus>> {
|
||||
parse::<R, httparse::Response, RawStatus>(buf)
|
||||
}
|
||||
|
||||
fn parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> HttpResult<Incoming<I>> {
|
||||
loop {
|
||||
match try!(try_parse::<R, T, I>(rdr)) {
|
||||
httparse::Status::Complete((inc, len)) => {
|
||||
rdr.consume(len);
|
||||
return Ok(inc);
|
||||
},
|
||||
_partial => ()
|
||||
}
|
||||
match try!(rdr.read_into_buf()) {
|
||||
0 => return Err(HttpTooLargeError),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> TryParseResult<I> {
|
||||
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
|
||||
<T as TryParse>::try_parse(&mut headers, rdr.get_buf())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
trait TryParse {
|
||||
type Subject;
|
||||
fn try_parse<'a>(headers: &'a mut [httparse::Header<'a>], buf: &'a [u8]) -> TryParseResult<Self::Subject>;
|
||||
}
|
||||
|
||||
type TryParseResult<T> = Result<httparse::Status<(Incoming<T>, usize)>, HttpError>;
|
||||
|
||||
impl<'a> TryParse for httparse::Request<'a> {
|
||||
type Subject = (Method, RequestUri);
|
||||
|
||||
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> TryParseResult<(Method, RequestUri)> {
|
||||
let mut req = httparse::Request::new(headers);
|
||||
Ok(match try!(req.parse(buf)) {
|
||||
httparse::Status::Complete(len) => {
|
||||
(Incoming {
|
||||
httparse::Status::Complete((Incoming {
|
||||
version: if req.version.unwrap() == 1 { Http11 } else { Http10 },
|
||||
subject: (
|
||||
try!(req.method.unwrap().parse()),
|
||||
try!(req.path.unwrap().parse())
|
||||
),
|
||||
headers: try!(Headers::from_raw(req.headers))
|
||||
}, len)
|
||||
}, len))
|
||||
},
|
||||
_ => {
|
||||
// request head is bigger than a BufRead's buffer? 400 that!
|
||||
return Err(HttpTooLargeError)
|
||||
}
|
||||
}
|
||||
};
|
||||
buf.consume(len);
|
||||
Ok(inc)
|
||||
httparse::Status::Partial => httparse::Status::Partial
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a response into an Incoming message head.
|
||||
pub fn parse_response<T: BufRead>(buf: &mut T) -> HttpResult<Incoming<RawStatus>> {
|
||||
let (inc, len) = {
|
||||
let mut headers = [httparse::Header { name: "", value: b"" }; 64];
|
||||
let mut res = httparse::Response::new(&mut headers);
|
||||
match try!(res.parse(try!(buf.fill_buf()))) {
|
||||
impl<'a> TryParse for httparse::Response<'a> {
|
||||
type Subject = RawStatus;
|
||||
|
||||
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> TryParseResult<RawStatus> {
|
||||
let mut res = httparse::Response::new(headers);
|
||||
Ok(match try!(res.parse(buf)) {
|
||||
httparse::Status::Complete(len) => {
|
||||
(Incoming {
|
||||
httparse::Status::Complete((Incoming {
|
||||
version: if res.version.unwrap() == 1 { Http11 } else { Http10 },
|
||||
subject: RawStatus(
|
||||
res.code.unwrap(), res.reason.unwrap().to_owned().into_cow()
|
||||
),
|
||||
headers: try!(Headers::from_raw(res.headers))
|
||||
}, len)
|
||||
}, len))
|
||||
},
|
||||
_ => {
|
||||
// response head is bigger than a BufRead's buffer?
|
||||
return Err(HttpTooLargeError)
|
||||
}
|
||||
}
|
||||
};
|
||||
buf.consume(len);
|
||||
Ok(inc)
|
||||
httparse::Status::Partial => httparse::Status::Partial
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An Incoming Message head. Includes request/status line, and headers.
|
||||
@@ -456,19 +489,30 @@ mod tests {
|
||||
read_err("1;no CRLF");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_incoming() {
|
||||
use buffer::BufReader;
|
||||
use mock::MockStream;
|
||||
|
||||
use super::parse_request;
|
||||
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
|
||||
let mut buf = BufReader::new(&mut raw);
|
||||
parse_request(&mut buf).unwrap();
|
||||
}
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_incoming(b: &mut Bencher) {
|
||||
use std::io::BufReader;
|
||||
use buffer::BufReader;
|
||||
use mock::MockStream;
|
||||
|
||||
use super::parse_request;
|
||||
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
|
||||
let mut buf = BufReader::new(&mut raw);
|
||||
b.iter(|| {
|
||||
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
|
||||
let mut buf = BufReader::new(&mut raw);
|
||||
|
||||
parse_request(&mut buf).unwrap();
|
||||
buf.get_mut().read.set_position(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,8 @@ macro_rules! inspect(
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod mock;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod buffer;
|
||||
pub mod client;
|
||||
pub mod error;
|
||||
pub mod method;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! HTTP Server
|
||||
use std::io::{BufReader, BufWriter, Write};
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::path::Path;
|
||||
@@ -14,6 +14,7 @@ pub use net::{Fresh, Streaming};
|
||||
|
||||
use HttpError::HttpIoError;
|
||||
use {HttpResult};
|
||||
use buffer::BufReader;
|
||||
use header::{Headers, Connection, Expect};
|
||||
use header::ConnectionOption::{Close, KeepAlive};
|
||||
use method::Method;
|
||||
@@ -227,6 +228,7 @@ mod tests {
|
||||
Host: example.domain\r\n\
|
||||
Expect: 100-continue\r\n\
|
||||
Content-Length: 10\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
1234567890\
|
||||
");
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
//!
|
||||
//! These are requests that a `hyper::Server` receives, and include its method,
|
||||
//! target URI, headers, and message body.
|
||||
use std::io::{self, Read, BufReader};
|
||||
use std::io::{self, Read};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use {HttpResult};
|
||||
use buffer::BufReader;
|
||||
use net::NetworkStream;
|
||||
use version::{HttpVersion};
|
||||
use method::Method::{self, Get, Head};
|
||||
@@ -81,12 +82,13 @@ impl<'a, 'b> Read for Request<'a, 'b> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use buffer::BufReader;
|
||||
use header::{Host, TransferEncoding, Encoding};
|
||||
use net::NetworkStream;
|
||||
use mock::MockStream;
|
||||
use super::Request;
|
||||
|
||||
use std::io::{self, Read, BufReader};
|
||||
use std::io::{self, Read};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
fn sock(s: &str) -> SocketAddr {
|
||||
|
||||
Reference in New Issue
Block a user