feat(lib): switch to non-blocking (asynchronous) IO

BREAKING CHANGE: This breaks a lot of the Client and Server APIs.
  Check the documentation for how Handlers can be used for asynchronous
  events.
This commit is contained in:
Sean McArthur
2016-05-03 20:45:43 -07:00
parent 1ec56fe6b6
commit d35992d019
65 changed files with 5599 additions and 5023 deletions

View File

@@ -3,6 +3,8 @@ matrix:
fast_finish: true
include:
- os: osx
rust: stable
env: FEATURES="--no-default-features --features security-framework"
- rust: nightly
env: FEATURES="--features nightly"
- rust: beta

View File

@@ -16,13 +16,15 @@ httparse = "1.0"
language-tags = "0.2"
log = "0.3"
mime = "0.2"
num_cpus = "0.2"
rotor = "0.6"
rustc-serialize = "0.3"
spmc = "0.2"
time = "0.1"
traitobject = "0.0.1"
typeable = "0.1"
unicase = "1.0"
url = "1.0"
vecio = "0.1"
[dependencies.cookie]
version = "0.2"
@@ -40,16 +42,13 @@ optional = true
version = "0.1.4"
optional = true
[dependencies.solicit]
version = "0.4"
default-features = false
[dependencies.serde]
version = "0.7"
optional = true
[dev-dependencies]
env_logger = "0.3"
num_cpus = "0.2"
[features]
default = ["ssl"]

View File

@@ -10,12 +10,12 @@ A Modern HTTP library for Rust.
### Documentation
- [Stable](http://hyperium.github.io/hyper)
- [Released](http://hyperium.github.io/hyper)
- [Master](http://hyperium.github.io/hyper/master)
## Overview
Hyper is a fast, modern HTTP implementation written in and for Rust. It
hyper is a fast, modern HTTP implementation written in and for Rust. It
is a low-level typesafe abstraction over raw HTTP, providing an elegant
layer over "stringly-typed" HTTP.
@@ -23,53 +23,3 @@ Hyper offers both an HTTP client and server which can be used to drive
complex web applications written entirely in Rust.
The documentation is located at [http://hyperium.github.io/hyper](http://hyperium.github.io/hyper).
## Example
### Hello World Server:
```rust
extern crate hyper;
use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
fn hello(_: Request, res: Response) {
res.send(b"Hello World!").unwrap();
}
fn main() {
Server::http("127.0.0.1:3000").unwrap()
.handle(hello).unwrap();
}
```
### Client:
```rust
extern crate hyper;
use std::io::Read;
use hyper::Client;
use hyper::header::Connection;
fn main() {
// Create a client.
let client = Client::new();
// Creating an outgoing request.
let mut res = client.get("http://rust-lang.org/")
// set a header
.header(Connection::close())
// let 'er go!
.send().unwrap();
// Read the Response.
let mut body = String::new();
res.read_to_string(&mut body).unwrap();
println!("Response: {}", body);
}
```

View File

@@ -1,111 +0,0 @@
#![deny(warnings)]
#![feature(test)]
extern crate hyper;
extern crate test;
use std::fmt;
use std::io::{self, Read, Write, Cursor};
use std::net::SocketAddr;
use std::time::Duration;
use hyper::net;
static README: &'static [u8] = include_bytes!("../README.md");
struct MockStream {
read: Cursor<Vec<u8>>
}
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.extend_from_slice(README);
MockStream {
read: Cursor::new(res)
}
}
}
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, Debug)]
struct Foo;
impl hyper::header::Header for Foo {
fn header_name() -> &'static str {
"x-foo"
}
fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> {
Err(hyper::Error::Header)
}
}
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_addr(&mut self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:1337".parse().unwrap())
}
fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
// can't time out
Ok(())
}
fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
// can't time out
Ok(())
}
}
struct MockConnector;
impl net::NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(&self, _: &str, _: u16, _: &str) -> hyper::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(), &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

@@ -5,9 +5,61 @@ extern crate env_logger;
use std::env;
use std::io;
use std::sync::mpsc;
use std::time::Duration;
use hyper::Client;
use hyper::client::{Client, Request, Response, DefaultTransport as HttpStream};
use hyper::header::Connection;
use hyper::{Decoder, Encoder, Next};
#[derive(Debug)]
struct Dump(mpsc::Sender<()>);
impl Drop for Dump {
fn drop(&mut self) {
let _ = self.0.send(());
}
}
fn read() -> Next {
Next::read().timeout(Duration::from_secs(10))
}
impl hyper::client::Handler<HttpStream> for Dump {
fn on_request(&mut self, req: &mut Request) -> Next {
req.headers_mut().set(Connection::close());
read()
}
fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
read()
}
fn on_response(&mut self, res: Response) -> Next {
println!("Response: {}", res.status());
println!("Headers:\n{}", res.headers());
read()
}
fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
match io::copy(decoder, &mut io::stdout()) {
Ok(0) => Next::end(),
Ok(_) => read(),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Next::read(),
_ => {
println!("ERROR: {}", e);
Next::end()
}
}
}
}
fn on_error(&mut self, err: hyper::Error) -> Next {
println!("ERROR: {}", err);
Next::remove()
}
}
fn main() {
env_logger::init().unwrap();
@@ -20,26 +72,11 @@ fn main() {
}
};
let client = match env::var("HTTP_PROXY") {
Ok(mut proxy) => {
// parse the proxy, message if it doesn't make sense
let mut port = 80;
if let Some(colon) = proxy.rfind(':') {
port = proxy[colon + 1..].parse().unwrap_or_else(|e| {
panic!("HTTP_PROXY is malformed: {:?}, port parse error: {}", proxy, e);
});
proxy.truncate(colon);
}
Client::with_http_proxy(proxy, port)
},
_ => Client::new()
};
let (tx, rx) = mpsc::channel();
let client = Client::new().expect("Failed to create a Client");
client.request(url.parse().unwrap(), Dump(tx)).unwrap();
let mut res = client.get(&*url)
.header(Connection::close())
.send().unwrap();
println!("Response: {}", res.status);
println!("Headers:\n{}", res.headers);
io::copy(&mut res, &mut io::stdout()).unwrap();
// wait till done
let _ = rx.recv();
client.close();
}

View File

@@ -1,34 +0,0 @@
#![deny(warnings)]
extern crate hyper;
extern crate env_logger;
use std::env;
use std::io;
use hyper::Client;
use hyper::header::Connection;
use hyper::http::h2;
fn main() {
env_logger::init().unwrap();
let url = match env::args().nth(1) {
Some(url) => url,
None => {
println!("Usage: client <url>");
return;
}
};
let client = Client::with_protocol(h2::new_protocol());
// `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully.
let mut res = client.get(&*url)
.header(Connection::close())
.send().unwrap();
println!("Response: {}", res.status);
println!("Headers:\n{}", res.headers);
io::copy(&mut res, &mut io::stdout()).unwrap();
}

View File

@@ -1,16 +0,0 @@
#![deny(warnings)]
#[macro_use]
// TODO: only import header!, blocked by https://github.com/rust-lang/rust/issues/25003
extern crate hyper;
#[cfg(feature = "serde-serialization")]
extern crate serde;
// A header in the form of `X-Foo: some random string`
header! {
(Foo, "X-Foo") => [String]
}
fn main() {
}

View File

@@ -1,18 +1,53 @@
#![deny(warnings)]
extern crate hyper;
extern crate env_logger;
extern crate num_cpus;
use hyper::server::{Request, Response};
use std::io::Write;
use hyper::{Decoder, Encoder, Next};
use hyper::net::{HttpStream, HttpListener};
use hyper::server::{Server, Handler, Request, Response};
static PHRASE: &'static [u8] = b"Hello World!";
fn hello(_: Request, res: Response) {
res.send(PHRASE).unwrap();
struct Hello;
impl Handler<HttpStream> for Hello {
fn on_request(&mut self, _: Request) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, response: &mut Response) -> Next {
use hyper::header::ContentLength;
response.headers_mut().set(ContentLength(PHRASE.len() as u64));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let n = encoder.write(PHRASE).unwrap();
debug_assert_eq!(n, PHRASE.len());
Next::end()
}
}
fn main() {
env_logger::init().unwrap();
let _listening = hyper::Server::http("127.0.0.1:3000").unwrap()
.handle(hello);
let listener = HttpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
let mut handles = Vec::new();
for _ in 0..num_cpus::get() {
let listener = listener.try_clone().unwrap();
handles.push(::std::thread::spawn(move || {
Server::new(listener)
.handle(|_| Hello).unwrap()
}));
}
println!("Listening on http://127.0.0.1:3000");
for handle in handles {
handle.join().unwrap();
}
}

View File

@@ -1,47 +1,171 @@
#![deny(warnings)]
extern crate hyper;
extern crate env_logger;
#[macro_use]
extern crate log;
use std::io::copy;
use std::io::{self, Read, Write};
use hyper::{Get, Post};
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, Next};
use hyper::header::ContentLength;
use hyper::net::HttpStream;
use hyper::server::{Server, Handler, Request, Response};
macro_rules! try_return(
($e:expr) => {{
match $e {
Ok(v) => v,
Err(e) => { println!("Error: {}", e); return; }
struct Echo {
buf: Vec<u8>,
read_pos: usize,
write_pos: usize,
eof: bool,
route: Route,
}
enum Route {
NotFound,
Index,
Echo(Body),
}
#[derive(Clone, Copy)]
enum Body {
Len(u64),
Chunked
}
static INDEX: &'static [u8] = b"Try POST /echo";
impl Echo {
fn new() -> Echo {
Echo {
buf: vec![0; 4096],
read_pos: 0,
write_pos: 0,
eof: false,
route: Route::NotFound,
}
}}
);
}
}
fn echo(mut req: Request, mut res: Response) {
match req.uri {
AbsolutePath(ref path) => match (&req.method, &path[..]) {
(&Get, "/") | (&Get, "/echo") => {
try_return!(res.send(b"Try POST /echo"));
return;
impl Handler<HttpStream> for Echo {
fn on_request(&mut self, req: Request) -> Next {
match *req.uri() {
RequestUri::AbsolutePath(ref path) => match (req.method(), &path[..]) {
(&Get, "/") | (&Get, "/echo") => {
info!("GET Index");
self.route = Route::Index;
Next::write()
}
(&Post, "/echo") => {
info!("POST Echo");
let mut is_more = true;
self.route = if let Some(len) = req.headers().get::<ContentLength>() {
is_more = **len > 0;
Route::Echo(Body::Len(**len))
} else {
Route::Echo(Body::Chunked)
};
if is_more {
Next::read_and_write()
} else {
Next::write()
}
}
_ => Next::write(),
},
(&Post, "/echo") => (), // fall through, fighting mutable borrows
_ => {
*res.status_mut() = hyper::NotFound;
return;
}
},
_ => {
return;
_ => Next::write()
}
};
}
fn on_request_readable(&mut self, transport: &mut Decoder<HttpStream>) -> Next {
match self.route {
Route::Echo(ref body) => {
if self.read_pos < self.buf.len() {
match transport.read(&mut self.buf[self.read_pos..]) {
Ok(0) => {
debug!("Read 0, eof");
self.eof = true;
Next::write()
},
Ok(n) => {
self.read_pos += n;
match *body {
Body::Len(max) if max <= self.read_pos as u64 => {
self.eof = true;
Next::write()
},
_ => Next::read_and_write()
}
}
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Next::read_and_write(),
_ => {
println!("read error {:?}", e);
Next::end()
}
}
}
} else {
Next::write()
}
}
_ => unreachable!()
}
}
let mut res = try_return!(res.start());
try_return!(copy(&mut req, &mut res));
fn on_response(&mut self, res: &mut Response) -> Next {
match self.route {
Route::NotFound => {
res.set_status(StatusCode::NotFound);
Next::end()
}
Route::Index => {
res.headers_mut().set(ContentLength(INDEX.len() as u64));
Next::write()
}
Route::Echo(body) => {
if let Body::Len(len) = body {
res.headers_mut().set(ContentLength(len));
}
Next::read_and_write()
}
}
}
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
match self.route {
Route::Index => {
transport.write(INDEX).unwrap();
Next::end()
}
Route::Echo(..) => {
if self.write_pos < self.read_pos {
match transport.write(&self.buf[self.write_pos..self.read_pos]) {
Ok(0) => panic!("write ZERO"),
Ok(n) => {
self.write_pos += n;
Next::write()
}
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Next::write(),
_ => {
println!("write error {:?}", e);
Next::end()
}
}
}
} else if !self.eof {
Next::read()
} else {
Next::end()
}
}
_ => unreachable!()
}
}
}
fn main() {
env_logger::init().unwrap();
let server = Server::http("127.0.0.1:1337").unwrap();
let _guard = server.handle(echo);
println!("Listening on http://127.0.0.1:1337");
let server = Server::http(&"127.0.0.1:1337".parse().unwrap()).unwrap();
let (listening, server) = server.handle(|_| Echo::new()).unwrap();
println!("Listening on http://{}", listening);
server.run();
}

278
examples/sync.rs Normal file
View File

@@ -0,0 +1,278 @@
extern crate hyper;
extern crate env_logger;
extern crate time;
use std::io::{self, Read, Write};
use std::marker::PhantomData;
use std::thread;
use std::sync::{Arc, mpsc};
pub struct Server {
listening: hyper::server::Listening,
}
pub struct Request<'a> {
#[allow(dead_code)]
inner: hyper::server::Request,
tx: &'a mpsc::Sender<Action>,
rx: &'a mpsc::Receiver<io::Result<usize>>,
ctrl: &'a hyper::Control,
}
impl<'a> Request<'a> {
fn new(inner: hyper::server::Request, tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Request<'a> {
Request {
inner: inner,
tx: tx,
rx: rx,
ctrl: ctrl,
}
}
}
impl<'a> io::Read for Request<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.tx.send(Action::Read(buf.as_mut_ptr(), buf.len())).unwrap();
self.ctrl.ready(hyper::Next::read()).unwrap();
self.rx.recv().unwrap()
}
}
pub enum Fresh {}
pub enum Streaming {}
pub struct Response<'a, W = Fresh> {
status: hyper::StatusCode,
headers: hyper::Headers,
version: hyper::HttpVersion,
tx: &'a mpsc::Sender<Action>,
rx: &'a mpsc::Receiver<io::Result<usize>>,
ctrl: &'a hyper::Control,
_marker: PhantomData<W>,
}
impl<'a> Response<'a, Fresh> {
fn new(tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Response<'a, Fresh> {
Response {
status: hyper::Ok,
headers: hyper::Headers::new(),
version: hyper::HttpVersion::Http11,
tx: tx,
rx: rx,
ctrl: ctrl,
_marker: PhantomData,
}
}
pub fn start(self) -> io::Result<Response<'a, Streaming>> {
self.tx.send(Action::Respond(self.version.clone(), self.status.clone(), self.headers.clone())).unwrap();
self.ctrl.ready(hyper::Next::write()).unwrap();
let res = self.rx.recv().unwrap();
res.map(move |_| Response {
status: self.status,
headers: self.headers,
version: self.version,
tx: self.tx,
rx: self.rx,
ctrl: self.ctrl,
_marker: PhantomData,
})
}
pub fn send(mut self, msg: &[u8]) -> io::Result<()> {
self.headers.set(hyper::header::ContentLength(msg.len() as u64));
self.start().and_then(|mut res| res.write_all(msg)).map(|_| ())
}
}
impl<'a> Write for Response<'a, Streaming> {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
self.tx.send(Action::Write(msg.as_ptr(), msg.len())).unwrap();
self.ctrl.ready(hyper::Next::write()).unwrap();
let res = self.rx.recv().unwrap();
res
}
fn flush(&mut self) -> io::Result<()> {
panic!("Response.flush() not impemented")
}
}
struct SynchronousHandler {
req_tx: mpsc::Sender<hyper::server::Request>,
tx: mpsc::Sender<io::Result<usize>>,
rx: mpsc::Receiver<Action>,
reading: Option<(*mut u8, usize)>,
writing: Option<(*const u8, usize)>,
respond: Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)>
}
unsafe impl Send for SynchronousHandler {}
impl SynchronousHandler {
fn next(&mut self) -> hyper::Next {
match self.rx.try_recv() {
Ok(Action::Read(ptr, len)) => {
self.reading = Some((ptr, len));
hyper::Next::read()
},
Ok(Action::Respond(ver, status, headers)) => {
self.respond = Some((ver, status, headers));
hyper::Next::write()
},
Ok(Action::Write(ptr, len)) => {
self.writing = Some((ptr, len));
hyper::Next::write()
}
Err(mpsc::TryRecvError::Empty) => {
// we're too fast, the other thread hasn't had a chance to respond
hyper::Next::wait()
}
Err(mpsc::TryRecvError::Disconnected) => {
// they dropped it
// TODO: should finish up sending response, whatever it was
hyper::Next::end()
}
}
}
fn reading(&mut self) -> Option<(*mut u8, usize)> {
self.reading.take().or_else(|| {
match self.rx.try_recv() {
Ok(Action::Read(ptr, len)) => {
Some((ptr, len))
},
_ => None
}
})
}
fn writing(&mut self) -> Option<(*const u8, usize)> {
self.writing.take().or_else(|| {
match self.rx.try_recv() {
Ok(Action::Write(ptr, len)) => {
Some((ptr, len))
},
_ => None
}
})
}
fn respond(&mut self) -> Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)> {
self.respond.take().or_else(|| {
match self.rx.try_recv() {
Ok(Action::Respond(ver, status, headers)) => {
Some((ver, status, headers))
},
_ => None
}
})
}
}
impl hyper::server::Handler<hyper::net::HttpStream> for SynchronousHandler {
fn on_request(&mut self, req: hyper::server::Request) -> hyper::Next {
if let Err(_) = self.req_tx.send(req) {
return hyper::Next::end();
}
self.next()
}
fn on_request_readable(&mut self, decoder: &mut hyper::Decoder<hyper::net::HttpStream>) -> hyper::Next {
if let Some(raw) = self.reading() {
let slice = unsafe { ::std::slice::from_raw_parts_mut(raw.0, raw.1) };
if self.tx.send(decoder.read(slice)).is_err() {
return hyper::Next::end();
}
}
self.next()
}
fn on_response(&mut self, req: &mut hyper::server::Response) -> hyper::Next {
use std::iter::Extend;
if let Some(head) = self.respond() {
req.set_status(head.1);
req.headers_mut().extend(head.2.iter());
if self.tx.send(Ok(0)).is_err() {
return hyper::Next::end();
}
} else {
// wtf happened?
panic!("no head to respond with");
}
self.next()
}
fn on_response_writable(&mut self, encoder: &mut hyper::Encoder<hyper::net::HttpStream>) -> hyper::Next {
if let Some(raw) = self.writing() {
let slice = unsafe { ::std::slice::from_raw_parts(raw.0, raw.1) };
if self.tx.send(encoder.write(slice)).is_err() {
return hyper::Next::end();
}
}
self.next()
}
}
enum Action {
Read(*mut u8, usize),
Write(*const u8, usize),
Respond(hyper::HttpVersion, hyper::StatusCode, hyper::Headers),
}
unsafe impl Send for Action {}
trait Handler: Send + Sync + 'static {
fn handle(&self, req: Request, res: Response);
}
impl<F> Handler for F where F: Fn(Request, Response) + Send + Sync + 'static {
fn handle(&self, req: Request, res: Response) {
(self)(req, res)
}
}
impl Server {
fn handle<H: Handler>(addr: &str, handler: H) -> Server {
let handler = Arc::new(handler);
let (listening, server) = hyper::Server::http(&addr.parse().unwrap()).unwrap()
.handle(move |ctrl| {
let (req_tx, req_rx) = mpsc::channel();
let (blocking_tx, blocking_rx) = mpsc::channel();
let (async_tx, async_rx) = mpsc::channel();
let handler = handler.clone();
thread::Builder::new().name("handler-thread".into()).spawn(move || {
let req = Request::new(req_rx.recv().unwrap(), &blocking_tx, &async_rx, &ctrl);
let res = Response::new(&blocking_tx, &async_rx, &ctrl);
handler.handle(req, res);
}).unwrap();
SynchronousHandler {
req_tx: req_tx,
tx: async_tx,
rx: blocking_rx,
reading: None,
writing: None,
respond: None,
}
}).unwrap();
thread::spawn(move || {
server.run();
});
Server {
listening: listening
}
}
}
fn main() {
env_logger::init().unwrap();
let s = Server::handle("127.0.0.1:0", |mut req: Request, res: Response| {
let mut body = [0; 256];
let n = req.read(&mut body).unwrap();
println!("!!!: received: {:?}", ::std::str::from_utf8(&body[..n]).unwrap());
res.send(b"Hello World!").unwrap();
});
println!("listening on {}", s.listening.addr());
}

View File

@@ -1,164 +0,0 @@
use std::cmp;
use std::io::{self, Read, BufRead};
pub struct BufReader<R> {
inner: R,
buf: Vec<u8>,
pos: usize,
cap: usize,
}
const INIT_BUFFER_SIZE: usize = 4096;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
impl<R: Read> BufReader<R> {
#[inline]
pub fn new(rdr: R) -> BufReader<R> {
BufReader::with_capacity(rdr, INIT_BUFFER_SIZE)
}
#[inline]
pub fn with_capacity(rdr: R, cap: usize) -> BufReader<R> {
BufReader {
inner: rdr,
buf: vec![0; cap],
pos: 0,
cap: 0,
}
}
#[inline]
pub fn get_ref(&self) -> &R { &self.inner }
#[inline]
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
#[inline]
pub fn get_buf(&self) -> &[u8] {
if self.pos < self.cap {
trace!("get_buf [u8; {}][{}..{}]", self.buf.len(), self.pos, self.cap);
&self.buf[self.pos..self.cap]
} else {
trace!("get_buf []");
&[]
}
}
#[inline]
pub fn into_inner(self) -> R { self.inner }
#[inline]
pub fn read_into_buf(&mut self) -> io::Result<usize> {
self.maybe_reserve();
let v = &mut self.buf;
trace!("read_into_buf buf[{}..{}]", self.cap, v.len());
if self.cap < v.capacity() {
let nread = try!(self.inner.read(&mut v[self.cap..]));
self.cap += nread;
Ok(nread)
} else {
trace!("read_into_buf at full capacity");
Ok(0)
}
}
#[inline]
fn maybe_reserve(&mut self) {
let cap = self.buf.capacity();
if self.cap == cap && cap < MAX_BUFFER_SIZE {
self.buf.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
let new = self.buf.capacity() - self.buf.len();
trace!("reserved {}", new);
unsafe { grow_zerofill(&mut self.buf, new) }
}
}
}
#[inline]
unsafe fn grow_zerofill(buf: &mut Vec<u8>, additional: usize) {
use std::ptr;
let len = buf.len();
buf.set_len(len + additional);
ptr::write_bytes(buf.as_mut_ptr().offset(len as isize), 0, additional);
}
impl<R: Read> Read for BufReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.cap == self.pos && buf.len() >= self.buf.len() {
return self.inner.read(buf);
}
let nread = {
let mut rem = try!(self.fill_buf());
try!(rem.read(buf))
};
self.consume(nread);
Ok(nread)
}
}
impl<R: Read> BufRead for BufReader<R> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.pos == self.cap {
self.cap = try!(self.inner.read(&mut self.buf));
self.pos = 0;
}
Ok(&self.buf[self.pos..self.cap])
}
#[inline]
fn consume(&mut self, amt: usize) {
self.pos = cmp::min(self.pos + amt, self.cap);
if self.pos == self.cap {
self.pos = 0;
self.cap = 0;
}
}
}
#[cfg(test)]
mod tests {
use std::io::{self, Read, BufRead};
use super::BufReader;
struct SlowRead(u8);
impl Read for SlowRead {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let state = self.0;
self.0 += 1;
(&match state % 3 {
0 => b"foo",
1 => b"bar",
_ => b"baz",
}[..]).read(buf)
}
}
#[test]
fn test_consume_and_get_buf() {
let mut rdr = BufReader::new(SlowRead(0));
rdr.read_into_buf().unwrap();
rdr.consume(1);
assert_eq!(rdr.get_buf(), b"oo");
rdr.read_into_buf().unwrap();
rdr.read_into_buf().unwrap();
assert_eq!(rdr.get_buf(), b"oobarbaz");
rdr.consume(5);
assert_eq!(rdr.get_buf(), b"baz");
rdr.consume(3);
assert_eq!(rdr.get_buf(), b"");
assert_eq!(rdr.pos, 0);
assert_eq!(rdr.cap, 0);
}
#[test]
fn test_resize() {
let raw = b"hello world";
let mut rdr = BufReader::with_capacity(&raw[..], 5);
rdr.read_into_buf().unwrap();
assert_eq!(rdr.get_buf(), b"hello");
rdr.read_into_buf().unwrap();
assert_eq!(rdr.get_buf(), b"hello world");
}
}

219
src/client/connect.rs Normal file
View File

@@ -0,0 +1,219 @@
use std::collections::hash_map::{HashMap, Entry};
use std::hash::Hash;
use std::fmt;
use std::io;
use std::net::SocketAddr;
use rotor::mio::tcp::TcpStream;
use url::Url;
use net::{HttpStream, HttpsStream, Transport, SslClient};
use super::dns::Dns;
use super::Registration;
/// A connector creates a Transport to a remote address..
pub trait Connect {
/// Type of Transport to create
type Output: Transport;
/// The key used to determine if an existing socket can be used.
type Key: Eq + Hash + Clone;
/// Returns the key based off the Url.
fn key(&self, &Url) -> Option<Self::Key>;
/// Connect to a remote address.
fn connect(&mut self, &Url) -> io::Result<Self::Key>;
/// Returns a connected socket and associated host.
fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)>;
#[doc(hidden)]
fn register(&mut self, Registration);
}
type Scheme = String;
type Port = u16;
/// A connector for the `http` scheme.
pub struct HttpConnector {
dns: Option<Dns>,
threads: usize,
resolving: HashMap<String, Vec<(&'static str, String, u16)>>,
}
impl HttpConnector {
/// Set the number of resolver threads.
///
/// Default is 4.
pub fn threads(mut self, threads: usize) -> HttpConnector {
debug_assert!(self.dns.is_none(), "setting threads after Dns is created does nothing");
self.threads = threads;
self
}
}
impl Default for HttpConnector {
fn default() -> HttpConnector {
HttpConnector {
dns: None,
threads: 4,
resolving: HashMap::new(),
}
}
}
impl fmt::Debug for HttpConnector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("HttpConnector")
.field("threads", &self.threads)
.field("resolving", &self.resolving)
.finish()
}
}
impl Connect for HttpConnector {
type Output = HttpStream;
type Key = (&'static str, String, u16);
fn key(&self, url: &Url) -> Option<Self::Key> {
if url.scheme() == "http" {
Some((
"http",
url.host_str().expect("http scheme must have host").to_owned(),
url.port().unwrap_or(80),
))
} else {
None
}
}
fn connect(&mut self, url: &Url) -> io::Result<Self::Key> {
debug!("Http::connect({:?})", url);
if let Some(key) = self.key(url) {
let host = url.host_str().expect("http scheme must have a host");
self.dns.as_ref().expect("dns workers lost").resolve(host);
self.resolving.entry(host.to_owned()).or_insert(Vec::new()).push(key.clone());
Ok(key)
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http"))
}
}
fn connected(&mut self) -> Option<(Self::Key, io::Result<HttpStream>)> {
let (host, addr) = match self.dns.as_ref().expect("dns workers lost").resolved() {
Ok(res) => res,
Err(_) => return None
};
debug!("Http::resolved <- ({:?}, {:?})", host, addr);
match self.resolving.entry(host) {
Entry::Occupied(mut entry) => {
let resolved = entry.get_mut().remove(0);
if entry.get().is_empty() {
entry.remove();
}
let port = resolved.2;
match addr {
Ok(addr) => {
Some((resolved, TcpStream::connect(&SocketAddr::new(addr, port))
.map(HttpStream)))
},
Err(e) => Some((resolved, Err(e)))
}
}
_ => {
trace!("^-- resolved but not in hashmap?");
return None
}
}
}
fn register(&mut self, reg: Registration) {
self.dns = Some(Dns::new(reg.notify, 4));
}
}
/// A connector that can protect HTTP streams using SSL.
#[derive(Debug, Default)]
pub struct HttpsConnector<S: SslClient> {
http: HttpConnector,
ssl: S
}
impl<S: SslClient> HttpsConnector<S> {
/// Create a new connector using the provided SSL implementation.
pub fn new(s: S) -> HttpsConnector<S> {
HttpsConnector {
http: HttpConnector::default(),
ssl: s,
}
}
}
impl<S: SslClient> Connect for HttpsConnector<S> {
type Output = HttpsStream<S::Stream>;
type Key = (&'static str, String, u16);
fn key(&self, url: &Url) -> Option<Self::Key> {
let scheme = match url.scheme() {
"http" => "http",
"https" => "https",
_ => return None
};
Some((
scheme,
url.host_str().expect("http scheme must have host").to_owned(),
url.port_or_known_default().expect("http scheme must have a port"),
))
}
fn connect(&mut self, url: &Url) -> io::Result<Self::Key> {
debug!("Https::connect({:?})", url);
if let Some(key) = self.key(url) {
let host = url.host_str().expect("http scheme must have a host");
self.http.dns.as_ref().expect("dns workers lost").resolve(host);
self.http.resolving.entry(host.to_owned()).or_insert(Vec::new()).push(key.clone());
Ok(key)
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "scheme must be http or https"))
}
}
fn connected(&mut self) -> Option<(Self::Key, io::Result<Self::Output>)> {
self.http.connected().map(|(key, res)| {
let res = res.and_then(|http| {
if key.0 == "https" {
self.ssl.wrap_client(http, &key.1)
.map(HttpsStream::Https)
.map_err(|e| match e {
::Error::Io(e) => e,
e => io::Error::new(io::ErrorKind::Other, e)
})
} else {
Ok(HttpsStream::Http(http))
}
});
(key, res)
})
}
fn register(&mut self, reg: Registration) {
self.http.register(reg);
}
}
#[cfg(not(any(feature = "openssl", feature = "security-framework")))]
#[doc(hidden)]
pub type DefaultConnector = HttpConnector;
#[cfg(all(feature = "openssl", not(feature = "security-framework")))]
#[doc(hidden)]
pub type DefaultConnector = HttpsConnector<::net::Openssl>;
#[cfg(feature = "security-framework")]
#[doc(hidden)]
pub type DefaultConnector = HttpsConnector<::net::SecureTransportClient>;
#[doc(hidden)]
pub type DefaultTransport = <DefaultConnector as Connect>::Output;
fn _assert_defaults() {
fn _assert<T, U>() where T: Connect<Output=U>, U: Transport {}
_assert::<DefaultConnector, DefaultTransport>();
}

84
src/client/dns.rs Normal file
View File

@@ -0,0 +1,84 @@
use std::io;
use std::net::{IpAddr, ToSocketAddrs};
use std::thread;
use ::spmc;
use http::channel;
pub struct Dns {
tx: spmc::Sender<String>,
rx: channel::Receiver<Answer>,
}
pub type Answer = (String, io::Result<IpAddr>);
impl Dns {
pub fn new(notify: (channel::Sender<Answer>, channel::Receiver<Answer>), threads: usize) -> Dns {
let (tx, rx) = spmc::channel();
for _ in 0..threads {
work(rx.clone(), notify.0.clone());
}
Dns {
tx: tx,
rx: notify.1,
}
}
pub fn resolve<T: Into<String>>(&self, hostname: T) {
self.tx.send(hostname.into()).expect("Workers all died horribly");
}
pub fn resolved(&self) -> Result<Answer, channel::TryRecvError> {
self.rx.try_recv()
}
}
fn work(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) {
thread::spawn(move || {
let mut worker = Worker::new(rx, notify);
let rx = worker.rx.as_ref().expect("Worker lost rx");
let notify = worker.notify.as_ref().expect("Worker lost notify");
while let Ok(host) = rx.recv() {
debug!("resolve {:?}", host);
let res = match (&*host, 80).to_socket_addrs().map(|mut i| i.next()) {
Ok(Some(addr)) => (host, Ok(addr.ip())),
Ok(None) => (host, Err(io::Error::new(io::ErrorKind::Other, "no addresses found"))),
Err(e) => (host, Err(e))
};
if let Err(_) = notify.send(res) {
break;
}
}
worker.shutdown = true;
});
}
struct Worker {
rx: Option<spmc::Receiver<String>>,
notify: Option<channel::Sender<Answer>>,
shutdown: bool,
}
impl Worker {
fn new(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) -> Worker {
Worker {
rx: Some(rx),
notify: Some(notify),
shutdown: false,
}
}
}
impl Drop for Worker {
fn drop(&mut self) {
if !self.shutdown {
trace!("Worker.drop panicked, restarting");
work(self.rx.take().expect("Worker lost rx"),
self.notify.take().expect("Worker lost notify"));
} else {
trace!("Worker.drop shutdown, closing");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,240 +0,0 @@
use std::borrow::Cow;
use std::io;
use std::net::{SocketAddr, Shutdown};
use std::time::Duration;
use method::Method;
use net::{NetworkConnector, HttpConnector, NetworkStream, SslClient};
#[cfg(all(feature = "openssl", not(feature = "security-framework")))]
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> {
Proxy {
connector: HttpConnector,
proxy: proxy,
ssl: Default::default()
}
}
#[cfg(feature = "security-framework")]
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> {
Proxy {
connector: HttpConnector,
proxy: proxy,
ssl: Default::default()
}
}
#[cfg(not(any(feature = "openssl", feature = "security-framework")))]
pub fn tunnel(proxy: (Cow<'static, str>, u16)) -> Proxy<HttpConnector, self::no_ssl::Plaintext> {
Proxy {
connector: HttpConnector,
proxy: proxy,
ssl: self::no_ssl::Plaintext,
}
}
pub struct Proxy<C, S>
where C: NetworkConnector + Send + Sync + 'static,
C::Stream: NetworkStream + Send + Clone,
S: SslClient<C::Stream> {
pub connector: C,
pub proxy: (Cow<'static, str>, u16),
pub ssl: S,
}
impl<C, S> NetworkConnector for Proxy<C, S>
where C: NetworkConnector + Send + Sync + 'static,
C::Stream: NetworkStream + Send + Clone,
S: SslClient<C::Stream> {
type Stream = Proxied<C::Stream, S::Stream>;
fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream> {
use httparse;
use std::io::{Read, Write};
use ::version::HttpVersion::Http11;
trace!("{:?} proxy for '{}://{}:{}'", self.proxy, scheme, host, port);
match scheme {
"http" => {
self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http")
.map(Proxied::Normal)
},
"https" => {
let mut stream = try!(self.connector.connect(self.proxy.0.as_ref(), self.proxy.1, "http"));
trace!("{:?} CONNECT {}:{}", self.proxy, host, port);
try!(write!(&mut stream, "{method} {host}:{port} {version}\r\nHost: {host}:{port}\r\n\r\n",
method=Method::Connect, host=host, port=port, version=Http11));
try!(stream.flush());
let mut buf = [0; 1024];
let mut n = 0;
while n < buf.len() {
n += try!(stream.read(&mut buf[n..]));
let mut headers = [httparse::EMPTY_HEADER; 10];
let mut res = httparse::Response::new(&mut headers);
if try!(res.parse(&buf[..n])).is_complete() {
let code = res.code.expect("complete parsing lost code");
if code >= 200 && code < 300 {
trace!("CONNECT success = {:?}", code);
return self.ssl.wrap_client(stream, host)
.map(Proxied::Tunneled)
} else {
trace!("CONNECT response = {:?}", code);
return Err(::Error::Status);
}
}
}
Err(::Error::TooLarge)
},
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme").into())
}
}
}
#[derive(Debug)]
pub enum Proxied<T1, T2> {
Normal(T1),
Tunneled(T2)
}
#[cfg(test)]
impl<T1, T2> Proxied<T1, T2> {
pub fn into_normal(self) -> Result<T1, Self> {
match self {
Proxied::Normal(t1) => Ok(t1),
_ => Err(self)
}
}
pub fn into_tunneled(self) -> Result<T2, Self> {
match self {
Proxied::Tunneled(t2) => Ok(t2),
_ => Err(self)
}
}
}
impl<T1: NetworkStream, T2: NetworkStream> io::Read for Proxied<T1, T2> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Proxied::Normal(ref mut t) => io::Read::read(t, buf),
Proxied::Tunneled(ref mut t) => io::Read::read(t, buf),
}
}
}
impl<T1: NetworkStream, T2: NetworkStream> io::Write for Proxied<T1, T2> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
Proxied::Normal(ref mut t) => io::Write::write(t, buf),
Proxied::Tunneled(ref mut t) => io::Write::write(t, buf),
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match *self {
Proxied::Normal(ref mut t) => io::Write::flush(t),
Proxied::Tunneled(ref mut t) => io::Write::flush(t),
}
}
}
impl<T1: NetworkStream, T2: NetworkStream> NetworkStream for Proxied<T1, T2> {
#[inline]
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
match *self {
Proxied::Normal(ref mut s) => s.peer_addr(),
Proxied::Tunneled(ref mut s) => s.peer_addr()
}
}
#[inline]
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
match *self {
Proxied::Normal(ref inner) => inner.set_read_timeout(dur),
Proxied::Tunneled(ref inner) => inner.set_read_timeout(dur)
}
}
#[inline]
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
match *self {
Proxied::Normal(ref inner) => inner.set_write_timeout(dur),
Proxied::Tunneled(ref inner) => inner.set_write_timeout(dur)
}
}
#[inline]
fn close(&mut self, how: Shutdown) -> io::Result<()> {
match *self {
Proxied::Normal(ref mut s) => s.close(how),
Proxied::Tunneled(ref mut s) => s.close(how)
}
}
}
#[cfg(not(any(feature = "openssl", feature = "security-framework")))]
mod no_ssl {
use std::io;
use std::net::{Shutdown, SocketAddr};
use std::time::Duration;
use net::{SslClient, NetworkStream};
pub struct Plaintext;
#[derive(Clone)]
pub enum Void {}
impl io::Read for Void {
#[inline]
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
match *self {}
}
}
impl io::Write for Void {
#[inline]
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
match *self {}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match *self {}
}
}
impl NetworkStream for Void {
#[inline]
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
match *self {}
}
#[inline]
fn set_read_timeout(&self, _dur: Option<Duration>) -> io::Result<()> {
match *self {}
}
#[inline]
fn set_write_timeout(&self, _dur: Option<Duration>) -> io::Result<()> {
match *self {}
}
#[inline]
fn close(&mut self, _how: Shutdown) -> io::Result<()> {
match *self {}
}
}
impl<T: NetworkStream + Send + Clone> SslClient<T> for Plaintext {
type Stream = Void;
fn wrap_client(&self, _stream: T, _host: &str) -> ::Result<Self::Stream> {
Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid scheme").into())
}
}
}

View File

@@ -1,177 +1,52 @@
//! Client Requests
use std::marker::PhantomData;
use std::io::{self, Write};
use std::time::Duration;
use url::Url;
use method::Method;
use header::Headers;
use header::Host;
use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming};
use version;
use client::{Response, get_host_and_port};
use http::RequestHead;
use method::Method;
use uri::RequestUri;
use version::HttpVersion;
use http::{HttpMessage, RequestHead};
use http::h1::Http11Message;
/// A client request to a remote server.
/// The W type tracks the state of the request, Fresh vs Streaming.
pub struct Request<W> {
/// The target URI for this request.
pub url: Url,
/// The HTTP version of this request.
pub version: version::HttpVersion,
message: Box<HttpMessage>,
headers: Headers,
method: Method,
_marker: PhantomData<W>,
#[derive(Debug)]
pub struct Request<'a> {
head: &'a mut RequestHead
}
impl<W> Request<W> {
impl<'a> Request<'a> {
/// Read the Request Url.
#[inline]
pub fn uri(&self) -> &RequestUri { &self.head.subject.1 }
/// Readthe Request Version.
#[inline]
pub fn version(&self) -> &HttpVersion { &self.head.version }
/// Read the Request headers.
#[inline]
pub fn headers(&self) -> &Headers { &self.headers }
pub fn headers(&self) -> &Headers { &self.head.headers }
/// Read the Request method.
#[inline]
pub fn method(&self) -> Method { self.method.clone() }
pub fn method(&self) -> &Method { &self.head.subject.0 }
/// Set the write timeout.
/// Set the Method of this request.
#[inline]
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.message.set_write_timeout(dur)
}
/// Set the read timeout.
#[inline]
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.message.set_read_timeout(dur)
}
}
impl Request<Fresh> {
/// Create a new `Request<Fresh>` that will use the given `HttpMessage` for its communication
/// with the server. This implies that the given `HttpMessage` instance has already been
/// properly initialized by the caller (e.g. a TCP connection's already established).
pub fn with_message(method: Method, url: Url, message: Box<HttpMessage>)
-> ::Result<Request<Fresh>> {
let mut headers = Headers::new();
{
let (host, port) = try!(get_host_and_port(&url));
headers.set(Host {
hostname: host.to_owned(),
port: Some(port),
});
}
Ok(Request::with_headers_and_message(method, url, headers, message))
}
#[doc(hidden)]
pub fn with_headers_and_message(method: Method, url: Url, headers: Headers, message: Box<HttpMessage>)
-> Request<Fresh> {
Request {
method: method,
headers: headers,
url: url,
version: version::HttpVersion::Http11,
message: message,
_marker: PhantomData,
}
}
/// Create a new client request.
pub fn new(method: Method, url: Url) -> ::Result<Request<Fresh>> {
let conn = DefaultConnector::default();
Request::with_connector(method, url, &conn)
}
/// Create a new client request with a specific underlying NetworkStream.
pub fn with_connector<C, S>(method: Method, url: Url, connector: &C)
-> ::Result<Request<Fresh>> where
C: NetworkConnector<Stream=S>,
S: Into<Box<NetworkStream + Send>> {
let stream = {
let (host, port) = try!(get_host_and_port(&url));
try!(connector.connect(host, port, url.scheme())).into()
};
Request::with_message(method, url, Box::new(Http11Message::with_stream(stream)))
}
/// Consume a Fresh Request, writing the headers and method,
/// returning a Streaming Request.
pub fn start(mut self) -> ::Result<Request<Streaming>> {
let head = match self.message.set_outgoing(RequestHead {
headers: self.headers,
method: self.method,
url: self.url,
}) {
Ok(head) => head,
Err(e) => {
let _ = self.message.close_connection();
return Err(From::from(e));
}
};
Ok(Request {
method: head.method,
headers: head.headers,
url: head.url,
version: self.version,
message: self.message,
_marker: PhantomData,
})
}
pub fn set_method(&mut self, method: Method) { self.head.subject.0 = method; }
/// Get a mutable reference to the Request headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.head.headers }
}
impl Request<Streaming> {
/// Completes writing the request, and returns a response to read from.
///
/// Consumes the Request.
pub fn send(self) -> ::Result<Response> {
Response::with_message(self.url, self.message)
}
}
impl Write for Request<Streaming> {
#[inline]
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
match self.message.write(msg) {
Ok(n) => Ok(n),
Err(e) => {
let _ = self.message.close_connection();
Err(e)
}
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match self.message.flush() {
Ok(r) => Ok(r),
Err(e) => {
let _ = self.message.close_connection();
Err(e)
}
}
}
pub fn new(head: &mut RequestHead) -> Request {
Request { head: head }
}
#[cfg(test)]
mod tests {
/*
use std::io::Write;
use std::str::from_utf8;
use url::Url;
@@ -311,4 +186,5 @@ mod tests {
.get_ref().downcast_ref::<MockStream>().unwrap()
.is_closed);
}
*/
}

View File

@@ -1,82 +1,57 @@
//! Client Responses
use std::io::{self, Read};
use url::Url;
use header;
use net::NetworkStream;
use http::{self, RawStatus, ResponseHead, HttpMessage};
use http::h1::Http11Message;
//use net::NetworkStream;
use http::{self, RawStatus};
use status;
use version;
pub fn new(incoming: http::ResponseHead) -> Response {
trace!("Response::new");
let status = status::StatusCode::from_u16(incoming.subject.0);
debug!("version={:?}, status={:?}", incoming.version, status);
debug!("headers={:?}", incoming.headers);
Response {
status: status,
version: incoming.version,
headers: incoming.headers,
status_raw: incoming.subject,
}
}
/// A response for a client request to a remote server.
#[derive(Debug)]
pub struct Response {
/// The status from the server.
pub status: status::StatusCode,
/// The headers from the server.
pub headers: header::Headers,
/// The HTTP version of this response from the server.
pub version: version::HttpVersion,
/// The final URL of this response.
pub url: Url,
status: status::StatusCode,
headers: header::Headers,
version: version::HttpVersion,
status_raw: RawStatus,
message: Box<HttpMessage>,
}
impl Response {
/// Get the headers from the server.
#[inline]
pub fn headers(&self) -> &header::Headers { &self.headers }
/// Creates a new response from a server.
pub fn new(url: Url, stream: Box<NetworkStream + Send>) -> ::Result<Response> {
trace!("Response::new");
Response::with_message(url, Box::new(Http11Message::with_stream(stream)))
}
/// Creates a new response received from the server on the given `HttpMessage`.
pub fn with_message(url: Url, mut message: Box<HttpMessage>) -> ::Result<Response> {
trace!("Response::with_message");
let ResponseHead { headers, raw_status, version } = match message.get_incoming() {
Ok(head) => head,
Err(e) => {
let _ = message.close_connection();
return Err(From::from(e));
}
};
let status = status::StatusCode::from_u16(raw_status.0);
debug!("version={:?}, status={:?}", version, status);
debug!("headers={:?}", headers);
Ok(Response {
status: status,
version: version,
headers: headers,
url: url,
status_raw: raw_status,
message: message,
})
}
/// Get the status from the server.
#[inline]
pub fn status(&self) -> &status::StatusCode { &self.status }
/// Get the raw status code and reason.
#[inline]
pub fn status_raw(&self) -> &RawStatus {
&self.status_raw
}
}
pub fn status_raw(&self) -> &RawStatus { &self.status_raw }
impl Read for Response {
/// Get the final URL of this response.
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.message.read(buf) {
Err(e) => {
let _ = self.message.close_connection();
Err(e)
}
r => r
}
}
//pub fn url(&self) -> &Url { &self.url }
/// Get the HTTP version of this response from the server.
#[inline]
pub fn version(&self) -> &version::HttpVersion { &self.version }
}
/*
impl Drop for Response {
fn drop(&mut self) {
// if not drained, theres old bits in the Reader. we can't reuse this,
@@ -94,9 +69,11 @@ impl Drop for Response {
}
}
}
*/
#[cfg(test)]
mod tests {
/*
use std::io::{self, Read};
use url::Url;
@@ -230,4 +207,5 @@ mod tests {
assert!(Response::new(url, Box::new(stream)).is_err());
}
*/
}

View File

@@ -7,7 +7,6 @@ use std::string::FromUtf8Error;
use httparse;
use url;
use solicit::http::HttpError as Http2Error;
#[cfg(feature = "openssl")]
use openssl::ssl::error::SslError;
@@ -18,10 +17,11 @@ use self::Error::{
Version,
Header,
Status,
Timeout,
Io,
Ssl,
TooLarge,
Http2,
Incomplete,
Utf8
};
@@ -42,14 +42,16 @@ pub enum Error {
Header,
/// A message head is too large to be reasonable.
TooLarge,
/// A message reached EOF before being a complete message.
Incomplete,
/// An invalid `Status`, such as `1337 ELITE`.
Status,
/// A timeout occurred waiting for an IO event.
Timeout,
/// An `io::Error` that occurred while trying to read or write to a network stream.
Io(IoError),
/// An error from a SSL library.
Ssl(Box<StdError + Send + Sync>),
/// An HTTP/2-specific error, coming from the `solicit` library.
Http2(Http2Error),
/// Parsing a field as string failed
Utf8(Utf8Error),
@@ -80,10 +82,11 @@ impl StdError for Error {
Header => "Invalid Header provided",
TooLarge => "Message head is too large",
Status => "Invalid Status provided",
Incomplete => "Message is incomplete",
Timeout => "Timeout",
Uri(ref e) => e.description(),
Io(ref e) => e.description(),
Ssl(ref e) => e.description(),
Http2(ref e) => e.description(),
Utf8(ref e) => e.description(),
Error::__Nonexhaustive(ref void) => match *void {}
}
@@ -94,7 +97,6 @@ impl StdError for Error {
Io(ref error) => Some(error),
Ssl(ref error) => Some(&**error),
Uri(ref error) => Some(error),
Http2(ref error) => Some(error),
_ => None,
}
}
@@ -148,18 +150,11 @@ impl From<httparse::Error> for Error {
}
}
impl From<Http2Error> for Error {
fn from(err: Http2Error) -> Error {
Error::Http2(err)
}
}
#[cfg(test)]
mod tests {
use std::error::Error as StdError;
use std::io;
use httparse;
use solicit::http::HttpError as Http2Error;
use url;
use super::Error;
use super::Error::*;
@@ -201,7 +196,6 @@ mod tests {
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..));
from_and_cause!(url::ParseError::EmptyHost => Uri(..));
from_and_cause!(Http2Error::UnknownStreamId => Http2(..));
from!(httparse::Error::HeaderName => Header);
from!(httparse::Error::HeaderName => Header);

View File

@@ -1,7 +1,7 @@
use std::fmt::{self, Display};
use std::str;
use unicase::UniCase;
use header::{Header, HeaderFormat};
use header::{Header};
/// `Access-Control-Allow-Credentials` header, part of
/// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)
@@ -62,9 +62,7 @@ impl Header for AccessControlAllowCredentials {
}
Err(::Error::Header)
}
}
impl HeaderFormat for AccessControlAllowCredentials {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("true")
}
@@ -86,4 +84,4 @@ mod test_access_control_allow_credentials {
test_header!(not_bool, vec![b"false"], None);
test_header!(only_single, vec![b"true", b"true"], None);
test_header!(no_gibberish, vec!["\u{645}\u{631}\u{62d}\u{628}\u{627}".as_bytes()], None);
}
}

View File

@@ -1,6 +1,6 @@
use std::fmt::{self, Display};
use header::{Header, HeaderFormat};
use header::{Header};
/// The `Access-Control-Allow-Origin` response header,
/// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
@@ -70,9 +70,7 @@ impl Header for AccessControlAllowOrigin {
_ => AccessControlAllowOrigin::Value(try!(String::from_utf8(value.clone())))
})
}
}
impl HeaderFormat for AccessControlAllowOrigin {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AccessControlAllowOrigin::Any => f.write_str("*"),

View File

@@ -3,7 +3,7 @@ use std::fmt::{self, Display};
use std::str::{FromStr, from_utf8};
use std::ops::{Deref, DerefMut};
use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline};
use header::{Header, HeaderFormat};
use header::{Header};
/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
///
@@ -97,9 +97,7 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st
}
}
}
}
impl<S: Scheme + Any> HeaderFormat for Authorization<S> where <S as FromStr>::Err: 'static {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(scheme) = <S as Scheme>::scheme() {
try!(write!(f, "{} ", scheme))

View File

@@ -1,6 +1,6 @@
use std::fmt;
use std::str::FromStr;
use header::{Header, HeaderFormat};
use header::Header;
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
@@ -62,9 +62,7 @@ impl Header for CacheControl {
Err(::Error::Header)
}
}
}
impl HeaderFormat for CacheControl {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_comma_delimited(f, &self[..])
}

View File

@@ -11,7 +11,7 @@ use std::fmt;
use unicase::UniCase;
use url::percent_encoding;
use header::{Header, HeaderFormat, parsing};
use header::{Header, parsing};
use header::parsing::{parse_extended_value, HTTP_VALUE};
use header::shared::Charset;
@@ -144,9 +144,7 @@ impl Header for ContentDisposition {
Ok(cd)
})
}
}
impl HeaderFormat for ContentDisposition {
#[inline]
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self, f)

View File

@@ -1,6 +1,6 @@
use std::fmt;
use header::{HeaderFormat, Header, parsing};
use header::{Header, parsing};
/// `Content-Length` header, defined in
/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2)
@@ -55,9 +55,7 @@ impl Header for ContentLength {
.unwrap_or(Err(::Error::Header))
.map(ContentLength)
}
}
impl HeaderFormat for ContentLength {
#[inline]
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)

View File

@@ -1,4 +1,4 @@
use header::{Header, HeaderFormat, CookiePair, CookieJar};
use header::{Header, CookiePair, CookieJar};
use std::fmt::{self, Display};
use std::str::from_utf8;
@@ -61,9 +61,7 @@ impl Header for Cookie {
Err(::Error::Header)
}
}
}
impl HeaderFormat for Cookie {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let cookies = &self.0;
for (i, cookie) in cookies.iter().enumerate() {

View File

@@ -3,7 +3,7 @@ use std::str;
use unicase::UniCase;
use header::{Header, HeaderFormat};
use header::{Header};
/// The `Expect` header.
///
@@ -53,9 +53,7 @@ impl Header for Expect {
Err(::Error::Header)
}
}
}
impl HeaderFormat for Expect {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("100-continue")
}

View File

@@ -1,4 +1,4 @@
use header::{Header, HeaderFormat};
use header::{Header};
use std::fmt;
use header::parsing::from_one_raw_str;
@@ -52,9 +52,7 @@ impl Header for Host {
// https://github.com/servo/rust-url/issues/42
let idx = {
let slice = &s[..];
let mut chars = slice.chars();
chars.next();
if chars.next().unwrap() == '[' {
if slice.starts_with('[') {
match slice.rfind(']') {
Some(idx) => {
if slice.len() > idx + 2 {
@@ -86,9 +84,7 @@ impl Header for Host {
})
})
}
}
impl HeaderFormat for Host {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.port {
None | Some(80) | Some(443) => f.write_str(&self.hostname[..]),

View File

@@ -1,5 +1,5 @@
use std::fmt::{self, Display};
use header::{self, Header, HeaderFormat, EntityTag, HttpDate};
use header::{self, Header, EntityTag, HttpDate};
/// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2)
///
@@ -59,18 +59,16 @@ impl Header for IfRange {
}
fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
if etag.is_ok() {
return Ok(IfRange::EntityTag(etag.unwrap()));
if let Ok(etag) = etag {
return Ok(IfRange::EntityTag(etag));
}
let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw);
if date.is_ok() {
return Ok(IfRange::Date(date.unwrap()));
if let Ok(date) = date {
return Ok(IfRange::Date(date));
}
Err(::Error::Header)
}
}
impl HeaderFormat for IfRange {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
IfRange::EntityTag(ref x) => Display::fmt(x, f),

View File

@@ -66,7 +66,7 @@ macro_rules! bench_header(
use test::Bencher;
use super::*;
use header::{Header, HeaderFormatter};
use header::{Header};
#[bench]
fn bench_parse(b: &mut Bencher) {
@@ -79,7 +79,7 @@ macro_rules! bench_header(
#[bench]
fn bench_format(b: &mut Bencher) {
let val: $ty = Header::parse_header(&$value[..]).unwrap();
let fmt = HeaderFormatter(&val);
let fmt = ::header::HeaderFormatter(&val);
b.iter(|| {
format!("{}", fmt);
});
@@ -222,15 +222,13 @@ macro_rules! header {
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id)
}
}
impl $crate::header::HeaderFormat for $id {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
$crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
}
}
impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::HeaderFormat;
use $crate::header::Header;
self.fmt_header(f)
}
}
@@ -250,15 +248,13 @@ macro_rules! header {
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id)
}
}
impl $crate::header::HeaderFormat for $id {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
$crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
}
}
impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::HeaderFormat;
use $crate::header::Header;
self.fmt_header(f)
}
}
@@ -277,8 +273,6 @@ macro_rules! header {
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_one_raw_str(raw).map($id)
}
}
impl $crate::header::HeaderFormat for $id {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
::std::fmt::Display::fmt(&**self, f)
}
@@ -313,8 +307,6 @@ macro_rules! header {
}
$crate::header::parsing::from_comma_delimited(raw).map($id::Items)
}
}
impl $crate::header::HeaderFormat for $id {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
$id::Any => f.write_str("*"),
@@ -325,7 +317,7 @@ macro_rules! header {
}
impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::HeaderFormat;
use $crate::header::Header;
self.fmt_header(f)
}
}

View File

@@ -1,7 +1,7 @@
use std::fmt;
use std::ascii::AsciiExt;
use header::{Header, HeaderFormat, parsing};
use header::{Header, parsing};
/// The `Pragma` header defined by HTTP/1.0.
///
@@ -52,9 +52,7 @@ impl Header for Pragma {
}
})
}
}
impl HeaderFormat for Pragma {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
Pragma::NoCache => "no-cache",

View File

@@ -1,6 +1,6 @@
use std::fmt;
use std::str::FromStr;
use header::{Header, HeaderFormat};
use header::{Header};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
@@ -64,9 +64,7 @@ impl Header for Prefer {
Err(::Error::Header)
}
}
}
impl HeaderFormat for Prefer {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_comma_delimited(f, &self[..])
}

View File

@@ -1,5 +1,5 @@
use std::fmt;
use header::{Header, HeaderFormat, Preference};
use header::{Header, Preference};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
@@ -61,9 +61,7 @@ impl Header for PreferenceApplied {
Err(::Error::Header)
}
}
}
impl HeaderFormat for PreferenceApplied {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let preferences: Vec<_> = self.0.iter().map(|pref| match pref {
// The spec ignores parameters in `Preferences-Applied`
@@ -80,7 +78,7 @@ impl HeaderFormat for PreferenceApplied {
#[cfg(test)]
mod tests {
use header::{HeaderFormat, Preference};
use header::{Header, Preference};
use super::*;
#[test]
@@ -90,7 +88,7 @@ mod tests {
"foo".to_owned(),
"bar".to_owned(),
vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())]
)]) as &(HeaderFormat + Send + Sync)),
)]) as &(Header + Send + Sync)),
"foo=bar".to_owned()
);
}

View File

@@ -1,7 +1,7 @@
use std::fmt::{self, Display};
use std::str::FromStr;
use header::{Header, HeaderFormat};
use header::Header;
use header::parsing::{from_one_raw_str, from_comma_delimited};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1)
@@ -182,9 +182,6 @@ impl Header for Range {
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {
from_one_raw_str(raw)
}
}
impl HeaderFormat for Range {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)

View File

@@ -1,4 +1,4 @@
use header::{Header, HeaderFormat, CookiePair, CookieJar};
use header::{Header, CookiePair, CookieJar};
use std::fmt::{self, Display};
use std::str::from_utf8;
@@ -104,10 +104,6 @@ impl Header for SetCookie {
}
}
}
impl HeaderFormat for SetCookie {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, cookie) in self.0.iter().enumerate() {
if i != 0 {

View File

@@ -3,7 +3,7 @@ use std::str::{self, FromStr};
use unicase::UniCase;
use header::{Header, HeaderFormat, parsing};
use header::{Header, parsing};
/// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797)
///
@@ -127,9 +127,7 @@ impl Header for StrictTransportSecurity {
fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> {
parsing::from_one_raw_str(raw)
}
}
impl HeaderFormat for StrictTransportSecurity {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.include_subdomains {
write!(f, "max-age={}; includeSubdomains", self.max_age)

View File

@@ -49,5 +49,12 @@ header! {
}
}
impl TransferEncoding {
/// Constructor for the most common Transfer-Encoding, `chunked`.
pub fn chunked() -> TransferEncoding {
TransferEncoding(vec![Encoding::Chunked])
}
}
bench_header!(normal, TransferEncoding, { vec![b"chunked, gzip".to_vec()] });
bench_header!(ext, TransferEncoding, { vec![b"ext".to_vec()] });

View File

@@ -1,7 +1,6 @@
use std::any::{Any, TypeId};
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::fmt;
use std::mem;
use std::ops::Deref;
@@ -53,7 +52,7 @@ enum PtrMap<T> {
Many(HashMap<TypeId, T>)
}
impl<V: ?Sized + fmt::Debug + Any + 'static> PtrMapCell<V> {
impl<V: ?Sized + Any + 'static> PtrMapCell<V> {
#[inline]
pub fn new() -> PtrMapCell<V> {
PtrMapCell(UnsafeCell::new(PtrMap::Empty))
@@ -114,12 +113,12 @@ impl<V: ?Sized + fmt::Debug + Any + 'static> PtrMapCell<V> {
let map = &*self.0.get();
match *map {
PtrMap::One(_, ref one) => one,
_ => panic!("not PtrMap::One value, {:?}", *map)
_ => panic!("not PtrMap::One value")
}
}
}
impl<V: ?Sized + fmt::Debug + Any + 'static> Clone for PtrMapCell<V> where Box<V>: Clone {
impl<V: ?Sized + Any + 'static> Clone for PtrMapCell<V> where Box<V>: Clone {
#[inline]
fn clone(&self) -> PtrMapCell<V> {
let cell = PtrMapCell::new();

View File

@@ -4,13 +4,13 @@ use std::fmt;
use std::str::from_utf8;
use super::cell::{OptCell, PtrMapCell};
use header::{Header, HeaderFormat};
use header::{Header};
#[derive(Clone)]
pub struct Item {
raw: OptCell<Vec<Vec<u8>>>,
typed: PtrMapCell<HeaderFormat + Send + Sync>
typed: PtrMapCell<Header + Send + Sync>
}
impl Item {
@@ -23,7 +23,7 @@ impl Item {
}
#[inline]
pub fn new_typed(ty: Box<HeaderFormat + Send + Sync>) -> Item {
pub fn new_typed(ty: Box<Header + Send + Sync>) -> Item {
let map = PtrMapCell::new();
unsafe { map.insert((*ty).get_type(), ty); }
Item {
@@ -52,7 +52,7 @@ impl Item {
&raw[..]
}
pub fn typed<H: Header + HeaderFormat + Any>(&self) -> Option<&H> {
pub fn typed<H: Header + Any>(&self) -> Option<&H> {
let tid = TypeId::of::<H>();
match self.typed.get(tid) {
Some(val) => Some(val),
@@ -68,7 +68,7 @@ impl Item {
}.map(|typed| unsafe { typed.downcast_ref_unchecked() })
}
pub fn typed_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> {
pub fn typed_mut<H: Header>(&mut self) -> Option<&mut H> {
let tid = TypeId::of::<H>();
if self.typed.get_mut(tid).is_none() {
match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) {
@@ -83,11 +83,11 @@ impl Item {
}
#[inline]
fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) ->
::Result<Box<HeaderFormat + Send + Sync>> {
fn parse<H: Header>(raw: &Vec<Vec<u8>>) ->
::Result<Box<Header + Send + Sync>> {
Header::parse_header(&raw[..]).map(|h: H| {
// FIXME: Use Type ascription
let h: Box<HeaderFormat + Send + Sync> = Box::new(h);
let h: Box<Header + Send + Sync> = Box::new(h);
h
})
}

View File

@@ -31,18 +31,17 @@
//! }
//! ```
//!
//! This works well for simple "string" headers. But the header system
//! actually involves 2 parts: parsing, and formatting. If you need to
//! customize either part, you can do so.
//! This works well for simple "string" headers. If you need more control,
//! you can implement the trait directly.
//!
//! ## `Header` and `HeaderFormat`
//! ## Implementing the `Header` trait
//!
//! Consider a Do Not Track header. It can be true or false, but it represents
//! that via the numerals `1` and `0`.
//!
//! ```
//! use std::fmt;
//! use hyper::header::{Header, HeaderFormat};
//! use hyper::header::Header;
//!
//! #[derive(Debug, Clone, Copy)]
//! struct Dnt(bool);
@@ -66,9 +65,7 @@
//! }
//! Err(hyper::Error::Header)
//! }
//! }
//!
//! impl HeaderFormat for Dnt {
//! fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! if self.0 {
//! f.write_str("1")
@@ -113,11 +110,11 @@ type HeaderName = UniCase<CowStr>;
///
/// This trait represents the construction and identification of headers,
/// and contains trait-object unsafe methods.
pub trait Header: Clone + Any + Send + Sync {
pub trait Header: HeaderClone + Any + Typeable + Send + Sync {
/// Returns the name of the header field this belongs to.
///
/// This will become an associated constant once available.
fn header_name() -> &'static str;
fn header_name() -> &'static str where Self: Sized;
/// Parse a header from a raw stream of bytes.
///
/// It's possible that a request can include a header field more than once,
@@ -125,35 +122,27 @@ pub trait Header: Clone + Any + Send + Sync {
/// it's not necessarily the case that a Header is *allowed* to have more
/// than one field value. If that's the case, you **should** return `None`
/// if `raw.len() > 1`.
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self>;
}
/// A trait for any object that will represent a header field and value.
///
/// This trait represents the formatting of a `Header` for output to a TcpStream.
pub trait HeaderFormat: fmt::Debug + HeaderClone + Any + Typeable + Send + Sync {
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self> where Self: Sized;
/// Format a header to be output into a TcpStream.
///
/// This method is not allowed to introduce an Err not produced
/// by the passed-in Formatter.
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
#[doc(hidden)]
pub trait HeaderClone {
fn clone_box(&self) -> Box<HeaderFormat + Send + Sync>;
fn clone_box(&self) -> Box<Header + Send + Sync>;
}
impl<T: HeaderFormat + Clone> HeaderClone for T {
impl<T: Header + Clone> HeaderClone for T {
#[inline]
fn clone_box(&self) -> Box<HeaderFormat + Send + Sync> {
fn clone_box(&self) -> Box<Header + Send + Sync> {
Box::new(self.clone())
}
}
impl HeaderFormat + Send + Sync {
impl Header + Send + Sync {
#[inline]
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
mem::transmute(traitobject::data(self))
@@ -165,9 +154,9 @@ impl HeaderFormat + Send + Sync {
}
}
impl Clone for Box<HeaderFormat + Send + Sync> {
impl Clone for Box<Header + Send + Sync> {
#[inline]
fn clone(&self) -> Box<HeaderFormat + Send + Sync> {
fn clone(&self) -> Box<Header + Send + Sync> {
self.clone_box()
}
}
@@ -183,6 +172,12 @@ pub struct Headers {
data: HashMap<HeaderName, Item>
}
impl Default for Headers {
fn default() -> Headers {
Headers::new()
}
}
impl Headers {
/// Creates a new, empty headers map.
@@ -212,8 +207,8 @@ impl Headers {
/// Set a header field to the corresponding value.
///
/// The field is determined by the type of the value being set.
pub fn set<H: Header + HeaderFormat>(&mut self, value: H) {
trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), value);
pub fn set<H: Header>(&mut self, value: H) {
trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value));
self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))),
Item::new_typed(Box::new(value)));
}
@@ -259,13 +254,13 @@ impl Headers {
}
/// Get a reference to the header field's value, if it exists.
pub fn get<H: Header + HeaderFormat>(&self) -> Option<&H> {
pub fn get<H: Header>(&self) -> Option<&H> {
self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed::<H>)
}
/// Get a mutable reference to the header field's value, if it exists.
pub fn get_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> {
pub fn get_mut<H: Header>(&mut self) -> Option<&mut H> {
self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed_mut::<H>)
}
@@ -280,13 +275,13 @@ impl Headers {
/// # let mut headers = Headers::new();
/// let has_type = headers.has::<ContentType>();
/// ```
pub fn has<H: Header + HeaderFormat>(&self) -> bool {
pub fn has<H: Header>(&self) -> bool {
self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
}
/// Removes a header from the map, if one existed.
/// Returns true if a header has been removed.
pub fn remove<H: Header + HeaderFormat>(&mut self) -> bool {
pub fn remove<H: Header>(&mut self) -> bool {
trace!("Headers.remove( {:?} )", header_name::<H>());
self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))).is_some()
}
@@ -380,6 +375,7 @@ impl Deserialize for Headers {
}
/// An `Iterator` over the fields in a `Headers` map.
#[allow(missing_debug_implementations)]
pub struct HeadersItems<'a> {
inner: Iter<'a, HeaderName, Item>
}
@@ -410,7 +406,7 @@ impl<'a> HeaderView<'a> {
/// Cast the value to a certain Header type.
#[inline]
pub fn value<H: Header + HeaderFormat>(&self) -> Option<&'a H> {
pub fn value<H: Header>(&self) -> Option<&'a H> {
self.1.typed::<H>()
}
@@ -449,7 +445,7 @@ impl<'a> FromIterator<HeaderView<'a>> for Headers {
}
}
impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) {
impl<'a> fmt::Display for &'a (Header + Send + Sync) {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt_header(f)
@@ -461,16 +457,16 @@ impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) {
/// This can be used like so: `format!("{}", HeaderFormatter(&header))` to
/// get the representation of a Header which will be written to an
/// outgoing `TcpStream`.
pub struct HeaderFormatter<'a, H: HeaderFormat>(pub &'a H);
pub struct HeaderFormatter<'a, H: Header>(pub &'a H);
impl<'a, H: HeaderFormat> fmt::Display for HeaderFormatter<'a, H> {
impl<'a, H: Header> fmt::Display for HeaderFormatter<'a, H> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f)
}
}
impl<'a, H: HeaderFormat> fmt::Debug for HeaderFormatter<'a, H> {
impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f)
@@ -519,7 +515,7 @@ mod tests {
use mime::Mime;
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
use super::{Headers, Header, HeaderFormat, ContentLength, ContentType,
use super::{Headers, Header, ContentLength, ContentType,
Accept, Host, qitem};
use httparse;
@@ -597,9 +593,7 @@ mod tests {
None => Err(::Error::Header),
}
}
}
impl HeaderFormat for CrazyLength {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let CrazyLength(ref opt, ref value) = *self;
write!(f, "{:?}, {:?}", opt, value)

View File

@@ -137,6 +137,12 @@ define_encode_set! {
}
}
impl fmt::Debug for HTTP_VALUE {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("HTTP_VALUE")
}
}
impl Display for ExtendedValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let encoded_value =

120
src/http/buffer.rs Normal file
View File

@@ -0,0 +1,120 @@
use std::cmp;
use std::io::{self, Read};
use std::ptr;
const INIT_BUFFER_SIZE: usize = 4096;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
#[derive(Debug)]
pub struct Buffer {
vec: Vec<u8>,
read_pos: usize,
write_pos: usize,
}
impl Buffer {
pub fn new() -> Buffer {
Buffer {
vec: Vec::new(),
read_pos: 0,
write_pos: 0,
}
}
pub fn reset(&mut self) {
*self = Buffer::new()
}
#[inline]
pub fn len(&self) -> usize {
self.read_pos - self.write_pos
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn bytes(&self) -> &[u8] {
&self.vec[self.write_pos..self.read_pos]
}
#[inline]
pub fn consume(&mut self, pos: usize) {
debug_assert!(self.read_pos >= self.write_pos + pos);
self.write_pos += pos;
if self.write_pos == self.read_pos {
self.write_pos = 0;
self.read_pos = 0;
}
}
pub fn read_from<R: Read>(&mut self, r: &mut R) -> io::Result<usize> {
self.maybe_reserve();
let n = try!(r.read(&mut self.vec[self.read_pos..]));
self.read_pos += n;
Ok(n)
}
#[inline]
fn maybe_reserve(&mut self) {
let cap = self.vec.len();
if cap == 0 {
trace!("reserving initial {}", INIT_BUFFER_SIZE);
self.vec = vec![0; INIT_BUFFER_SIZE];
} else if self.write_pos > 0 && self.read_pos == cap {
let count = self.read_pos - self.write_pos;
trace!("moving buffer bytes over by {}", count);
unsafe {
ptr::copy(
self.vec.as_ptr().offset(self.write_pos as isize),
self.vec.as_mut_ptr(),
count
);
}
self.read_pos -= count;
self.write_pos = 0;
} else if self.read_pos == cap && cap < MAX_BUFFER_SIZE {
self.vec.reserve(cmp::min(cap * 4, MAX_BUFFER_SIZE) - cap);
let new = self.vec.capacity() - cap;
trace!("reserved {}", new);
unsafe { grow_zerofill(&mut self.vec, new) }
}
}
pub fn wrap<'a, 'b: 'a, R: io::Read>(&'a mut self, reader: &'b mut R) -> BufReader<'a, R> {
BufReader {
buf: self,
reader: reader
}
}
}
#[derive(Debug)]
pub struct BufReader<'a, R: io::Read + 'a> {
buf: &'a mut Buffer,
reader: &'a mut R
}
impl<'a, R: io::Read> Read for BufReader<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
trace!("BufReader.read self={}, buf={}", self.buf.len(), buf.len());
let n = try!(self.buf.bytes().read(buf));
self.buf.consume(n);
if n == 0 {
self.buf.reset();
self.reader.read(&mut buf[n..])
} else {
Ok(n)
}
}
}
#[inline]
unsafe fn grow_zerofill(buf: &mut Vec<u8>, additional: usize) {
let len = buf.len();
buf.set_len(len + additional);
ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());
}

96
src/http/channel.rs Normal file
View File

@@ -0,0 +1,96 @@
use std::fmt;
use std::sync::{Arc, mpsc};
use std::sync::atomic::{AtomicBool, Ordering};
use ::rotor;
pub use std::sync::mpsc::TryRecvError;
pub fn new<T>(notify: rotor::Notifier) -> (Sender<T>, Receiver<T>) {
let b = Arc::new(AtomicBool::new(false));
let (tx, rx) = mpsc::channel();
(Sender {
awake: b.clone(),
notify: notify,
tx: tx,
},
Receiver {
awake: b,
rx: rx,
})
}
pub fn share<T, U>(other: &Sender<U>) -> (Sender<T>, Receiver<T>) {
let (tx, rx) = mpsc::channel();
let notify = other.notify.clone();
let b = other.awake.clone();
(Sender {
awake: b.clone(),
notify: notify,
tx: tx,
},
Receiver {
awake: b,
rx: rx,
})
}
pub struct Sender<T> {
awake: Arc<AtomicBool>,
notify: rotor::Notifier,
tx: mpsc::Sender<T>,
}
impl<T: Send> Sender<T> {
pub fn send(&self, val: T) -> Result<(), SendError<T>> {
try!(self.tx.send(val));
if !self.awake.swap(true, Ordering::SeqCst) {
try!(self.notify.wakeup());
}
Ok(())
}
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Sender<T> {
Sender {
awake: self.awake.clone(),
notify: self.notify.clone(),
tx: self.tx.clone(),
}
}
}
impl<T> fmt::Debug for Sender<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Sender")
.field("notify", &self.notify)
.finish()
}
}
#[derive(Debug)]
pub struct SendError<T>(pub Option<T>);
impl<T> From<mpsc::SendError<T>> for SendError<T> {
fn from(e: mpsc::SendError<T>) -> SendError<T> {
SendError(Some(e.0))
}
}
impl<T> From<rotor::WakeupError> for SendError<T> {
fn from(_e: rotor::WakeupError) -> SendError<T> {
SendError(None)
}
}
pub struct Receiver<T> {
awake: Arc<AtomicBool>,
rx: mpsc::Receiver<T>,
}
impl<T: Send> Receiver<T> {
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
self.awake.store(false, Ordering::Relaxed);
self.rx.try_recv()
}
}

915
src/http/conn.rs Normal file
View File

@@ -0,0 +1,915 @@
use std::borrow::Cow;
use std::fmt;
use std::hash::Hash;
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::time::Duration;
use rotor::{self, EventSet, PollOpt, Scope};
use http::{self, h1, Http1Message, Encoder, Decoder, Next, Next_, Reg, Control};
use http::channel;
use http::internal::WriteBuf;
use http::buffer::Buffer;
use net::{Transport, Blocked};
use version::HttpVersion;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
/// This handles a connection, which will have been established over a
/// Transport (like a socket), and will likely include multiple
/// `Message`s over HTTP.
///
/// The connection will determine when a message begins and ends, creating
/// a new message `MessageHandler` for each one, as well as determine if this
/// connection can be kept alive after the message, or if it is complete.
pub struct Conn<K: Key, T: Transport, H: MessageHandler<T>> {
buf: Buffer,
ctrl: (channel::Sender<Next>, channel::Receiver<Next>),
keep_alive_enabled: bool,
key: K,
state: State<H, T>,
transport: T,
}
impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for Conn<K, T, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Conn")
.field("keep_alive_enabled", &self.keep_alive_enabled)
.field("state", &self.state)
.field("buf", &self.buf)
.finish()
}
}
impl<K: Key, T: Transport, H: MessageHandler<T>> Conn<K, T, H> {
pub fn new(key: K, transport: T, notify: rotor::Notifier) -> Conn<K, T, H> {
Conn {
buf: Buffer::new(),
ctrl: channel::new(notify),
keep_alive_enabled: true,
key: key,
state: State::Init,
transport: transport,
}
}
pub fn keep_alive(mut self, val: bool) -> Conn<K, T, H> {
self.keep_alive_enabled = val;
self
}
/// Desired Register interest based on state of current connection.
///
/// This includes the user interest, such as when they return `Next::read()`.
fn interest(&self) -> Reg {
match self.state {
State::Closed => Reg::Remove,
State::Init => {
<H as MessageHandler>::Message::initial_interest().interest()
}
State::Http1(Http1 { reading: Reading::Closed, writing: Writing::Closed, .. }) => {
Reg::Remove
}
State::Http1(Http1 { ref reading, ref writing, .. }) => {
let read = match *reading {
Reading::Parse |
Reading::Body(..) => Reg::Read,
Reading::Init |
Reading::Wait(..) |
Reading::KeepAlive |
Reading::Closed => Reg::Wait
};
let write = match *writing {
Writing::Head |
Writing::Chunk(..) |
Writing::Ready(..) => Reg::Write,
Writing::Init |
Writing::Wait(..) |
Writing::KeepAlive => Reg::Wait,
Writing::Closed => Reg::Wait,
};
match (read, write) {
(Reg::Read, Reg::Write) => Reg::ReadWrite,
(Reg::Read, Reg::Wait) => Reg::Read,
(Reg::Wait, Reg::Write) => Reg::Write,
(Reg::Wait, Reg::Wait) => Reg::Wait,
_ => unreachable!("bad read/write reg combo")
}
}
}
}
/// Actual register action.
///
/// Considers the user interest(), but also compares if the underlying
/// transport is blocked(), and adjusts accordingly.
fn register(&self) -> Reg {
let reg = self.interest();
match (reg, self.transport.blocked()) {
(Reg::Remove, _) |
(Reg::Wait, _) |
(_, None) => reg,
(_, Some(Blocked::Read)) => Reg::Read,
(_, Some(Blocked::Write)) => Reg::Write,
}
}
fn parse(&mut self) -> ::Result<http::MessageHead<<<H as MessageHandler<T>>::Message as Http1Message>::Incoming>> {
let n = try!(self.buf.read_from(&mut self.transport));
if n == 0 {
trace!("parse eof");
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into());
}
match try!(http::parse::<<H as MessageHandler<T>>::Message, _>(self.buf.bytes())) {
Some((head, len)) => {
trace!("parsed {} bytes out of {}", len, self.buf.len());
self.buf.consume(len);
Ok(head)
},
None => {
if self.buf.len() >= MAX_BUFFER_SIZE {
//TODO: Handler.on_too_large_error()
debug!("MAX_BUFFER_SIZE reached, closing");
Err(::Error::TooLarge)
} else {
Err(io::Error::new(io::ErrorKind::WouldBlock, "incomplete parse").into())
}
},
}
}
fn read<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, state: State<H, T>) -> State<H, T> {
match state {
State::Init => {
let head = match self.parse() {
Ok(head) => head,
Err(::Error::Io(e)) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => return State::Init,
_ => {
debug!("io error trying to parse {:?}", e);
return State::Closed;
}
},
Err(e) => {
//TODO: send proper error codes depending on error
trace!("parse eror: {:?}", e);
return State::Closed;
}
};
match <<H as MessageHandler<T>>::Message as Http1Message>::decoder(&head) {
Ok(decoder) => {
trace!("decoder = {:?}", decoder);
let keep_alive = self.keep_alive_enabled && head.should_keep_alive();
let mut handler = scope.create(Seed(&self.key, &self.ctrl.0));
let next = handler.on_incoming(head);
trace!("handler.on_incoming() -> {:?}", next);
match next.interest {
Next_::Read => self.read(scope, State::Http1(Http1 {
handler: handler,
reading: Reading::Body(decoder),
writing: Writing::Init,
keep_alive: keep_alive,
timeout: next.timeout,
_marker: PhantomData,
})),
Next_::Write => State::Http1(Http1 {
handler: handler,
reading: if decoder.is_eof() {
if keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
} else {
Reading::Wait(decoder)
},
writing: Writing::Head,
keep_alive: keep_alive,
timeout: next.timeout,
_marker: PhantomData,
}),
Next_::ReadWrite => self.read(scope, State::Http1(Http1 {
handler: handler,
reading: Reading::Body(decoder),
writing: Writing::Head,
keep_alive: keep_alive,
timeout: next.timeout,
_marker: PhantomData,
})),
Next_::Wait => State::Http1(Http1 {
handler: handler,
reading: Reading::Wait(decoder),
writing: Writing::Init,
keep_alive: keep_alive,
timeout: next.timeout,
_marker: PhantomData,
}),
Next_::End |
Next_::Remove => State::Closed,
}
},
Err(e) => {
debug!("error creating decoder: {:?}", e);
//TODO: respond with 400
State::Closed
}
}
},
State::Http1(mut http1) => {
let next = match http1.reading {
Reading::Init => None,
Reading::Parse => match self.parse() {
Ok(head) => match <<H as MessageHandler<T>>::Message as Http1Message>::decoder(&head) {
Ok(decoder) => {
trace!("decoder = {:?}", decoder);
// if client request asked for keep alive,
// then it depends entirely on if the server agreed
if http1.keep_alive {
http1.keep_alive = head.should_keep_alive();
}
let next = http1.handler.on_incoming(head);
http1.reading = Reading::Wait(decoder);
trace!("handler.on_incoming() -> {:?}", next);
Some(next)
},
Err(e) => {
debug!("error creating decoder: {:?}", e);
//TODO: respond with 400
return State::Closed;
}
},
Err(::Error::Io(e)) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => None,
_ => {
debug!("io error trying to parse {:?}", e);
return State::Closed;
}
},
Err(e) => {
//TODO: send proper error codes depending on error
trace!("parse eror: {:?}", e);
return State::Closed;
}
},
Reading::Body(ref mut decoder) => {
let wrapped = if !self.buf.is_empty() {
super::Trans::Buf(self.buf.wrap(&mut self.transport))
} else {
super::Trans::Port(&mut self.transport)
};
Some(http1.handler.on_decode(&mut Decoder::h1(decoder, wrapped)))
},
_ => {
trace!("Conn.on_readable State::Http1(reading = {:?})", http1.reading);
None
}
};
let mut s = State::Http1(http1);
trace!("h1 read completed, next = {:?}", next);
if let Some(next) = next {
s.update(next);
}
trace!("h1 read completed, state = {:?}", s);
let again = match s {
State::Http1(Http1 { reading: Reading::Body(ref encoder), .. }) => encoder.is_eof(),
_ => false
};
if again {
self.read(scope, s)
} else {
s
}
},
State::Closed => {
error!("on_readable State::Closed");
State::Closed
}
}
}
fn write<F: MessageHandlerFactory<K, T, Output=H>>(&mut self, scope: &mut Scope<F>, mut state: State<H, T>) -> State<H, T> {
let next = match state {
State::Init => {
// this could be a Client request, which writes first, so pay
// attention to the version written here, which will adjust
// our internal state to Http1 or Http2
let mut handler = scope.create(Seed(&self.key, &self.ctrl.0));
let mut head = http::MessageHead::default();
let interest = handler.on_outgoing(&mut head);
if head.version == HttpVersion::Http11 {
let mut buf = Vec::new();
let keep_alive = self.keep_alive_enabled && head.should_keep_alive();
let mut encoder = H::Message::encode(head, &mut buf);
let writing = match interest.interest {
// user wants to write some data right away
// try to write the headers and the first chunk
// together, so they are in the same packet
Next_::Write |
Next_::ReadWrite => {
encoder.prefix(WriteBuf {
bytes: buf,
pos: 0
});
Writing::Ready(encoder)
},
_ => Writing::Chunk(Chunk {
buf: Cow::Owned(buf),
pos: 0,
next: (encoder, interest.clone())
})
};
state = State::Http1(Http1 {
reading: Reading::Init,
writing: writing,
handler: handler,
keep_alive: keep_alive,
timeout: interest.timeout,
_marker: PhantomData,
})
}
Some(interest)
}
State::Http1(Http1 { ref mut handler, ref mut writing, ref mut keep_alive, .. }) => {
match *writing {
Writing::Init => {
trace!("Conn.on_writable Http1::Writing::Init");
None
}
Writing::Head => {
let mut head = http::MessageHead::default();
let interest = handler.on_outgoing(&mut head);
// if the request wants to close, server cannot stop it
if *keep_alive {
// if the request wants to stay alive, then it depends
// on the server to agree
*keep_alive = head.should_keep_alive();
}
let mut buf = Vec::new();
let mut encoder = <<H as MessageHandler<T>>::Message as Http1Message>::encode(head, &mut buf);
*writing = match interest.interest {
// user wants to write some data right away
// try to write the headers and the first chunk
// together, so they are in the same packet
Next_::Write |
Next_::ReadWrite => {
encoder.prefix(WriteBuf {
bytes: buf,
pos: 0
});
Writing::Ready(encoder)
},
_ => Writing::Chunk(Chunk {
buf: Cow::Owned(buf),
pos: 0,
next: (encoder, interest.clone())
})
};
Some(interest)
},
Writing::Chunk(ref mut chunk) => {
trace!("Http1.Chunk on_writable");
match self.transport.write(&chunk.buf.as_ref()[chunk.pos..]) {
Ok(n) => {
chunk.pos += n;
trace!("Http1.Chunk wrote={}, done={}", n, chunk.is_written());
if chunk.is_written() {
Some(chunk.next.1.clone())
} else {
None
}
},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock |
io::ErrorKind::Interrupted => None,
_ => {
Some(handler.on_error(e.into()))
}
}
}
},
Writing::Ready(ref mut encoder) => {
trace!("Http1.Ready on_writable");
Some(handler.on_encode(&mut Encoder::h1(encoder, &mut self.transport)))
},
Writing::Wait(..) => {
trace!("Conn.on_writable Http1::Writing::Wait");
None
}
Writing::KeepAlive => {
trace!("Conn.on_writable Http1::Writing::KeepAlive");
None
}
Writing::Closed => {
trace!("on_writable Http1::Writing::Closed");
None
}
}
},
State::Closed => {
trace!("on_writable State::Closed");
None
}
};
if let Some(next) = next {
state.update(next);
}
state
}
fn can_read_more(&self) -> bool {
match self.state {
State::Init => false,
_ => !self.buf.is_empty()
}
}
pub fn ready<F>(mut self, events: EventSet, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)>
where F: MessageHandlerFactory<K, T, Output=H> {
trace!("Conn::ready events='{:?}', blocked={:?}", events, self.transport.blocked());
if events.is_error() {
match self.transport.take_socket_error() {
Ok(_) => {
trace!("is_error, but not socket error");
// spurious?
},
Err(e) => self.on_error(e.into())
}
}
// if the user had an io interest, but the transport was blocked differently,
// the event needs to be translated to what the user was actually expecting.
//
// Example:
// - User asks for `Next::write().
// - But transport is in the middle of renegotiating TLS, and is blocked on reading.
// - hyper should not wait on the `write` event, since epoll already
// knows it is writable. We would just loop a whole bunch, and slow down.
// - So instead, hyper waits on the event needed to unblock the transport, `read`.
// - Once epoll detects the transport is readable, it will alert hyper
// with a `readable` event.
// - hyper needs to translate that `readable` event back into a `write`,
// since that is actually what the Handler wants.
let events = if let Some(blocked) = self.transport.blocked() {
let interest = self.interest();
trace!("translating blocked={:?}, interest={:?}", blocked, interest);
match (blocked, interest) {
(Blocked::Read, Reg::Write) => EventSet::writable(),
(Blocked::Write, Reg::Read) => EventSet::readable(),
// otherwise, the transport was blocked on the same thing the user wanted
_ => events
}
} else {
events
};
if events.is_readable() {
self.on_readable(scope);
}
if events.is_writable() {
self.on_writable(scope);
}
let events = match self.register() {
Reg::Read => EventSet::readable(),
Reg::Write => EventSet::writable(),
Reg::ReadWrite => EventSet::readable() | EventSet::writable(),
Reg::Wait => EventSet::none(),
Reg::Remove => {
trace!("removing transport");
let _ = scope.deregister(&self.transport);
self.on_remove();
return None;
},
};
if events.is_readable() && self.can_read_more() {
return self.ready(events, scope);
}
trace!("scope.reregister({:?})", events);
match scope.reregister(&self.transport, events, PollOpt::level()) {
Ok(..) => {
let timeout = self.state.timeout();
Some((self, timeout))
},
Err(e) => {
error!("error reregistering: {:?}", e);
None
}
}
}
pub fn wakeup<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)>
where F: MessageHandlerFactory<K, T, Output=H> {
loop {
match self.ctrl.1.try_recv() {
Ok(next) => {
trace!("woke up with {:?}", next);
self.state.update(next);
},
Err(_) => break
}
}
self.ready(EventSet::readable() | EventSet::writable(), scope)
}
pub fn timeout<F>(mut self, scope: &mut Scope<F>) -> Option<(Self, Option<Duration>)>
where F: MessageHandlerFactory<K, T, Output=H> {
//TODO: check if this was a spurious timeout?
self.on_error(::Error::Timeout);
self.ready(EventSet::none(), scope)
}
fn on_error(&mut self, err: ::Error) {
debug!("on_error err = {:?}", err);
trace!("on_error state = {:?}", self.state);
let next = match self.state {
State::Init => Next::remove(),
State::Http1(ref mut http1) => http1.handler.on_error(err),
State::Closed => Next::remove(),
};
self.state.update(next);
}
fn on_remove(self) {
debug!("on_remove");
match self.state {
State::Init | State::Closed => (),
State::Http1(http1) => http1.handler.on_remove(self.transport),
}
}
fn on_readable<F>(&mut self, scope: &mut Scope<F>)
where F: MessageHandlerFactory<K, T, Output=H> {
trace!("on_readable -> {:?}", self.state);
let state = mem::replace(&mut self.state, State::Closed);
self.state = self.read(scope, state);
trace!("on_readable <- {:?}", self.state);
}
fn on_writable<F>(&mut self, scope: &mut Scope<F>)
where F: MessageHandlerFactory<K, T, Output=H> {
trace!("on_writable -> {:?}", self.state);
let state = mem::replace(&mut self.state, State::Closed);
self.state = self.write(scope, state);
trace!("on_writable <- {:?}", self.state);
}
}
enum State<H: MessageHandler<T>, T: Transport> {
Init,
/// Http1 will only ever use a connection to send and receive a single
/// message at a time. Once a H1 status has been determined, we will either
/// be reading or writing an H1 message, and optionally multiple if
/// keep-alive is true.
Http1(Http1<H, T>),
/// Http2 allows multiplexing streams over a single connection. So even
/// when we've identified a certain message, we must always parse frame
/// head to determine if the incoming frame is part of a current message,
/// or a new one. This also means we could have multiple messages at once.
//Http2 {},
Closed,
}
impl<H: MessageHandler<T>, T: Transport> State<H, T> {
fn timeout(&self) -> Option<Duration> {
match *self {
State::Init => None,
State::Http1(ref http1) => http1.timeout,
State::Closed => None,
}
}
}
impl<H: MessageHandler<T>, T: Transport> fmt::Debug for State<H, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
State::Init => f.write_str("Init"),
State::Http1(ref h1) => f.debug_tuple("Http1")
.field(h1)
.finish(),
State::Closed => f.write_str("Closed")
}
}
}
impl<H: MessageHandler<T>, T: Transport> State<H, T> {
fn update(&mut self, next: Next) {
let timeout = next.timeout;
let state = mem::replace(self, State::Closed);
let new_state = match (state, next.interest) {
(_, Next_::Remove) => State::Closed,
(State::Closed, _) => State::Closed,
(State::Init, _) => State::Init,
(State::Http1(http1), Next_::End) => {
let reading = match http1.reading {
Reading::Body(ref decoder) if decoder.is_eof() => {
if http1.keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
},
Reading::KeepAlive => http1.reading,
_ => Reading::Closed,
};
let writing = match http1.writing {
Writing::Ready(ref encoder) if encoder.is_eof() => {
if http1.keep_alive {
Writing::KeepAlive
} else {
Writing::Closed
}
},
Writing::Ready(encoder) => {
if encoder.is_eof() {
if http1.keep_alive {
Writing::KeepAlive
} else {
Writing::Closed
}
} else if let Some(buf) = encoder.end() {
Writing::Chunk(Chunk {
buf: buf.bytes,
pos: buf.pos,
next: (h1::Encoder::length(0), Next::end())
})
} else {
Writing::Closed
}
}
Writing::Chunk(mut chunk) => {
if chunk.is_written() {
let encoder = chunk.next.0;
//TODO: de-dupe this code and from Writing::Ready
if encoder.is_eof() {
if http1.keep_alive {
Writing::KeepAlive
} else {
Writing::Closed
}
} else if let Some(buf) = encoder.end() {
Writing::Chunk(Chunk {
buf: buf.bytes,
pos: buf.pos,
next: (h1::Encoder::length(0), Next::end())
})
} else {
Writing::Closed
}
} else {
chunk.next.1 = next;
Writing::Chunk(chunk)
}
},
_ => Writing::Closed,
};
match (reading, writing) {
(Reading::KeepAlive, Writing::KeepAlive) => State::Init,
(reading, Writing::Chunk(chunk)) => {
State::Http1(Http1 {
reading: reading,
writing: Writing::Chunk(chunk),
.. http1
})
}
_ => State::Closed
}
},
(State::Http1(mut http1), Next_::Read) => {
http1.reading = match http1.reading {
Reading::Init => Reading::Parse,
Reading::Wait(decoder) => Reading::Body(decoder),
same => same
};
http1.writing = match http1.writing {
Writing::Ready(encoder) => if encoder.is_eof() {
if http1.keep_alive {
Writing::KeepAlive
} else {
Writing::Closed
}
} else {
Writing::Wait(encoder)
},
Writing::Chunk(chunk) => if chunk.is_written() {
Writing::Wait(chunk.next.0)
} else {
Writing::Chunk(chunk)
},
same => same
};
State::Http1(http1)
},
(State::Http1(mut http1), Next_::Write) => {
http1.writing = match http1.writing {
Writing::Wait(encoder) => Writing::Ready(encoder),
Writing::Init => Writing::Head,
Writing::Chunk(chunk) => if chunk.is_written() {
Writing::Ready(chunk.next.0)
} else {
Writing::Chunk(chunk)
},
same => same
};
http1.reading = match http1.reading {
Reading::Body(decoder) => if decoder.is_eof() {
if http1.keep_alive {
Reading::KeepAlive
} else {
Reading::Closed
}
} else {
Reading::Wait(decoder)
},
same => same
};
State::Http1(http1)
},
(State::Http1(mut http1), Next_::ReadWrite) => {
http1.reading = match http1.reading {
Reading::Init => Reading::Parse,
Reading::Wait(decoder) => Reading::Body(decoder),
same => same
};
http1.writing = match http1.writing {
Writing::Wait(encoder) => Writing::Ready(encoder),
Writing::Init => Writing::Head,
Writing::Chunk(chunk) => if chunk.is_written() {
Writing::Ready(chunk.next.0)
} else {
Writing::Chunk(chunk)
},
same => same
};
State::Http1(http1)
},
(State::Http1(mut http1), Next_::Wait) => {
http1.reading = match http1.reading {
Reading::Body(decoder) => Reading::Wait(decoder),
same => same
};
http1.writing = match http1.writing {
Writing::Ready(encoder) => Writing::Wait(encoder),
Writing::Chunk(chunk) => if chunk.is_written() {
Writing::Wait(chunk.next.0)
} else {
Writing::Chunk(chunk)
},
same => same
};
State::Http1(http1)
}
};
let new_state = match new_state {
State::Http1(mut http1) => {
http1.timeout = timeout;
State::Http1(http1)
}
other => other
};
mem::replace(self, new_state);
}
}
// These Reading and Writing stuff should probably get moved into h1/message.rs
struct Http1<H, T> {
handler: H,
reading: Reading,
writing: Writing,
keep_alive: bool,
timeout: Option<Duration>,
_marker: PhantomData<T>,
}
impl<H, T> fmt::Debug for Http1<H, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Http1")
.field("reading", &self.reading)
.field("writing", &self.writing)
.field("keep_alive", &self.keep_alive)
.field("timeout", &self.timeout)
.finish()
}
}
#[derive(Debug)]
enum Reading {
Init,
Parse,
Body(h1::Decoder),
Wait(h1::Decoder),
KeepAlive,
Closed
}
#[derive(Debug)]
enum Writing {
Init,
Head,
Chunk(Chunk) ,
Ready(h1::Encoder),
Wait(h1::Encoder),
KeepAlive,
Closed
}
#[derive(Debug)]
struct Chunk {
buf: Cow<'static, [u8]>,
pos: usize,
next: (h1::Encoder, Next),
}
impl Chunk {
fn is_written(&self) -> bool {
self.pos >= self.buf.len()
}
}
pub trait MessageHandler<T: Transport> {
type Message: Http1Message;
fn on_incoming(&mut self, head: http::MessageHead<<Self::Message as Http1Message>::Incoming>) -> Next;
fn on_outgoing(&mut self, head: &mut http::MessageHead<<Self::Message as Http1Message>::Outgoing>) -> Next;
fn on_decode(&mut self, &mut http::Decoder<T>) -> Next;
fn on_encode(&mut self, &mut http::Encoder<T>) -> Next;
fn on_error(&mut self, err: ::Error) -> Next;
fn on_remove(self, T) where Self: Sized;
}
pub struct Seed<'a, K: Key + 'a>(&'a K, &'a channel::Sender<Next>);
impl<'a, K: Key + 'a> Seed<'a, K> {
pub fn control(&self) -> Control {
Control {
tx: self.1.clone(),
}
}
pub fn key(&self) -> &K {
&self.0
}
}
pub trait MessageHandlerFactory<K: Key, T: Transport> {
type Output: MessageHandler<T>;
fn create(&mut self, seed: Seed<K>) -> Self::Output;
}
impl<F, K, H, T> MessageHandlerFactory<K, T> for F
where F: FnMut(Seed<K>) -> H,
K: Key,
H: MessageHandler<T>,
T: Transport {
type Output = H;
fn create(&mut self, seed: Seed<K>) -> H {
self(seed)
}
}
pub trait Key: Eq + Hash + Clone {}
impl<T: Eq + Hash + Clone> Key for T {}
#[cfg(test)]
mod tests {
/* TODO:
test when the underlying Transport of a Conn is blocked on an action that
differs from the desired interest().
Ex:
transport.blocked() == Some(Blocked::Read)
self.interest() == Reg::Write
Should call `scope.register(EventSet::read())`, not with write
#[test]
fn test_conn_register_when_transport_blocked() {
}
*/
}

File diff suppressed because it is too large Load Diff

293
src/http/h1/decode.rs Normal file
View File

@@ -0,0 +1,293 @@
use std::cmp;
use std::io::{self, Read};
use self::Kind::{Length, Chunked, Eof};
/// Decoders to handle different Transfer-Encodings.
///
/// If a message body does not include a Transfer-Encoding, it *should*
/// include a Content-Length header.
#[derive(Debug, Clone)]
pub struct Decoder {
kind: Kind,
}
impl Decoder {
pub fn length(x: u64) -> Decoder {
Decoder {
kind: Kind::Length(x)
}
}
pub fn chunked() -> Decoder {
Decoder {
kind: Kind::Chunked(None)
}
}
pub fn eof() -> Decoder {
Decoder {
kind: Kind::Eof(false)
}
}
}
#[derive(Debug, Clone)]
enum Kind {
/// A Reader used when a Content-Length header is passed with a positive integer.
Length(u64),
/// A Reader used when Transfer-Encoding is `chunked`.
Chunked(Option<u64>),
/// A Reader used for responses that don't indicate a length or chunked.
///
/// Note: This should only used for `Response`s. It is illegal for a
/// `Request` to be made with both `Content-Length` and
/// `Transfer-Encoding: chunked` missing, as explained from the spec:
///
/// > If a Transfer-Encoding header field is present in a response and
/// > the chunked transfer coding is not the final encoding, the
/// > message body length is determined by reading the connection until
/// > it is closed by the server. If a Transfer-Encoding header field
/// > is present in a request and the chunked transfer coding is not
/// > the final encoding, the message body length cannot be determined
/// > reliably; the server MUST respond with the 400 (Bad Request)
/// > status code and then close the connection.
Eof(bool),
}
impl Decoder {
pub fn is_eof(&self) -> bool {
trace!("is_eof? {:?}", self);
match self.kind {
Length(0) |
Chunked(Some(0)) |
Eof(true) => true,
_ => false
}
}
}
impl Decoder {
pub fn decode<R: Read>(&mut self, body: &mut R, buf: &mut [u8]) -> io::Result<usize> {
match self.kind {
Length(ref mut remaining) => {
trace!("Sized read, remaining={:?}", remaining);
if *remaining == 0 {
Ok(0)
} else {
let to_read = cmp::min(*remaining as usize, buf.len());
let num = try!(body.read(&mut buf[..to_read])) as u64;
trace!("Length read: {}", num);
if num > *remaining {
*remaining = 0;
} else if num == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "early eof"));
} else {
*remaining -= num;
}
Ok(num as usize)
}
},
Chunked(ref mut opt_remaining) => {
let mut rem = match *opt_remaining {
Some(ref rem) => *rem,
// None means we don't know the size of the next chunk
None => try!(read_chunk_size(body))
};
trace!("Chunked read, remaining={:?}", rem);
if rem == 0 {
*opt_remaining = Some(0);
// chunk of size 0 signals the end of the chunked stream
// if the 0 digit was missing from the stream, it would
// be an InvalidInput error instead.
trace!("end of chunked");
return Ok(0)
}
let to_read = cmp::min(rem as usize, buf.len());
let count = try!(body.read(&mut buf[..to_read])) as u64;
if count == 0 {
*opt_remaining = Some(0);
return Err(io::Error::new(io::ErrorKind::Other, "early eof"));
}
rem -= count;
*opt_remaining = if rem > 0 {
Some(rem)
} else {
try!(eat(body, b"\r\n"));
None
};
Ok(count as usize)
},
Eof(ref mut is_eof) => {
match body.read(buf) {
Ok(0) => {
*is_eof = true;
Ok(0)
}
other => other
}
},
}
}
}
fn eat<R: Read>(rdr: &mut R, bytes: &[u8]) -> io::Result<()> {
let mut buf = [0];
for &b in bytes.iter() {
match try!(rdr.read(&mut buf)) {
1 if buf[0] == b => (),
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput,
"Invalid characters found")),
}
}
Ok(())
}
/// Chunked chunks start with 1*HEXDIGIT, indicating the size of the chunk.
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")),
}
})
);
let mut size = 0u64;
let radix = 16;
let mut in_ext = false;
let mut in_chunk_size = true;
loop {
match byte!(rdr) {
b@b'0'...b'9' if in_chunk_size => {
size *= radix;
size += (b - b'0') as u64;
},
b@b'a'...b'f' if in_chunk_size => {
size *= radix;
size += (b + 10 - b'a') as u64;
},
b@b'A'...b'F' if in_chunk_size => {
size *= radix;
size += (b + 10 - b'A') as u64;
},
b'\r' => {
match byte!(rdr) {
b'\n' => break,
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput,
"Invalid chunk size line"))
}
},
// If we weren't in the extension yet, the ";" signals its start
b';' if !in_ext => {
in_ext = true;
in_chunk_size = false;
},
// "Linear white space" is ignored between the chunk size and the
// extension separator token (";") due to the "implied *LWS rule".
b'\t' | b' ' if !in_ext & !in_chunk_size => {},
// LWS can follow the chunk size, but no more digits can come
b'\t' | b' ' if in_chunk_size => in_chunk_size = false,
// We allow any arbitrary octet once we are in the extension, since
// they all get ignored anyway. According to the HTTP spec, valid
// extensions would have a more strict syntax:
// (token ["=" (token | quoted-string)])
// but we gain nothing by rejecting an otherwise valid chunk size.
_ext if in_ext => {
//TODO: chunk extension byte;
},
// Finally, if we aren't in the extension and we're reading any
// other octet, the chunk size line is invalid!
_ => {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"Invalid chunk size line"));
}
}
}
trace!("chunk size={:?}", size);
Ok(size)
}
#[cfg(test)]
mod tests {
use std::error::Error;
use std::io;
use super::{Decoder, read_chunk_size};
#[test]
fn test_read_chunk_size() {
fn read(s: &str, result: u64) {
assert_eq!(read_chunk_size(&mut s.as_bytes()).unwrap(), 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", 1);
read("01\r\n", 1);
read("0\r\n", 0);
read("00\r\n", 0);
read("A\r\n", 10);
read("a\r\n", 10);
read("Ff\r\n", 255);
read("Ff \r\n", 255);
// Missing LF or CRLF
read_err("F\rF");
read_err("F");
// Invalid hex digit
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", 1);
read("a;ext name=value\r\n", 10);
read("1;extension;extension2\r\n", 1);
read("1;;; ;\r\n", 1);
read("2; extension...\r\n", 2);
read("3 ; extension=123\r\n", 3);
read("3 ;\r\n", 3);
read("3 ; \r\n", 3);
// Invalid extensions cause an error
read_err("1 invalid extension\r\n");
read_err("1 A\r\n");
read_err("1;no CRLF");
}
#[test]
fn test_read_sized_early_eof() {
let mut bytes = &b"foo bar"[..];
let mut decoder = Decoder::length(10);
let mut buf = [0u8; 10];
assert_eq!(decoder.decode(&mut bytes, &mut buf).unwrap(), 7);
let e = decoder.decode(&mut bytes, &mut buf).unwrap_err();
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.description(), "early eof");
}
#[test]
fn test_read_chunked_early_eof() {
let mut bytes = &b"\
9\r\n\
foo bar\
"[..];
let mut decoder = Decoder::chunked();
let mut buf = [0u8; 10];
assert_eq!(decoder.decode(&mut bytes, &mut buf).unwrap(), 7);
let e = decoder.decode(&mut bytes, &mut buf).unwrap_err();
assert_eq!(e.kind(), io::ErrorKind::Other);
assert_eq!(e.description(), "early eof");
}
}

371
src/http/h1/encode.rs Normal file
View File

@@ -0,0 +1,371 @@
use std::borrow::Cow;
use std::cmp;
use std::io::{self, Write};
use http::internal::{AtomicWrite, WriteBuf};
/// Encoders to handle different Transfer-Encodings.
#[derive(Debug, Clone)]
pub struct Encoder {
kind: Kind,
prefix: Prefix, //Option<WriteBuf<Vec<u8>>>
}
#[derive(Debug, PartialEq, Clone)]
enum Kind {
/// An Encoder for when Transfer-Encoding includes `chunked`.
Chunked(Chunked),
/// An Encoder for when Content-Length is set.
///
/// Enforces that the body is not longer than the Content-Length header.
Length(u64),
}
impl Encoder {
pub fn chunked() -> Encoder {
Encoder {
kind: Kind::Chunked(Chunked::Init),
prefix: Prefix(None)
}
}
pub fn length(len: u64) -> Encoder {
Encoder {
kind: Kind::Length(len),
prefix: Prefix(None)
}
}
pub fn prefix(&mut self, prefix: WriteBuf<Vec<u8>>) {
self.prefix.0 = Some(prefix);
}
pub fn is_eof(&self) -> bool {
if self.prefix.0.is_some() {
return false;
}
match self.kind {
Kind::Length(0) |
Kind::Chunked(Chunked::End) => true,
_ => false
}
}
pub fn end(self) -> Option<WriteBuf<Cow<'static, [u8]>>> {
let trailer = self.trailer();
let buf = self.prefix.0;
match (buf, trailer) {
(Some(mut buf), Some(trailer)) => {
buf.bytes.extend_from_slice(trailer);
Some(WriteBuf {
bytes: Cow::Owned(buf.bytes),
pos: buf.pos,
})
},
(Some(buf), None) => Some(WriteBuf {
bytes: Cow::Owned(buf.bytes),
pos: buf.pos
}),
(None, Some(trailer)) => {
Some(WriteBuf {
bytes: Cow::Borrowed(trailer),
pos: 0,
})
},
(None, None) => None
}
}
fn trailer(&self) -> Option<&'static [u8]> {
match self.kind {
Kind::Chunked(Chunked::Init) => {
Some(b"0\r\n\r\n")
}
_ => None
}
}
pub fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> {
match self.kind {
Kind::Chunked(ref mut chunked) => {
chunked.encode(w, &mut self.prefix, msg)
},
Kind::Length(ref mut remaining) => {
let mut n = {
let max = cmp::min(*remaining as usize, msg.len());
let slice = &msg[..max];
let prefix = self.prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b"");
try!(w.write_atomic(&[prefix, slice]))
};
n = self.prefix.update(n);
if n == 0 {
return Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"));
}
*remaining -= n as u64;
Ok(n)
},
}
}
}
#[derive(Debug, PartialEq, Clone)]
enum Chunked {
Init,
Size(ChunkSize),
SizeCr,
SizeLf,
Body(usize),
BodyCr,
BodyLf,
End,
}
impl Chunked {
fn encode<W: AtomicWrite>(&mut self, w: &mut W, prefix: &mut Prefix, msg: &[u8]) -> io::Result<usize> {
match *self {
Chunked::Init => {
let mut size = ChunkSize {
bytes: [0; CHUNK_SIZE_MAX_BYTES],
pos: 0,
len: 0,
};
trace!("chunked write, size = {:?}", msg.len());
write!(&mut size, "{:X}", msg.len())
.expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
*self = Chunked::Size(size);
}
Chunked::End => return Ok(0),
_ => {}
}
let mut n = {
let pieces = match *self {
Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"),
Chunked::Size(ref size) => [
prefix.0.as_ref().map(|buf| &buf.bytes[buf.pos..]).unwrap_or(b""),
&size.bytes[size.pos.into() .. size.len.into()],
&b"\r\n"[..],
msg,
&b"\r\n"[..],
],
Chunked::SizeCr => [
&b""[..],
&b""[..],
&b"\r\n"[..],
msg,
&b"\r\n"[..],
],
Chunked::SizeLf => [
&b""[..],
&b""[..],
&b"\n"[..],
msg,
&b"\r\n"[..],
],
Chunked::Body(pos) => [
&b""[..],
&b""[..],
&b""[..],
&msg[pos..],
&b"\r\n"[..],
],
Chunked::BodyCr => [
&b""[..],
&b""[..],
&b""[..],
&b""[..],
&b"\r\n"[..],
],
Chunked::BodyLf => [
&b""[..],
&b""[..],
&b""[..],
&b""[..],
&b"\n"[..],
],
Chunked::End => unreachable!("Chunked::End shouldn't write more")
};
try!(w.write_atomic(&pieces))
};
if n > 0 {
n = prefix.update(n);
}
while n > 0 {
match *self {
Chunked::Init => unreachable!("Chunked::Init should have become Chunked::Size"),
Chunked::Size(mut size) => {
n = size.update(n);
if size.len == 0 {
*self = Chunked::SizeCr;
} else {
*self = Chunked::Size(size);
}
},
Chunked::SizeCr => {
*self = Chunked::SizeLf;
n -= 1;
}
Chunked::SizeLf => {
*self = Chunked::Body(0);
n -= 1;
}
Chunked::Body(pos) => {
let left = msg.len() - pos;
if n >= left {
*self = Chunked::BodyCr;
n -= left;
} else {
*self = Chunked::Body(pos + n);
n = 0;
}
}
Chunked::BodyCr => {
*self = Chunked::BodyLf;
n -= 1;
}
Chunked::BodyLf => {
assert!(n == 1);
*self = if msg.len() == 0 {
Chunked::End
} else {
Chunked::Init
};
n = 0;
},
Chunked::End => unreachable!("Chunked::End shouldn't have any to write")
}
}
match *self {
Chunked::Init |
Chunked::End => Ok(msg.len()),
_ => Err(io::Error::new(io::ErrorKind::WouldBlock, "chunked incomplete"))
}
}
}
#[cfg(target_pointer_width = "32")]
const USIZE_BYTES: usize = 4;
#[cfg(target_pointer_width = "64")]
const USIZE_BYTES: usize = 8;
// each byte will become 2 hex
const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2;
#[derive(Clone, Copy)]
struct ChunkSize {
bytes: [u8; CHUNK_SIZE_MAX_BYTES],
pos: u8,
len: u8,
}
impl ChunkSize {
fn update(&mut self, n: usize) -> usize {
let diff = (self.len - self.pos).into();
if n >= diff {
self.pos = 0;
self.len = 0;
n - diff
} else {
self.pos += n as u8; // just verified it was a small usize
0
}
}
}
impl ::std::fmt::Debug for ChunkSize {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct("ChunkSize")
.field("bytes", &&self.bytes[..self.len.into()])
.field("pos", &self.pos)
.finish()
}
}
impl ::std::cmp::PartialEq for ChunkSize {
fn eq(&self, other: &ChunkSize) -> bool {
self.len == other.len &&
self.pos == other.pos &&
(&self.bytes[..]) == (&other.bytes[..])
}
}
impl io::Write for ChunkSize {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
let n = (&mut self.bytes[self.len.into() ..]).write(msg)
.expect("&mut [u8].write() cannot error");
self.len += n as u8; // safe because bytes is never bigger than 256
Ok(n)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug, Clone)]
struct Prefix(Option<WriteBuf<Vec<u8>>>);
impl Prefix {
fn update(&mut self, n: usize) -> usize {
if let Some(mut buf) = self.0.take() {
if buf.bytes.len() - buf.pos > n {
buf.pos += n;
self.0 = Some(buf);
0
} else {
let nbuf = buf.bytes.len() - buf.pos;
n - nbuf
}
} else {
n
}
}
}
#[cfg(test)]
mod tests {
use super::Encoder;
use mock::{Async, Buf};
#[test]
fn test_write_chunked_sync() {
let mut dst = Buf::new();
let mut encoder = Encoder::chunked();
encoder.encode(&mut dst, b"foo bar").unwrap();
encoder.encode(&mut dst, b"baz quux herp").unwrap();
encoder.encode(&mut dst, b"").unwrap();
assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]);
}
#[test]
fn test_write_chunked_async() {
let mut dst = Async::new(Buf::new(), 7);
let mut encoder = Encoder::chunked();
assert!(encoder.encode(&mut dst, b"foo bar").is_err());
dst.block_in(6);
assert_eq!(7, encoder.encode(&mut dst, b"foo bar").unwrap());
dst.block_in(30);
assert_eq!(13, encoder.encode(&mut dst, b"baz quux herp").unwrap());
encoder.encode(&mut dst, b"").unwrap();
assert_eq!(&dst[..], &b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n"[..]);
}
#[test]
fn test_write_sized() {
let mut dst = Buf::new();
let mut encoder = Encoder::length(8);
encoder.encode(&mut dst, b"foo bar").unwrap();
assert_eq!(encoder.encode(&mut dst, b"baz").unwrap(), 1);
assert_eq!(dst, b"foo barb");
}
}

136
src/http/h1/mod.rs Normal file
View File

@@ -0,0 +1,136 @@
/*
use std::fmt;
use std::io::{self, Write};
use std::marker::PhantomData;
use std::sync::mpsc;
use url::Url;
use tick;
use time::now_utc;
use header::{self, Headers};
use http::{self, conn};
use method::Method;
use net::{Fresh, Streaming};
use status::StatusCode;
use version::HttpVersion;
*/
pub use self::decode::Decoder;
pub use self::encode::Encoder;
pub use self::parse::parse;
mod decode;
mod encode;
mod parse;
/*
fn should_have_response_body(method: &Method, status: u16) -> bool {
trace!("should_have_response_body({:?}, {})", method, status);
match (method, status) {
(&Method::Head, _) |
(_, 100...199) |
(_, 204) |
(_, 304) |
(&Method::Connect, 200...299) => false,
_ => true
}
}
*/
/*
const MAX_INVALID_RESPONSE_BYTES: usize = 1024 * 128;
impl HttpMessage for Http11Message {
fn get_incoming(&mut self) -> ::Result<ResponseHead> {
unimplemented!();
/*
try!(self.flush_outgoing());
let stream = match self.stream.take() {
Some(stream) => stream,
None => {
// The message was already in the reading state...
// TODO Decide what happens in case we try to get a new incoming at that point
return Err(From::from(
io::Error::new(io::ErrorKind::Other,
"Read already in progress")));
}
};
let expected_no_content = stream.previous_response_expected_no_content();
trace!("previous_response_expected_no_content = {}", expected_no_content);
let mut stream = BufReader::new(stream);
let mut invalid_bytes_read = 0;
let head;
loop {
head = match parse_response(&mut stream) {
Ok(head) => head,
Err(::Error::Version)
if expected_no_content && invalid_bytes_read < MAX_INVALID_RESPONSE_BYTES => {
trace!("expected_no_content, found content");
invalid_bytes_read += 1;
stream.consume(1);
continue;
}
Err(e) => {
self.stream = Some(stream.into_inner());
return Err(e);
}
};
break;
}
let raw_status = head.subject;
let headers = head.headers;
let method = self.method.take().unwrap_or(Method::Get);
let is_empty = !should_have_response_body(&method, raw_status.0);
stream.get_mut().set_previous_response_expected_no_content(is_empty);
// According to https://tools.ietf.org/html/rfc7230#section-3.3.3
// 1. HEAD reponses, and Status 1xx, 204, and 304 cannot have a body.
// 2. Status 2xx to a CONNECT cannot have a body.
// 3. Transfer-Encoding: chunked has a chunked body.
// 4. If multiple differing Content-Length headers or invalid, close connection.
// 5. Content-Length header has a sized body.
// 6. Not Client.
// 7. Read till EOF.
self.reader = Some(if is_empty {
SizedReader(stream, 0)
} else {
if let Some(&TransferEncoding(ref codings)) = headers.get() {
if codings.last() == Some(&Chunked) {
ChunkedReader(stream, None)
} else {
trace!("not chuncked. read till eof");
EofReader(stream)
}
} else if let Some(&ContentLength(len)) = headers.get() {
SizedReader(stream, len)
} else if headers.has::<ContentLength>() {
trace!("illegal Content-Length: {:?}", headers.get_raw("Content-Length"));
return Err(Error::Header);
} else {
trace!("neither Transfer-Encoding nor Content-Length");
EofReader(stream)
}
});
trace!("Http11Message.reader = {:?}", self.reader);
Ok(ResponseHead {
headers: headers,
raw_status: raw_status,
version: head.version,
})
*/
}
}
*/

246
src/http/h1/parse.rs Normal file
View File

@@ -0,0 +1,246 @@
use std::borrow::Cow;
use std::io::Write;
use httparse;
use header::{self, Headers, ContentLength, TransferEncoding};
use http::{MessageHead, RawStatus, Http1Message, ParseResult, Next, ServerMessage, ClientMessage, Next_, RequestLine};
use http::h1::{Encoder, Decoder};
use method::Method;
use status::StatusCode;
use version::HttpVersion::{Http10, Http11};
const MAX_HEADERS: usize = 100;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
pub fn parse<T: Http1Message<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> {
if buf.len() == 0 {
return Ok(None);
}
trace!("parse({:?})", buf);
<T as Http1Message>::parse(buf)
}
impl Http1Message for ServerMessage {
type Incoming = RequestLine;
type Outgoing = StatusCode;
fn initial_interest() -> Next {
Next::new(Next_::Read)
}
fn parse(buf: &[u8]) -> ParseResult<RequestLine> {
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut req = httparse::Request::new(&mut headers);
Ok(match try!(req.parse(buf)) {
httparse::Status::Complete(len) => {
trace!("Request.parse Complete({})", len);
Some((MessageHead {
version: if req.version.unwrap() == 1 { Http11 } else { Http10 },
subject: RequestLine(
try!(req.method.unwrap().parse()),
try!(req.path.unwrap().parse())
),
headers: try!(Headers::from_raw(req.headers))
}, len))
},
httparse::Status::Partial => None
})
}
fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<Decoder> {
use ::header;
if let Some(&header::ContentLength(len)) = head.headers.get() {
Ok(Decoder::length(len))
} else if head.headers.has::<header::TransferEncoding>() {
//TODO: check for Transfer-Encoding: chunked
Ok(Decoder::chunked())
} else {
Ok(Decoder::length(0))
}
}
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
use ::header;
trace!("writing head: {:?}", head);
if !head.headers.has::<header::Date>() {
head.headers.set(header::Date(header::HttpDate(::time::now_utc())));
}
let mut is_chunked = true;
let mut body = Encoder::chunked();
if let Some(cl) = head.headers.get::<header::ContentLength>() {
body = Encoder::length(**cl);
is_chunked = false
}
if is_chunked {
let encodings = match head.headers.get_mut::<header::TransferEncoding>() {
Some(&mut header::TransferEncoding(ref mut encodings)) => {
if encodings.last() != Some(&header::Encoding::Chunked) {
encodings.push(header::Encoding::Chunked);
}
false
},
None => true
};
if encodings {
head.headers.set(header::TransferEncoding(vec![header::Encoding::Chunked]));
}
}
let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE;
dst.reserve(init_cap);
debug!("writing {:#?}", head.headers);
let _ = write!(dst, "{} {}\r\n{}\r\n", head.version, head.subject, head.headers);
body
}
}
impl Http1Message for ClientMessage {
type Incoming = RawStatus;
type Outgoing = RequestLine;
fn initial_interest() -> Next {
Next::new(Next_::Write)
}
fn parse(buf: &[u8]) -> ParseResult<RawStatus> {
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
trace!("Response.parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut res = httparse::Response::new(&mut headers);
Ok(match try!(res.parse(buf)) {
httparse::Status::Complete(len) => {
trace!("Response.try_parse Complete({})", len);
let code = res.code.unwrap();
let reason = match StatusCode::from_u16(code).canonical_reason() {
Some(reason) if reason == res.reason.unwrap() => Cow::Borrowed(reason),
_ => Cow::Owned(res.reason.unwrap().to_owned())
};
Some((MessageHead {
version: if res.version.unwrap() == 1 { Http11 } else { Http10 },
subject: RawStatus(code, reason),
headers: try!(Headers::from_raw(res.headers))
}, len))
},
httparse::Status::Partial => None
})
}
fn decoder(inc: &MessageHead<Self::Incoming>) -> ::Result<Decoder> {
use ::header;
// According to https://tools.ietf.org/html/rfc7230#section-3.3.3
// 1. HEAD reponses, and Status 1xx, 204, and 304 cannot have a body.
// 2. Status 2xx to a CONNECT cannot have a body.
//
// First two steps taken care of before this method.
//
// 3. Transfer-Encoding: chunked has a chunked body.
// 4. If multiple differing Content-Length headers or invalid, close connection.
// 5. Content-Length header has a sized body.
// 6. Not Client.
// 7. Read till EOF.
if let Some(&header::TransferEncoding(ref codings)) = inc.headers.get() {
if codings.last() == Some(&header::Encoding::Chunked) {
Ok(Decoder::chunked())
} else {
trace!("not chuncked. read till eof");
Ok(Decoder::eof())
}
} else if let Some(&header::ContentLength(len)) = inc.headers.get() {
Ok(Decoder::length(len))
} else if inc.headers.has::<header::ContentLength>() {
trace!("illegal Content-Length: {:?}", inc.headers.get_raw("Content-Length"));
Err(::Error::Header)
} else {
trace!("neither Transfer-Encoding nor Content-Length");
Ok(Decoder::eof())
}
}
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
trace!("writing head: {:?}", head);
let mut body = Encoder::length(0);
let expects_no_body = match head.subject.0 {
Method::Head | Method::Get | Method::Connect => true,
_ => false
};
let mut chunked = false;
if let Some(con_len) = head.headers.get::<ContentLength>() {
body = Encoder::length(**con_len);
} else {
chunked = !expects_no_body;
}
if chunked {
body = Encoder::chunked();
let encodings = match head.headers.get_mut::<TransferEncoding>() {
Some(encodings) => {
//TODO: check if Chunked already exists
encodings.push(header::Encoding::Chunked);
true
},
None => false
};
if !encodings {
head.headers.set(TransferEncoding(vec![header::Encoding::Chunked]));
}
}
let init_cap = 30 + head.headers.len() * AVERAGE_HEADER_SIZE;
dst.reserve(init_cap);
debug!("writing {:#?}", head.headers);
let _ = write!(dst, "{} {}\r\n{}\r\n", head.subject, head.version, head.headers);
body
}
}
#[cfg(test)]
mod tests {
use http;
use super::{parse};
#[test]
fn test_parse_request() {
let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n";
parse::<http::ServerMessage, _>(raw).unwrap();
}
#[test]
fn test_parse_raw_status() {
let raw = b"HTTP/1.1 200 OK\r\n\r\n";
let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap();
assert_eq!(res.subject.1, "OK");
let raw = b"HTTP/1.1 200 Howdy\r\n\r\n";
let (res, _) = parse::<http::ClientMessage, _>(raw).unwrap().unwrap();
assert_eq!(res.subject.1, "Howdy");
}
#[cfg(feature = "nightly")]
use test::Bencher;
#[cfg(feature = "nightly")]
#[bench]
fn bench_parse_incoming(b: &mut Bencher) {
let raw = b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n";
b.iter(|| {
parse::<http::ServerMessage, _>(raw).unwrap()
});
}
}

View File

@@ -1,133 +0,0 @@
//! Defines the `HttpMessage` trait that serves to encapsulate the operations of a single
//! request-response cycle on any HTTP connection.
use std::any::{Any, TypeId};
use std::fmt::Debug;
use std::io::{Read, Write};
use std::mem;
use std::io;
use std::time::Duration;
use typeable::Typeable;
use header::Headers;
use http::RawStatus;
use url::Url;
use method;
use version;
use traitobject;
/// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP
/// protocol.
pub trait Protocol {
/// Creates a fresh `HttpMessage` bound to the given host, based on the given protocol scheme.
fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>>;
}
/// Describes a request.
#[derive(Clone, Debug)]
pub struct RequestHead {
/// The headers of the request
pub headers: Headers,
/// The method of the request
pub method: method::Method,
/// The URL of the request
pub url: Url,
}
/// Describes a response.
#[derive(Clone, Debug)]
pub struct ResponseHead {
/// The headers of the reponse
pub headers: Headers,
/// The raw status line of the response
pub raw_status: RawStatus,
/// The HTTP/2 version which generated the response
pub version: version::HttpVersion,
}
/// The trait provides an API for sending an receiving HTTP messages.
pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug {
/// Initiates a new outgoing request.
///
/// Only the request's head is provided (in terms of the `RequestHead` struct).
///
/// After this, the `HttpMessage` instance can be used as an `io::Write` in order to write the
/// body of the request.
fn set_outgoing(&mut self, head: RequestHead) -> ::Result<RequestHead>;
/// Obtains the incoming response and returns its head (i.e. the `ResponseHead` struct)
///
/// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out
/// the response body.
fn get_incoming(&mut self) -> ::Result<ResponseHead>;
/// Set the read timeout duration for this message.
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
/// Set the write timeout duration for this message.
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
/// Closes the underlying HTTP connection.
fn close_connection(&mut self) -> ::Result<()>;
/// Returns whether the incoming message has a body.
fn has_body(&self) -> bool;
/// Called when the Client wishes to use a Proxy.
fn set_proxied(&mut self, val: bool) {
// default implementation so as to not be a breaking change.
warn!("default set_proxied({:?})", val);
}
}
impl HttpMessage {
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
mem::transmute(traitobject::data(self))
}
unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
mem::transmute(traitobject::data_mut(self))
}
unsafe fn downcast_unchecked<T: 'static>(self: Box<HttpMessage>) -> Box<T> {
let raw: *mut HttpMessage = mem::transmute(self);
mem::transmute(traitobject::data_mut(raw))
}
}
impl HttpMessage {
/// Is the underlying type in this trait object a T?
#[inline]
pub fn is<T: Any>(&self) -> bool {
(*self).get_type() == TypeId::of::<T>()
}
/// If the underlying type is T, get a reference to the contained data.
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { self.downcast_ref_unchecked() })
} else {
None
}
}
/// If the underlying type is T, get a mutable reference to the contained
/// data.
#[inline]
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
Some(unsafe { self.downcast_mut_unchecked() })
} else {
None
}
}
/// If the underlying type is T, extract it.
#[inline]
pub fn downcast<T: Any>(self: Box<HttpMessage>)
-> Result<Box<T>, Box<HttpMessage>> {
if self.is::<T>() {
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(self)
}
}
}

View File

@@ -1,25 +1,196 @@
//! Pieces pertaining to the HTTP message protocol.
use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::time::Duration;
use header::Connection;
use header::ConnectionOption::{KeepAlive, Close};
use header::Headers;
use method::Method;
use net::Transport;
use status::StatusCode;
use uri::RequestUri;
use version::HttpVersion;
use version::HttpVersion::{Http10, Http11};
#[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use self::message::{HttpMessage, RequestHead, ResponseHead, Protocol};
pub use self::conn::{Conn, MessageHandler, MessageHandlerFactory, Seed, Key};
pub mod h1;
pub mod h2;
pub mod message;
mod buffer;
pub mod channel;
mod conn;
mod h1;
//mod h2;
/// Wraps a `Transport` to provide HTTP decoding when reading.
#[derive(Debug)]
pub struct Decoder<'a, T: Read + 'a>(DecoderImpl<'a, T>);
/// Wraps a `Transport` to provide HTTP encoding when writing.
#[derive(Debug)]
pub struct Encoder<'a, T: Transport + 'a>(EncoderImpl<'a, T>);
#[derive(Debug)]
enum DecoderImpl<'a, T: Read + 'a> {
H1(&'a mut h1::Decoder, Trans<'a, T>),
}
#[derive(Debug)]
enum Trans<'a, T: Read + 'a> {
Port(&'a mut T),
Buf(self::buffer::BufReader<'a, T>)
}
impl<'a, T: Read + 'a> Read for Trans<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Trans::Port(ref mut t) => t.read(buf),
Trans::Buf(ref mut b) => b.read(buf)
}
}
}
#[derive(Debug)]
enum EncoderImpl<'a, T: Transport + 'a> {
H1(&'a mut h1::Encoder, &'a mut T),
}
impl<'a, T: Read> Decoder<'a, T> {
fn h1(decoder: &'a mut h1::Decoder, transport: Trans<'a, T>) -> Decoder<'a, T> {
Decoder(DecoderImpl::H1(decoder, transport))
}
}
impl<'a, T: Transport> Encoder<'a, T> {
fn h1(encoder: &'a mut h1::Encoder, transport: &'a mut T) -> Encoder<'a, T> {
Encoder(EncoderImpl::H1(encoder, transport))
}
}
impl<'a, T: Read> Read for Decoder<'a, T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.0 {
DecoderImpl::H1(ref mut decoder, ref mut transport) => {
decoder.decode(transport, buf)
}
}
}
}
impl<'a, T: Transport> Write for Encoder<'a, T> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
if data.is_empty() {
return Ok(0);
}
match self.0 {
EncoderImpl::H1(ref mut encoder, ref mut transport) => {
encoder.encode(*transport, data)
}
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match self.0 {
EncoderImpl::H1(_, ref mut transport) => {
transport.flush()
}
}
}
}
/// Because privacy rules. Reasons.
/// https://github.com/rust-lang/rust/issues/30905
mod internal {
use std::io::{self, Write};
#[derive(Debug, Clone)]
pub struct WriteBuf<T: AsRef<[u8]>> {
pub bytes: T,
pub pos: usize,
}
pub trait AtomicWrite {
fn write_atomic(&mut self, data: &[&[u8]]) -> io::Result<usize>;
}
#[cfg(not(windows))]
impl<T: Write + ::vecio::Writev> AtomicWrite for T {
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
self.writev(bufs)
}
}
#[cfg(windows)]
impl<T: Write> AtomicWrite for T {
fn write_atomic(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let vec = bufs.concat();
self.write(&vec)
}
}
}
/// An Incoming Message head. Includes request/status line, and headers.
#[derive(Debug, Default)]
pub struct MessageHead<S> {
/// HTTP version of the message.
pub version: HttpVersion,
/// Subject (request line or status line) of Incoming message.
pub subject: S,
/// Headers of the Incoming message.
pub headers: Headers
}
/// An incoming request message.
pub type RequestHead = MessageHead<RequestLine>;
#[derive(Debug, Default)]
pub struct RequestLine(pub Method, pub RequestUri);
impl fmt::Display for RequestLine {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.0, self.1)
}
}
/// An incoming response message.
pub type ResponseHead = MessageHead<RawStatus>;
impl<S> MessageHead<S> {
pub fn should_keep_alive(&self) -> bool {
should_keep_alive(self.version, &self.headers)
}
}
/// The raw status code and reason-phrase.
#[derive(Clone, PartialEq, Debug)]
pub struct RawStatus(pub u16, pub Cow<'static, str>);
impl fmt::Display for RawStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.0, self.1)
}
}
impl From<StatusCode> for RawStatus {
fn from(status: StatusCode) -> RawStatus {
RawStatus(status.to_u16(), Cow::Borrowed(status.canonical_reason().unwrap_or("")))
}
}
impl Default for RawStatus {
fn default() -> RawStatus {
RawStatus(200, Cow::Borrowed("OK"))
}
}
#[cfg(feature = "serde-serialization")]
impl Serialize for RawStatus {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
@@ -46,6 +217,158 @@ pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool {
_ => true
}
}
pub type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>;
pub fn parse<T: Http1Message<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> {
h1::parse::<T, I>(rdr)
}
// These 2 enums are not actually dead_code. They are used in the server and
// and client modules, respectively. However, their being used as associated
// types doesn't mark them as used, so the dead_code linter complains.
#[allow(dead_code)]
#[derive(Debug)]
pub enum ServerMessage {}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ClientMessage {}
pub trait Http1Message {
type Incoming;
type Outgoing: Default;
//TODO: replace with associated const when stable
fn initial_interest() -> Next;
fn parse(bytes: &[u8]) -> ParseResult<Self::Incoming>;
fn decoder(head: &MessageHead<Self::Incoming>) -> ::Result<h1::Decoder>;
fn encode(head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder;
}
/// Used to signal desired events when working with asynchronous IO.
#[must_use]
#[derive(Clone)]
pub struct Next {
interest: Next_,
timeout: Option<Duration>,
}
impl fmt::Debug for Next {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "Next::{:?}", &self.interest));
match self.timeout {
Some(ref d) => write!(f, "({:?})", d),
None => Ok(())
}
}
}
#[derive(Debug, Clone, Copy)]
enum Next_ {
Read,
Write,
ReadWrite,
Wait,
End,
Remove,
}
#[derive(Debug, Clone, Copy)]
enum Reg {
Read,
Write,
ReadWrite,
Wait,
Remove
}
/// A notifier to wakeup a socket after having used `Next::wait()`
#[derive(Debug, Clone)]
pub struct Control {
tx: self::channel::Sender<Next>,
}
impl Control {
/// Wakeup a waiting socket to listen for a certain event.
pub fn ready(&self, next: Next) -> Result<(), ControlError> {
//TODO: assert!( next.interest != Next_::Wait ) ?
self.tx.send(next).map_err(|_| ControlError(()))
}
}
/// An error occured trying to tell a Control it is ready.
#[derive(Debug)]
pub struct ControlError(());
impl ::std::error::Error for ControlError {
fn description(&self) -> &str {
"Cannot wakeup event loop: loop is closed"
}
}
impl fmt::Display for ControlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(::std::error::Error::description(self))
}
}
impl Next {
fn new(interest: Next_) -> Next {
Next {
interest: interest,
timeout: None,
}
}
fn interest(&self) -> Reg {
match self.interest {
Next_::Read => Reg::Read,
Next_::Write => Reg::Write,
Next_::ReadWrite => Reg::ReadWrite,
Next_::Wait => Reg::Wait,
Next_::End => Reg::Remove,
Next_::Remove => Reg::Remove,
}
}
/// Signals the desire to read from the transport.
pub fn read() -> Next {
Next::new(Next_::Read)
}
/// Signals the desire to write to the transport.
pub fn write() -> Next {
Next::new(Next_::Write)
}
/// Signals the desire to read and write to the transport.
pub fn read_and_write() -> Next {
Next::new(Next_::ReadWrite)
}
/// Signals the desire to end the current HTTP message.
pub fn end() -> Next {
Next::new(Next_::End)
}
/// Signals the desire to abruptly remove the current transport from the
/// event loop.
pub fn remove() -> Next {
Next::new(Next_::Remove)
}
/// Signals the desire to wait until some future time before acting again.
pub fn wait() -> Next {
Next::new(Next_::Wait)
}
/// Signals a maximum duration to be waited for the desired event.
pub fn timeout(mut self, dur: Duration) -> Next {
self.timeout = Some(dur);
self
}
}
#[test]
fn test_should_keep_alive() {

View File

@@ -1,6 +1,7 @@
#![doc(html_root_url = "https://hyperium.github.io/hyper/")]
#![cfg_attr(test, deny(missing_docs))]
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(missing_debug_implementations)]
#![cfg_attr(all(test, feature = "nightly"), feature(test))]
//! # Hyper
@@ -9,125 +10,9 @@
//! is a low-level typesafe abstraction over raw HTTP, providing an elegant
//! layer over "stringly-typed" HTTP.
//!
//! Hyper offers both a [Client](client/index.html) and a
//! [Server](server/index.html) which can be used to drive complex web
//! applications written entirely in Rust.
//!
//! ## Internal Design
//!
//! Hyper is designed as a relatively low-level wrapper over raw HTTP. It should
//! allow the implementation of higher-level abstractions with as little pain as
//! possible, and should not irrevocably hide any information from its users.
//!
//! ### Common Functionality
//!
//! Functionality and code shared between the Server and Client implementations
//! can be found in `src` directly - this includes `NetworkStream`s, `Method`s,
//! `StatusCode`, and so on.
//!
//! #### Methods
//!
//! Methods are represented as a single `enum` to remain as simple as possible.
//! Extension Methods are represented as raw `String`s. A method's safety and
//! idempotence can be accessed using the `safe` and `idempotent` methods.
//!
//! #### StatusCode
//!
//! Status codes are also represented as a single, exhaustive, `enum`. This
//! representation is efficient, typesafe, and ergonomic as it allows the use of
//! `match` to disambiguate known status codes.
//!
//! #### Headers
//!
//! Hyper's [header](header/index.html) representation is likely the most
//! complex API exposed by Hyper.
//!
//! Hyper's headers are an abstraction over an internal `HashMap` and provides a
//! typesafe API for interacting with headers that does not rely on the use of
//! "string-typing."
//!
//! Each HTTP header in Hyper has an associated type and implementation of the
//! `Header` trait, which defines an HTTP headers name as a string, how to parse
//! that header, and how to format that header.
//!
//! Headers are then parsed from the string representation lazily when the typed
//! representation of a header is requested and formatted back into their string
//! representation when headers are written back to the client.
//!
//! #### NetworkStream and NetworkAcceptor
//!
//! These are found in `src/net.rs` and define the interface that acceptors and
//! streams must fulfill for them to be used within Hyper. They are by and large
//! internal tools and you should only need to mess around with them if you want to
//! mock or replace `TcpStream` and `TcpAcceptor`.
//!
//! ### Server
//!
//! Server-specific functionality, such as `Request` and `Response`
//! representations, are found in in `src/server`.
//!
//! #### Handler + Server
//!
//! A `Handler` in Hyper accepts a `Request` and `Response`. This is where
//! user-code can handle each connection. The server accepts connections in a
//! task pool with a customizable number of threads, and passes the Request /
//! Response to the handler.
//!
//! #### Request
//!
//! An incoming HTTP Request is represented as a struct containing
//! a `Reader` over a `NetworkStream`, which represents the body, headers, a remote
//! address, an HTTP version, and a `Method` - relatively standard stuff.
//!
//! `Request` implements `Reader` itself, meaning that you can ergonomically get
//! the body out of a `Request` using standard `Reader` methods and helpers.
//!
//! #### Response
//!
//! An outgoing HTTP Response is also represented as a struct containing a `Writer`
//! over a `NetworkStream` which represents the Response body in addition to
//! standard items such as the `StatusCode` and HTTP version. `Response`'s `Writer`
//! implementation provides a streaming interface for sending data over to the
//! client.
//!
//! One of the traditional problems with representing outgoing HTTP Responses is
//! tracking the write-status of the Response - have we written the status-line,
//! the headers, the body, etc.? Hyper tracks this information statically using the
//! type system and prevents you, using the type system, from writing headers after
//! you have started writing to the body or vice versa.
//!
//! Hyper does this through a phantom type parameter in the definition of Response,
//! which tracks whether you are allowed to write to the headers or the body. This
//! phantom type can have two values `Fresh` or `Streaming`, with `Fresh`
//! indicating that you can write the headers and `Streaming` indicating that you
//! may write to the body, but not the headers.
//!
//! ### Client
//!
//! Client-specific functionality, such as `Request` and `Response`
//! representations, are found in `src/client`.
//!
//! #### Request
//!
//! An outgoing HTTP Request is represented as a struct containing a `Writer` over
//! a `NetworkStream` which represents the Request body in addition to the standard
//! information such as headers and the request method.
//!
//! Outgoing Requests track their write-status in almost exactly the same way as
//! outgoing HTTP Responses do on the Server, so we will defer to the explanation
//! in the documentation for server Response.
//!
//! Requests expose an efficient streaming interface instead of a builder pattern,
//! but they also provide the needed interface for creating a builder pattern over
//! the API exposed by core Hyper.
//!
//! #### Response
//!
//! Incoming HTTP Responses are represented as a struct containing a `Reader` over
//! a `NetworkStream` and contain headers, a status, and an http version. They
//! implement `Reader` and can be read to get the data out of a `Response`.
//!
//! Hyper provides both a [Client](client/index.html) and a
//! [Server](server/index.html), along with a
//! [typed Headers system](header/index.html).
extern crate rustc_serialize as serialize;
extern crate time;
#[macro_use] extern crate url;
@@ -142,10 +27,11 @@ extern crate serde;
extern crate cookie;
extern crate unicase;
extern crate httparse;
extern crate num_cpus;
extern crate rotor;
extern crate spmc;
extern crate traitobject;
extern crate typeable;
extern crate solicit;
extern crate vecio;
#[macro_use]
extern crate language_tags;
@@ -163,35 +49,31 @@ extern crate test;
pub use url::Url;
pub use client::Client;
pub use error::{Result, Error};
pub use method::Method::{Get, Head, Post, Delete};
pub use status::StatusCode::{Ok, BadRequest, NotFound};
pub use http::{Next, Encoder, Decoder, Control};
pub use header::Headers;
pub use method::Method::{self, Get, Head, Post, Delete};
pub use status::StatusCode::{self, Ok, BadRequest, NotFound};
pub use server::Server;
pub use uri::RequestUri;
pub use version::HttpVersion;
pub use language_tags::LanguageTag;
macro_rules! todo(
($($arg:tt)*) => (if cfg!(not(ndebug)) {
trace!("TODO: {:?}", format_args!($($arg)*))
})
);
macro_rules! inspect(
($name:expr, $value:expr) => ({
let v = $value;
trace!("inspect: {:?} = {:?}", $name, v);
v
})
);
macro_rules! rotor_try {
($e:expr) => ({
match $e {
Ok(v) => v,
Err(e) => return ::rotor::Response::error(e.into())
}
});
}
#[cfg(test)]
#[macro_use]
mod mock;
#[doc(hidden)]
pub mod buffer;
pub mod client;
pub mod error;
pub mod method;
pub mod header;
pub mod http;
mod http;
pub mod net;
pub mod server;
pub mod status;
@@ -203,6 +85,7 @@ pub mod mime {
pub use mime_crate::*;
}
/*
#[allow(unconditional_recursion)]
fn _assert_send<T: Send>() {
_assert_send::<Client>();
@@ -216,3 +99,4 @@ fn _assert_sync<T: Sync>() {
_assert_sync::<Client>();
_assert_sync::<error::Error>();
}
*/

View File

@@ -128,6 +128,12 @@ impl fmt::Display for Method {
}
}
impl Default for Method {
fn default() -> Method {
Method::Get
}
}
#[cfg(feature = "serde-serialization")]
impl Serialize for Method {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {

View File

@@ -1,87 +1,37 @@
use std::ascii::AsciiExt;
use std::io::{self, Read, Write, Cursor};
use std::cell::RefCell;
use std::net::{SocketAddr, Shutdown};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::cell::Cell;
use std::cmp;
use std::io::{self, Read, Write};
use solicit::http::HttpScheme;
use solicit::http::transport::TransportStream;
use solicit::http::frame::{SettingsFrame, Frame};
use solicit::http::connection::{HttpConnection, EndStream, DataChunk};
use header::Headers;
use net::{NetworkStream, NetworkConnector, SslClient};
#[derive(Clone, Debug)]
pub struct MockStream {
pub read: Cursor<Vec<u8>>,
next_reads: Vec<Vec<u8>>,
pub write: Vec<u8>,
pub is_closed: bool,
pub error_on_write: bool,
pub error_on_read: bool,
pub read_timeout: Cell<Option<Duration>>,
pub write_timeout: Cell<Option<Duration>>,
#[derive(Debug)]
pub struct Buf {
vec: Vec<u8>
}
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::with_input(b"")
}
pub fn with_input(input: &[u8]) -> MockStream {
MockStream::with_responses(vec![input])
}
pub fn with_responses(mut responses: Vec<&[u8]>) -> MockStream {
MockStream {
read: Cursor::new(responses.remove(0).to_vec()),
next_reads: responses.into_iter().map(|arr| arr.to_vec()).collect(),
write: vec![],
is_closed: false,
error_on_write: false,
error_on_read: false,
read_timeout: Cell::new(None),
write_timeout: Cell::new(None),
impl Buf {
pub fn new() -> Buf {
Buf {
vec: vec![]
}
}
}
impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.error_on_read {
Err(io::Error::new(io::ErrorKind::Other, "mock error"))
} else {
match self.read.read(buf) {
Ok(n) => {
if self.read.position() as usize == self.read.get_ref().len() {
if self.next_reads.len() > 0 {
self.read = Cursor::new(self.next_reads.remove(0));
}
}
Ok(n)
},
r => r
}
}
impl ::std::ops::Deref for Buf {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.vec
}
}
impl Write for MockStream {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
if self.error_on_write {
Err(io::Error::new(io::ErrorKind::Other, "mock error"))
} else {
Write::write(&mut self.write, msg)
}
impl<S: AsRef<[u8]>> PartialEq<S> for Buf {
fn eq(&self, other: &S) -> bool {
self.vec == other.as_ref()
}
}
impl Write for Buf {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.vec.extend(data);
Ok(data.len())
}
fn flush(&mut self) -> io::Result<()> {
@@ -89,239 +39,89 @@ impl Write for MockStream {
}
}
impl NetworkStream for MockStream {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:1337".parse().unwrap())
}
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.read_timeout.set(dur);
Ok(())
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.write_timeout.set(dur);
Ok(())
}
fn close(&mut self, _how: Shutdown) -> io::Result<()> {
self.is_closed = true;
Ok(())
impl Read for Buf {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&*self.vec).read(buf)
}
}
/// A wrapper around a `MockStream` that allows one to clone it and keep an independent copy to the
/// same underlying stream.
#[derive(Clone)]
pub struct CloneableMockStream {
pub inner: Arc<Mutex<MockStream>>,
impl ::vecio::Writev for Buf {
fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next);
let mut vec = Vec::with_capacity(cap);
for &buf in bufs {
vec.extend(buf);
}
self.write(&vec)
}
}
impl Write for CloneableMockStream {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
self.inner.lock().unwrap().write(msg)
#[derive(Debug)]
pub struct Async<T> {
inner: T,
bytes_until_block: usize,
}
impl<T> Async<T> {
pub fn new(inner: T, bytes: usize) -> Async<T> {
Async {
inner: inner,
bytes_until_block: bytes
}
}
pub fn block_in(&mut self, bytes: usize) {
self.bytes_until_block = bytes;
}
}
impl<T: Read> Read for Async<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.bytes_until_block == 0 {
Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block"))
} else {
let n = cmp::min(self.bytes_until_block, buf.len());
let n = try!(self.inner.read(&mut buf[..n]));
self.bytes_until_block -= n;
Ok(n)
}
}
}
impl<T: Write> Write for Async<T> {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
if self.bytes_until_block == 0 {
Err(io::Error::new(io::ErrorKind::WouldBlock, "mock block"))
} else {
let n = cmp::min(self.bytes_until_block, data.len());
let n = try!(self.inner.write(&data[..n]));
self.bytes_until_block -= n;
Ok(n)
}
}
fn flush(&mut self) -> io::Result<()> {
self.inner.lock().unwrap().flush()
self.inner.flush()
}
}
impl Read for CloneableMockStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.lock().unwrap().read(buf)
}
}
impl TransportStream for CloneableMockStream {
fn try_split(&self) -> Result<CloneableMockStream, io::Error> {
Ok(self.clone())
}
fn close(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
impl NetworkStream for CloneableMockStream {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
self.inner.lock().unwrap().peer_addr()
}
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.lock().unwrap().set_read_timeout(dur)
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.lock().unwrap().set_write_timeout(dur)
}
fn close(&mut self, how: Shutdown) -> io::Result<()> {
NetworkStream::close(&mut *self.inner.lock().unwrap(), how)
}
}
impl CloneableMockStream {
pub fn with_stream(stream: MockStream) -> CloneableMockStream {
CloneableMockStream {
inner: Arc::new(Mutex::new(stream)),
}
}
}
pub struct MockConnector;
impl NetworkConnector for MockConnector {
type Stream = MockStream;
fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> ::Result<MockStream> {
Ok(MockStream::new())
}
}
/// new connectors must be created if you wish to intercept requests.
macro_rules! mock_connector (
($name:ident {
$($url:expr => $res:expr)*
}) => (
struct $name;
impl $crate::net::NetworkConnector for $name {
type Stream = ::mock::MockStream;
fn connect(&self, host: &str, port: u16, scheme: &str)
-> $crate::Result<::mock::MockStream> {
use std::collections::HashMap;
debug!("MockStream::connect({:?}, {:?}, {:?})", host, port, scheme);
let mut map = HashMap::new();
$(map.insert($url, $res);)*
let key = format!("{}://{}", scheme, host);
// ignore port for now
match map.get(&*key) {
Some(&res) => Ok($crate::mock::MockStream::with_input(res.as_bytes())),
None => panic!("{:?} doesn't know url {}", stringify!($name), key)
}
}
impl<T: Write> ::vecio::Writev for Async<T> {
fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next);
let mut vec = Vec::with_capacity(cap);
for &buf in bufs {
vec.extend(buf);
}
);
($name:ident { $($response:expr),+ }) => (
struct $name;
impl $crate::net::NetworkConnector for $name {
type Stream = $crate::mock::MockStream;
fn connect(&self, _: &str, _: u16, _: &str)
-> $crate::Result<$crate::mock::MockStream> {
Ok($crate::mock::MockStream::with_responses(vec![
$($response),+
]))
}
}
);
);
impl TransportStream for MockStream {
fn try_split(&self) -> Result<MockStream, io::Error> {
Ok(self.clone())
}
fn close(&mut self) -> Result<(), io::Error> {
Ok(())
self.write(&vec)
}
}
impl MockStream {
/// Creates a new `MockStream` that will return the response described by the parameters as an
/// HTTP/2 response. This will also include the correct server preface.
pub fn new_http2_response(status: &[u8], headers: &Headers, body: Option<Vec<u8>>)
-> MockStream {
let resp_bytes = build_http2_response(status, headers, body);
MockStream::with_input(&resp_bytes)
}
}
/// Builds up a sequence of bytes that represent a server's response based on the given parameters.
pub fn build_http2_response(status: &[u8], headers: &Headers, body: Option<Vec<u8>>) -> Vec<u8> {
let mut conn = HttpConnection::new(MockStream::new(), MockStream::new(), HttpScheme::Http);
// Server preface first
conn.sender.write(&SettingsFrame::new().serialize()).unwrap();
let mut resp_headers: Vec<_> = headers.iter().map(|h| {
(h.name().to_ascii_lowercase().into_bytes(), h.value_string().into_bytes())
}).collect();
resp_headers.insert(0, (b":status".to_vec(), status.into()));
let end = if body.is_none() {
EndStream::Yes
} else {
EndStream::No
};
conn.send_headers(resp_headers, 1, end).unwrap();
if body.is_some() {
let chunk = DataChunk::new_borrowed(&body.as_ref().unwrap()[..], 1, EndStream::Yes);
conn.send_data(chunk).unwrap();
}
conn.sender.write
}
/// A mock connector that produces `MockStream`s that are set to return HTTP/2 responses.
///
/// This means that the streams' payloads are fairly opaque byte sequences (as HTTP/2 is a binary
/// protocol), which can be understood only be HTTP/2 clients.
pub struct MockHttp2Connector {
/// The list of streams that the connector returns, in the given order.
pub streams: RefCell<Vec<CloneableMockStream>>,
}
impl MockHttp2Connector {
/// Creates a new `MockHttp2Connector` with no streams.
pub fn new() -> MockHttp2Connector {
MockHttp2Connector {
streams: RefCell::new(Vec::new()),
}
}
/// Adds a new `CloneableMockStream` to the end of the connector's stream queue.
///
/// Streams are returned in a FIFO manner.
pub fn add_stream(&mut self, stream: CloneableMockStream) {
self.streams.borrow_mut().push(stream);
}
/// Adds a new response stream that will be placed to the end of the connector's stream queue.
///
/// Returns a separate `CloneableMockStream` that allows the user to inspect what is written
/// into the original stream.
pub fn new_response_stream(&mut self, status: &[u8], headers: &Headers, body: Option<Vec<u8>>)
-> CloneableMockStream {
let stream = MockStream::new_http2_response(status, headers, body);
let stream = CloneableMockStream::with_stream(stream);
let ret = stream.clone();
self.add_stream(stream);
ret
}
}
impl NetworkConnector for MockHttp2Connector {
type Stream = CloneableMockStream;
#[inline]
fn connect(&self, _host: &str, _port: u16, _scheme: &str)
-> ::Result<CloneableMockStream> {
Ok(self.streams.borrow_mut().remove(0))
}
}
#[derive(Debug, Default)]
pub struct MockSsl;
impl<T: NetworkStream + Send + Clone> SslClient<T> for MockSsl {
type Stream = T;
fn wrap_client(&self, stream: T, _host: &str) -> ::Result<T> {
Ok(stream)
impl ::std::ops::Deref for Async<Buf> {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.inner
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,79 +0,0 @@
use std::sync::{Arc, mpsc};
use std::thread;
use net::NetworkListener;
pub struct ListenerPool<A: NetworkListener> {
acceptor: A
}
impl<A: NetworkListener + Send + 'static> ListenerPool<A> {
/// Create a thread pool to manage the acceptor.
pub fn new(acceptor: A) -> ListenerPool<A> {
ListenerPool { acceptor: acceptor }
}
/// Runs the acceptor pool. Blocks until the acceptors are closed.
///
/// ## Panics
///
/// Panics if threads == 0.
pub fn accept<F>(self, work: F, threads: usize)
where F: Fn(A::Stream) + Send + Sync + 'static {
assert!(threads != 0, "Can't accept on 0 threads.");
let (super_tx, supervisor_rx) = mpsc::channel();
let work = Arc::new(work);
// Begin work.
for _ in 0..threads {
spawn_with(super_tx.clone(), work.clone(), self.acceptor.clone())
}
// Monitor for panics.
// FIXME(reem): This won't ever exit since we still have a super_tx handle.
for _ in supervisor_rx.iter() {
spawn_with(super_tx.clone(), work.clone(), self.acceptor.clone());
}
}
}
fn spawn_with<A, F>(supervisor: mpsc::Sender<()>, work: Arc<F>, mut acceptor: A)
where A: NetworkListener + Send + 'static,
F: Fn(<A as NetworkListener>::Stream) + Send + Sync + 'static {
thread::spawn(move || {
let _sentinel = Sentinel::new(supervisor, ());
loop {
match acceptor.accept() {
Ok(stream) => work(stream),
Err(e) => {
error!("Connection failed: {}", e);
}
}
}
});
}
struct Sentinel<T: Send + 'static> {
value: Option<T>,
supervisor: mpsc::Sender<T>,
}
impl<T: Send + 'static> Sentinel<T> {
fn new(channel: mpsc::Sender<T>, data: T) -> Sentinel<T> {
Sentinel {
value: Some(data),
supervisor: channel,
}
}
}
impl<T: Send + 'static> Drop for Sentinel<T> {
fn drop(&mut self) {
// Respawn ourselves
let _ = self.supervisor.send(self.value.take().unwrap());
}
}

58
src/server/message.rs Normal file
View File

@@ -0,0 +1,58 @@
use std::marker::PhantomData;
use http::{self, Next};
use net::Transport;
use super::{Handler, request, response};
/// A MessageHandler for a Server.
///
/// This should be really thin glue between http::MessageHandler and
/// server::Handler, but largely just providing the proper types one
/// would expect in a Server Handler.
pub struct Message<H: Handler<T>, T: Transport> {
handler: H,
_marker: PhantomData<T>
}
impl<H: Handler<T>, T: Transport> Message<H, T> {
pub fn new(handler: H) -> Message<H, T> {
Message {
handler: handler,
_marker: PhantomData,
}
}
}
impl<H: Handler<T>, T: Transport> http::MessageHandler<T> for Message<H, T> {
type Message = http::ServerMessage;
fn on_incoming(&mut self, head: http::RequestHead) -> Next {
trace!("on_incoming {:?}", head);
let req = request::new(head);
self.handler.on_request(req)
}
fn on_decode(&mut self, transport: &mut http::Decoder<T>) -> Next {
self.handler.on_request_readable(transport)
}
fn on_outgoing(&mut self, head: &mut http::MessageHead<::status::StatusCode>) -> Next {
let mut res = response::new(head);
self.handler.on_response(&mut res)
}
fn on_encode(&mut self, transport: &mut http::Encoder<T>) -> Next {
self.handler.on_response_writable(transport)
}
fn on_error(&mut self, error: ::Error) -> Next {
self.handler.on_error(error)
}
fn on_remove(self, transport: T) {
self.handler.on_remove(transport);
}
}

View File

@@ -1,510 +1,369 @@
//! HTTP Server
//!
//! # Server
//!
//! A `Server` is created to listen on port, parse HTTP requests, and hand
//! them off to a `Handler`. By default, the Server will listen across multiple
//! threads, but that can be configured to a single thread if preferred.
//!
//! # Handling requests
//!
//! You must pass a `Handler` to the Server that will handle requests. There is
//! a default implementation for `fn`s and closures, allowing you pass one of
//! those easily.
//!
//!
//! ```no_run
//! use hyper::server::{Server, Request, Response};
//!
//! fn hello(req: Request, res: Response) {
//! // handle things here
//! }
//!
//! Server::http("0.0.0.0:0").unwrap().handle(hello).unwrap();
//! ```
//!
//! As with any trait, you can also define a struct and implement `Handler`
//! directly on your own type, and pass that to the `Server` instead.
//!
//! ```no_run
//! use std::sync::Mutex;
//! use std::sync::mpsc::{channel, Sender};
//! use hyper::server::{Handler, Server, Request, Response};
//!
//! struct SenderHandler {
//! sender: Mutex<Sender<&'static str>>
//! }
//!
//! impl Handler for SenderHandler {
//! fn handle(&self, req: Request, res: Response) {
//! self.sender.lock().unwrap().send("start").unwrap();
//! }
//! }
//!
//!
//! let (tx, rx) = channel();
//! Server::http("0.0.0.0:0").unwrap().handle(SenderHandler {
//! sender: Mutex::new(tx)
//! }).unwrap();
//! ```
//!
//! Since the `Server` will be listening on multiple threads, the `Handler`
//! must implement `Sync`: any mutable state must be synchronized.
//!
//! ```no_run
//! use std::sync::atomic::{AtomicUsize, Ordering};
//! use hyper::server::{Server, Request, Response};
//!
//! let counter = AtomicUsize::new(0);
//! Server::http("0.0.0.0:0").unwrap().handle(move |req: Request, res: Response| {
//! counter.fetch_add(1, Ordering::Relaxed);
//! }).unwrap();
//! ```
//!
//! # The `Request` and `Response` pair
//!
//! A `Handler` receives a pair of arguments, a `Request` and a `Response`. The
//! `Request` includes access to the `method`, `uri`, and `headers` of the
//! incoming HTTP request. It also implements `std::io::Read`, in order to
//! read any body, such as with `POST` or `PUT` messages.
//!
//! Likewise, the `Response` includes ways to set the `status` and `headers`,
//! and implements `std::io::Write` to allow writing the response body.
//!
//! ```no_run
//! use std::io;
//! use hyper::server::{Server, Request, Response};
//! use hyper::status::StatusCode;
//!
//! Server::http("0.0.0.0:0").unwrap().handle(|mut req: Request, mut res: Response| {
//! match req.method {
//! hyper::Post => {
//! io::copy(&mut req, &mut res.start().unwrap()).unwrap();
//! },
//! _ => *res.status_mut() = StatusCode::MethodNotAllowed
//! }
//! }).unwrap();
//! ```
//!
//! ## An aside: Write Status
//!
//! The `Response` uses a phantom type parameter to determine its write status.
//! What does that mean? In short, it ensures you never write a body before
//! adding all headers, and never add a header after writing some of the body.
//!
//! This is often done in most implementations by include a boolean property
//! on the response, such as `headers_written`, checking that each time the
//! body has something to write, so as to make sure the headers are sent once,
//! and only once. But this has 2 downsides:
//!
//! 1. You are typically never notified that your late header is doing nothing.
//! 2. There's a runtime cost to checking on every write.
//!
//! Instead, hyper handles this statically, or at compile-time. A
//! `Response<Fresh>` includes a `headers_mut()` method, allowing you add more
//! headers. It also does not implement `Write`, so you can't accidentally
//! write early. Once the "head" of the response is correct, you can "send" it
//! out by calling `start` on the `Response<Fresh>`. This will return a new
//! `Response<Streaming>` object, that no longer has `headers_mut()`, but does
//! implement `Write`.
//! them off to a `Handler`.
use std::fmt;
use std::io::{self, ErrorKind, BufWriter, Write};
use std::net::{SocketAddr, ToSocketAddrs};
use std::thread::{self, JoinHandle};
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use num_cpus;
use rotor::mio::{EventSet, PollOpt};
use rotor::{self, Scope};
pub use self::request::Request;
pub use self::response::Response;
pub use net::{Fresh, Streaming};
use http::{self, Next};
use net::{Accept, HttpListener, HttpsListener, SslServer, Transport};
use Error;
use buffer::BufReader;
use header::{Headers, Expect, Connection};
use http;
use method::Method;
use net::{NetworkListener, NetworkStream, HttpListener, HttpsListener, Ssl};
use status::StatusCode;
use uri::RequestUri;
use version::HttpVersion::Http11;
use self::listener::ListenerPool;
mod request;
mod response;
mod message;
pub mod request;
pub mod response;
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.
#[derive(Debug)]
pub struct Server<L = HttpListener> {
listener: L,
timeouts: Timeouts,
/// A configured `Server` ready to run.
pub struct ServerLoop<A, H> where A: Accept, H: HandlerFactory<A::Output> {
inner: Option<(rotor::Loop<ServerFsm<A, H>>, Context<H>)>,
}
#[derive(Clone, Copy, Debug)]
struct Timeouts {
read: Option<Duration>,
write: Option<Duration>,
keep_alive: Option<Duration>,
}
impl Default for Timeouts {
fn default() -> Timeouts {
Timeouts {
read: None,
write: None,
keep_alive: Some(Duration::from_secs(5))
}
impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("ServerLoop")
}
}
macro_rules! try_option(
($e:expr) => {{
match $e {
Some(v) => v,
None => return None
}
}}
);
/// A Server that can accept incoming network requests.
#[derive(Debug)]
pub struct Server<T: Accept> {
listener: T,
keep_alive: bool,
idle_timeout: Duration,
max_sockets: usize,
}
impl<L: NetworkListener> Server<L> {
/// Creates a new server with the provided handler.
impl<T> Server<T> where T: Accept, T::Output: Transport {
/// Creates a new server with the provided Listener.
#[inline]
pub fn new(listener: L) -> Server<L> {
pub fn new(listener: T) -> Server<T> {
Server {
listener: listener,
timeouts: Timeouts::default()
keep_alive: true,
idle_timeout: Duration::from_secs(10),
max_sockets: 4096,
}
}
/// Controls keep-alive for this server.
/// Enables or disables HTTP keep-alive.
///
/// The timeout duration passed will be used to determine how long
/// to keep the connection alive before dropping it.
///
/// Passing `None` will disable keep-alive.
///
/// Default is enabled with a 5 second timeout.
#[inline]
pub fn keep_alive(&mut self, timeout: Option<Duration>) {
self.timeouts.keep_alive = timeout;
/// Default is true.
pub fn keep_alive(mut self, val: bool) -> Server<T> {
self.keep_alive = val;
self
}
/// Sets the read timeout for all Request reads.
pub fn set_read_timeout(&mut self, dur: Option<Duration>) {
self.timeouts.read = dur;
/// Sets how long an idle connection will be kept before closing.
///
/// Default is 10 seconds.
pub fn idle_timeout(mut self, val: Duration) -> Server<T> {
self.idle_timeout = val;
self
}
/// Sets the write timeout for all Response writes.
pub fn set_write_timeout(&mut self, dur: Option<Duration>) {
self.timeouts.write = dur;
/// Sets the maximum open sockets for this Server.
///
/// Default is 4096, but most servers can handle much more than this.
pub fn max_sockets(mut self, val: usize) -> Server<T> {
self.max_sockets = val;
self
}
}
impl Server<HttpListener> {
/// Creates a new server that will handle `HttpStream`s.
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<Server<HttpListener>> {
HttpListener::new(addr).map(Server::new)
impl Server<HttpListener> { //<H: HandlerFactory<<HttpListener as Accept>::Output>> Server<HttpListener, H> {
/// Creates a new HTTP server config listening on the provided address.
pub fn http(addr: &SocketAddr) -> ::Result<Server<HttpListener>> {
use ::rotor::mio::tcp::TcpListener;
TcpListener::bind(addr)
.map(HttpListener)
.map(Server::new)
.map_err(From::from)
}
}
impl<S: Ssl + Clone + Send> Server<HttpsListener<S>> {
/// Creates a new server that will handle `HttpStream`s over SSL.
impl<S: SslServer> Server<HttpsListener<S>> {
/// Creates a new server config that will handle `HttpStream`s over SSL.
///
/// You can use any SSL implementation, as long as implements `hyper::net::Ssl`.
pub fn https<A: ToSocketAddrs>(addr: A, ssl: S) -> ::Result<Server<HttpsListener<S>>> {
HttpsListener::new(addr, ssl).map(Server::new)
pub fn https(addr: &SocketAddr, ssl: S) -> ::Result<Server<HttpsListener<S>>> {
HttpsListener::new(addr, ssl)
.map(Server::new)
.map_err(From::from)
}
}
impl<L: NetworkListener + Send + 'static> Server<L> {
impl<A: Accept> Server<A> where A::Output: Transport {
/// Binds to a socket and starts handling connections.
pub fn handle<H: Handler + 'static>(self, handler: H) -> ::Result<Listening> {
self.handle_threads(handler, num_cpus::get() * 5 / 4)
}
pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)>
where H: HandlerFactory<A::Output> {
let addr = try!(self.listener.local_addr());
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_rx = shutdown.clone();
/// Binds to a socket and starts handling connections with the provided
/// number of threads.
pub fn handle_threads<H: Handler + 'static>(self, handler: H,
threads: usize) -> ::Result<Listening> {
handle(self, handler, threads)
}
}
fn handle<H, L>(mut server: Server<L>, handler: H, threads: usize) -> ::Result<Listening>
where H: Handler + 'static, L: NetworkListener + Send + 'static {
let socket = try!(server.listener.local_addr());
debug!("threads = {:?}", threads);
let pool = ListenerPool::new(server.listener);
let worker = Worker::new(handler, server.timeouts);
let work = move |mut stream| worker.handle_connection(&mut stream);
let guard = thread::spawn(move || pool.accept(work, threads));
Ok(Listening {
_guard: Some(guard),
socket: socket,
})
}
struct Worker<H: Handler + 'static> {
handler: H,
timeouts: Timeouts,
}
impl<H: Handler + 'static> Worker<H> {
fn new(handler: H, timeouts: Timeouts) -> Worker<H> {
Worker {
handler: handler,
timeouts: timeouts,
}
}
fn handle_connection<S>(&self, mut stream: &mut S) where S: NetworkStream + Clone {
debug!("Incoming stream");
self.handler.on_connection_start();
if let Err(e) = self.set_timeouts(&*stream) {
error!("set_timeouts error: {:?}", e);
return;
}
let addr = match stream.peer_addr() {
Ok(addr) => addr,
Err(e) => {
error!("Peer Name error: {:?}", e);
return;
}
};
// FIXME: Use Type ascription
let stream_clone: &mut NetworkStream = &mut stream.clone();
let mut rdr = BufReader::new(stream_clone);
let mut wrt = BufWriter::new(stream);
while self.keep_alive_loop(&mut rdr, &mut wrt, addr) {
if let Err(e) = self.set_read_timeout(*rdr.get_ref(), self.timeouts.keep_alive) {
error!("set_read_timeout keep_alive {:?}", e);
break;
}
}
self.handler.on_connection_end();
debug!("keep_alive loop ending for {}", addr);
}
fn set_timeouts(&self, s: &NetworkStream) -> io::Result<()> {
try!(self.set_read_timeout(s, self.timeouts.read));
self.set_write_timeout(s, self.timeouts.write)
}
fn set_write_timeout(&self, s: &NetworkStream, timeout: Option<Duration>) -> io::Result<()> {
s.set_write_timeout(timeout)
}
fn set_read_timeout(&self, s: &NetworkStream, timeout: Option<Duration>) -> io::Result<()> {
s.set_read_timeout(timeout)
}
fn keep_alive_loop<W: Write>(&self, mut rdr: &mut BufReader<&mut NetworkStream>,
wrt: &mut W, addr: SocketAddr) -> bool {
let req = match Request::new(rdr, addr) {
Ok(req) => req,
Err(Error::Io(ref e)) if e.kind() == ErrorKind::ConnectionAborted => {
trace!("tcp closed, cancelling keep-alive loop");
return false;
}
Err(Error::Io(e)) => {
debug!("ioerror in keepalive loop = {:?}", e);
return false;
}
Err(e) => {
//TODO: send a 400 response
error!("request error = {:?}", e);
return false;
}
};
if !self.handle_expect(&req, wrt) {
return false;
}
if let Err(e) = req.set_read_timeout(self.timeouts.read) {
error!("set_read_timeout {:?}", e);
return false;
}
let mut keep_alive = self.timeouts.keep_alive.is_some() &&
http::should_keep_alive(req.version, &req.headers);
let version = req.version;
let mut res_headers = Headers::new();
if !keep_alive {
res_headers.set(Connection::close());
}
let mut config = rotor::Config::new();
config.slab_capacity(self.max_sockets);
config.mio().notify_capacity(self.max_sockets);
let keep_alive = self.keep_alive;
let mut loop_ = rotor::Loop::new(&config).unwrap();
let mut notifier = None;
{
let mut res = Response::new(wrt, &mut res_headers);
res.version = version;
self.handler.handle(req, res);
let notifier = &mut notifier;
loop_.add_machine_with(move |scope| {
*notifier = Some(scope.notifier());
rotor_try!(scope.register(&self.listener, EventSet::readable(), PollOpt::level()));
rotor::Response::ok(ServerFsm::Listener::<A, H>(self.listener, shutdown_rx))
}).unwrap();
}
let notifier = notifier.expect("loop.add_machine failed");
// if the request was keep-alive, we need to check that the server agrees
// if it wasn't, then the server cannot force it to be true anyways
if keep_alive {
keep_alive = http::should_keep_alive(version, &res_headers);
}
let listening = Listening {
addr: addr,
shutdown: (shutdown, notifier),
};
let server = ServerLoop {
inner: Some((loop_, Context {
keep_alive: keep_alive,
factory: factory
}))
};
Ok((listening, server))
}
}
debug!("keep_alive = {:?} for {}", keep_alive, addr);
keep_alive
impl<A: Accept, H: HandlerFactory<A::Output>> ServerLoop<A, H> {
/// Runs the server forever in this loop.
///
/// This will block the current thread.
pub fn run(self) {
// drop will take care of it.
}
}
impl<A: Accept, H: HandlerFactory<A::Output>> Drop for ServerLoop<A, H> {
fn drop(&mut self) {
self.inner.take().map(|(loop_, ctx)| {
let _ = loop_.run(ctx);
});
}
}
struct Context<F> {
keep_alive: bool,
factory: F,
}
impl<F: HandlerFactory<T>, T: Transport> http::MessageHandlerFactory<(), T> for Context<F> {
type Output = message::Message<F::Output, T>;
fn create(&mut self, seed: http::Seed<()>) -> Self::Output {
message::Message::new(self.factory.create(seed.control()))
}
}
enum ServerFsm<A, H>
where A: Accept,
A::Output: Transport,
H: HandlerFactory<A::Output> {
Listener(A, Arc<AtomicBool>),
Conn(http::Conn<(), A::Output, message::Message<H::Output, A::Output>>)
}
impl<A, H> rotor::Machine for ServerFsm<A, H>
where A: Accept,
A::Output: Transport,
H: HandlerFactory<A::Output> {
type Context = Context<H>;
type Seed = A::Output;
fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, rotor::Void> {
rotor_try!(scope.register(&seed, EventSet::readable(), PollOpt::level()));
rotor::Response::ok(
ServerFsm::Conn(
http::Conn::new((), seed, scope.notifier())
.keep_alive(scope.keep_alive)
)
)
}
fn handle_expect<W: Write>(&self, req: &Request, wrt: &mut W) -> bool {
if req.version == Http11 && req.headers.get() == Some(&Expect::Continue) {
let status = self.handler.check_continue((&req.method, &req.uri, &req.headers));
match write!(wrt, "{} {}\r\n\r\n", Http11, status).and_then(|_| wrt.flush()) {
Ok(..) => (),
Err(e) => {
error!("error writing 100-continue: {:?}", e);
return false;
fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(listener, rx) => {
match listener.accept() {
Ok(Some(conn)) => {
rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn)
},
Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)),
Err(e) => {
error!("listener accept error {}", e);
// usually fine, just keep listening
rotor::Response::ok(ServerFsm::Listener(listener, rx))
}
}
},
ServerFsm::Conn(conn) => {
match conn.ready(events, scope) {
Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)),
Some((conn, Some(dur))) => {
rotor::Response::ok(ServerFsm::Conn(conn))
.deadline(scope.now() + dur)
}
None => rotor::Response::done()
}
}
}
}
if status != StatusCode::Continue {
debug!("non-100 status ({}) for Expect 100 request", status);
return false;
}
fn spawned(self, _scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(listener, rx) => {
match listener.accept() {
Ok(Some(conn)) => {
rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn)
},
Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)),
Err(e) => {
error!("listener accept error {}", e);
// usually fine, just keep listening
rotor::Response::ok(ServerFsm::Listener(listener, rx))
}
}
},
sock => rotor::Response::ok(sock)
}
true
}
fn timeout(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(..) => unreachable!("Listener cannot timeout"),
ServerFsm::Conn(conn) => {
match conn.timeout(scope) {
Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)),
Some((conn, Some(dur))) => {
rotor::Response::ok(ServerFsm::Conn(conn))
.deadline(scope.now() + dur)
}
None => rotor::Response::done()
}
}
}
}
fn wakeup(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(lst, shutdown) => {
if shutdown.load(Ordering::Acquire) {
let _ = scope.deregister(&lst);
scope.shutdown_loop();
rotor::Response::done()
} else {
rotor::Response::ok(ServerFsm::Listener(lst, shutdown))
}
},
ServerFsm::Conn(conn) => match conn.wakeup(scope) {
Some((conn, None)) => rotor::Response::ok(ServerFsm::Conn(conn)),
Some((conn, Some(dur))) => {
rotor::Response::ok(ServerFsm::Conn(conn))
.deadline(scope.now() + dur)
}
None => rotor::Response::done()
}
}
}
}
/// A listening server, which can later be closed.
/// A handle of the running server.
pub struct Listening {
_guard: Option<JoinHandle<()>>,
/// The socket addresses that the server is bound to.
pub socket: SocketAddr,
addr: SocketAddr,
shutdown: (Arc<AtomicBool>, rotor::Notifier),
}
impl fmt::Debug for Listening {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Listening {{ socket: {:?} }}", self.socket)
f.debug_struct("Listening")
.field("addr", &self.addr)
.field("closed", &self.shutdown.0.load(Ordering::Relaxed))
.finish()
}
}
impl Drop for Listening {
fn drop(&mut self) {
let _ = self._guard.take().map(|g| g.join());
impl fmt::Display for Listening {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.addr, f)
}
}
impl Listening {
/// Warning: This function doesn't work. The server remains listening after you called
/// it. See https://github.com/hyperium/hyper/issues/338 for more details.
///
/// The address this server is listening on.
pub fn addr(&self) -> &SocketAddr {
&self.addr
}
/// Stop the server from listening to its socket address.
pub fn close(&mut self) -> ::Result<()> {
let _ = self._guard.take();
debug!("closing server");
Ok(())
pub fn close(self) {
debug!("closing server {}", self);
self.shutdown.0.store(true, Ordering::Release);
self.shutdown.1.wakeup().unwrap();
}
}
/// A handler that can handle incoming requests for a server.
pub trait Handler: Sync + Send {
/// Receives a `Request`/`Response` pair, and should perform some action on them.
///
/// This could reading from the request, and writing to the response.
fn handle<'a, 'k>(&'a self, Request<'a, 'k>, Response<'a, Fresh>);
/// A trait to react to server events that happen for each message.
///
/// Each event handler returns it's desired `Next` action.
pub trait Handler<T: Transport> {
/// This event occurs first, triggering when a `Request` has been parsed.
fn on_request(&mut self, request: Request) -> Next;
/// This event occurs each time the `Request` is ready to be read from.
fn on_request_readable(&mut self, request: &mut http::Decoder<T>) -> Next;
/// This event occurs after the first time this handled signals `Next::write()`.
fn on_response(&mut self, response: &mut Response) -> Next;
/// This event occurs each time the `Response` is ready to be written to.
fn on_response_writable(&mut self, response: &mut http::Encoder<T>) -> Next;
/// Called when a Request includes a `Expect: 100-continue` header.
/// This event occurs whenever an `Error` occurs outside of the other events.
///
/// By default, this will always immediately response with a `StatusCode::Continue`,
/// but can be overridden with custom behavior.
fn check_continue(&self, _: (&Method, &RequestUri, &Headers)) -> StatusCode {
StatusCode::Continue
/// This could IO errors while waiting for events, or a timeout, etc.
fn on_error(&mut self, err: ::Error) -> Next where Self: Sized {
debug!("default Handler.on_error({:?})", err);
http::Next::remove()
}
/// This is run after a connection is received, on a per-connection basis (not a
/// per-request basis, as a connection with keep-alive may handle multiple
/// requests)
fn on_connection_start(&self) { }
/// This is run before a connection is closed, on a per-connection basis (not a
/// per-request basis, as a connection with keep-alive may handle multiple
/// requests)
fn on_connection_end(&self) { }
/// This event occurs when this Handler has requested to remove the Transport.
fn on_remove(self, _transport: T) where Self: Sized {
debug!("default Handler.on_remove");
}
}
impl<F> Handler for F where F: Fn(Request, Response<Fresh>), F: Sync + Send {
fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a, Fresh>) {
self(req, res)
/// Used to create a `Handler` when a new message is received by the server.
pub trait HandlerFactory<T: Transport> {
/// The `Handler` to use for the incoming message.
type Output: Handler<T>;
/// Creates the associated `Handler`.
fn create(&mut self, ctrl: http::Control) -> Self::Output;
}
impl<F, H, T> HandlerFactory<T> for F
where F: FnMut(http::Control) -> H, H: Handler<T>, T: Transport {
type Output = H;
fn create(&mut self, ctrl: http::Control) -> H {
self(ctrl)
}
}
#[cfg(test)]
mod tests {
use header::Headers;
use method::Method;
use mock::MockStream;
use status::StatusCode;
use uri::RequestUri;
use super::{Request, Response, Fresh, Handler, Worker};
#[test]
fn test_check_continue_default() {
let mut mock = MockStream::with_input(b"\
POST /upload HTTP/1.1\r\n\
Host: example.domain\r\n\
Expect: 100-continue\r\n\
Content-Length: 10\r\n\
\r\n\
1234567890\
");
fn handle(_: Request, res: Response<Fresh>) {
res.start().unwrap().end().unwrap();
}
Worker::new(handle, Default::default()).handle_connection(&mut mock);
let cont = b"HTTP/1.1 100 Continue\r\n\r\n";
assert_eq!(&mock.write[..cont.len()], cont);
let res = b"HTTP/1.1 200 OK\r\n";
assert_eq!(&mock.write[cont.len()..cont.len() + res.len()], res);
}
#[test]
fn test_check_continue_reject() {
struct Reject;
impl Handler for Reject {
fn handle<'a, 'k>(&'a self, _: Request<'a, 'k>, res: Response<'a, Fresh>) {
res.start().unwrap().end().unwrap();
}
fn check_continue(&self, _: (&Method, &RequestUri, &Headers)) -> StatusCode {
StatusCode::ExpectationFailed
}
}
let mut mock = MockStream::with_input(b"\
POST /upload HTTP/1.1\r\n\
Host: example.domain\r\n\
Expect: 100-continue\r\n\
Content-Length: 10\r\n\
\r\n\
1234567890\
");
Worker::new(Reject, Default::default()).handle_connection(&mut mock);
assert_eq!(mock.write, &b"HTTP/1.1 417 Expectation Failed\r\n\r\n"[..]);
}
}

View File

@@ -2,324 +2,75 @@
//!
//! These are requests that a `hyper::Server` receives, and include its method,
//! target URI, headers, and message body.
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;
//use std::net::SocketAddr;
use buffer::BufReader;
use net::NetworkStream;
use version::{HttpVersion};
use version::HttpVersion;
use method::Method;
use header::{Headers, ContentLength, TransferEncoding};
use http::h1::{self, Incoming, HttpReader};
use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader};
use header::Headers;
use http::{RequestHead, MessageHead, RequestLine};
use uri::RequestUri;
pub fn new(incoming: RequestHead) -> Request {
let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming;
debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
debug!("{:#?}", headers);
Request {
//remote_addr: addr,
method: method,
uri: uri,
headers: headers,
version: version,
}
}
/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
pub struct Request<'a, 'b: 'a> {
/// The IP address of the remote connection.
pub remote_addr: SocketAddr,
/// The `Method`, such as `Get`, `Post`, etc.
pub method: Method,
/// The headers of the incoming request.
pub headers: Headers,
/// The target request-uri for this request.
pub uri: RequestUri,
/// The version of HTTP for this request.
pub version: HttpVersion,
body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>>
#[derive(Debug)]
pub struct Request {
// The IP address of the remote connection.
//remote_addr: SocketAddr,
method: Method,
headers: Headers,
uri: RequestUri,
version: HttpVersion,
}
impl<'a, 'b: 'a> Request<'a, 'b> {
/// Create a new Request, reading the StartLine and Headers so they are
/// immediately useful.
pub fn new(mut stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr)
-> ::Result<Request<'a, 'b>> {
let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream));
debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
debug!("{:?}", headers);
let body = if headers.has::<ContentLength>() {
match headers.get::<ContentLength>() {
Some(&ContentLength(len)) => SizedReader(stream, len),
None => unreachable!()
}
} else if headers.has::<TransferEncoding>() {
todo!("check for Transfer-Encoding: chunked");
ChunkedReader(stream, None)
} else {
EmptyReader(stream)
};
Ok(Request {
remote_addr: addr,
method: method,
uri: uri,
headers: headers,
version: version,
body: body
})
}
/// Set the read timeout of the underlying NetworkStream.
impl Request {
/// The `Method`, such as `Get`, `Post`, etc.
#[inline]
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
self.body.get_ref().get_ref().set_read_timeout(timeout)
}
pub fn method(&self) -> &Method { &self.method }
/// Get a reference to the underlying `NetworkStream`.
/// The headers of the incoming request.
#[inline]
pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> {
self.body.get_ref().get_ref().downcast_ref()
}
pub fn headers(&self) -> &Headers { &self.headers }
/// Get a reference to the underlying Ssl stream, if connected
/// over HTTPS.
///
/// # Example
///
/// ```rust
/// # extern crate hyper;
/// # #[cfg(feature = "openssl")]
/// extern crate openssl;
/// # #[cfg(feature = "openssl")]
/// use openssl::ssl::SslStream;
/// use hyper::net::HttpStream;
/// # fn main() {}
/// # #[cfg(feature = "openssl")]
/// # fn doc_ssl(req: hyper::server::Request) {
/// let maybe_ssl = req.ssl::<SslStream<HttpStream>>();
/// # }
/// ```
/// The target request-uri for this request.
#[inline]
pub fn ssl<T: NetworkStream>(&self) -> Option<&T> {
use ::net::HttpsStream;
match self.downcast_ref() {
Some(&HttpsStream::Https(ref s)) => Some(s),
pub fn uri(&self) -> &RequestUri { &self.uri }
/// The version of HTTP for this request.
#[inline]
pub fn version(&self) -> &HttpVersion { &self.version }
/*
/// The target path of this Request.
#[inline]
pub fn path(&self) -> Option<&str> {
match *self.uri {
RequestUri::AbsolutePath(ref s) => Some(s),
RequestUri::AbsoluteUri(ref url) => Some(&url[::url::Position::BeforePath..]),
_ => None
}
}
*/
/// Deconstruct a Request into its constituent parts.
/// Deconstruct this Request into its pieces.
///
/// Modifying these pieces will have no effect on how hyper behaves.
#[inline]
pub fn deconstruct(self) -> (SocketAddr, Method, Headers,
RequestUri, HttpVersion,
HttpReader<&'a mut BufReader<&'b mut NetworkStream>>) {
(self.remote_addr, self.method, self.headers,
self.uri, self.version, self.body)
}
}
impl<'a, 'b> Read for Request<'a, 'b> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.body.read(buf)
}
}
#[cfg(test)]
mod tests {
use buffer::BufReader;
use header::{Host, TransferEncoding, Encoding};
use net::NetworkStream;
use mock::MockStream;
use super::Request;
use std::io::{self, Read};
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 mock = MockStream::with_input(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
\r\n\
I'm a bad request.\r\n\
");
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req).unwrap(), "".to_owned());
}
#[test]
fn test_get_with_body() {
let mut mock = MockStream::with_input(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Content-Length: 19\r\n\
\r\n\
I'm a good request.\r\n\
");
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req).unwrap(), "I'm a good request.".to_owned());
}
#[test]
fn test_head_empty_body() {
let mut mock = MockStream::with_input(b"\
HEAD / HTTP/1.1\r\n\
Host: example.domain\r\n\
\r\n\
I'm a bad request.\r\n\
");
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req).unwrap(), "".to_owned());
}
#[test]
fn test_post_empty_body() {
let mut mock = MockStream::with_input(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
\r\n\
I'm a bad request.\r\n\
");
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req).unwrap(), "".to_owned());
}
#[test]
fn test_parse_chunked_request() {
let mut mock = MockStream::with_input(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
1\r\n\
q\r\n\
2\r\n\
we\r\n\
2\r\n\
rt\r\n\
0\r\n\
\r\n"
);
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
// The headers are correct?
match req.headers.get::<Host>() {
Some(host) => {
assert_eq!("example.domain", host.hostname);
},
None => panic!("Host header expected!"),
};
match req.headers.get::<TransferEncoding>() {
Some(encodings) => {
assert_eq!(1, encodings.len());
assert_eq!(Encoding::Chunked, encodings[0]);
}
None => panic!("Transfer-Encoding: chunked expected!"),
};
// The content is correctly read?
assert_eq!(read_to_string(req).unwrap(), "qwert".to_owned());
}
/// Tests that when a chunk size is not a valid radix-16 number, an error
/// is returned.
#[test]
fn test_invalid_chunk_size_not_hex_digit() {
let mut mock = MockStream::with_input(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
X\r\n\
1\r\n\
0\r\n\
\r\n"
);
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert!(read_to_string(req).is_err());
}
/// Tests that when a chunk size contains an invalid extension, an error is
/// returned.
#[test]
fn test_invalid_chunk_size_extension() {
let mut mock = MockStream::with_input(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
1 this is an invalid extension\r\n\
1\r\n\
0\r\n\
\r\n"
);
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert!(read_to_string(req).is_err());
}
/// Tests that when a valid extension that contains a digit is appended to
/// the chunk size, the chunk is correctly read.
#[test]
fn test_chunk_size_with_extension() {
let mut mock = MockStream::with_input(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
1;this is an extension with a digit 1\r\n\
1\r\n\
0\r\n\
\r\n"
);
// FIXME: Use Type ascription
let mock: &mut NetworkStream = &mut mock;
let mut stream = BufReader::new(mock);
let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
assert_eq!(read_to_string(req).unwrap(), "1".to_owned());
pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers) {
(self.method, self.uri, self.version, self.headers)
}
}

View File

@@ -2,431 +2,49 @@
//!
//! These are responses sent by a `hyper::Server` to clients, after
//! receiving a request.
use std::any::{Any, TypeId};
use std::marker::PhantomData;
use std::mem;
use std::io::{self, Write};
use std::ptr;
use std::thread;
use time::now_utc;
use header;
use http::h1::{LINE_ENDING, HttpWriter};
use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter};
use status;
use net::{Fresh, Streaming};
use http;
use status::StatusCode;
use version;
/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
///
/// The default `StatusCode` for a `Response` is `200 OK`.
///
/// There is a `Drop` implementation for `Response` that will automatically
/// write the head and flush the body, if the handler has not already done so,
/// so that the server doesn't accidentally leave dangling requests.
#[derive(Debug)]
pub struct Response<'a, W: Any = 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 (Write + 'a)>,
// The status code for the request.
status: status::StatusCode,
// The outgoing headers on this response.
headers: &'a mut header::Headers,
_writing: PhantomData<W>
pub struct Response<'a> {
head: &'a mut http::MessageHead<StatusCode>,
}
impl<'a, W: Any> Response<'a, W> {
/// The status of this response.
#[inline]
pub fn status(&self) -> status::StatusCode { self.status }
impl<'a> Response<'a> {
/// The headers of this response.
#[inline]
pub fn headers(&self) -> &header::Headers { &*self.headers }
pub fn headers(&self) -> &header::Headers { &self.head.headers }
/// Construct a Response from its constituent parts.
/// The status of this response.
#[inline]
pub fn construct(version: version::HttpVersion,
body: HttpWriter<&'a mut (Write + 'a)>,
status: status::StatusCode,
headers: &'a mut header::Headers) -> Response<'a, Fresh> {
Response {
status: status,
version: version,
body: body,
headers: headers,
_writing: PhantomData,
}
pub fn status(&self) -> &StatusCode {
&self.head.subject
}
/// Deconstruct this Response into its constituent parts.
/// The HTTP version of this response.
#[inline]
pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Write + 'a)>,
status::StatusCode, &'a mut header::Headers) {
unsafe {
let parts = (
self.version,
ptr::read(&self.body),
self.status,
ptr::read(&self.headers)
);
mem::forget(self);
parts
}
}
fn write_head(&mut self) -> io::Result<Body> {
debug!("writing head: {:?} {:?}", self.version, self.status);
try!(write!(&mut self.body, "{} {}\r\n", self.version, self.status));
if !self.headers.has::<header::Date>() {
self.headers.set(header::Date(header::HttpDate(now_utc())));
}
let body_type = match self.status {
status::StatusCode::NoContent | status::StatusCode::NotModified => Body::Empty,
c if c.class() == status::StatusClass::Informational => Body::Empty,
_ => if let Some(cl) = self.headers.get::<header::ContentLength>() {
Body::Sized(**cl)
} else {
Body::Chunked
}
};
// can't do in match above, thanks borrowck
if body_type == Body::Chunked {
let encodings = match self.headers.get_mut::<header::TransferEncoding>() {
Some(&mut header::TransferEncoding(ref mut encodings)) => {
//TODO: check if chunked is already in encodings. use HashSet?
encodings.push(header::Encoding::Chunked);
false
},
None => true
};
if encodings {
self.headers.set::<header::TransferEncoding>(
header::TransferEncoding(vec![header::Encoding::Chunked]))
}
}
debug!("headers [\n{:?}]", self.headers);
try!(write!(&mut self.body, "{}", self.headers));
try!(write!(&mut self.body, "{}", LINE_ENDING));
Ok(body_type)
}
}
impl<'a> Response<'a, Fresh> {
/// Creates a new Response that can be used to write to a network stream.
#[inline]
pub fn new(stream: &'a mut (Write + 'a), headers: &'a mut header::Headers) ->
Response<'a, Fresh> {
Response {
status: status::StatusCode::Ok,
version: version::HttpVersion::Http11,
headers: headers,
body: ThroughWriter(stream),
_writing: PhantomData,
}
}
/// Writes the body and ends the response.
///
/// This is a shortcut method for when you have a response with a fixed
/// size, and would only need a single `write` call normally.
///
/// # Example
///
/// ```
/// # use hyper::server::Response;
/// fn handler(res: Response) {
/// res.send(b"Hello World!").unwrap();
/// }
/// ```
///
/// The above is the same, but shorter, than the longer:
///
/// ```
/// # use hyper::server::Response;
/// use std::io::Write;
/// use hyper::header::ContentLength;
/// fn handler(mut res: Response) {
/// let body = b"Hello World!";
/// res.headers_mut().set(ContentLength(body.len() as u64));
/// let mut res = res.start().unwrap();
/// res.write_all(body).unwrap();
/// }
/// ```
#[inline]
pub fn send(mut self, body: &[u8]) -> io::Result<()> {
self.headers.set(header::ContentLength(body.len() as u64));
let mut stream = try!(self.start());
try!(stream.write_all(body));
stream.end()
}
/// Consume this Response<Fresh>, writing the Headers and Status and
/// creating a Response<Streaming>
pub fn start(mut self) -> io::Result<Response<'a, Streaming>> {
let body_type = try!(self.write_head());
let (version, body, status, headers) = self.deconstruct();
let stream = match body_type {
Body::Chunked => ChunkedWriter(body.into_inner()),
Body::Sized(len) => SizedWriter(body.into_inner(), len),
Body::Empty => EmptyWriter(body.into_inner()),
};
// "copy" to change the phantom type
Ok(Response {
version: version,
body: stream,
status: status,
headers: headers,
_writing: PhantomData,
})
}
/// Get a mutable reference to the status.
#[inline]
pub fn status_mut(&mut self) -> &mut status::StatusCode { &mut self.status }
pub fn version(&self) -> &version::HttpVersion { &self.head.version }
/// Get a mutable reference to the Headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut header::Headers { self.headers }
}
pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.head.headers }
impl<'a> Response<'a, Streaming> {
/// Flushes all writing of a response to the client.
/// Get a mutable reference to the status.
#[inline]
pub fn end(self) -> io::Result<()> {
trace!("ending");
let (_, body, _, _) = self.deconstruct();
try!(body.end());
Ok(())
pub fn set_status(&mut self, status: StatusCode) {
self.head.subject = status;
}
}
impl<'a> Write for Response<'a, Streaming> {
#[inline]
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
debug!("write {:?} bytes", msg.len());
self.body.write(msg)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.body.flush()
}
}
#[derive(PartialEq)]
enum Body {
Chunked,
Sized(u64),
Empty,
}
impl<'a, T: Any> Drop for Response<'a, T> {
fn drop(&mut self) {
if TypeId::of::<T>() == TypeId::of::<Fresh>() {
if thread::panicking() {
self.status = status::StatusCode::InternalServerError;
}
let mut body = match self.write_head() {
Ok(Body::Chunked) => ChunkedWriter(self.body.get_mut()),
Ok(Body::Sized(len)) => SizedWriter(self.body.get_mut(), len),
Ok(Body::Empty) => EmptyWriter(self.body.get_mut()),
Err(e) => {
debug!("error dropping request: {:?}", e);
return;
}
};
end(&mut body);
} else {
end(&mut self.body);
};
#[inline]
fn end<W: Write>(w: &mut W) {
match w.write(&[]) {
Ok(_) => match w.flush() {
Ok(_) => debug!("drop successful"),
Err(e) => debug!("error dropping request: {:?}", e)
},
Err(e) => debug!("error dropping request: {:?}", e)
}
}
}
}
#[cfg(test)]
mod tests {
use header::Headers;
use mock::MockStream;
use super::Response;
macro_rules! lines {
($s:ident = $($line:pat),+) => ({
let s = String::from_utf8($s.write).unwrap();
let mut lines = s.split_terminator("\r\n");
$(
match lines.next() {
Some($line) => (),
other => panic!("line mismatch: {:?} != {:?}", other, stringify!($line))
}
)+
assert_eq!(lines.next(), None);
})
}
#[test]
fn test_fresh_start() {
let mut headers = Headers::new();
let mut stream = MockStream::new();
{
let res = Response::new(&mut stream, &mut headers);
res.start().unwrap().deconstruct();
}
lines! { stream =
"HTTP/1.1 200 OK",
_date,
_transfer_encoding,
""
}
}
#[test]
fn test_streaming_end() {
let mut headers = Headers::new();
let mut stream = MockStream::new();
{
let res = Response::new(&mut stream, &mut headers);
res.start().unwrap().end().unwrap();
}
lines! { stream =
"HTTP/1.1 200 OK",
_date,
_transfer_encoding,
"",
"0",
"" // empty zero body
}
}
#[test]
fn test_fresh_drop() {
use status::StatusCode;
let mut headers = Headers::new();
let mut stream = MockStream::new();
{
let mut res = Response::new(&mut stream, &mut headers);
*res.status_mut() = StatusCode::NotFound;
}
lines! { stream =
"HTTP/1.1 404 Not Found",
_date,
_transfer_encoding,
"",
"0",
"" // empty zero body
}
}
// x86 windows msvc does not support unwinding
// See https://github.com/rust-lang/rust/issues/25869
#[cfg(not(all(windows, target_arch="x86", target_env="msvc")))]
#[test]
fn test_fresh_drop_panicing() {
use std::thread;
use std::sync::{Arc, Mutex};
use status::StatusCode;
let stream = MockStream::new();
let stream = Arc::new(Mutex::new(stream));
let inner_stream = stream.clone();
let join_handle = thread::spawn(move || {
let mut headers = Headers::new();
let mut stream = inner_stream.lock().unwrap();
let mut res = Response::new(&mut *stream, &mut headers);
*res.status_mut() = StatusCode::NotFound;
panic!("inside")
});
assert!(join_handle.join().is_err());
let stream = match stream.lock() {
Err(poisoned) => poisoned.into_inner().clone(),
Ok(_) => unreachable!()
};
lines! { stream =
"HTTP/1.1 500 Internal Server Error",
_date,
_transfer_encoding,
"",
"0",
"" // empty zero body
}
}
#[test]
fn test_streaming_drop() {
use std::io::Write;
use status::StatusCode;
let mut headers = Headers::new();
let mut stream = MockStream::new();
{
let mut res = Response::new(&mut stream, &mut headers);
*res.status_mut() = StatusCode::NotFound;
let mut stream = res.start().unwrap();
stream.write_all(b"foo").unwrap();
}
lines! { stream =
"HTTP/1.1 404 Not Found",
_date,
_transfer_encoding,
"",
"3",
"foo",
"0",
"" // empty zero body
}
}
#[test]
fn test_no_content() {
use status::StatusCode;
let mut headers = Headers::new();
let mut stream = MockStream::new();
{
let mut res = Response::new(&mut stream, &mut headers);
*res.status_mut() = StatusCode::NoContent;
res.start().unwrap();
}
lines! { stream =
"HTTP/1.1 204 No Content",
_date,
""
}
/// Creates a new Response that can be used to write to a network stream.
pub fn new<'a>(head: &'a mut http::MessageHead<StatusCode>) -> Response<'a> {
Response {
head: head
}
}

View File

@@ -547,6 +547,12 @@ impl Ord for StatusCode {
}
}
impl Default for StatusCode {
fn default() -> StatusCode {
StatusCode::Ok
}
}
/// The class of an HTTP `status-code`.
///
/// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6):

View File

@@ -50,6 +50,12 @@ pub enum RequestUri {
Star,
}
impl Default for RequestUri {
fn default() -> RequestUri {
RequestUri::Star
}
}
impl FromStr for RequestUri {
type Err = Error;
@@ -67,7 +73,7 @@ impl FromStr for RequestUri {
let mut temp = "http://".to_owned();
temp.push_str(s);
try!(Url::parse(&temp[..]));
todo!("compare vs u.authority()");
//TODO: compare vs u.authority()?
Ok(RequestUri::Authority(s.to_owned()))
}
}

View File

@@ -4,7 +4,7 @@
//! the `HttpVersion` enum.
use std::fmt;
use self::HttpVersion::{Http09, Http10, Http11, Http20};
use self::HttpVersion::{Http09, Http10, Http11, H2, H2c};
/// Represents a version of the HTTP spec.
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash, Debug)]
@@ -15,8 +15,10 @@ pub enum HttpVersion {
Http10,
/// `HTTP/1.1`
Http11,
/// `HTTP/2.0`
Http20
/// `HTTP/2.0` over TLS
H2,
/// `HTTP/2.0` over cleartext
H2c,
}
impl fmt::Display for HttpVersion {
@@ -25,7 +27,14 @@ impl fmt::Display for HttpVersion {
Http09 => "HTTP/0.9",
Http10 => "HTTP/1.0",
Http11 => "HTTP/1.1",
Http20 => "HTTP/2.0",
H2 => "h2",
H2c => "h2c",
})
}
}
impl Default for HttpVersion {
fn default() -> HttpVersion {
Http11
}
}

205
tests/client.rs Normal file
View File

@@ -0,0 +1,205 @@
#![deny(warnings)]
extern crate hyper;
use std::io::{self, Read, Write};
use std::net::TcpListener;
use std::sync::mpsc;
use std::time::Duration;
use hyper::client::{Handler, Request, Response, HttpConnector};
use hyper::header;
use hyper::{Method, StatusCode, Next, Encoder, Decoder};
use hyper::net::HttpStream;
fn s(bytes: &[u8]) -> &str {
::std::str::from_utf8(bytes.as_ref()).unwrap()
}
#[derive(Debug)]
struct TestHandler {
opts: Opts,
tx: mpsc::Sender<Msg>
}
impl TestHandler {
fn new(opts: Opts) -> (TestHandler, mpsc::Receiver<Msg>) {
let (tx, rx) = mpsc::channel();
(TestHandler {
opts: opts,
tx: tx
}, rx)
}
}
#[derive(Debug)]
enum Msg {
Head(Response),
Chunk(Vec<u8>),
Error(hyper::Error),
}
fn read(opts: &Opts) -> Next {
if let Some(timeout) = opts.read_timeout {
Next::read().timeout(timeout)
} else {
Next::read()
}
}
impl Handler<HttpStream> for TestHandler {
fn on_request(&mut self, req: &mut Request) -> Next {
req.set_method(self.opts.method.clone());
read(&self.opts)
}
fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
read(&self.opts)
}
fn on_response(&mut self, res: Response) -> Next {
use hyper::header;
// server responses can include a body until eof, if not size is specified
let mut has_body = true;
if let Some(len) = res.headers().get::<header::ContentLength>() {
if **len == 0 {
has_body = false;
}
}
self.tx.send(Msg::Head(res)).unwrap();
if has_body {
read(&self.opts)
} else {
Next::end()
}
}
fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
let mut v = vec![0; 512];
match decoder.read(&mut v) {
Ok(n) => {
v.truncate(n);
self.tx.send(Msg::Chunk(v)).unwrap();
if n == 0 {
Next::end()
} else {
read(&self.opts)
}
},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => read(&self.opts),
_ => panic!("io read error: {:?}", e)
}
}
}
fn on_error(&mut self, err: hyper::Error) -> Next {
self.tx.send(Msg::Error(err)).unwrap();
Next::remove()
}
}
struct Client {
client: Option<hyper::Client<TestHandler>>,
}
#[derive(Debug)]
struct Opts {
method: Method,
read_timeout: Option<Duration>,
}
impl Default for Opts {
fn default() -> Opts {
Opts {
method: Method::Get,
read_timeout: None,
}
}
}
fn opts() -> Opts {
Opts::default()
}
impl Opts {
fn method(mut self, method: Method) -> Opts {
self.method = method;
self
}
fn read_timeout(mut self, timeout: Duration) -> Opts {
self.read_timeout = Some(timeout);
self
}
}
impl Client {
fn request<U>(&self, url: U, opts: Opts) -> mpsc::Receiver<Msg>
where U: AsRef<str> {
let (handler, rx) = TestHandler::new(opts);
self.client.as_ref().unwrap()
.request(url.as_ref().parse().unwrap(), handler).unwrap();
rx
}
}
impl Drop for Client {
fn drop(&mut self) {
self.client.take().map(|c| c.close());
}
}
fn client() -> Client {
let c = hyper::Client::<TestHandler>::configure()
.connector(HttpConnector::default())
.build().unwrap();
Client {
client: Some(c),
}
}
#[test]
fn client_get() {
let server = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = server.local_addr().unwrap();
let client = client();
let res = client.request(format!("http://{}/", addr), opts().method(Method::Get));
let mut inc = server.accept().unwrap().0;
inc.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
inc.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
let mut buf = [0; 4096];
let n = inc.read(&mut buf).unwrap();
let expected = format!("GET / HTTP/1.1\r\nHost: {}\r\n\r\n", addr);
assert_eq!(s(&buf[..n]), expected);
inc.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap();
if let Msg::Head(head) = res.recv().unwrap() {
assert_eq!(head.status(), &StatusCode::Ok);
assert_eq!(head.headers().get(), Some(&header::ContentLength(0)));
} else {
panic!("we lost the head!");
}
//drop(inc);
assert!(res.recv().is_err());
}
#[test]
fn client_read_timeout() {
let server = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = server.local_addr().unwrap();
let client = client();
let res = client.request(format!("http://{}/", addr), opts().read_timeout(Duration::from_secs(3)));
let mut inc = server.accept().unwrap().0;
let mut buf = [0; 4096];
inc.read(&mut buf).unwrap();
match res.recv() {
Ok(Msg::Error(hyper::Error::Timeout)) => (),
other => panic!("expected timeout, actual: {:?}", other)
}
}

379
tests/server.rs Normal file
View File

@@ -0,0 +1,379 @@
#![deny(warnings)]
extern crate hyper;
use std::net::{TcpStream, SocketAddr};
use std::io::{self, Read, Write};
use std::sync::mpsc;
use std::time::Duration;
use hyper::{Next, Encoder, Decoder};
use hyper::net::HttpStream;
use hyper::server::{Server, Handler, Request, Response};
struct Serve {
listening: Option<hyper::server::Listening>,
msg_rx: mpsc::Receiver<Msg>,
reply_tx: mpsc::Sender<Reply>,
}
impl Serve {
fn addr(&self) -> &SocketAddr {
self.listening.as_ref().unwrap().addr()
}
/*
fn head(&self) -> Request {
unimplemented!()
}
*/
fn body(&self) -> Vec<u8> {
let mut buf = vec![];
while let Ok(Msg::Chunk(msg)) = self.msg_rx.try_recv() {
buf.extend(&msg);
}
buf
}
fn reply(&self) -> ReplyBuilder {
ReplyBuilder {
tx: &self.reply_tx
}
}
}
struct ReplyBuilder<'a> {
tx: &'a mpsc::Sender<Reply>,
}
impl<'a> ReplyBuilder<'a> {
fn status(self, status: hyper::StatusCode) -> Self {
self.tx.send(Reply::Status(status)).unwrap();
self
}
fn header<H: hyper::header::Header>(self, header: H) -> Self {
let mut headers = hyper::Headers::new();
headers.set(header);
self.tx.send(Reply::Headers(headers)).unwrap();
self
}
fn body<T: AsRef<[u8]>>(self, body: T) {
self.tx.send(Reply::Body(body.as_ref().into())).unwrap();
}
}
impl Drop for Serve {
fn drop(&mut self) {
self.listening.take().unwrap().close();
}
}
struct TestHandler {
tx: mpsc::Sender<Msg>,
rx: mpsc::Receiver<Reply>,
peeked: Option<Vec<u8>>,
timeout: Option<Duration>,
}
enum Reply {
Status(hyper::StatusCode),
Headers(hyper::Headers),
Body(Vec<u8>),
}
enum Msg {
//Head(Request),
Chunk(Vec<u8>),
}
impl TestHandler {
fn next(&self, next: Next) -> Next {
if let Some(dur) = self.timeout {
next.timeout(dur)
} else {
next
}
}
}
impl Handler<HttpStream> for TestHandler {
fn on_request(&mut self, _req: Request) -> Next {
//self.tx.send(Msg::Head(req)).unwrap();
self.next(Next::read())
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
let mut vec = vec![0; 1024];
match decoder.read(&mut vec) {
Ok(0) => {
self.next(Next::write())
}
Ok(n) => {
vec.truncate(n);
self.tx.send(Msg::Chunk(vec)).unwrap();
self.next(Next::read())
}
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => self.next(Next::read()),
_ => panic!("test error: {}", e)
}
}
}
fn on_response(&mut self, res: &mut Response) -> Next {
loop {
match self.rx.try_recv() {
Ok(Reply::Status(s)) => {
res.set_status(s);
},
Ok(Reply::Headers(headers)) => {
use std::iter::Extend;
res.headers_mut().extend(headers.iter());
},
Ok(Reply::Body(body)) => {
self.peeked = Some(body);
},
Err(..) => {
return if self.peeked.is_some() {
self.next(Next::write())
} else {
self.next(Next::end())
};
},
}
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
match self.peeked {
Some(ref body) => {
encoder.write(body).unwrap();
self.next(Next::end())
},
None => self.next(Next::end())
}
}
}
fn serve() -> Serve {
serve_with_timeout(None)
}
fn serve_with_timeout(dur: Option<Duration>) -> Serve {
use std::thread;
let (msg_tx, msg_rx) = mpsc::channel();
let (reply_tx, reply_rx) = mpsc::channel();
let mut reply_rx = Some(reply_rx);
let (listening, server) = Server::http(&"127.0.0.1:0".parse().unwrap()).unwrap()
.handle(move |_| TestHandler {
tx: msg_tx.clone(),
timeout: dur,
rx: reply_rx.take().unwrap(),
peeked: None,
}).unwrap();
let thread_name = format!("test-server-{}: {:?}", listening.addr(), dur);
thread::Builder::new().name(thread_name).spawn(move || {
server.run();
}).unwrap();
Serve {
listening: Some(listening),
msg_rx: msg_rx,
reply_tx: reply_tx,
}
}
#[test]
fn server_get_should_ignore_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
\r\n\
I shouldn't be read.\r\n\
").unwrap();
req.read(&mut [0; 256]).unwrap();
assert_eq!(server.body(), b"");
}
#[test]
fn server_get_with_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Content-Length: 19\r\n\
\r\n\
I'm a good request.\r\n\
").unwrap();
req.read(&mut [0; 256]).unwrap();
// note: doesnt include trailing \r\n, cause Content-Length wasn't 21
assert_eq!(server.body(), b"I'm a good request.");
}
#[test]
fn server_get_fixed_response() {
let foo_bar = b"foo bar baz";
let server = serve();
server.reply()
.status(hyper::Ok)
.header(hyper::header::ContentLength(foo_bar.len() as u64))
.body(foo_bar);
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
").unwrap();
let mut body = String::new();
req.read_to_string(&mut body).unwrap();
let n = body.find("\r\n\r\n").unwrap() + 4;
assert_eq!(&body[n..], "foo bar baz");
}
#[test]
fn server_get_chunked_response() {
let foo_bar = b"foo bar baz";
let server = serve();
server.reply()
.status(hyper::Ok)
.header(hyper::header::TransferEncoding::chunked())
.body(foo_bar);
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
").unwrap();
let mut body = String::new();
req.read_to_string(&mut body).unwrap();
let n = body.find("\r\n\r\n").unwrap() + 4;
assert_eq!(&body[n..], "B\r\nfoo bar baz\r\n0\r\n\r\n");
}
#[test]
fn server_post_with_chunked_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
1\r\n\
q\r\n\
2\r\n\
we\r\n\
2\r\n\
rt\r\n\
0\r\n\
\r\n
").unwrap();
req.read(&mut [0; 256]).unwrap();
assert_eq!(server.body(), b"qwert");
}
/*
#[test]
fn server_empty_response() {
let server = serve();
server.reply()
.status(hyper::Ok);
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
").unwrap();
let mut response = String::new();
req.read_to_string(&mut response).unwrap();
assert_eq!(response, "foo");
assert!(!response.contains("Transfer-Encoding: chunked\r\n"));
let mut lines = response.lines();
assert_eq!(lines.next(), Some("HTTP/1.1 200 OK"));
let mut lines = lines.skip_while(|line| !line.is_empty());
assert_eq!(lines.next(), Some(""));
assert_eq!(lines.next(), None);
}
*/
#[test]
fn server_empty_response_chunked() {
let server = serve();
server.reply()
.status(hyper::Ok)
.body("");
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
").unwrap();
let mut response = String::new();
req.read_to_string(&mut response).unwrap();
assert!(response.contains("Transfer-Encoding: chunked\r\n"));
let mut lines = response.lines();
assert_eq!(lines.next(), Some("HTTP/1.1 200 OK"));
let mut lines = lines.skip_while(|line| !line.is_empty());
assert_eq!(lines.next(), Some(""));
// 0\r\n\r\n
assert_eq!(lines.next(), Some("0"));
assert_eq!(lines.next(), Some(""));
assert_eq!(lines.next(), None);
}
#[test]
fn server_empty_response_chunked_without_calling_write() {
let server = serve();
server.reply()
.status(hyper::Ok)
.header(hyper::header::TransferEncoding::chunked());
let mut req = TcpStream::connect(server.addr()).unwrap();
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
").unwrap();
let mut response = String::new();
req.read_to_string(&mut response).unwrap();
assert!(response.contains("Transfer-Encoding: chunked\r\n"));
let mut lines = response.lines();
assert_eq!(lines.next(), Some("HTTP/1.1 200 OK"));
let mut lines = lines.skip_while(|line| !line.is_empty());
assert_eq!(lines.next(), Some(""));
// 0\r\n\r\n
assert_eq!(lines.next(), Some("0"));
assert_eq!(lines.next(), Some(""));
assert_eq!(lines.next(), None);
}