Skip to content

Commit 6c31ea8

Browse files
committed
Merge pull request #401 from hyperium/packets
fix(http): read more before triggering TooLargeError
2 parents 48700aa + cb59f60 commit 6c31ea8

File tree

6 files changed

+189
-43
lines changed

6 files changed

+189
-43
lines changed

src/buffer.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::cmp;
2+
use std::iter;
3+
use std::io::{self, Read, BufRead, Cursor};
4+
5+
pub struct BufReader<R> {
6+
buf: Cursor<Vec<u8>>,
7+
inner: R
8+
}
9+
10+
const INIT_BUFFER_SIZE: usize = 4096;
11+
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
12+
13+
impl<R: Read> BufReader<R> {
14+
pub fn new(rdr: R) -> BufReader<R> {
15+
BufReader::with_capacity(rdr, INIT_BUFFER_SIZE)
16+
}
17+
18+
pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> {
19+
BufReader {
20+
buf: Cursor::new(Vec::with_capacity(cap)),
21+
inner: rdr
22+
}
23+
}
24+
25+
pub fn get_ref(&self) -> &R { &self.inner }
26+
27+
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
28+
29+
pub fn get_buf(&self) -> &[u8] {
30+
self.buf.get_ref()
31+
}
32+
33+
pub fn into_inner(self) -> R { self.inner }
34+
35+
pub fn read_into_buf(&mut self) -> io::Result<usize> {
36+
let v = self.buf.get_mut();
37+
reserve(v);
38+
let inner = &mut self.inner;
39+
with_end_to_cap(v, |b| inner.read(b))
40+
}
41+
}
42+
43+
impl<R: Read> Read for BufReader<R> {
44+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
45+
if self.buf.get_ref().len() == self.buf.position() as usize &&
46+
buf.len() >= self.buf.get_ref().capacity() {
47+
return self.inner.read(buf);
48+
}
49+
try!(self.fill_buf());
50+
self.buf.read(buf)
51+
}
52+
}
53+
54+
impl<R: Read> BufRead for BufReader<R> {
55+
fn fill_buf(&mut self) -> io::Result<&[u8]> {
56+
if self.buf.position() as usize == self.buf.get_ref().len() {
57+
self.buf.set_position(0);
58+
let v = self.buf.get_mut();
59+
v.truncate(0);
60+
let inner = &mut self.inner;
61+
try!(with_end_to_cap(v, |b| inner.read(b)));
62+
}
63+
self.buf.fill_buf()
64+
}
65+
66+
fn consume(&mut self, amt: usize) {
67+
self.buf.consume(amt)
68+
}
69+
}
70+
71+
fn with_end_to_cap<F>(v: &mut Vec<u8>, f: F) -> io::Result<usize>
72+
where F: FnOnce(&mut [u8]) -> io::Result<usize>
73+
{
74+
let len = v.len();
75+
let new_area = v.capacity() - len;
76+
v.extend(iter::repeat(0).take(new_area));
77+
match f(&mut v[len..]) {
78+
Ok(n) => {
79+
v.truncate(len + n);
80+
Ok(n)
81+
}
82+
Err(e) => {
83+
v.truncate(len);
84+
Err(e)
85+
}
86+
}
87+
}
88+
89+
#[inline]
90+
fn reserve(v: &mut Vec<u8>) {
91+
let cap = v.capacity();
92+
if v.len() == cap {
93+
v.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
94+
}
95+
}

src/client/response.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Client Responses
2-
use std::io::{self, Read, BufReader};
2+
use std::io::{self, Read};
33
use std::num::FromPrimitive;
44
use std::marker::PhantomData;
55

6+
use buffer::BufReader;
67
use header;
78
use header::{ContentLength, TransferEncoding};
89
use header::Encoding::Chunked;
@@ -103,9 +104,10 @@ impl Read for Response {
103104
mod tests {
104105
use std::borrow::Cow::Borrowed;
105106
use std::boxed::BoxAny;
106-
use std::io::{self, Read, BufReader};
107+
use std::io::{self, Read};
107108
use std::marker::PhantomData;
108109

110+
use buffer::BufReader;
109111
use header::Headers;
110112
use header::TransferEncoding;
111113
use header::Encoding;

src/http.rs

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use std::io::{self, Read, Write, BufRead};
55

66
use httparse;
77

8+
use buffer::BufReader;
89
use header::Headers;
910
use method::Method;
1011
use uri::RequestUri;
1112
use version::HttpVersion::{self, Http10, Http11};
1213
use HttpError:: HttpTooLargeError;
13-
use HttpResult;
14+
use {HttpError, HttpResult};
1415

1516
use self::HttpReader::{SizedReader, ChunkedReader, EofReader, EmptyReader};
1617
use self::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter};
@@ -307,56 +308,88 @@ impl<W: Write> Write for HttpWriter<W> {
307308
}
308309
}
309310

311+
const MAX_HEADERS: usize = 100;
312+
310313
/// Parses a request into an Incoming message head.
311-
pub fn parse_request<T: BufRead>(buf: &mut T) -> HttpResult<Incoming<(Method, RequestUri)>> {
312-
let (inc, len) = {
313-
let slice = try!(buf.fill_buf());
314-
let mut headers = [httparse::Header { name: "", value: b"" }; 64];
315-
let mut req = httparse::Request::new(&mut headers);
316-
match try!(req.parse(slice)) {
314+
#[inline]
315+
pub fn parse_request<R: Read>(buf: &mut BufReader<R>) -> HttpResult<Incoming<(Method, RequestUri)>> {
316+
parse::<R, httparse::Request, (Method, RequestUri)>(buf)
317+
}
318+
319+
/// Parses a response into an Incoming message head.
320+
#[inline]
321+
pub fn parse_response<R: Read>(buf: &mut BufReader<R>) -> HttpResult<Incoming<RawStatus>> {
322+
parse::<R, httparse::Response, RawStatus>(buf)
323+
}
324+
325+
fn parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> HttpResult<Incoming<I>> {
326+
loop {
327+
match try!(try_parse::<R, T, I>(rdr)) {
328+
httparse::Status::Complete((inc, len)) => {
329+
rdr.consume(len);
330+
return Ok(inc);
331+
},
332+
_partial => ()
333+
}
334+
match try!(rdr.read_into_buf()) {
335+
0 => return Err(HttpTooLargeError),
336+
_ => ()
337+
}
338+
}
339+
}
340+
341+
fn try_parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> TryParseResult<I> {
342+
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
343+
<T as TryParse>::try_parse(&mut headers, rdr.get_buf())
344+
}
345+
346+
#[doc(hidden)]
347+
trait TryParse {
348+
type Subject;
349+
fn try_parse<'a>(headers: &'a mut [httparse::Header<'a>], buf: &'a [u8]) -> TryParseResult<Self::Subject>;
350+
}
351+
352+
type TryParseResult<T> = Result<httparse::Status<(Incoming<T>, usize)>, HttpError>;
353+
354+
impl<'a> TryParse for httparse::Request<'a> {
355+
type Subject = (Method, RequestUri);
356+
357+
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> TryParseResult<(Method, RequestUri)> {
358+
let mut req = httparse::Request::new(headers);
359+
Ok(match try!(req.parse(buf)) {
317360
httparse::Status::Complete(len) => {
318-
(Incoming {
361+
httparse::Status::Complete((Incoming {
319362
version: if req.version.unwrap() == 1 { Http11 } else { Http10 },
320363
subject: (
321364
try!(req.method.unwrap().parse()),
322365
try!(req.path.unwrap().parse())
323366
),
324367
headers: try!(Headers::from_raw(req.headers))
325-
}, len)
368+
}, len))
326369
},
327-
_ => {
328-
// request head is bigger than a BufRead's buffer? 400 that!
329-
return Err(HttpTooLargeError)
330-
}
331-
}
332-
};
333-
buf.consume(len);
334-
Ok(inc)
370+
httparse::Status::Partial => httparse::Status::Partial
371+
})
372+
}
335373
}
336374

337-
/// Parses a response into an Incoming message head.
338-
pub fn parse_response<T: BufRead>(buf: &mut T) -> HttpResult<Incoming<RawStatus>> {
339-
let (inc, len) = {
340-
let mut headers = [httparse::Header { name: "", value: b"" }; 64];
341-
let mut res = httparse::Response::new(&mut headers);
342-
match try!(res.parse(try!(buf.fill_buf()))) {
375+
impl<'a> TryParse for httparse::Response<'a> {
376+
type Subject = RawStatus;
377+
378+
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> TryParseResult<RawStatus> {
379+
let mut res = httparse::Response::new(headers);
380+
Ok(match try!(res.parse(buf)) {
343381
httparse::Status::Complete(len) => {
344-
(Incoming {
382+
httparse::Status::Complete((Incoming {
345383
version: if res.version.unwrap() == 1 { Http11 } else { Http10 },
346384
subject: RawStatus(
347385
res.code.unwrap(), res.reason.unwrap().to_owned().into_cow()
348386
),
349387
headers: try!(Headers::from_raw(res.headers))
350-
}, len)
388+
}, len))
351389
},
352-
_ => {
353-
// response head is bigger than a BufRead's buffer?
354-
return Err(HttpTooLargeError)
355-
}
356-
}
357-
};
358-
buf.consume(len);
359-
Ok(inc)
390+
httparse::Status::Partial => httparse::Status::Partial
391+
})
392+
}
360393
}
361394

362395
/// An Incoming Message head. Includes request/status line, and headers.
@@ -456,19 +489,30 @@ mod tests {
456489
read_err("1;no CRLF");
457490
}
458491

492+
#[test]
493+
fn test_parse_incoming() {
494+
use buffer::BufReader;
495+
use mock::MockStream;
496+
497+
use super::parse_request;
498+
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
499+
let mut buf = BufReader::new(&mut raw);
500+
parse_request(&mut buf).unwrap();
501+
}
502+
459503
use test::Bencher;
460504

461505
#[bench]
462506
fn bench_parse_incoming(b: &mut Bencher) {
463-
use std::io::BufReader;
507+
use buffer::BufReader;
464508
use mock::MockStream;
465509

466510
use super::parse_request;
511+
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
512+
let mut buf = BufReader::new(&mut raw);
467513
b.iter(|| {
468-
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");
469-
let mut buf = BufReader::new(&mut raw);
470-
471514
parse_request(&mut buf).unwrap();
515+
buf.get_mut().read.set_position(0);
472516
});
473517
}
474518
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ macro_rules! inspect(
167167
#[cfg(test)]
168168
#[macro_use]
169169
mod mock;
170-
170+
#[doc(hidden)]
171+
pub mod buffer;
171172
pub mod client;
172173
pub mod error;
173174
pub mod method;

src/server/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! HTTP Server
2-
use std::io::{BufReader, BufWriter, Write};
2+
use std::io::{BufWriter, Write};
33
use std::marker::PhantomData;
44
use std::net::{SocketAddr, ToSocketAddrs};
55
use std::path::Path;
@@ -14,6 +14,7 @@ pub use net::{Fresh, Streaming};
1414

1515
use HttpError::HttpIoError;
1616
use {HttpResult};
17+
use buffer::BufReader;
1718
use header::{Headers, Connection, Expect};
1819
use header::ConnectionOption::{Close, KeepAlive};
1920
use method::Method;
@@ -227,6 +228,7 @@ mod tests {
227228
Host: example.domain\r\n\
228229
Expect: 100-continue\r\n\
229230
Content-Length: 10\r\n\
231+
Connection: close\r\n\
230232
\r\n\
231233
1234567890\
232234
");

src/server/request.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
//!
33
//! These are requests that a `hyper::Server` receives, and include its method,
44
//! target URI, headers, and message body.
5-
use std::io::{self, Read, BufReader};
5+
use std::io::{self, Read};
66
use std::net::SocketAddr;
77

88
use {HttpResult};
9+
use buffer::BufReader;
910
use net::NetworkStream;
1011
use version::{HttpVersion};
1112
use method::Method::{self, Get, Head};
@@ -81,12 +82,13 @@ impl<'a, 'b> Read for Request<'a, 'b> {
8182

8283
#[cfg(test)]
8384
mod tests {
85+
use buffer::BufReader;
8486
use header::{Host, TransferEncoding, Encoding};
8587
use net::NetworkStream;
8688
use mock::MockStream;
8789
use super::Request;
8890

89-
use std::io::{self, Read, BufReader};
91+
use std::io::{self, Read};
9092
use std::net::SocketAddr;
9193

9294
fn sock(s: &str) -> SocketAddr {

0 commit comments

Comments
 (0)