fix(http): fix several cases in HttpReader

- reading 0 bytes when SizedReader.remaining is more than 0 returns
"early eof" error
- reading 0 bytes when ChunkedReader.remaining is more than 0 returns
"early eof" error
- if SizedReader.remaining is less than buf.len(), the buf is sliced to
the remaining size
This commit is contained in:
Sean McArthur
2015-08-31 19:30:52 -07:00
parent 93e6d29a5e
commit 5c7195ab4a

View File

@@ -214,6 +214,8 @@ impl HttpMessage for Http11Message {
} }
}); });
trace!("Http11Message.reader = {:?}", self.reader);
Ok(ResponseHead { Ok(ResponseHead {
headers: headers, headers: headers,
@@ -456,9 +458,13 @@ impl<R: Read> Read for HttpReader<R> {
if *remaining == 0 { if *remaining == 0 {
Ok(0) Ok(0)
} else { } else {
let num = try!(body.read(buf)) as u64; let to_read = min(*remaining as usize, buf.len());
let num = try!(body.read(&mut buf[..to_read])) as u64;
trace!("Sized read: {}", num);
if num > *remaining { if num > *remaining {
*remaining = 0; *remaining = 0;
} else if num == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "early eof"));
} else { } else {
*remaining -= num; *remaining -= num;
} }
@@ -486,6 +492,11 @@ impl<R: Read> Read for HttpReader<R> {
let to_read = min(rem as usize, buf.len()); let to_read = min(rem as usize, buf.len());
let count = try!(body.read(&mut buf[..to_read])) as u64; let count = try!(body.read(&mut buf[..to_read])) as u64;
if count == 0 {
*opt_remaining = Some(0);
return Err(io::Error::new(io::ErrorKind::Other, "early eof"));
}
rem -= count; rem -= count;
*opt_remaining = if rem > 0 { *opt_remaining = if rem > 0 {
Some(rem) Some(rem)
@@ -759,7 +770,12 @@ fn parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> ::Result
fn try_parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> TryParseResult<I> { fn try_parse<R: Read, T: TryParse<Subject=I>, I>(rdr: &mut BufReader<R>) -> TryParseResult<I> {
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS]; let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
<T as TryParse>::try_parse(&mut headers, rdr.get_buf()) let buf = rdr.get_buf();
if buf.len() == 0 {
return Ok(httparse::Status::Partial);
}
trace!("try_parse({:?})", buf);
<T as TryParse>::try_parse(&mut headers, buf)
} }
#[doc(hidden)] #[doc(hidden)]
@@ -776,9 +792,11 @@ impl<'a> TryParse for httparse::Request<'a, 'a> {
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) ->
TryParseResult<(Method, RequestUri)> { TryParseResult<(Method, RequestUri)> {
trace!("Request.try_parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut req = httparse::Request::new(headers); let mut req = httparse::Request::new(headers);
Ok(match try!(req.parse(buf)) { Ok(match try!(req.parse(buf)) {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
trace!("Request.try_parse Complete({})", len);
httparse::Status::Complete((Incoming { httparse::Status::Complete((Incoming {
version: if req.version.unwrap() == 1 { Http11 } else { Http10 }, version: if req.version.unwrap() == 1 { Http11 } else { Http10 },
subject: ( subject: (
@@ -798,9 +816,11 @@ impl<'a> TryParse for httparse::Response<'a, 'a> {
fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) -> fn try_parse<'b>(headers: &'b mut [httparse::Header<'b>], buf: &'b [u8]) ->
TryParseResult<RawStatus> { TryParseResult<RawStatus> {
trace!("Response.try_parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut res = httparse::Response::new(headers); let mut res = httparse::Response::new(headers);
Ok(match try!(res.parse(buf)) { Ok(match try!(res.parse(buf)) {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
trace!("Response.try_parse Complete({})", len);
let code = res.code.unwrap(); let code = res.code.unwrap();
let reason = match StatusCode::from_u16(code).canonical_reason() { let reason = match StatusCode::from_u16(code).canonical_reason() {
Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason), Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason),
@@ -837,7 +857,8 @@ pub const LINE_ENDING: &'static str = "\r\n";
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::{self, Write}; use std::error::Error;
use std::io::{self, Read, Write};
use buffer::BufReader; use buffer::BufReader;
use mock::MockStream; use mock::MockStream;
@@ -909,6 +930,30 @@ mod tests {
read_err("1;no CRLF"); read_err("1;no CRLF");
} }
#[test]
fn test_read_sized_early_eof() {
let mut r = super::HttpReader::SizedReader(MockStream::with_input(b"foo bar"), 10);
let mut buf = [0u8; 10];
assert_eq!(r.read(&mut buf).unwrap(), 7);
let e = r.read(&mut buf).unwrap_err();
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.description(), "early eof");
}
#[test]
fn test_read_chunked_early_eof() {
let mut r = super::HttpReader::ChunkedReader(MockStream::with_input(b"\
9\r\n\
foo bar\
"), None);
let mut buf = [0u8; 10];
assert_eq!(r.read(&mut buf).unwrap(), 7);
let e = r.read(&mut buf).unwrap_err();
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.description(), "early eof");
}
#[test] #[test]
fn test_parse_incoming() { fn test_parse_incoming() {
let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n");