feat(hyper): switch to std::io, std::net, and std::path.
All instances of `old_io` and `old_path` were switched to use the new shiny `std::io`, `std::net`, and `std::path` modules. This means that `Request` and `Response` implement `Read` and `Write` now. Because of the changes to `TcpListener`, this also takes the opportunity to correct the method usage of `Server`. As with other languages/frameworks, the server is first created with a handler, and then a host/port is passed to a `listen` method. This reverses what `Server` used to do. Closes #347 BREAKING CHANGE: Check the docs. Everything was touched.
This commit is contained in:
351
src/http.rs
351
src/http.rs
@@ -2,7 +2,7 @@
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::borrow::IntoCow;
|
||||
use std::cmp::min;
|
||||
use std::old_io::{self, Reader, IoResult, BufWriter};
|
||||
use std::io::{self, Read, Write, Cursor};
|
||||
use std::num::from_u16;
|
||||
use std::str;
|
||||
|
||||
@@ -14,8 +14,8 @@ use status::StatusCode;
|
||||
use uri;
|
||||
use uri::RequestUri::{AbsolutePath, AbsoluteUri, Authority, Star};
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http09, Http10, Http11, Http20};
|
||||
use HttpError::{HttpHeaderError, HttpIoError, HttpMethodError, HttpStatusError,
|
||||
use version::HttpVersion::{Http09, Http10, Http11};
|
||||
use HttpError::{HttpHeaderError, HttpMethodError, HttpStatusError,
|
||||
HttpUriError, HttpVersionError};
|
||||
use HttpResult;
|
||||
|
||||
@@ -52,10 +52,10 @@ pub enum HttpReader<R> {
|
||||
EmptyReader(R),
|
||||
}
|
||||
|
||||
impl<R: Reader> HttpReader<R> {
|
||||
impl<R: Read> HttpReader<R> {
|
||||
|
||||
/// Unwraps this HttpReader and returns the underlying Reader.
|
||||
pub fn unwrap(self) -> R {
|
||||
pub fn into_inner(self) -> R {
|
||||
match self {
|
||||
SizedReader(r, _) => r,
|
||||
ChunkedReader(r, _) => r,
|
||||
@@ -65,13 +65,13 @@ impl<R: Reader> HttpReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Reader> Reader for HttpReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
impl<R: Read> Read for HttpReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
SizedReader(ref mut body, ref mut remaining) => {
|
||||
debug!("Sized read, remaining={:?}", remaining);
|
||||
if *remaining == 0 {
|
||||
Err(old_io::standard_error(old_io::EndOfFile))
|
||||
Ok(0)
|
||||
} else {
|
||||
let num = try!(body.read(buf)) as u64;
|
||||
if num > *remaining {
|
||||
@@ -97,7 +97,7 @@ impl<R: Reader> Reader for HttpReader<R> {
|
||||
// if the 0 digit was missing from the stream, it would
|
||||
// be an InvalidInput error instead.
|
||||
debug!("end of chunked");
|
||||
return Err(old_io::standard_error(old_io::EndOfFile));
|
||||
return Ok(0)
|
||||
}
|
||||
|
||||
let to_read = min(rem as usize, buf.len());
|
||||
@@ -115,29 +115,44 @@ impl<R: Reader> Reader for HttpReader<R> {
|
||||
EofReader(ref mut body) => {
|
||||
body.read(buf)
|
||||
},
|
||||
EmptyReader(_) => Err(old_io::standard_error(old_io::EndOfFile))
|
||||
EmptyReader(_) => Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eat<R: Reader>(rdr: &mut R, bytes: &[u8]) -> IoResult<()> {
|
||||
fn eat<R: Read>(rdr: &mut R, bytes: &[u8]) -> io::Result<()> {
|
||||
let mut buf = [0];
|
||||
for &b in bytes.iter() {
|
||||
match try!(rdr.read_byte()) {
|
||||
byte if byte == b => (),
|
||||
_ => return Err(old_io::standard_error(old_io::InvalidInput))
|
||||
match try!(rdr.read(&mut buf)) {
|
||||
1 if buf[0] == b => (),
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid characters found",
|
||||
None))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Chunked chunks start with 1*HEXDIGIT, indicating the size of the chunk.
|
||||
fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> {
|
||||
fn read_chunk_size<R: Read>(rdr: &mut R) -> io::Result<u64> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size line",
|
||||
None)),
|
||||
|
||||
}
|
||||
})
|
||||
);
|
||||
let mut size = 0u64;
|
||||
let radix = 16;
|
||||
let mut in_ext = false;
|
||||
let mut in_chunk_size = true;
|
||||
loop {
|
||||
match try!(rdr.read_byte()) {
|
||||
match byte!(rdr) {
|
||||
b@b'0'...b'9' if in_chunk_size => {
|
||||
size *= radix;
|
||||
size += (b - b'0') as u64;
|
||||
@@ -151,9 +166,12 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> {
|
||||
size += (b + 10 - b'A') as u64;
|
||||
},
|
||||
CR => {
|
||||
match try!(rdr.read_byte()) {
|
||||
match byte!(rdr) {
|
||||
LF => break,
|
||||
_ => return Err(old_io::standard_error(old_io::InvalidInput))
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size line",
|
||||
None))
|
||||
|
||||
}
|
||||
},
|
||||
// If we weren't in the extension yet, the ";" signals its start
|
||||
@@ -177,7 +195,9 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> {
|
||||
// Finally, if we aren't in the extension and we're reading any
|
||||
// other octet, the chunk size line is invalid!
|
||||
_ => {
|
||||
return Err(old_io::standard_error(old_io::InvalidInput));
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk size line",
|
||||
None))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +206,7 @@ fn read_chunk_size<R: Reader>(rdr: &mut R) -> IoResult<u64> {
|
||||
}
|
||||
|
||||
/// Writers to handle different Transfer-Encodings.
|
||||
pub enum HttpWriter<W: Writer> {
|
||||
pub enum HttpWriter<W: Write> {
|
||||
/// A no-op Writer, used initially before Transfer-Encoding is determined.
|
||||
ThroughWriter(W),
|
||||
/// A Writer for when Transfer-Encoding includes `chunked`.
|
||||
@@ -199,10 +219,10 @@ pub enum HttpWriter<W: Writer> {
|
||||
EmptyWriter(W),
|
||||
}
|
||||
|
||||
impl<W: Writer> HttpWriter<W> {
|
||||
impl<W: Write> HttpWriter<W> {
|
||||
/// Unwraps the HttpWriter and returns the underlying Writer.
|
||||
#[inline]
|
||||
pub fn unwrap(self) -> W {
|
||||
pub fn into_inner(self) -> W {
|
||||
match self {
|
||||
ThroughWriter(w) => w,
|
||||
ChunkedWriter(w) => w,
|
||||
@@ -241,24 +261,25 @@ impl<W: Writer> HttpWriter<W> {
|
||||
/// A final `write_all()` is called with an empty message, and then flushed.
|
||||
/// The ChunkedWriter variant will use this to write the 0-sized last-chunk.
|
||||
#[inline]
|
||||
pub fn end(mut self) -> IoResult<W> {
|
||||
try!(self.write_all(&[]));
|
||||
pub fn end(mut self) -> io::Result<W> {
|
||||
try!(self.write(&[]));
|
||||
try!(self.flush());
|
||||
Ok(self.unwrap())
|
||||
Ok(self.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Writer> Writer for HttpWriter<W> {
|
||||
impl<W: Write> Write for HttpWriter<W> {
|
||||
#[inline]
|
||||
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> {
|
||||
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
||||
match *self {
|
||||
ThroughWriter(ref mut w) => w.write_all(msg),
|
||||
ThroughWriter(ref mut w) => w.write(msg),
|
||||
ChunkedWriter(ref mut w) => {
|
||||
let chunk_size = msg.len();
|
||||
debug!("chunked write, size = {:?}", chunk_size);
|
||||
try!(write!(w, "{:X}{}", chunk_size, LINE_ENDING));
|
||||
try!(w.write_all(msg));
|
||||
w.write_str(LINE_ENDING)
|
||||
try!(w.write_all(LINE_ENDING.as_bytes()));
|
||||
Ok(msg.len())
|
||||
},
|
||||
SizedWriter(ref mut w, ref mut remaining) => {
|
||||
let len = msg.len() as u64;
|
||||
@@ -266,29 +287,24 @@ impl<W: Writer> Writer for HttpWriter<W> {
|
||||
let len = *remaining;
|
||||
*remaining = 0;
|
||||
try!(w.write_all(&msg[..len as usize]));
|
||||
Err(old_io::standard_error(old_io::ShortWrite(len as usize)))
|
||||
Ok(len as usize)
|
||||
} else {
|
||||
*remaining -= len;
|
||||
w.write_all(msg)
|
||||
try!(w.write_all(msg));
|
||||
Ok(len as usize)
|
||||
}
|
||||
},
|
||||
EmptyWriter(..) => {
|
||||
let bytes = msg.len();
|
||||
if bytes == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(old_io::IoError {
|
||||
kind: old_io::ShortWrite(bytes),
|
||||
desc: "EmptyWriter cannot write any bytes",
|
||||
detail: Some("Cannot include a body with this kind of message".to_string())
|
||||
})
|
||||
if msg.len() != 0 {
|
||||
error!("Cannot include a body with this kind of message");
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match *self {
|
||||
ThroughWriter(ref mut w) => w.flush(),
|
||||
ChunkedWriter(ref mut w) => w.flush(),
|
||||
@@ -345,24 +361,36 @@ pub fn is_token(b: u8) -> bool {
|
||||
/// otherwise returns any error encountered reading the stream.
|
||||
///
|
||||
/// The remaining contents of `buf` are left untouched.
|
||||
fn read_token_until_space<R: Reader>(stream: &mut R, buf: &mut [u8]) -> HttpResult<bool> {
|
||||
use std::old_io::BufWriter;
|
||||
let mut bufwrt = BufWriter::new(buf);
|
||||
fn read_method_token_until_space<R: Read>(stream: &mut R, buf: &mut [u8]) -> HttpResult<bool> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut slot = [0];
|
||||
match try!($rdr.read(&mut slot)) {
|
||||
1 => slot[0],
|
||||
_ => return Err(HttpMethodError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let mut cursor = Cursor::new(buf);
|
||||
|
||||
loop {
|
||||
let byte = try!(stream.read_byte());
|
||||
let b = byte!(stream);
|
||||
|
||||
if byte == SP {
|
||||
if b == SP {
|
||||
break;
|
||||
} else if !is_token(byte) {
|
||||
} else if !is_token(b) {
|
||||
return Err(HttpMethodError);
|
||||
// Read to end but there's still more
|
||||
} else if bufwrt.write_u8(byte).is_err() {
|
||||
return Ok(false);
|
||||
} else {
|
||||
match cursor.write(&[b]) {
|
||||
Ok(1) => (),
|
||||
_ => return Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bufwrt.tell().unwrap() == 0 {
|
||||
if cursor.position() == 0 {
|
||||
return Err(HttpMethodError);
|
||||
}
|
||||
|
||||
@@ -372,10 +400,10 @@ fn read_token_until_space<R: Reader>(stream: &mut R, buf: &mut [u8]) -> HttpResu
|
||||
/// Read a `Method` from a raw stream, such as `GET`.
|
||||
/// ### Note:
|
||||
/// Extension methods are only parsed to 16 characters.
|
||||
pub fn read_method<R: Reader>(stream: &mut R) -> HttpResult<method::Method> {
|
||||
pub fn read_method<R: Read>(stream: &mut R) -> HttpResult<method::Method> {
|
||||
let mut buf = [SP; 16];
|
||||
|
||||
if !try!(read_token_until_space(stream, &mut buf)) {
|
||||
if !try!(read_method_token_until_space(stream, &mut buf)) {
|
||||
return Err(HttpMethodError);
|
||||
}
|
||||
|
||||
@@ -404,20 +432,29 @@ pub fn read_method<R: Reader>(stream: &mut R) -> HttpResult<method::Method> {
|
||||
}
|
||||
|
||||
/// Read a `RequestUri` from a raw stream.
|
||||
pub fn read_uri<R: Reader>(stream: &mut R) -> HttpResult<uri::RequestUri> {
|
||||
let mut b = try!(stream.read_byte());
|
||||
pub fn read_uri<R: Read>(stream: &mut R) -> HttpResult<uri::RequestUri> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpUriError(UrlError::InvalidCharacter)),
|
||||
}
|
||||
})
|
||||
);
|
||||
let mut b = byte!(stream);
|
||||
while b == SP {
|
||||
b = try!(stream.read_byte());
|
||||
b = byte!(stream);
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
if b == STAR {
|
||||
try!(expect(stream.read_byte(), SP));
|
||||
try!(expect(byte!(stream), SP));
|
||||
return Ok(Star)
|
||||
} else {
|
||||
s.push(b as char);
|
||||
loop {
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
SP => {
|
||||
break;
|
||||
},
|
||||
@@ -448,32 +485,37 @@ pub fn read_uri<R: Reader>(stream: &mut R) -> HttpResult<uri::RequestUri> {
|
||||
|
||||
|
||||
/// Read the `HttpVersion` from a raw stream, such as `HTTP/1.1`.
|
||||
pub fn read_http_version<R: Reader>(stream: &mut R) -> HttpResult<HttpVersion> {
|
||||
try!(expect(stream.read_byte(), b'H'));
|
||||
try!(expect(stream.read_byte(), b'T'));
|
||||
try!(expect(stream.read_byte(), b'T'));
|
||||
try!(expect(stream.read_byte(), b'P'));
|
||||
try!(expect(stream.read_byte(), b'/'));
|
||||
pub fn read_http_version<R: Read>(stream: &mut R) -> HttpResult<HttpVersion> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpVersionError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
match try!(stream.read_byte()) {
|
||||
try!(expect(byte!(stream), b'H'));
|
||||
try!(expect(byte!(stream), b'T'));
|
||||
try!(expect(byte!(stream), b'T'));
|
||||
try!(expect(byte!(stream), b'P'));
|
||||
try!(expect(byte!(stream), b'/'));
|
||||
|
||||
match byte!(stream) {
|
||||
b'0' => {
|
||||
try!(expect(stream.read_byte(), b'.'));
|
||||
try!(expect(stream.read_byte(), b'9'));
|
||||
try!(expect(byte!(stream), b'.'));
|
||||
try!(expect(byte!(stream), b'9'));
|
||||
Ok(Http09)
|
||||
},
|
||||
b'1' => {
|
||||
try!(expect(stream.read_byte(), b'.'));
|
||||
match try!(stream.read_byte()) {
|
||||
try!(expect(byte!(stream), b'.'));
|
||||
match byte!(stream) {
|
||||
b'0' => Ok(Http10),
|
||||
b'1' => Ok(Http11),
|
||||
_ => Err(HttpVersionError)
|
||||
}
|
||||
},
|
||||
b'2' => {
|
||||
try!(expect(stream.read_byte(), b'.'));
|
||||
try!(expect(stream.read_byte(), b'0'));
|
||||
Ok(Http20)
|
||||
},
|
||||
_ => Err(HttpVersionError)
|
||||
}
|
||||
}
|
||||
@@ -507,14 +549,24 @@ pub type RawHeaderLine = (String, Vec<u8>);
|
||||
/// > ; obsolete line folding
|
||||
/// > ; see Section 3.2.4
|
||||
/// > ```
|
||||
pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine>> {
|
||||
pub fn read_header<R: Read>(stream: &mut R) -> HttpResult<Option<RawHeaderLine>> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpHeaderError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let mut name = String::new();
|
||||
let mut value = vec![];
|
||||
|
||||
loop {
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
CR if name.len() == 0 => {
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
LF => return Ok(None),
|
||||
_ => return Err(HttpHeaderError)
|
||||
}
|
||||
@@ -534,7 +586,7 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine
|
||||
|
||||
todo!("handle obs-folding (gross!)");
|
||||
loop {
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
CR => break,
|
||||
LF => return Err(HttpHeaderError),
|
||||
b' ' if ows => {},
|
||||
@@ -549,7 +601,7 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine
|
||||
let real_len = value.len() - value.iter().rev().take_while(|&&x| b' ' == x).count();
|
||||
value.truncate(real_len);
|
||||
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
LF => Ok(Some((name, value))),
|
||||
_ => Err(HttpHeaderError)
|
||||
}
|
||||
@@ -560,7 +612,17 @@ pub fn read_header<R: Reader>(stream: &mut R) -> HttpResult<Option<RawHeaderLine
|
||||
pub type RequestLine = (method::Method, uri::RequestUri, HttpVersion);
|
||||
|
||||
/// Read the `RequestLine`, such as `GET / HTTP/1.1`.
|
||||
pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> {
|
||||
pub fn read_request_line<R: Read>(stream: &mut R) -> HttpResult<RequestLine> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpVersionError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
debug!("read request line");
|
||||
let method = try!(read_method(stream));
|
||||
debug!("method = {:?}", method);
|
||||
@@ -569,10 +631,10 @@ pub fn read_request_line<R: Reader>(stream: &mut R) -> HttpResult<RequestLine> {
|
||||
let version = try!(read_http_version(stream));
|
||||
debug!("version = {:?}", version);
|
||||
|
||||
if try!(stream.read_byte()) != CR {
|
||||
if byte!(stream) != CR {
|
||||
return Err(HttpVersionError);
|
||||
}
|
||||
if try!(stream.read_byte()) != LF {
|
||||
if byte!(stream) != LF {
|
||||
return Err(HttpVersionError);
|
||||
}
|
||||
|
||||
@@ -606,9 +668,19 @@ impl Clone for RawStatus {
|
||||
/// > status-code = 3DIGIT
|
||||
/// > reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
||||
/// >```
|
||||
pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> {
|
||||
pub fn read_status_line<R: Read>(stream: &mut R) -> HttpResult<StatusLine> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpVersionError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let version = try!(read_http_version(stream));
|
||||
if try!(stream.read_byte()) != SP {
|
||||
if byte!(stream) != SP {
|
||||
return Err(HttpVersionError);
|
||||
}
|
||||
let code = try!(read_status(stream));
|
||||
@@ -617,11 +689,21 @@ pub fn read_status_line<R: Reader>(stream: &mut R) -> HttpResult<StatusLine> {
|
||||
}
|
||||
|
||||
/// Read the StatusCode from a stream.
|
||||
pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> {
|
||||
pub fn read_status<R: Read>(stream: &mut R) -> HttpResult<RawStatus> {
|
||||
macro_rules! byte (
|
||||
($rdr:ident) => ({
|
||||
let mut buf = [0];
|
||||
match try!($rdr.read(&mut buf)) {
|
||||
1 => buf[0],
|
||||
_ => return Err(HttpStatusError),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
let code = [
|
||||
try!(stream.read_byte()),
|
||||
try!(stream.read_byte()),
|
||||
try!(stream.read_byte()),
|
||||
byte!(stream),
|
||||
byte!(stream),
|
||||
byte!(stream),
|
||||
];
|
||||
|
||||
let code = match str::from_utf8(code.as_slice()).ok().and_then(|x| x.parse().ok()) {
|
||||
@@ -629,27 +711,25 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> {
|
||||
None => return Err(HttpStatusError)
|
||||
};
|
||||
|
||||
match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
b' ' => (),
|
||||
_ => return Err(HttpStatusError)
|
||||
}
|
||||
|
||||
let mut buf = [b' '; 32];
|
||||
|
||||
let mut buf = [SP; 32];
|
||||
let mut cursor = Cursor::new(&mut buf[..]);
|
||||
{
|
||||
let mut bufwrt = BufWriter::new(&mut buf);
|
||||
'read: loop {
|
||||
match try!(stream.read_byte()) {
|
||||
CR => match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
CR => match byte!(stream) {
|
||||
LF => break,
|
||||
_ => return Err(HttpStatusError)
|
||||
},
|
||||
b => match bufwrt.write_u8(b) {
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
b => match cursor.write(&[b]) {
|
||||
Ok(0) | Err(_) => {
|
||||
for _ in 0u8..128 {
|
||||
match try!(stream.read_byte()) {
|
||||
CR => match try!(stream.read_byte()) {
|
||||
match byte!(stream) {
|
||||
CR => match byte!(stream) {
|
||||
LF => break 'read,
|
||||
_ => return Err(HttpStatusError)
|
||||
},
|
||||
@@ -658,12 +738,13 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> {
|
||||
}
|
||||
return Err(HttpStatusError)
|
||||
}
|
||||
Ok(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let reason = match str::from_utf8(&buf[..]) {
|
||||
let reason = match str::from_utf8(cursor.into_inner()) {
|
||||
Ok(s) => s.trim(),
|
||||
Err(_) => return Err(HttpStatusError)
|
||||
};
|
||||
@@ -686,39 +767,34 @@ pub fn read_status<R: Reader>(stream: &mut R) -> HttpResult<RawStatus> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn expect(r: IoResult<u8>, expected: u8) -> HttpResult<()> {
|
||||
match r {
|
||||
Ok(b) if b == expected => Ok(()),
|
||||
Ok(_) => Err(HttpVersionError),
|
||||
Err(e) => Err(HttpIoError(e))
|
||||
fn expect(actual: u8, expected: u8) -> HttpResult<()> {
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(HttpVersionError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::old_io::{self, MemReader, MemWriter, IoResult};
|
||||
use std::io::{self, Write};
|
||||
use std::borrow::Cow::{Borrowed, Owned};
|
||||
use test::Bencher;
|
||||
use uri::RequestUri;
|
||||
use uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority};
|
||||
use method;
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http10, Http11, Http20};
|
||||
use version::HttpVersion::{Http10, Http11};
|
||||
use HttpError::{HttpVersionError, HttpMethodError};
|
||||
use HttpResult;
|
||||
use url::Url;
|
||||
|
||||
use super::{read_method, read_uri, read_http_version, read_header,
|
||||
RawHeaderLine, read_status, RawStatus, read_chunk_size};
|
||||
|
||||
fn mem(s: &str) -> MemReader {
|
||||
MemReader::new(s.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_method() {
|
||||
fn read(s: &str, result: HttpResult<method::Method>) {
|
||||
assert_eq!(read_method(&mut mem(s)), result);
|
||||
assert_eq!(read_method(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
read("GET /", Ok(method::Method::Get));
|
||||
@@ -737,7 +813,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_read_uri() {
|
||||
fn read(s: &str, result: HttpResult<RequestUri>) {
|
||||
assert_eq!(read_uri(&mut mem(s)), result);
|
||||
assert_eq!(read_uri(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
read("* ", Ok(Star));
|
||||
@@ -749,12 +825,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_read_http_version() {
|
||||
fn read(s: &str, result: HttpResult<HttpVersion>) {
|
||||
assert_eq!(read_http_version(&mut mem(s)), result);
|
||||
assert_eq!(read_http_version(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
read("HTTP/1.0", Ok(Http10));
|
||||
read("HTTP/1.1", Ok(Http11));
|
||||
read("HTTP/2.0", Ok(Http20));
|
||||
read("HTP/2.0", Err(HttpVersionError));
|
||||
read("HTTP.2.0", Err(HttpVersionError));
|
||||
read("HTTP 2.0", Err(HttpVersionError));
|
||||
@@ -764,11 +839,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_read_status() {
|
||||
fn read(s: &str, result: HttpResult<RawStatus>) {
|
||||
assert_eq!(read_status(&mut mem(s)), result);
|
||||
assert_eq!(read_status(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
fn read_ignore_string(s: &str, result: HttpResult<RawStatus>) {
|
||||
match (read_status(&mut mem(s)), result) {
|
||||
match (read_status(&mut s.as_bytes()), result) {
|
||||
(Ok(RawStatus(ref c1, _)), Ok(RawStatus(ref c2, _))) => {
|
||||
assert_eq!(c1, c2);
|
||||
},
|
||||
@@ -788,7 +863,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_read_header() {
|
||||
fn read(s: &str, result: HttpResult<Option<RawHeaderLine>>) {
|
||||
assert_eq!(read_header(&mut mem(s)), result);
|
||||
assert_eq!(read_header(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
read("Host: rust-lang.org\r\n", Ok(Some(("Host".to_string(),
|
||||
@@ -798,10 +873,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_write_chunked() {
|
||||
use std::str::from_utf8;
|
||||
let mut w = super::HttpWriter::ChunkedWriter(MemWriter::new());
|
||||
let mut w = super::HttpWriter::ChunkedWriter(Vec::new());
|
||||
w.write_all(b"foo bar").unwrap();
|
||||
w.write_all(b"baz quux herp").unwrap();
|
||||
let buf = w.end().unwrap().into_inner();
|
||||
let buf = w.end().unwrap();
|
||||
let s = from_utf8(buf.as_slice()).unwrap();
|
||||
assert_eq!(s, "7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n");
|
||||
}
|
||||
@@ -809,19 +884,23 @@ mod tests {
|
||||
#[test]
|
||||
fn test_write_sized() {
|
||||
use std::str::from_utf8;
|
||||
let mut w = super::HttpWriter::SizedWriter(MemWriter::new(), 8);
|
||||
let mut w = super::HttpWriter::SizedWriter(Vec::new(), 8);
|
||||
w.write_all(b"foo bar").unwrap();
|
||||
assert_eq!(w.write_all(b"baz"), Err(old_io::standard_error(old_io::ShortWrite(1))));
|
||||
assert_eq!(w.write(b"baz"), Ok(1));
|
||||
|
||||
let buf = w.end().unwrap().into_inner();
|
||||
let buf = w.end().unwrap();
|
||||
let s = from_utf8(buf.as_slice()).unwrap();
|
||||
assert_eq!(s, "foo barb");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_chunk_size() {
|
||||
fn read(s: &str, result: IoResult<u64>) {
|
||||
assert_eq!(read_chunk_size(&mut mem(s)), result);
|
||||
fn read(s: &str, result: io::Result<u64>) {
|
||||
assert_eq!(read_chunk_size(&mut s.as_bytes()), result);
|
||||
}
|
||||
|
||||
fn read_err(s: &str) {
|
||||
assert_eq!(read_chunk_size(&mut s.as_bytes()).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||
}
|
||||
|
||||
read("1\r\n", Ok(1));
|
||||
@@ -833,13 +912,13 @@ mod tests {
|
||||
read("Ff\r\n", Ok(255));
|
||||
read("Ff \r\n", Ok(255));
|
||||
// Missing LF or CRLF
|
||||
read("F\rF", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("F", Err(old_io::standard_error(old_io::EndOfFile)));
|
||||
read_err("F\rF");
|
||||
read_err("F");
|
||||
// Invalid hex digit
|
||||
read("X\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("1X\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("-\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("-1\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read_err("X\r\n");
|
||||
read_err("1X\r\n");
|
||||
read_err("-\r\n");
|
||||
read_err("-1\r\n");
|
||||
// Acceptable (if not fully valid) extensions do not influence the size
|
||||
read("1;extension\r\n", Ok(1));
|
||||
read("a;ext name=value\r\n", Ok(10));
|
||||
@@ -850,21 +929,21 @@ mod tests {
|
||||
read("3 ;\r\n", Ok(3));
|
||||
read("3 ; \r\n", Ok(3));
|
||||
// Invalid extensions cause an error
|
||||
read("1 invalid extension\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("1 A\r\n", Err(old_io::standard_error(old_io::InvalidInput)));
|
||||
read("1;no CRLF", Err(old_io::standard_error(old_io::EndOfFile)));
|
||||
read_err("1 invalid extension\r\n");
|
||||
read_err("1 A\r\n");
|
||||
read_err("1;no CRLF");
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_read_method(b: &mut Bencher) {
|
||||
b.bytes = b"CONNECT ".len() as u64;
|
||||
b.iter(|| assert_eq!(read_method(&mut mem("CONNECT ")), Ok(method::Method::Connect)));
|
||||
b.iter(|| assert_eq!(read_method(&mut b"CONNECT "), Ok(method::Method::Connect)));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_read_status(b: &mut Bencher) {
|
||||
b.bytes = b"404 Not Found\r\n".len() as u64;
|
||||
b.iter(|| assert_eq!(read_status(&mut mem("404 Not Found\r\n")), Ok(RawStatus(404, Borrowed("Not Found")))));
|
||||
b.iter(|| assert_eq!(read_status(&mut b"404 Not Found\r\n"), Ok(RawStatus(404, Borrowed("Not Found")))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user