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:
Sean McArthur
2015-02-17 15:29:52 -08:00
parent 7235d3f74a
commit 0fd6fcd7c7
22 changed files with 641 additions and 639 deletions

View File

@@ -27,12 +27,13 @@ Hello World Server:
```rust
extern crate hyper;
use hyper::status::StatusCode;
use hyper::server::Server;
use hyper::server::request::Request;
use hyper::server::response::Response;
use std::io::Write;
use std::net::IpAddr;
use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
use hyper::net::Fresh;
use hyper::IpAddr::Ipv4Addr;
fn hello(_: Request, mut res: Response<Fresh>) {
let mut res = res.start().unwrap();
@@ -41,8 +42,7 @@ fn hello(_: Request, mut res: Response<Fresh>) {
}
fn main() {
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337);
server.listen(hello).unwrap();
Server::http(hello).listen(IpAddr::new_v4(127, 0, 0, 1), 3000).unwrap();
}
```
@@ -51,7 +51,9 @@ Client:
```rust
extern crate hyper;
use hyper::client::Client;
use std::io::Read;
use hyper::Client;
use hyper::header::Connection;
use hyper::header::ConnectionOption;
@@ -67,7 +69,8 @@ fn main() {
.send().unwrap();
// Read the Response.
let body = res.read_to_string().unwrap();
let mut body = String::new();
res.read_to_string(&mut body).unwrap();
println!("Response: {}", body);
}

View File

@@ -1,33 +1,53 @@
#![feature(core, old_io, test)]
#![feature(collections, io, net, test)]
extern crate hyper;
extern crate test;
use std::fmt;
use std::old_io::net::ip::Ipv4Addr;
use hyper::server::{Request, Response, Server};
use hyper::header::Headers;
use hyper::Client;
use std::io::{self, Read, Write, Cursor};
use std::net::SocketAddr;
fn listen() -> hyper::server::Listening {
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 0);
server.listen(handle).unwrap()
use hyper::net;
static README: &'static [u8] = include_bytes!("../README.md");
struct MockStream {
read: Cursor<Vec<u8>>
}
macro_rules! try_return(
($e:expr) => {{
match $e {
Ok(v) => v,
Err(..) => return
impl MockStream {
fn new() -> MockStream {
let head = b"HTTP/1.1 200 OK\r\nServer: Mock\r\n\r\n";
let mut res = head.to_vec();
res.push_all(README);
MockStream {
read: Cursor::new(res)
}
}
}
}}
);
fn handle(_r: Request, res: Response) {
static BODY: &'static [u8] = b"Benchmarking hyper vs others!";
let mut res = try_return!(res.start());
try_return!(res.write_all(BODY));
try_return!(res.end());
impl Clone for MockStream {
fn clone(&self) -> MockStream {
MockStream {
read: Cursor::new(self.read.get_ref().clone())
}
}
}
impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read.read(buf)
}
}
impl Write for MockStream {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
// we're mocking, what do we care.
Ok(msg.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Clone)]
@@ -48,17 +68,36 @@ impl hyper::header::HeaderFormat for Foo {
}
}
#[bench]
fn bench_hyper(b: &mut test::Bencher) {
let mut listening = listen();
let s = format!("http://{}/", listening.socket);
let url = s.as_slice();
let mut client = Client::new();
let mut headers = Headers::new();
headers.set(Foo);
b.iter(|| {
client.get(url).header(Foo).send().unwrap().read_to_string().unwrap();
});
listening.close().unwrap()
impl net::NetworkStream for MockStream {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:1337".parse().unwrap())
}
}
struct MockConnector;
impl net::NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(&mut self, _: &str, _: u16, _: &str) -> io::Result<MockStream> {
Ok(MockStream::new())
}
}
#[bench]
fn bench_mock_hyper(b: &mut test::Bencher) {
let url = "http://127.0.0.1:1337/";
b.iter(|| {
let mut req = hyper::client::Request::with_connector(
hyper::Get, hyper::Url::parse(url).unwrap(), &mut MockConnector
).unwrap();
req.headers_mut().set(Foo);
let mut s = String::new();
req
.start().unwrap()
.send().unwrap()
.read_to_string(&mut s).unwrap()
});
}

View File

@@ -1,98 +0,0 @@
#![feature(collections, old_io, test)]
extern crate hyper;
extern crate test;
use std::fmt;
use std::old_io::{IoResult, MemReader};
use std::old_io::net::ip::SocketAddr;
use hyper::net;
static README: &'static [u8] = include_bytes!("../README.md");
struct MockStream {
read: MemReader,
}
impl Clone for MockStream {
fn clone(&self) -> MockStream {
MockStream::new()
}
}
impl MockStream {
fn new() -> MockStream {
let head = b"HTTP/1.1 200 OK\r\nServer: Mock\r\n\r\n";
let mut res = head.to_vec();
res.push_all(README);
MockStream {
read: MemReader::new(res),
}
}
}
impl Reader for MockStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
self.read.read(buf)
}
}
impl Writer for MockStream {
fn write_all(&mut self, _msg: &[u8]) -> IoResult<()> {
// we're mocking, what do we care.
Ok(())
}
}
#[derive(Clone)]
struct Foo;
impl hyper::header::Header for Foo {
fn header_name() -> &'static str {
"x-foo"
}
fn parse_header(_: &[Vec<u8>]) -> Option<Foo> {
None
}
}
impl hyper::header::HeaderFormat for Foo {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("Bar")
}
}
impl net::NetworkStream for MockStream {
fn peer_name(&mut self) -> IoResult<SocketAddr> {
Ok("127.0.0.1:1337".parse().unwrap())
}
}
struct MockConnector;
impl net::NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(&mut self, _: &str, _: u16, _: &str) -> IoResult<MockStream> {
Ok(MockStream::new())
}
}
#[bench]
fn bench_mock_hyper(b: &mut test::Bencher) {
let url = "http://127.0.0.1:1337/";
b.iter(|| {
let mut req = hyper::client::Request::with_connector(
hyper::Get, hyper::Url::parse(url).unwrap(), &mut MockConnector
).unwrap();
req.headers_mut().set(Foo);
req
.start().unwrap()
.send().unwrap()
.read_to_string().unwrap()
});
}

View File

@@ -1,9 +1,10 @@
#![feature(old_io, test)]
#![feature(io, net, test)]
extern crate hyper;
extern crate test;
use test::Bencher;
use std::old_io::net::ip::Ipv4Addr;
use std::io::{Read, Write};
use std::net::IpAddr;
use hyper::method::Method::Get;
use hyper::server::{Request, Response};
@@ -12,7 +13,8 @@ static PHRASE: &'static [u8] = b"Benchmarking hyper vs others!";
fn request(url: hyper::Url) {
let req = hyper::client::Request::new(Get, url).unwrap();
req.start().unwrap().send().unwrap().read_to_string().unwrap();
let mut s = String::new();
req.start().unwrap().send().unwrap().read_to_string(&mut s).unwrap();
}
fn hyper_handle(_: Request, res: Response) {
@@ -23,8 +25,8 @@ fn hyper_handle(_: Request, res: Response) {
#[bench]
fn bench_hyper(b: &mut Bencher) {
let server = hyper::Server::http(Ipv4Addr(127, 0, 0, 1), 0);
let mut listener = server.listen(hyper_handle).unwrap();
let server = hyper::Server::http(hyper_handle);
let mut listener = server.listen(IpAddr::new_v4(127, 0, 0, 1), 0).unwrap();
let url = hyper::Url::parse(&*format!("http://{}", listener.socket)).unwrap();
b.iter(|| request(url.clone()));

View File

@@ -1,9 +1,7 @@
#![feature(env, old_io)]
#![feature(env)]
extern crate hyper;
use std::env;
use std::old_io::stdout;
use std::old_io::util::copy;
use hyper::Client;
@@ -18,16 +16,12 @@ fn main() {
let mut client = Client::new();
let mut res = match client.get(&*url).send() {
let res = match client.get(&*url).send() {
Ok(res) => res,
Err(err) => panic!("Failed to connect: {:?}", err)
};
println!("Response: {}", res.status);
println!("Headers:\n{}", res.headers);
match copy(&mut res, &mut stdout()) {
Ok(..) => (),
Err(e) => panic!("Stream failure: {:?}", e)
};
//TODO: add copy back when std::stdio impls std::io::Write.
}

View File

@@ -1,7 +1,8 @@
#![feature(old_io)]
#![feature(io, net)]
extern crate hyper;
use std::old_io::net::ip::Ipv4Addr;
use std::io::Write;
use std::net::IpAddr;
use hyper::server::{Request, Response};
static PHRASE: &'static [u8] = b"Hello World!";
@@ -13,7 +14,7 @@ fn hello(_: Request, res: Response) {
}
fn main() {
let _listening = hyper::Server::http(Ipv4Addr(127, 0, 0, 1), 3000)
.listen(hello).unwrap();
let _listening = hyper::Server::http(hello)
.listen(IpAddr::new_v4(127, 0, 0, 1), 3000).unwrap();
println!("Listening on http://127.0.0.1:3000");
}

View File

@@ -1,9 +1,9 @@
#![feature(old_io)]
#![feature(io, net)]
extern crate hyper;
#[macro_use] extern crate log;
use std::old_io::util::copy;
use std::old_io::net::ip::Ipv4Addr;
use std::io::{Write, copy};
use std::net::IpAddr;
use hyper::{Get, Post};
use hyper::header::ContentLength;
@@ -50,7 +50,7 @@ fn echo(mut req: Request, mut res: Response) {
}
fn main() {
let server = Server::http(Ipv4Addr(127, 0, 0, 1), 1337);
let _guard = server.listen(echo).unwrap();
let server = Server::http(echo);
let _guard = server.listen(IpAddr::new_v4(127, 0, 0, 1), 1337).unwrap();
println!("Listening on http://127.0.0.1:1337");
}

View File

@@ -18,8 +18,7 @@
//! to the `status`, the `headers`, and the response body via the `Writer`
//! trait.
use std::default::Default;
use std::old_io::IoResult;
use std::old_io::util::copy;
use std::io::{self, copy, Read};
use std::iter::Extend;
use url::UrlParser;
@@ -30,7 +29,7 @@ use header::{ContentLength, Location};
use method::Method;
use net::{NetworkConnector, HttpConnector, ContextVerifier};
use status::StatusClass::Redirection;
use {Url, Port, HttpResult};
use {Url, HttpResult};
use HttpError::HttpUriError;
pub use self::request::Request;
@@ -238,9 +237,9 @@ pub trait IntoBody<'a> {
/// The target enum for the IntoBody trait.
pub enum Body<'a> {
/// A Reader does not necessarily know it's size, so it is chunked.
ChunkedBody(&'a mut (Reader + 'a)),
ChunkedBody(&'a mut (Read + 'a)),
/// For Readers that can know their size, like a `File`.
SizedBody(&'a mut (Reader + 'a), u64),
SizedBody(&'a mut (Read + 'a), u64),
/// A String has a size, and uses Content-Length.
BufBody(&'a [u8] , usize),
}
@@ -255,13 +254,13 @@ impl<'a> Body<'a> {
}
}
impl<'a> Reader for Body<'a> {
impl<'a> Read for Body<'a> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Body::ChunkedBody(ref mut r) => r.read(buf),
Body::SizedBody(ref mut r, _) => r.read(buf),
Body::BufBody(ref mut r, _) => r.read(buf),
Body::BufBody(ref mut r, _) => Read::read(r, buf),
}
}
}
@@ -288,7 +287,7 @@ impl<'a> IntoBody<'a> for &'a str {
}
}
impl<'a, R: Reader> IntoBody<'a> for &'a mut R {
impl<'a, R: Read> IntoBody<'a> for &'a mut R {
#[inline]
fn into_body(self) -> Body<'a> {
Body::ChunkedBody(self)
@@ -337,7 +336,7 @@ impl Default for RedirectPolicy {
}
}
fn get_host_and_port(url: &Url) -> HttpResult<(String, Port)> {
fn get_host_and_port(url: &Url) -> HttpResult<(String, u16)> {
let host = match url.serialize_host() {
Some(host) => host,
None => return Err(HttpUriError(UrlError::EmptyHost))

View File

@@ -1,6 +1,6 @@
//! Client Requests
use std::old_io::{BufferedWriter, IoResult};
use std::marker::PhantomData;
use std::io::{self, Write, BufWriter};
use url::Url;
@@ -23,7 +23,7 @@ pub struct Request<W> {
/// The HTTP version of this request.
pub version: version::HttpVersion,
body: HttpWriter<BufferedWriter<Box<NetworkStream + Send>>>,
body: HttpWriter<BufWriter<Box<NetworkStream + Send>>>,
headers: Headers,
method: method::Method,
@@ -59,7 +59,7 @@ impl Request<Fresh> {
let (host, port) = try!(get_host_and_port(&url));
let stream = try!(connector.connect(&*host, port, &*url.scheme));
let stream = ThroughWriter(BufferedWriter::new(box stream as Box<NetworkStream + Send>));
let stream = ThroughWriter(BufWriter::new(box stream as Box<NetworkStream + Send>));
let mut headers = Headers::new();
headers.set(Host {
@@ -96,7 +96,7 @@ impl Request<Fresh> {
Method::Get | Method::Head => {
debug!("headers [\n{:?}]", self.headers);
try!(write!(&mut self.body, "{}{}", self.headers, LINE_ENDING));
EmptyWriter(self.body.unwrap())
EmptyWriter(self.body.into_inner())
},
_ => {
let mut chunked = true;
@@ -131,9 +131,9 @@ impl Request<Fresh> {
try!(write!(&mut self.body, "{}{}", self.headers, LINE_ENDING));
if chunked {
ChunkedWriter(self.body.unwrap())
ChunkedWriter(self.body.into_inner())
} else {
SizedWriter(self.body.unwrap(), len)
SizedWriter(self.body.into_inner(), len)
}
}
};
@@ -158,19 +158,19 @@ impl Request<Streaming> {
///
/// Consumes the Request.
pub fn send(self) -> HttpResult<Response> {
let raw = try!(self.body.end()).into_inner();
let raw = try!(self.body.end()).into_inner().unwrap(); // end() already flushes
Response::new(raw)
}
}
impl Writer for Request<Streaming> {
impl Write for Request<Streaming> {
#[inline]
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> {
self.body.write_all(msg)
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
self.body.write(msg)
}
#[inline]
fn flush(&mut self) -> IoResult<()> {
fn flush(&mut self) -> io::Result<()> {
self.body.flush()
}
}
@@ -191,8 +191,8 @@ mod tests {
).unwrap();
let req = req.start().unwrap();
let stream = *req.body.end().unwrap()
.into_inner().downcast::<MockStream>().ok().unwrap();
let bytes = stream.write.into_inner();
.into_inner().unwrap().downcast::<MockStream>().ok().unwrap();
let bytes = stream.write;
let s = from_utf8(&bytes[..]).unwrap();
assert!(!s.contains("Content-Length:"));
assert!(!s.contains("Transfer-Encoding:"));
@@ -205,8 +205,8 @@ mod tests {
).unwrap();
let req = req.start().unwrap();
let stream = *req.body.end().unwrap()
.into_inner().downcast::<MockStream>().ok().unwrap();
let bytes = stream.write.into_inner();
.into_inner().unwrap().downcast::<MockStream>().ok().unwrap();
let bytes = stream.write;
let s = from_utf8(&bytes[..]).unwrap();
assert!(!s.contains("Content-Length:"));
assert!(!s.contains("Transfer-Encoding:"));

View File

@@ -1,6 +1,6 @@
//! Client Responses
use std::io::{self, Read, BufReader};
use std::num::FromPrimitive;
use std::old_io::{BufferedReader, IoResult};
use std::marker::PhantomData;
use header;
@@ -23,7 +23,7 @@ pub struct Response<S = HttpStream> {
/// The HTTP version of this response from the server.
pub version: version::HttpVersion,
status_raw: RawStatus,
body: HttpReader<BufferedReader<Box<NetworkStream + Send>>>,
body: HttpReader<BufReader<Box<NetworkStream + Send>>>,
_marker: PhantomData<S>,
}
@@ -35,7 +35,7 @@ impl Response {
/// Creates a new response from a server.
pub fn new(stream: Box<NetworkStream + Send>) -> HttpResult<Response> {
let mut stream = BufferedReader::new(stream);
let mut stream = BufReader::new(stream);
let (version, raw_status) = try!(read_status_line(&mut stream));
let status = match FromPrimitive::from_u16(raw_status.0) {
Some(status) => status,
@@ -89,13 +89,13 @@ impl Response {
/// Consumes the Request to return the NetworkStream underneath.
pub fn into_inner(self) -> Box<NetworkStream + Send> {
self.body.unwrap().into_inner()
self.body.into_inner().into_inner()
}
}
impl Reader for Response {
impl Read for Response {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.body.read(buf)
}
}
@@ -104,7 +104,7 @@ impl Reader for Response {
mod tests {
use std::borrow::Cow::Borrowed;
use std::boxed::BoxAny;
use std::old_io::BufferedReader;
use std::io::{self, Read, BufReader};
use std::marker::PhantomData;
use header::Headers;
@@ -119,14 +119,20 @@ mod tests {
use super::Response;
fn read_to_string(mut r: Response) -> io::Result<String> {
let mut s = String::new();
try!(r.read_to_string(&mut s));
Ok(s)
}
#[test]
fn test_unwrap() {
fn test_into_inner() {
let res = Response {
status: status::StatusCode::Ok,
headers: Headers::new(),
version: version::HttpVersion::Http11,
body: EofReader(BufferedReader::new(box MockStream::new() as Box<NetworkStream + Send>)),
body: EofReader(BufReader::new(box MockStream::new() as Box<NetworkStream + Send>)),
status_raw: RawStatus(200, Borrowed("OK")),
_marker: PhantomData,
};
@@ -152,7 +158,7 @@ mod tests {
\r\n"
);
let mut res = Response::new(box stream).unwrap();
let res = Response::new(box stream).unwrap();
// The status line is correct?
assert_eq!(res.status, status::StatusCode::Ok);
@@ -166,8 +172,7 @@ mod tests {
None => panic!("Transfer-Encoding: chunked expected!"),
};
// The body is correct?
let body = res.read_to_string().unwrap();
assert_eq!("qwert", body);
assert_eq!(read_to_string(res), Ok("qwert".to_string()));
}
/// Tests that when a chunk size is not a valid radix-16 number, an error
@@ -184,9 +189,9 @@ mod tests {
\r\n"
);
let mut res = Response::new(box stream).unwrap();
let res = Response::new(box stream).unwrap();
assert!(res.read_to_string().is_err());
assert!(read_to_string(res).is_err());
}
/// Tests that when a chunk size contains an invalid extension, an error is
@@ -203,9 +208,9 @@ mod tests {
\r\n"
);
let mut res = Response::new(box stream).unwrap();
let res = Response::new(box stream).unwrap();
assert!(res.read_to_string().is_err());
assert!(read_to_string(res).is_err());
}
/// Tests that when a valid extension that contains a digit is appended to
@@ -222,8 +227,8 @@ mod tests {
\r\n"
);
let mut res = Response::new(box stream).unwrap();
let res = Response::new(box stream).unwrap();
assert_eq!("1", res.read_to_string().unwrap())
assert_eq!(read_to_string(res), Ok("1".to_string()));
}
}

View File

@@ -1,4 +1,4 @@
use header::{self, QualityItem};
use header::QualityItem;
use std::str::FromStr;
use std::fmt;
@@ -49,7 +49,6 @@ impl_list_header!(AcceptLanguage,
#[cfg(test)]
mod tests {
use header::{Header, qitem, Quality, QualityItem};
use super::*;
#[test]

View File

@@ -142,14 +142,9 @@ impl FromStr for Basic {
#[cfg(test)]
mod tests {
use std::old_io::MemReader;
use super::{Authorization, Basic};
use super::super::super::{Headers};
fn mem(s: &str) -> MemReader {
MemReader::new(s.as_bytes().to_vec())
}
#[test]
fn test_raw_auth() {
let mut headers = Headers::new();
@@ -159,7 +154,7 @@ mod tests {
#[test]
fn test_raw_auth_parse() {
let headers = Headers::from_raw(&mut mem("Authorization: foo bar baz\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Authorization: foo bar baz\r\n\r\n").unwrap();
assert_eq!(&headers.get::<Authorization<String>>().unwrap().0[..], "foo bar baz");
}
@@ -179,7 +174,7 @@ mod tests {
#[test]
fn test_basic_auth_parse() {
let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n").unwrap();
let auth = headers.get::<Authorization<Basic>>().unwrap();
assert_eq!(&auth.0.username[..], "Aladdin");
assert_eq!(auth.0.password, Some("open sesame".to_string()));
@@ -187,7 +182,7 @@ mod tests {
#[test]
fn test_basic_auth_parse_no_password() {
let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjo=\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Authorization: Basic QWxhZGRpbjo=\r\n\r\n").unwrap();
let auth = headers.get::<Authorization<Basic>>().unwrap();
assert_eq!(auth.0.username.as_slice(), "Aladdin");
assert_eq!(auth.0.password, Some("".to_string()));

View File

@@ -1,5 +1,4 @@
use header::{Header, HeaderFormat};
use Port;
use std::fmt;
use header::parsing::from_one_raw_str;
@@ -15,7 +14,7 @@ pub struct Host {
/// The hostname, such a example.domain.
pub hostname: String,
/// An optional port number.
pub port: Option<Port>
pub port: Option<u16>
}
impl Header for Host {

View File

@@ -7,6 +7,7 @@
use std::any::{Any, TypeId};
use std::borrow::Cow::{Borrowed, Owned};
use std::fmt;
use std::io::Read;
use std::raw::TraitObject;
use std::str::from_utf8;
use std::collections::HashMap;
@@ -132,7 +133,7 @@ impl Headers {
}
#[doc(hidden)]
pub fn from_raw<R: Reader>(rdr: &mut R) -> HttpResult<Headers> {
pub fn from_raw<R: Read>(rdr: &mut R) -> HttpResult<Headers> {
let mut headers = Headers::new();
let mut count = 0u32;
loop {
@@ -534,7 +535,6 @@ impl<'a, H: HeaderFormat> fmt::Debug for HeaderFormatter<'a, H> {
#[cfg(test)]
mod tests {
use std::old_io::MemReader;
use std::fmt;
use mime::Mime;
use mime::TopLevel::Text;
@@ -544,13 +544,9 @@ mod tests {
use test::Bencher;
fn mem(s: &str) -> MemReader {
MemReader::new(s.as_bytes().to_vec())
}
#[test]
fn test_from_raw() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
assert_eq!(headers.get(), Some(&ContentLength(10)));
}
@@ -603,21 +599,21 @@ mod tests {
#[test]
fn test_different_structs_for_same_header() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
assert!(headers.get::<CrazyLength>().is_none());
}
#[test]
fn test_trailing_whitespace() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10 \r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10 \r\n\r\n").unwrap();
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
assert!(headers.get::<CrazyLength>().is_none());
}
#[test]
fn test_multiple_reads() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
let ContentLength(one) = *headers.get::<ContentLength>().unwrap();
let ContentLength(two) = *headers.get::<ContentLength>().unwrap();
assert_eq!(one, two);
@@ -625,14 +621,14 @@ mod tests {
#[test]
fn test_different_reads() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap();
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
let ContentType(_) = *headers.get::<ContentType>().unwrap();
}
#[test]
fn test_get_mutable() {
let mut headers = Headers::from_raw(&mut mem("Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n")).unwrap();
let mut headers = Headers::from_raw(&mut b"Content-Length: 10\r\nContent-Type: text/plain\r\n\r\n").unwrap();
*headers.get_mut::<ContentLength>().unwrap() = ContentLength(20);
assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20));
}
@@ -653,7 +649,7 @@ mod tests {
#[test]
fn test_headers_show_raw() {
let headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap();
let headers = Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap();
let s = headers.to_string();
assert_eq!(s, "Content-Length: 10\r\n");
}
@@ -720,7 +716,7 @@ mod tests {
#[bench]
fn bench_headers_from_raw(b: &mut Bencher) {
b.iter(|| Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap())
b.iter(|| Headers::from_raw(&mut b"Content-Length: 10\r\n\r\n").unwrap())
}
#[bench]

View File

@@ -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")))));
}
}

View File

@@ -1,6 +1,6 @@
#![feature(core, collections, io, old_io, os, old_path,
#![feature(core, collections, io, net, os, path,
std_misc, box_syntax, unsafe_destructor)]
#![deny(missing_docs)]
#![cfg_attr(test, deny(missing_docs))]
#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, feature(alloc, test))]
@@ -140,7 +140,7 @@ extern crate log;
#[cfg(test)]
extern crate test;
pub use std::old_io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port};
pub use mimewrapper::mime;
pub use url::Url;
pub use client::Client;
@@ -150,7 +150,7 @@ pub use server::Server;
use std::error::{Error, FromError};
use std::fmt;
use std::old_io::IoError;
use std::io::Error as IoError;
use self::HttpError::{HttpMethodError, HttpUriError, HttpVersionError,
HttpHeaderError, HttpStatusError, HttpIoError};
@@ -164,7 +164,7 @@ macro_rules! todo(
macro_rules! inspect(
($name:expr, $value:expr) => ({
let v = $value;
debug!("inspect: {:?} = {:?}", $name, v);
trace!("inspect: {:?} = {:?}", $name, v);
v
})
);

View File

@@ -1,67 +1,69 @@
use std::fmt;
use std::old_io::{IoResult, MemReader, MemWriter};
use std::old_io::net::ip::SocketAddr;
use std::io::{self, Read, Write, Cursor};
use std::net::SocketAddr;
use net::{NetworkStream, NetworkConnector};
pub struct MockStream {
pub read: MemReader,
pub write: MemWriter,
pub read: Cursor<Vec<u8>>,
pub write: Vec<u8>,
}
impl Clone for MockStream {
fn clone(&self) -> MockStream {
MockStream {
read: MemReader::new(self.read.get_ref().to_vec()),
write: MemWriter::from_vec(self.write.get_ref().to_vec()),
read: Cursor::new(self.read.get_ref().clone()),
write: self.write.clone()
}
}
}
impl PartialEq for MockStream {
fn eq(&self, other: &MockStream) -> bool {
self.read.get_ref() == other.read.get_ref() &&
self.write.get_ref() == other.write.get_ref()
}
}
impl fmt::Debug for MockStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MockStream {{ read: {:?}, write: {:?} }}",
self.read.get_ref(), self.write.get_ref())
write!(f, "MockStream {{ read: {:?}, write: {:?} }}", self.read.get_ref(), self.write)
}
}
impl PartialEq for MockStream {
fn eq(&self, other: &MockStream) -> bool {
self.read.get_ref() == other.read.get_ref() && self.write == other.write
}
}
impl MockStream {
pub fn new() -> MockStream {
MockStream {
read: MemReader::new(vec![]),
write: MemWriter::new(),
read: Cursor::new(vec![]),
write: vec![],
}
}
pub fn with_input(input: &[u8]) -> MockStream {
MockStream {
read: MemReader::new(input.to_vec()),
write: MemWriter::new(),
read: Cursor::new(input.to_vec()),
write: vec![]
}
}
}
impl Reader for MockStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read.read(buf)
}
}
impl Writer for MockStream {
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> {
self.write.write_all(msg)
impl Write for MockStream {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
Write::write(&mut self.write, msg)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl NetworkStream for MockStream {
fn peer_name(&mut self) -> IoResult<SocketAddr> {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:1337".parse().unwrap())
}
}
@@ -71,7 +73,7 @@ pub struct MockConnector;
impl NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(&mut self, _host: &str, _port: u16, _scheme: &str) -> IoResult<MockStream> {
fn connect(&mut self, _host: &str, _port: u16, _scheme: &str) -> io::Result<MockStream> {
Ok(MockStream::new())
}
}
@@ -86,8 +88,9 @@ macro_rules! mock_connector (
impl ::net::NetworkConnector for $name {
type Stream = ::mock::MockStream;
fn connect(&mut self, host: &str, port: u16, scheme: &str) -> ::std::old_io::IoResult<::mock::MockStream> {
fn connect(&mut self, host: &str, port: u16, scheme: &str) -> ::std::io::Result<::mock::MockStream> {
use std::collections::HashMap;
use std::io::Cursor;
debug!("MockStream::connect({:?}, {:?}, {:?})", host, port, scheme);
let mut map = HashMap::new();
$(map.insert($url, $res);)*
@@ -97,8 +100,8 @@ macro_rules! mock_connector (
// ignore port for now
match map.get(&*key) {
Some(res) => Ok(::mock::MockStream {
write: ::std::old_io::MemWriter::new(),
read: ::std::old_io::MemReader::new(res.to_string().into_bytes())
write: vec![],
read: Cursor::new(res.to_string().into_bytes()),
}),
None => panic!("{:?} doesn't know url {}", stringify!($name), key)
}

View File

@@ -1,11 +1,10 @@
//! A collection of traits abstracting over Listeners and Streams.
use std::any::{Any, TypeId};
use std::fmt;
use std::old_io::{IoResult, IoError, ConnectionAborted, InvalidInput, OtherIoError,
Stream, Listener, Acceptor};
use std::old_io::net::ip::{SocketAddr, ToSocketAddr, Port};
use std::old_io::net::tcp::{TcpStream, TcpListener, TcpAcceptor};
use std::io::{self, Read, Write};
use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener};
use std::mem;
use std::path::Path;
use std::raw::{self, TraitObject};
use std::sync::Arc;
@@ -24,34 +23,26 @@ macro_rules! try_some {
}
/// The write-status indicating headers have not been written.
#[allow(missing_copy_implementations)]
pub struct Fresh;
pub enum Fresh {}
/// The write-status indicating headers have been written.
#[allow(missing_copy_implementations)]
pub struct Streaming;
pub enum Streaming {}
/// An abstraction to listen for connections on a certain port.
pub trait NetworkListener {
/// Type of Acceptor
type Acceptor: NetworkAcceptor;
/// Listens on a socket.
fn listen<To: ToSocketAddr>(&mut self, addr: To) -> IoResult<Self::Acceptor>;
}
/// An abstraction to receive `NetworkStream`s.
pub trait NetworkAcceptor: Clone + Send {
/// Type of Stream to receive
pub trait NetworkListener: Clone {
/// The stream produced for each connection.
type Stream: NetworkStream + Send + Clone;
/// Listens on a socket.
//fn listen<To: ToSocketAddrs>(&mut self, addr: To) -> io::Result<Self::Acceptor>;
/// Returns an iterator of streams.
fn accept(&mut self) -> IoResult<Self::Stream>;
fn accept(&mut self) -> io::Result<Self::Stream>;
/// Get the address this Listener ended up listening on.
fn socket_name(&self) -> IoResult<SocketAddr>;
fn socket_addr(&mut self) -> io::Result<SocketAddr>;
/// Closes the Acceptor, so no more incoming connections will be handled.
fn close(&mut self) -> IoResult<()>;
// fn close(&mut self) -> io::Result<()>;
/// Returns an iterator over incoming connections.
fn incoming(&mut self) -> NetworkConnections<Self> {
@@ -60,20 +51,20 @@ pub trait NetworkAcceptor: Clone + Send {
}
/// An iterator wrapper over a NetworkAcceptor.
pub struct NetworkConnections<'a, N: NetworkAcceptor + 'a>(&'a mut N);
pub struct NetworkConnections<'a, N: NetworkListener + 'a>(&'a mut N);
impl<'a, N: NetworkAcceptor> Iterator for NetworkConnections<'a, N> {
type Item = IoResult<N::Stream>;
fn next(&mut self) -> Option<IoResult<N::Stream>> {
impl<'a, N: NetworkListener + 'a> Iterator for NetworkConnections<'a, N> {
type Item = io::Result<N::Stream>;
fn next(&mut self) -> Option<io::Result<N::Stream>> {
Some(self.0.accept())
}
}
/// An abstraction over streams that a Server can utilize.
pub trait NetworkStream: Stream + Any + StreamClone + Send {
pub trait NetworkStream: Read + Write + Any + StreamClone + Send {
/// Get the remote address of the underlying connection.
fn peer_name(&mut self) -> IoResult<SocketAddr>;
fn peer_addr(&mut self) -> io::Result<SocketAddr>;
}
@@ -94,7 +85,7 @@ pub trait NetworkConnector {
/// Type of Stream to create
type Stream: NetworkStream + Send;
/// Connect to a remote address.
fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult<Self::Stream>;
fn connect(&mut self, host: &str, port: u16, scheme: &str) -> io::Result<Self::Stream>;
}
impl fmt::Debug for Box<NetworkStream + Send> {
@@ -108,32 +99,6 @@ impl Clone for Box<NetworkStream + Send> {
fn clone(&self) -> Box<NetworkStream + Send> { self.clone_box() }
}
impl Reader for Box<NetworkStream + Send> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { (**self).read(buf) }
}
impl Writer for Box<NetworkStream + Send> {
#[inline]
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write_all(msg) }
#[inline]
fn flush(&mut self) -> IoResult<()> { (**self).flush() }
}
impl<'a> Reader for &'a mut NetworkStream {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { (**self).read(buf) }
}
impl<'a> Writer for &'a mut NetworkStream {
#[inline]
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> { (**self).write_all(msg) }
#[inline]
fn flush(&mut self) -> IoResult<()> { (**self).flush() }
}
impl UnsafeAnyExt for NetworkStream {
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
mem::transmute(mem::transmute::<&NetworkStream,
@@ -191,24 +156,31 @@ impl NetworkStream {
}
/// A `NetworkListener` for `HttpStream`s.
#[allow(missing_copy_implementations)]
pub enum HttpListener {
/// Http variant.
Http,
Http(TcpListener),
/// Https variant. The two paths point to the certificate and key PEM files, in that order.
Https(Path, Path),
Https(TcpListener, Arc<SslContext>)
}
impl NetworkListener for HttpListener {
type Acceptor = HttpAcceptor;
impl Clone for HttpListener {
fn clone(&self) -> HttpListener {
match *self {
HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()),
HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()),
}
}
}
#[inline]
fn listen<To: ToSocketAddr>(&mut self, addr: To) -> IoResult<HttpAcceptor> {
let mut tcp = try!(TcpListener::bind(addr));
let addr = try!(tcp.socket_name());
Ok(match *self {
HttpListener::Http => HttpAcceptor::Http(try!(tcp.listen()), addr),
HttpListener::Https(ref cert, ref key) => {
impl HttpListener {
/// Start listening to an address over HTTP.
pub fn http<To: ToSocketAddrs>(addr: &To) -> io::Result<HttpListener> {
Ok(HttpListener::Http(try!(TcpListener::bind(addr))))
}
/// Start listening to an address over HTTPS.
pub fn https<To: ToSocketAddrs>(addr: &To, cert: &Path, key: &Path) -> io::Result<HttpListener> {
let mut ssl_context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error));
try_some!(ssl_context.set_cipher_list("DEFAULT").map(lift_ssl_error));
try_some!(ssl_context.set_certificate_file(
@@ -216,38 +188,25 @@ impl NetworkListener for HttpListener {
try_some!(ssl_context.set_private_key_file(
key, X509FileType::PEM).map(lift_ssl_error));
ssl_context.set_verify(SslVerifyNone, None);
HttpAcceptor::Https(try!(tcp.listen()), addr, Arc::new(ssl_context))
}
})
Ok(HttpListener::Https(try!(TcpListener::bind(addr)), Arc::new(ssl_context)))
}
}
/// A `NetworkAcceptor` for `HttpStream`s.
#[derive(Clone)]
pub enum HttpAcceptor {
/// Http variant.
Http(TcpAcceptor, SocketAddr),
/// Https variant.
Https(TcpAcceptor, SocketAddr, Arc<SslContext>),
}
impl NetworkAcceptor for HttpAcceptor {
impl NetworkListener for HttpListener {
type Stream = HttpStream;
#[inline]
fn accept(&mut self) -> IoResult<HttpStream> {
fn accept(&mut self) -> io::Result<HttpStream> {
Ok(match *self {
HttpAcceptor::Http(ref mut tcp, _) => HttpStream::Http(try!(tcp.accept())),
HttpAcceptor::Https(ref mut tcp, _, ref ssl_context) => {
let stream = try!(tcp.accept());
match SslStream::<TcpStream>::new_server(&**ssl_context, stream) {
HttpListener::Http(ref mut tcp) => HttpStream::Http(CloneTcpStream(try!(tcp.accept()).0)),
HttpListener::Https(ref mut tcp, ref ssl_context) => {
let stream = CloneTcpStream(try!(tcp.accept()).0);
match SslStream::new_server(&**ssl_context, stream) {
Ok(ssl_stream) => HttpStream::Https(ssl_stream),
Err(StreamError(ref e)) => {
return Err(IoError {
kind: ConnectionAborted,
desc: "SSL Handshake Interrupted",
detail: Some(e.desc.to_string())
});
return Err(io::Error::new(io::ErrorKind::ConnectionAborted,
"SSL Handshake Interrupted",
Some(e.to_string())));
},
Err(e) => return Err(lift_ssl_error(e))
}
@@ -256,34 +215,54 @@ impl NetworkAcceptor for HttpAcceptor {
}
#[inline]
fn close(&mut self) -> IoResult<()> {
fn socket_addr(&mut self) -> io::Result<SocketAddr> {
match *self {
HttpAcceptor::Http(ref mut tcp, _) => tcp.close_accept(),
HttpAcceptor::Https(ref mut tcp, _, _) => tcp.close_accept(),
HttpListener::Http(ref mut tcp) => tcp.socket_addr(),
HttpListener::Https(ref mut tcp, _) => tcp.socket_addr(),
}
}
}
#[doc(hidden)]
pub struct CloneTcpStream(TcpStream);
impl Clone for CloneTcpStream{
#[inline]
fn socket_name(&self) -> IoResult<SocketAddr> {
match *self {
HttpAcceptor::Http(_, addr) => Ok(addr),
HttpAcceptor::Https(_, addr, _) => Ok(addr),
fn clone(&self) -> CloneTcpStream {
CloneTcpStream(self.0.try_clone().unwrap())
}
}
impl Read for CloneTcpStream {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl Write for CloneTcpStream {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
/// A wrapper around a TcpStream.
#[derive(Clone)]
pub enum HttpStream {
/// A stream over the HTTP protocol.
Http(TcpStream),
Http(CloneTcpStream),
/// A stream over the HTTP protocol, protected by SSL.
Https(SslStream<TcpStream>),
Https(SslStream<CloneTcpStream>),
}
impl Reader for HttpStream {
impl Read for HttpStream {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
HttpStream::Http(ref mut inner) => inner.read(buf),
HttpStream::Https(ref mut inner) => inner.read(buf)
@@ -291,16 +270,16 @@ impl Reader for HttpStream {
}
}
impl Writer for HttpStream {
impl Write for HttpStream {
#[inline]
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
match *self {
HttpStream::Http(ref mut inner) => inner.write_all(msg),
HttpStream::Https(ref mut inner) => inner.write_all(msg)
HttpStream::Http(ref mut inner) => inner.write(msg),
HttpStream::Https(ref mut inner) => inner.write(msg)
}
}
#[inline]
fn flush(&mut self) -> IoResult<()> {
fn flush(&mut self) -> io::Result<()> {
match *self {
HttpStream::Http(ref mut inner) => inner.flush(),
HttpStream::Https(ref mut inner) => inner.flush(),
@@ -309,10 +288,10 @@ impl Writer for HttpStream {
}
impl NetworkStream for HttpStream {
fn peer_name(&mut self) -> IoResult<SocketAddr> {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
match *self {
HttpStream::Http(ref mut inner) => inner.peer_name(),
HttpStream::Https(ref mut inner) => inner.get_mut().peer_name()
HttpStream::Http(ref mut inner) => inner.0.peer_addr(),
HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr()
}
}
}
@@ -327,16 +306,16 @@ pub type ContextVerifier<'v> = Box<FnMut(&mut SslContext) -> ()+'v>;
impl<'v> NetworkConnector for HttpConnector<'v> {
type Stream = HttpStream;
fn connect(&mut self, host: &str, port: Port, scheme: &str) -> IoResult<HttpStream> {
let addr = (host, port);
fn connect(&mut self, host: &str, port: u16, scheme: &str) -> io::Result<HttpStream> {
let addr = &(host, port);
match scheme {
"http" => {
debug!("http scheme");
Ok(HttpStream::Http(try!(TcpStream::connect(addr))))
Ok(HttpStream::Http(CloneTcpStream(try!(TcpStream::connect(addr)))))
},
"https" => {
debug!("https scheme");
let stream = try!(TcpStream::connect(addr));
let stream = CloneTcpStream(try!(TcpStream::connect(addr)));
let mut context = try!(SslContext::new(Sslv23).map_err(lift_ssl_error));
if let Some(ref mut verifier) = self.0 {
verifier(&mut context);
@@ -347,32 +326,26 @@ impl<'v> NetworkConnector for HttpConnector<'v> {
Ok(HttpStream::Https(stream))
},
_ => {
Err(IoError {
kind: InvalidInput,
desc: "Invalid scheme for Http",
detail: None
})
Err(io::Error::new(io::ErrorKind::InvalidInput,
"Invalid scheme for Http",
None))
}
}
}
}
fn lift_ssl_error(ssl: SslError) -> IoError {
fn lift_ssl_error(ssl: SslError) -> io::Error {
debug!("lift_ssl_error: {:?}", ssl);
match ssl {
StreamError(err) => err,
SslSessionClosed => IoError {
kind: ConnectionAborted,
desc: "SSL Connection Closed",
detail: None
},
SslSessionClosed => io::Error::new(io::ErrorKind::ConnectionAborted,
"SSL Connection Closed",
None),
// Unfortunately throw this away. No way to support this
// detail without a better Error abstraction.
OpenSslErrors(errs) => IoError {
kind: OtherIoError,
desc: "Error in OpenSSL",
detail: Some(format!("{:?}", errs))
}
OpenSslErrors(errs) => io::Error::new(io::ErrorKind::Other,
"Error in OpenSSL",
Some(format!("{:?}", errs)))
}
}

View File

@@ -1,16 +1,16 @@
use std::thread::{self, JoinGuard};
use std::sync::mpsc;
use std::collections::VecMap;
use net::NetworkAcceptor;
use net::NetworkListener;
pub struct AcceptorPool<A: NetworkAcceptor> {
pub struct ListenerPool<A: NetworkListener> {
acceptor: A
}
impl<'a, A: NetworkAcceptor + 'a> AcceptorPool<A> {
impl<'a, A: NetworkListener + Send + 'a> ListenerPool<A> {
/// Create a thread pool to manage the acceptor.
pub fn new(acceptor: A) -> AcceptorPool<A> {
AcceptorPool { acceptor: acceptor }
pub fn new(acceptor: A) -> ListenerPool<A> {
ListenerPool { acceptor: acceptor }
}
/// Runs the acceptor pool. Blocks until the acceptors are closed.
@@ -44,23 +44,16 @@ impl<'a, A: NetworkAcceptor + 'a> AcceptorPool<A> {
}
}
fn spawn_with<'a, A, F>(supervisor: mpsc::Sender<usize>, work: &'a F, mut acceptor: A, id: usize) -> JoinGuard<'a, ()>
where A: NetworkAcceptor + 'a,
F: Fn(<A as NetworkAcceptor>::Stream) + Send + Sync + 'a {
use std::old_io::EndOfFile;
fn spawn_with<'a, A, F>(supervisor: mpsc::Sender<usize>, work: &'a F, mut acceptor: A, id: usize) -> thread::JoinGuard<'a, ()>
where A: NetworkListener + Send + 'a,
F: Fn(<A as NetworkListener>::Stream) + Send + Sync + 'a {
thread::scoped(move || {
let sentinel = Sentinel::new(supervisor, id);
let _sentinel = Sentinel::new(supervisor, id);
loop {
match acceptor.accept() {
Ok(stream) => work(stream),
Err(ref e) if e.kind == EndOfFile => {
debug!("Server closed.");
sentinel.cancel();
return;
},
Err(e) => {
error!("Connection failed: {}", e);
}
@@ -72,7 +65,7 @@ where A: NetworkAcceptor + 'a,
struct Sentinel<T: Send> {
value: Option<T>,
supervisor: mpsc::Sender<T>,
active: bool
//active: bool
}
impl<T: Send> Sentinel<T> {
@@ -80,18 +73,18 @@ impl<T: Send> Sentinel<T> {
Sentinel {
value: Some(data),
supervisor: channel,
active: true
//active: true
}
}
fn cancel(mut self) { self.active = false; }
//fn cancel(mut self) { self.active = false; }
}
#[unsafe_destructor]
impl<T: Send + 'static> Drop for Sentinel<T> {
fn drop(&mut self) {
// If we were cancelled, get out of here.
if !self.active { return; }
//if !self.active { return; }
// Respawn ourselves
let _ = self.supervisor.send(self.value.take().unwrap());

View File

@@ -1,7 +1,9 @@
//! HTTP Server
use std::old_io::{Listener, BufferedReader, BufferedWriter};
use std::old_io::net::ip::{IpAddr, Port, SocketAddr};
use std::io::{BufReader, BufWriter};
use std::marker::PhantomData;
use std::net::{IpAddr, SocketAddr};
use std::os;
use std::path::Path;
use std::thread::{self, JoinGuard};
pub use self::request::Request;
@@ -13,25 +15,24 @@ use HttpError::HttpIoError;
use {HttpResult};
use header::Connection;
use header::ConnectionOption::{Close, KeepAlive};
use net::{NetworkListener, NetworkStream, NetworkAcceptor,
HttpAcceptor, HttpListener};
use net::{NetworkListener, NetworkStream, HttpListener};
use version::HttpVersion::{Http10, Http11};
use self::acceptor::AcceptorPool;
use self::listener::ListenerPool;
pub mod request;
pub mod response;
mod acceptor;
mod listener;
/// A server can listen on a TCP socket.
///
/// Once listening, it will create a `Request`/`Response` pair for each
/// incoming connection, and hand them to the provided handler.
pub struct Server<L = HttpListener> {
ip: IpAddr,
port: Port,
listener: L,
pub struct Server<'a, H: Handler, L = HttpListener> {
handler: H,
ssl: Option<(&'a Path, &'a Path)>,
_marker: PhantomData<L>
}
macro_rules! try_option(
@@ -43,38 +44,59 @@ macro_rules! try_option(
}}
);
impl Server<HttpListener> {
impl<'a, H: Handler, L: NetworkListener> Server<'a, H, L> {
pub fn new(handler: H) -> Server<'a, H, L> {
Server {
handler: handler,
ssl: None,
_marker: PhantomData
}
}
}
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
/// Creates a new server that will handle `HttpStream`s.
pub fn http(ip: IpAddr, port: Port) -> Server {
Server::with_listener(ip, port, HttpListener::Http)
pub fn http(handler: H) -> Server<'a, H, HttpListener> {
Server::new(handler)
}
/// Creates a new server that will handler `HttpStreams`s using a TLS connection.
pub fn https(ip: IpAddr, port: Port, cert: Path, key: Path) -> Server {
Server::with_listener(ip, port, HttpListener::Https(cert, key))
}
}
impl<
L: NetworkListener<Acceptor=A> + Send,
A: NetworkAcceptor<Stream=S> + Send + 'static,
S: NetworkStream + Clone + Send> Server<L> {
/// Creates a new server that will handle `HttpStream`s.
pub fn with_listener(ip: IpAddr, port: Port, listener: L) -> Server<L> {
pub fn https(handler: H, cert: &'a Path, key: &'a Path) -> Server<'a, H, HttpListener> {
Server {
ip: ip,
port: port,
listener: listener,
handler: handler,
ssl: Some((cert, key)),
_marker: PhantomData
}
}
}
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
/// Binds to a socket, and starts handling connections using a task pool.
pub fn listen_threads<H: Handler + 'static>(mut self, handler: H, threads: usize) -> HttpResult<Listening<L::Acceptor>> {
debug!("binding to {:?}:{:?}", self.ip, self.port);
let acceptor = try!(self.listener.listen((self.ip, self.port)));
let socket = try!(acceptor.socket_name());
pub fn listen_threads(self, ip: IpAddr, port: u16, threads: usize) -> HttpResult<Listening> {
let addr = &(ip, port);
let listener = try!(match self.ssl {
Some((cert, key)) => HttpListener::https(addr, cert, key),
None => HttpListener::http(addr)
});
self.with_listener(listener, threads)
}
/// Binds to a socket and starts handling connections.
pub fn listen(self, ip: IpAddr, port: u16) -> HttpResult<Listening> {
self.listen_threads(ip, port, os::num_cpus() * 5 / 4)
}
}
impl<
'a,
H: Handler + 'static,
L: NetworkListener<Stream=S> + Send + 'static,
S: NetworkStream + Clone + Send> Server<'a, H, L> {
/// Creates a new server that will handle `HttpStream`s.
pub fn with_listener(self, mut listener: L, threads: usize) -> HttpResult<Listening> {
let socket = try!(listener.socket_addr());
let handler = self.handler;
debug!("threads = {:?}", threads);
let pool = AcceptorPool::new(acceptor.clone());
let pool = ListenerPool::new(listener.clone());
let work = move |stream| handle_connection(stream, &handler);
let guard = thread::scoped(move || pool.accept(work, threads));
@@ -82,21 +104,15 @@ S: NetworkStream + Clone + Send> Server<L> {
Ok(Listening {
_guard: guard,
socket: socket,
acceptor: acceptor
})
}
/// Binds to a socket and starts handling connections.
pub fn listen<H: Handler + 'static>(self, handler: H) -> HttpResult<Listening<L::Acceptor>> {
self.listen_threads(handler, os::num_cpus() * 5 / 4)
}
}
fn handle_connection<S, H>(mut stream: S, handler: &H)
where S: NetworkStream + Clone, H: Handler {
debug!("Incoming stream");
let addr = match stream.peer_name() {
let addr = match stream.peer_addr() {
Ok(addr) => addr,
Err(e) => {
error!("Peer Name error: {:?}", e);
@@ -104,8 +120,8 @@ where S: NetworkStream + Clone, H: Handler {
}
};
let mut rdr = BufferedReader::new(stream.clone());
let mut wrt = BufferedWriter::new(stream);
let mut rdr = BufReader::new(stream.clone());
let mut wrt = BufWriter::new(stream);
let mut keep_alive = true;
while keep_alive {
@@ -135,18 +151,17 @@ where S: NetworkStream + Clone, H: Handler {
}
/// A listening server, which can later be closed.
pub struct Listening<A = HttpAcceptor> {
acceptor: A,
pub struct Listening {
_guard: JoinGuard<'static, ()>,
/// The socket addresses that the server is bound to.
pub socket: SocketAddr,
}
impl<A: NetworkAcceptor> Listening<A> {
impl Listening {
/// Stop the server from listening to its socket address.
pub fn close(&mut self) -> HttpResult<()> {
debug!("closing server");
try!(self.acceptor.close());
//try!(self.acceptor.close());
Ok(())
}
}

View File

@@ -2,8 +2,8 @@
//!
//! These are requests that a `hyper::Server` receives, and include its method,
//! target URI, headers, and message body.
use std::old_io::IoResult;
use std::old_io::net::ip::SocketAddr;
use std::io::{self, Read};
use std::net::SocketAddr;
use {HttpResult};
use version::{HttpVersion};
@@ -26,14 +26,14 @@ pub struct Request<'a> {
pub uri: RequestUri,
/// The version of HTTP for this request.
pub version: HttpVersion,
body: HttpReader<&'a mut (Reader + 'a)>
body: HttpReader<&'a mut (Read + 'a)>
}
impl<'a> Request<'a> {
/// Create a new Request, reading the StartLine and Headers so they are
/// immediately useful.
pub fn new(mut stream: &'a mut (Reader + 'a), addr: SocketAddr) -> HttpResult<Request<'a>> {
pub fn new(mut stream: &'a mut (Read + 'a), addr: SocketAddr) -> HttpResult<Request<'a>> {
let (method, uri, version) = try!(read_request_line(&mut stream));
debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
let headers = try!(Headers::from_raw(&mut stream));
@@ -66,14 +66,14 @@ impl<'a> Request<'a> {
/// Deconstruct a Request into its constituent parts.
pub fn deconstruct(self) -> (SocketAddr, Method, Headers,
RequestUri, HttpVersion,
HttpReader<&'a mut (Reader + 'a)>,) {
HttpReader<&'a mut (Read + 'a)>,) {
(self.remote_addr, self.method, self.headers,
self.uri, self.version, self.body)
}
}
impl<'a> Reader for Request<'a> {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
impl<'a> Read for Request<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.body.read(buf)
}
}
@@ -84,12 +84,19 @@ mod tests {
use mock::MockStream;
use super::Request;
use std::old_io::net::ip::SocketAddr;
use std::io::{self, Read};
use std::net::SocketAddr;
fn sock(s: &str) -> SocketAddr {
s.parse().unwrap()
}
fn read_to_string(mut req: Request) -> io::Result<String> {
let mut s = String::new();
try!(req.read_to_string(&mut s));
Ok(s)
}
#[test]
fn test_get_empty_body() {
let mut stream = MockStream::with_input(b"\
@@ -99,8 +106,8 @@ mod tests {
I'm a bad request.\r\n\
");
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(req.read_to_string(), Ok("".to_string()));
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req), Ok("".to_string()));
}
#[test]
@@ -112,8 +119,8 @@ mod tests {
I'm a bad request.\r\n\
");
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(req.read_to_string(), Ok("".to_string()));
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req), Ok("".to_string()));
}
#[test]
@@ -125,8 +132,8 @@ mod tests {
I'm a bad request.\r\n\
");
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(req.read_to_string(), Ok("".to_string()));
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req), Ok("".to_string()));
}
#[test]
@@ -146,7 +153,7 @@ mod tests {
\r\n"
);
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
// The headers are correct?
match req.headers.get::<Host>() {
@@ -163,8 +170,7 @@ mod tests {
None => panic!("Transfer-Encoding: chunked expected!"),
};
// The content is correctly read?
let body = req.read_to_string().unwrap();
assert_eq!("qwert", body);
assert_eq!(read_to_string(req), Ok("qwert".to_string()));
}
/// Tests that when a chunk size is not a valid radix-16 number, an error
@@ -182,9 +188,9 @@ mod tests {
\r\n"
);
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert!(req.read_to_string().is_err());
assert!(read_to_string(req).is_err());
}
/// Tests that when a chunk size contains an invalid extension, an error is
@@ -202,9 +208,9 @@ mod tests {
\r\n"
);
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert!(req.read_to_string().is_err());
assert!(read_to_string(req).is_err());
}
/// Tests that when a valid extension that contains a digit is appended to
@@ -222,9 +228,9 @@ mod tests {
\r\n"
);
let mut req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!("1", req.read_to_string().unwrap())
assert_eq!(read_to_string(req), Ok("1".to_string()));
}
}

View File

@@ -2,8 +2,8 @@
//!
//! These are responses sent by a `hyper::Server` to clients, after
//! receiving a request.
use std::old_io::IoResult;
use std::marker::PhantomData;
use std::io::{self, Write};
use time::now_utc;
@@ -19,7 +19,7 @@ pub struct Response<'a, W = Fresh> {
/// The HTTP version of this response.
pub version: version::HttpVersion,
// Stream the Response is writing to, not accessible through UnwrittenResponse
body: HttpWriter<&'a mut (Writer + 'a)>,
body: HttpWriter<&'a mut (Write + 'a)>,
// The status code for the request.
status: status::StatusCode,
// The outgoing headers on this response.
@@ -38,7 +38,7 @@ impl<'a, W> Response<'a, W> {
/// Construct a Response from its constituent parts.
pub fn construct(version: version::HttpVersion,
body: HttpWriter<&'a mut (Writer + 'a)>,
body: HttpWriter<&'a mut (Write + 'a)>,
status: status::StatusCode,
headers: header::Headers) -> Response<'a, Fresh> {
Response {
@@ -51,7 +51,7 @@ impl<'a, W> Response<'a, W> {
}
/// Deconstruct this Response into its constituent parts.
pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Writer + 'a)>,
pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Write + 'a)>,
status::StatusCode, header::Headers) {
(self.version, self.body, self.status, self.headers)
}
@@ -59,7 +59,7 @@ impl<'a, W> Response<'a, W> {
impl<'a> Response<'a, Fresh> {
/// Creates a new Response that can be used to write to a network stream.
pub fn new(stream: &'a mut (Writer + 'a)) -> Response<'a, Fresh> {
pub fn new(stream: &'a mut (Write + 'a)) -> Response<'a, Fresh> {
Response {
status: status::StatusCode::Ok,
version: version::HttpVersion::Http11,
@@ -70,7 +70,7 @@ impl<'a> Response<'a, Fresh> {
}
/// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming>
pub fn start(mut self) -> IoResult<Response<'a, Streaming>> {
pub fn start(mut self) -> io::Result<Response<'a, Streaming>> {
debug!("writing head: {:?} {:?}", self.version, self.status);
try!(write!(&mut self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char));
@@ -110,13 +110,12 @@ impl<'a> Response<'a, Fresh> {
debug!("headers [\n{:?}]", self.headers);
try!(write!(&mut self.body, "{}", self.headers));
try!(self.body.write_str(LINE_ENDING));
try!(write!(&mut self.body, "{}", LINE_ENDING));
let stream = if chunked {
ChunkedWriter(self.body.unwrap())
ChunkedWriter(self.body.into_inner())
} else {
SizedWriter(self.body.unwrap(), len)
SizedWriter(self.body.into_inner(), len)
};
// "copy" to change the phantom type
@@ -139,20 +138,20 @@ impl<'a> Response<'a, Fresh> {
impl<'a> Response<'a, Streaming> {
/// Flushes all writing of a response to the client.
pub fn end(self) -> IoResult<()> {
pub fn end(self) -> io::Result<()> {
debug!("ending");
try!(self.body.end());
Ok(())
}
}
impl<'a> Writer for Response<'a, Streaming> {
fn write_all(&mut self, msg: &[u8]) -> IoResult<()> {
impl<'a> Write for Response<'a, Streaming> {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
debug!("write {:?} bytes", msg.len());
self.body.write_all(msg)
self.body.write(msg)
}
fn flush(&mut self) -> IoResult<()> {
fn flush(&mut self) -> io::Result<()> {
self.body.flush()
}
}