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 fast_finish: true
include: include:
- os: osx - os: osx
rust: stable
env: FEATURES="--no-default-features --features security-framework"
- rust: nightly - rust: nightly
env: FEATURES="--features nightly" env: FEATURES="--features nightly"
- rust: beta - rust: beta

View File

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

View File

@@ -10,12 +10,12 @@ A Modern HTTP library for Rust.
### Documentation ### Documentation
- [Stable](http://hyperium.github.io/hyper) - [Released](http://hyperium.github.io/hyper)
- [Master](http://hyperium.github.io/hyper/master) - [Master](http://hyperium.github.io/hyper/master)
## Overview ## 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 is a low-level typesafe abstraction over raw HTTP, providing an elegant
layer over "stringly-typed" HTTP. 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. complex web applications written entirely in Rust.
The documentation is located at [http://hyperium.github.io/hyper](http://hyperium.github.io/hyper). 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::env;
use std::io; 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::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() { fn main() {
env_logger::init().unwrap(); env_logger::init().unwrap();
@@ -20,26 +72,11 @@ fn main() {
} }
}; };
let client = match env::var("HTTP_PROXY") { let (tx, rx) = mpsc::channel();
Ok(mut proxy) => { let client = Client::new().expect("Failed to create a Client");
// parse the proxy, message if it doesn't make sense client.request(url.parse().unwrap(), Dump(tx)).unwrap();
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 mut res = client.get(&*url) // wait till done
.header(Connection::close()) let _ = rx.recv();
.send().unwrap(); client.close();
println!("Response: {}", res.status);
println!("Headers:\n{}", res.headers);
io::copy(&mut res, &mut io::stdout()).unwrap();
} }

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)] #![deny(warnings)]
extern crate hyper; extern crate hyper;
extern crate env_logger; 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!"; static PHRASE: &'static [u8] = b"Hello World!";
fn hello(_: Request, res: Response) { struct Hello;
res.send(PHRASE).unwrap();
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() { fn main() {
env_logger::init().unwrap(); 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"); println!("Listening on http://127.0.0.1:3000");
for handle in handles {
handle.join().unwrap();
}
} }

View File

@@ -1,47 +1,171 @@
#![deny(warnings)] #![deny(warnings)]
extern crate hyper; extern crate hyper;
extern crate env_logger; 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::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, Next};
use hyper::server::{Server, Request, Response}; use hyper::header::ContentLength;
use hyper::uri::RequestUri::AbsolutePath; use hyper::net::HttpStream;
use hyper::server::{Server, Handler, Request, Response};
macro_rules! try_return( struct Echo {
($e:expr) => {{ buf: Vec<u8>,
match $e { read_pos: usize,
Ok(v) => v, write_pos: usize,
Err(e) => { println!("Error: {}", e); return; } 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) { impl Handler<HttpStream> for Echo {
match req.uri { fn on_request(&mut self, req: Request) -> Next {
AbsolutePath(ref path) => match (&req.method, &path[..]) { match *req.uri() {
(&Get, "/") | (&Get, "/echo") => { RequestUri::AbsolutePath(ref path) => match (req.method(), &path[..]) {
try_return!(res.send(b"Try POST /echo")); (&Get, "/") | (&Get, "/echo") => {
return; 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 _ => Next::write()
_ => {
*res.status_mut() = hyper::NotFound;
return;
}
},
_ => {
return;
} }
}; }
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()); fn on_response(&mut self, res: &mut Response) -> Next {
try_return!(copy(&mut req, &mut res)); 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() { fn main() {
env_logger::init().unwrap(); env_logger::init().unwrap();
let server = Server::http("127.0.0.1:1337").unwrap(); let server = Server::http(&"127.0.0.1:1337".parse().unwrap()).unwrap();
let _guard = server.handle(echo); let (listening, server) = server.handle(|_| Echo::new()).unwrap();
println!("Listening on http://127.0.0.1:1337"); 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 //! 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::Headers;
use header::Host; use http::RequestHead;
use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming}; use method::Method;
use version; use uri::RequestUri;
use client::{Response, get_host_and_port}; use version::HttpVersion;
use http::{HttpMessage, RequestHead};
use http::h1::Http11Message;
/// A client request to a remote server. /// A client request to a remote server.
/// The W type tracks the state of the request, Fresh vs Streaming. #[derive(Debug)]
pub struct Request<W> { pub struct Request<'a> {
/// The target URI for this request. head: &'a mut RequestHead
pub url: Url,
/// The HTTP version of this request.
pub version: version::HttpVersion,
message: Box<HttpMessage>,
headers: Headers,
method: Method,
_marker: PhantomData<W>,
} }
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. /// Read the Request headers.
#[inline] #[inline]
pub fn headers(&self) -> &Headers { &self.headers } pub fn headers(&self) -> &Headers { &self.head.headers }
/// Read the Request method. /// Read the Request method.
#[inline] #[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] #[inline]
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { pub fn set_method(&mut self, method: Method) { self.head.subject.0 = method; }
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,
})
}
/// Get a mutable reference to the Request headers. /// Get a mutable reference to the Request headers.
#[inline] #[inline]
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } pub fn headers_mut(&mut self) -> &mut Headers { &mut self.head.headers }
} }
pub fn new(head: &mut RequestHead) -> Request {
Request { head: head }
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)
}
}
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
/*
use std::io::Write; use std::io::Write;
use std::str::from_utf8; use std::str::from_utf8;
use url::Url; use url::Url;
@@ -311,4 +186,5 @@ mod tests {
.get_ref().downcast_ref::<MockStream>().unwrap() .get_ref().downcast_ref::<MockStream>().unwrap()
.is_closed); .is_closed);
} }
*/
} }

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::str; use std::str;
use unicase::UniCase; use unicase::UniCase;
use header::{Header, HeaderFormat}; use header::{Header};
/// `Access-Control-Allow-Credentials` header, part of /// `Access-Control-Allow-Credentials` header, part of
/// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header) /// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)
@@ -62,9 +62,7 @@ impl Header for AccessControlAllowCredentials {
} }
Err(::Error::Header) Err(::Error::Header)
} }
}
impl HeaderFormat for AccessControlAllowCredentials {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("true") f.write_str("true")
} }
@@ -86,4 +84,4 @@ mod test_access_control_allow_credentials {
test_header!(not_bool, vec![b"false"], None); test_header!(not_bool, vec![b"false"], None);
test_header!(only_single, vec![b"true", b"true"], 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); 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 std::fmt::{self, Display};
use header::{Header, HeaderFormat}; use header::{Header};
/// The `Access-Control-Allow-Origin` response header, /// The `Access-Control-Allow-Origin` response header,
/// part of [CORS](http://www.w3.org/TR/cors/#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()))) _ => AccessControlAllowOrigin::Value(try!(String::from_utf8(value.clone())))
}) })
} }
}
impl HeaderFormat for AccessControlAllowOrigin {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
AccessControlAllowOrigin::Any => f.write_str("*"), AccessControlAllowOrigin::Any => f.write_str("*"),

View File

@@ -3,7 +3,7 @@ use std::fmt::{self, Display};
use std::str::{FromStr, from_utf8}; use std::str::{FromStr, from_utf8};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline}; 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) /// `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 { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(scheme) = <S as Scheme>::scheme() { if let Some(scheme) = <S as Scheme>::scheme() {
try!(write!(f, "{} ", scheme)) try!(write!(f, "{} ", scheme))

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
use header::{HeaderFormat, Header, parsing}; use header::{Header, parsing};
/// `Content-Length` header, defined in /// `Content-Length` header, defined in
/// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2) /// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2)
@@ -55,9 +55,7 @@ impl Header for ContentLength {
.unwrap_or(Err(::Error::Header)) .unwrap_or(Err(::Error::Header))
.map(ContentLength) .map(ContentLength)
} }
}
impl HeaderFormat for ContentLength {
#[inline] #[inline]
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) 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::fmt::{self, Display};
use std::str::from_utf8; use std::str::from_utf8;
@@ -61,9 +61,7 @@ impl Header for Cookie {
Err(::Error::Header) Err(::Error::Header)
} }
} }
}
impl HeaderFormat for Cookie {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let cookies = &self.0; let cookies = &self.0;
for (i, cookie) in cookies.iter().enumerate() { for (i, cookie) in cookies.iter().enumerate() {

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
use std::fmt::{self, Display}; 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) /// `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> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
if etag.is_ok() { if let Ok(etag) = etag {
return Ok(IfRange::EntityTag(etag.unwrap())); return Ok(IfRange::EntityTag(etag));
} }
let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw); let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw);
if date.is_ok() { if let Ok(date) = date {
return Ok(IfRange::Date(date.unwrap())); return Ok(IfRange::Date(date));
} }
Err(::Error::Header) Err(::Error::Header)
} }
}
impl HeaderFormat for IfRange {
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
IfRange::EntityTag(ref x) => Display::fmt(x, f), IfRange::EntityTag(ref x) => Display::fmt(x, f),

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::str::FromStr; use std::str::FromStr;
use header::{Header, HeaderFormat}; use header::Header;
use header::parsing::{from_one_raw_str, from_comma_delimited}; use header::parsing::{from_one_raw_str, from_comma_delimited};
/// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) /// `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> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {
from_one_raw_str(raw) from_one_raw_str(raw)
} }
}
impl HeaderFormat for Range {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f) 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::fmt::{self, Display};
use std::str::from_utf8; 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 { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, cookie) in self.0.iter().enumerate() { for (i, cookie) in self.0.iter().enumerate() {
if i != 0 { if i != 0 {

View File

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

View File

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

View File

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

View File

@@ -31,18 +31,17 @@
//! } //! }
//! ``` //! ```
//! //!
//! This works well for simple "string" headers. But the header system //! This works well for simple "string" headers. If you need more control,
//! actually involves 2 parts: parsing, and formatting. If you need to //! you can implement the trait directly.
//! customize either part, you can do so.
//! //!
//! ## `Header` and `HeaderFormat` //! ## Implementing the `Header` trait
//! //!
//! Consider a Do Not Track header. It can be true or false, but it represents //! Consider a Do Not Track header. It can be true or false, but it represents
//! that via the numerals `1` and `0`. //! that via the numerals `1` and `0`.
//! //!
//! ``` //! ```
//! use std::fmt; //! use std::fmt;
//! use hyper::header::{Header, HeaderFormat}; //! use hyper::header::Header;
//! //!
//! #[derive(Debug, Clone, Copy)] //! #[derive(Debug, Clone, Copy)]
//! struct Dnt(bool); //! struct Dnt(bool);
@@ -66,9 +65,7 @@
//! } //! }
//! Err(hyper::Error::Header) //! Err(hyper::Error::Header)
//! } //! }
//! }
//! //!
//! impl HeaderFormat for Dnt {
//! fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { //! fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
//! if self.0 { //! if self.0 {
//! f.write_str("1") //! f.write_str("1")
@@ -113,11 +110,11 @@ type HeaderName = UniCase<CowStr>;
/// ///
/// This trait represents the construction and identification of headers, /// This trait represents the construction and identification of headers,
/// and contains trait-object unsafe methods. /// 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. /// Returns the name of the header field this belongs to.
/// ///
/// This will become an associated constant once available. /// 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. /// Parse a header from a raw stream of bytes.
/// ///
/// It's possible that a request can include a header field more than once, /// 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 /// 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` /// than one field value. If that's the case, you **should** return `None`
/// if `raw.len() > 1`. /// if `raw.len() > 1`.
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self>; fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self> where Self: Sized;
}
/// 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 {
/// Format a header to be output into a TcpStream. /// Format a header to be output into a TcpStream.
/// ///
/// This method is not allowed to introduce an Err not produced /// This method is not allowed to introduce an Err not produced
/// by the passed-in Formatter. /// by the passed-in Formatter.
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result; fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result;
} }
#[doc(hidden)] #[doc(hidden)]
pub trait HeaderClone { 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] #[inline]
fn clone_box(&self) -> Box<HeaderFormat + Send + Sync> { fn clone_box(&self) -> Box<Header + Send + Sync> {
Box::new(self.clone()) Box::new(self.clone())
} }
} }
impl HeaderFormat + Send + Sync { impl Header + Send + Sync {
#[inline] #[inline]
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T { unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
mem::transmute(traitobject::data(self)) 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] #[inline]
fn clone(&self) -> Box<HeaderFormat + Send + Sync> { fn clone(&self) -> Box<Header + Send + Sync> {
self.clone_box() self.clone_box()
} }
} }
@@ -183,6 +172,12 @@ pub struct Headers {
data: HashMap<HeaderName, Item> data: HashMap<HeaderName, Item>
} }
impl Default for Headers {
fn default() -> Headers {
Headers::new()
}
}
impl Headers { impl Headers {
/// Creates a new, empty headers map. /// Creates a new, empty headers map.
@@ -212,8 +207,8 @@ impl Headers {
/// Set a header field to the corresponding value. /// Set a header field to the corresponding value.
/// ///
/// The field is determined by the type of the value being set. /// The field is determined by the type of the value being set.
pub fn set<H: Header + HeaderFormat>(&mut self, value: H) { pub fn set<H: Header>(&mut self, value: H) {
trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), value); trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value));
self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))), self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))),
Item::new_typed(Box::new(value))); Item::new_typed(Box::new(value)));
} }
@@ -259,13 +254,13 @@ impl Headers {
} }
/// Get a reference to the header field's value, if it exists. /// 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>())))) self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed::<H>) .and_then(Item::typed::<H>)
} }
/// Get a mutable reference to the header field's value, if it exists. /// 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>())))) self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed_mut::<H>) .and_then(Item::typed_mut::<H>)
} }
@@ -280,13 +275,13 @@ impl Headers {
/// # let mut headers = Headers::new(); /// # let mut headers = Headers::new();
/// let has_type = headers.has::<ContentType>(); /// 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>())))) self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::<H>()))))
} }
/// Removes a header from the map, if one existed. /// Removes a header from the map, if one existed.
/// Returns true if a header has been removed. /// 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>()); trace!("Headers.remove( {:?} )", header_name::<H>());
self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))).is_some() 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. /// An `Iterator` over the fields in a `Headers` map.
#[allow(missing_debug_implementations)]
pub struct HeadersItems<'a> { pub struct HeadersItems<'a> {
inner: Iter<'a, HeaderName, Item> inner: Iter<'a, HeaderName, Item>
} }
@@ -410,7 +406,7 @@ impl<'a> HeaderView<'a> {
/// Cast the value to a certain Header type. /// Cast the value to a certain Header type.
#[inline] #[inline]
pub fn value<H: Header + HeaderFormat>(&self) -> Option<&'a H> { pub fn value<H: Header>(&self) -> Option<&'a H> {
self.1.typed::<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] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt_header(f) (**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 /// This can be used like so: `format!("{}", HeaderFormatter(&header))` to
/// get the representation of a Header which will be written to an /// get the representation of a Header which will be written to an
/// outgoing `TcpStream`. /// 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] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f) 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] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f) self.0.fmt_header(f)
@@ -519,7 +515,7 @@ mod tests {
use mime::Mime; use mime::Mime;
use mime::TopLevel::Text; use mime::TopLevel::Text;
use mime::SubLevel::Plain; use mime::SubLevel::Plain;
use super::{Headers, Header, HeaderFormat, ContentLength, ContentType, use super::{Headers, Header, ContentLength, ContentType,
Accept, Host, qitem}; Accept, Host, qitem};
use httparse; use httparse;
@@ -597,9 +593,7 @@ mod tests {
None => Err(::Error::Header), None => Err(::Error::Header),
} }
} }
}
impl HeaderFormat for CrazyLength {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let CrazyLength(ref opt, ref value) = *self; let CrazyLength(ref opt, ref value) = *self;
write!(f, "{:?}, {:?}", opt, value) 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 { impl Display for ExtendedValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let encoded_value = 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. //! Pieces pertaining to the HTTP message protocol.
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::time::Duration;
use header::Connection; use header::Connection;
use header::ConnectionOption::{KeepAlive, Close}; use header::ConnectionOption::{KeepAlive, Close};
use header::Headers; use header::Headers;
use method::Method;
use net::Transport;
use status::StatusCode;
use uri::RequestUri;
use version::HttpVersion; use version::HttpVersion;
use version::HttpVersion::{Http10, Http11}; use version::HttpVersion::{Http10, Http11};
#[cfg(feature = "serde-serialization")] #[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer}; 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; mod buffer;
pub mod h2; pub mod channel;
pub mod message; 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. /// The raw status code and reason-phrase.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct RawStatus(pub u16, pub Cow<'static, str>); 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")] #[cfg(feature = "serde-serialization")]
impl Serialize for RawStatus { impl Serialize for RawStatus {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { 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 _ => 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] #[test]
fn test_should_keep_alive() { fn test_should_keep_alive() {

View File

@@ -1,6 +1,7 @@
#![doc(html_root_url = "https://hyperium.github.io/hyper/")] #![doc(html_root_url = "https://hyperium.github.io/hyper/")]
#![cfg_attr(test, deny(missing_docs))] #![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))] #![deny(warnings)]
#![deny(missing_debug_implementations)]
#![cfg_attr(all(test, feature = "nightly"), feature(test))] #![cfg_attr(all(test, feature = "nightly"), feature(test))]
//! # Hyper //! # Hyper
@@ -9,125 +10,9 @@
//! is a low-level typesafe abstraction over raw HTTP, providing an elegant //! is a low-level typesafe abstraction over raw HTTP, providing an elegant
//! layer over "stringly-typed" HTTP. //! layer over "stringly-typed" HTTP.
//! //!
//! Hyper offers both a [Client](client/index.html) and a //! Hyper provides both a [Client](client/index.html) and a
//! [Server](server/index.html) which can be used to drive complex web //! [Server](server/index.html), along with a
//! applications written entirely in Rust. //! [typed Headers system](header/index.html).
//!
//! ## 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`.
//!
extern crate rustc_serialize as serialize; extern crate rustc_serialize as serialize;
extern crate time; extern crate time;
#[macro_use] extern crate url; #[macro_use] extern crate url;
@@ -142,10 +27,11 @@ extern crate serde;
extern crate cookie; extern crate cookie;
extern crate unicase; extern crate unicase;
extern crate httparse; extern crate httparse;
extern crate num_cpus; extern crate rotor;
extern crate spmc;
extern crate traitobject; extern crate traitobject;
extern crate typeable; extern crate typeable;
extern crate solicit; extern crate vecio;
#[macro_use] #[macro_use]
extern crate language_tags; extern crate language_tags;
@@ -163,35 +49,31 @@ extern crate test;
pub use url::Url; pub use url::Url;
pub use client::Client; pub use client::Client;
pub use error::{Result, Error}; pub use error::{Result, Error};
pub use method::Method::{Get, Head, Post, Delete}; pub use http::{Next, Encoder, Decoder, Control};
pub use status::StatusCode::{Ok, BadRequest, NotFound}; 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 server::Server;
pub use uri::RequestUri;
pub use version::HttpVersion;
pub use language_tags::LanguageTag; pub use language_tags::LanguageTag;
macro_rules! todo( macro_rules! rotor_try {
($($arg:tt)*) => (if cfg!(not(ndebug)) { ($e:expr) => ({
trace!("TODO: {:?}", format_args!($($arg)*)) match $e {
}) Ok(v) => v,
); Err(e) => return ::rotor::Response::error(e.into())
}
macro_rules! inspect( });
($name:expr, $value:expr) => ({ }
let v = $value;
trace!("inspect: {:?} = {:?}", $name, v);
v
})
);
#[cfg(test)] #[cfg(test)]
#[macro_use]
mod mock; mod mock;
#[doc(hidden)]
pub mod buffer;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod method; pub mod method;
pub mod header; pub mod header;
pub mod http; mod http;
pub mod net; pub mod net;
pub mod server; pub mod server;
pub mod status; pub mod status;
@@ -203,6 +85,7 @@ pub mod mime {
pub use mime_crate::*; pub use mime_crate::*;
} }
/*
#[allow(unconditional_recursion)] #[allow(unconditional_recursion)]
fn _assert_send<T: Send>() { fn _assert_send<T: Send>() {
_assert_send::<Client>(); _assert_send::<Client>();
@@ -216,3 +99,4 @@ fn _assert_sync<T: Sync>() {
_assert_sync::<Client>(); _assert_sync::<Client>();
_assert_sync::<error::Error>(); _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")] #[cfg(feature = "serde-serialization")]
impl Serialize for Method { impl Serialize for Method {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { 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::cmp;
use std::io::{self, Read, Write, Cursor}; use std::io::{self, Read, Write};
use std::cell::RefCell;
use std::net::{SocketAddr, Shutdown};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::cell::Cell;
use solicit::http::HttpScheme; #[derive(Debug)]
use solicit::http::transport::TransportStream; pub struct Buf {
use solicit::http::frame::{SettingsFrame, Frame}; vec: Vec<u8>
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>>,
} }
impl PartialEq for MockStream { impl Buf {
fn eq(&self, other: &MockStream) -> bool { pub fn new() -> Buf {
self.read.get_ref() == other.read.get_ref() && self.write == other.write Buf {
} vec: vec![]
}
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 Read for MockStream { impl ::std::ops::Deref for Buf {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { type Target = [u8];
if self.error_on_read {
Err(io::Error::new(io::ErrorKind::Other, "mock error")) fn deref(&self) -> &[u8] {
} else { &self.vec
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 Write for MockStream { impl<S: AsRef<[u8]>> PartialEq<S> for Buf {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> { fn eq(&self, other: &S) -> bool {
if self.error_on_write { self.vec == other.as_ref()
Err(io::Error::new(io::ErrorKind::Other, "mock error")) }
} else { }
Write::write(&mut self.write, msg)
} 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<()> { fn flush(&mut self) -> io::Result<()> {
@@ -89,239 +39,89 @@ impl Write for MockStream {
} }
} }
impl NetworkStream for MockStream { impl Read for Buf {
fn peer_addr(&mut self) -> io::Result<SocketAddr> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok("127.0.0.1:1337".parse().unwrap()) (&*self.vec).read(buf)
}
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(())
} }
} }
/// A wrapper around a `MockStream` that allows one to clone it and keep an independent copy to the impl ::vecio::Writev for Buf {
/// same underlying stream. fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
#[derive(Clone)] let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next);
pub struct CloneableMockStream { let mut vec = Vec::with_capacity(cap);
pub inner: Arc<Mutex<MockStream>>, for &buf in bufs {
vec.extend(buf);
}
self.write(&vec)
}
} }
impl Write for CloneableMockStream { #[derive(Debug)]
fn write(&mut self, msg: &[u8]) -> io::Result<usize> { pub struct Async<T> {
self.inner.lock().unwrap().write(msg) 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<()> { fn flush(&mut self) -> io::Result<()> {
self.inner.lock().unwrap().flush() self.inner.flush()
} }
} }
impl Read for CloneableMockStream { impl<T: Write> ::vecio::Writev for Async<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
self.inner.lock().unwrap().read(buf) 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);
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)
}
}
} }
); self.write(&vec)
($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(())
} }
} }
impl MockStream { impl ::std::ops::Deref for Async<Buf> {
/// Creates a new `MockStream` that will return the response described by the parameters as an type Target = [u8];
/// HTTP/2 response. This will also include the correct server preface.
pub fn new_http2_response(status: &[u8], headers: &Headers, body: Option<Vec<u8>>) fn deref(&self) -> &[u8] {
-> MockStream { &self.inner
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)
} }
} }

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 //! HTTP Server
//! //!
//! # Server
//!
//! A `Server` is created to listen on port, parse HTTP requests, and hand //! 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 //! them off to a `Handler`.
//! 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`.
use std::fmt; use std::fmt;
use std::io::{self, ErrorKind, BufWriter, Write}; use std::net::SocketAddr;
use std::net::{SocketAddr, ToSocketAddrs}; use std::sync::Arc;
use std::thread::{self, JoinHandle}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration; use std::time::Duration;
use num_cpus; use rotor::mio::{EventSet, PollOpt};
use rotor::{self, Scope};
pub use self::request::Request; pub use self::request::Request;
pub use self::response::Response; 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; /// A configured `Server` ready to run.
pub mod response; pub struct ServerLoop<A, H> where A: Accept, H: HandlerFactory<A::Output> {
inner: Option<(rotor::Loop<ServerFsm<A, H>>, Context<H>)>,
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,
} }
#[derive(Clone, Copy, Debug)] impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> {
struct Timeouts { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
read: Option<Duration>, f.pad("ServerLoop")
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))
}
} }
} }
macro_rules! try_option( /// A Server that can accept incoming network requests.
($e:expr) => {{ #[derive(Debug)]
match $e { pub struct Server<T: Accept> {
Some(v) => v, listener: T,
None => return None keep_alive: bool,
} idle_timeout: Duration,
}} max_sockets: usize,
); }
impl<L: NetworkListener> Server<L> { impl<T> Server<T> where T: Accept, T::Output: Transport {
/// Creates a new server with the provided handler. /// Creates a new server with the provided Listener.
#[inline] #[inline]
pub fn new(listener: L) -> Server<L> { pub fn new(listener: T) -> Server<T> {
Server { Server {
listener: listener, 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 /// Default is true.
/// to keep the connection alive before dropping it. pub fn keep_alive(mut self, val: bool) -> Server<T> {
/// self.keep_alive = val;
/// Passing `None` will disable keep-alive. self
///
/// Default is enabled with a 5 second timeout.
#[inline]
pub fn keep_alive(&mut self, timeout: Option<Duration>) {
self.timeouts.keep_alive = timeout;
} }
/// Sets the read timeout for all Request reads. /// Sets how long an idle connection will be kept before closing.
pub fn set_read_timeout(&mut self, dur: Option<Duration>) { ///
self.timeouts.read = dur; /// 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. /// Sets the maximum open sockets for this Server.
pub fn set_write_timeout(&mut self, dur: Option<Duration>) { ///
self.timeouts.write = dur; /// 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> { impl Server<HttpListener> { //<H: HandlerFactory<<HttpListener as Accept>::Output>> Server<HttpListener, H> {
/// Creates a new server that will handle `HttpStream`s. /// Creates a new HTTP server config listening on the provided address.
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<Server<HttpListener>> { pub fn http(addr: &SocketAddr) -> ::Result<Server<HttpListener>> {
HttpListener::new(addr).map(Server::new) 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`. /// 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>>> { pub fn https(addr: &SocketAddr, ssl: S) -> ::Result<Server<HttpsListener<S>>> {
HttpsListener::new(addr, ssl).map(Server::new) 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. /// Binds to a socket and starts handling connections.
pub fn handle<H: Handler + 'static>(self, handler: H) -> ::Result<Listening> { pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)>
self.handle_threads(handler, num_cpus::get() * 5 / 4) 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 let mut config = rotor::Config::new();
/// number of threads. config.slab_capacity(self.max_sockets);
pub fn handle_threads<H: Handler + 'static>(self, handler: H, config.mio().notify_capacity(self.max_sockets);
threads: usize) -> ::Result<Listening> { let keep_alive = self.keep_alive;
handle(self, handler, threads) let mut loop_ = rotor::Loop::new(&config).unwrap();
} let mut notifier = None;
}
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 res = Response::new(wrt, &mut res_headers); let notifier = &mut notifier;
res.version = version; loop_.add_machine_with(move |scope| {
self.handler.handle(req, res); *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 let listening = Listening {
// if it wasn't, then the server cannot force it to be true anyways addr: addr,
if keep_alive { shutdown: (shutdown, notifier),
keep_alive = http::should_keep_alive(version, &res_headers); };
} 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 { fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
if req.version == Http11 && req.headers.get() == Some(&Expect::Continue) { match self {
let status = self.handler.check_continue((&req.method, &req.uri, &req.headers)); ServerFsm::Listener(listener, rx) => {
match write!(wrt, "{} {}\r\n\r\n", Http11, status).and_then(|_| wrt.flush()) { match listener.accept() {
Ok(..) => (), Ok(Some(conn)) => {
Err(e) => { rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn)
error!("error writing 100-continue: {:?}", e); },
return false; 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 { fn spawned(self, _scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
debug!("non-100 status ({}) for Expect 100 request", status); match self {
return false; 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 { pub struct Listening {
_guard: Option<JoinHandle<()>>, addr: SocketAddr,
/// The socket addresses that the server is bound to. shutdown: (Arc<AtomicBool>, rotor::Notifier),
pub socket: SocketAddr,
} }
impl fmt::Debug for Listening { impl fmt::Debug for Listening {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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 { impl fmt::Display for Listening {
fn drop(&mut self) { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = self._guard.take().map(|g| g.join()); fmt::Display::fmt(&self.addr, f)
} }
} }
impl Listening { impl Listening {
/// Warning: This function doesn't work. The server remains listening after you called /// The address this server is listening on.
/// it. See https://github.com/hyperium/hyper/issues/338 for more details. pub fn addr(&self) -> &SocketAddr {
/// &self.addr
}
/// Stop the server from listening to its socket address. /// Stop the server from listening to its socket address.
pub fn close(&mut self) -> ::Result<()> { pub fn close(self) {
let _ = self._guard.take(); debug!("closing server {}", self);
debug!("closing server"); self.shutdown.0.store(true, Ordering::Release);
Ok(()) self.shutdown.1.wakeup().unwrap();
} }
} }
/// A handler that can handle incoming requests for a server. /// A trait to react to server events that happen for each message.
pub trait Handler: Sync + Send { ///
/// Receives a `Request`/`Response` pair, and should perform some action on them. /// Each event handler returns it's desired `Next` action.
/// pub trait Handler<T: Transport> {
/// This could reading from the request, and writing to the response. /// This event occurs first, triggering when a `Request` has been parsed.
fn handle<'a, 'k>(&'a self, Request<'a, 'k>, Response<'a, Fresh>); 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`, /// This could IO errors while waiting for events, or a timeout, etc.
/// but can be overridden with custom behavior. fn on_error(&mut self, err: ::Error) -> Next where Self: Sized {
fn check_continue(&self, _: (&Method, &RequestUri, &Headers)) -> StatusCode { debug!("default Handler.on_error({:?})", err);
StatusCode::Continue http::Next::remove()
} }
/// This is run after a connection is received, on a per-connection basis (not a /// This event occurs when this Handler has requested to remove the Transport.
/// per-request basis, as a connection with keep-alive may handle multiple fn on_remove(self, _transport: T) where Self: Sized {
/// requests) debug!("default Handler.on_remove");
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) { }
} }
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>) { /// Used to create a `Handler` when a new message is received by the server.
self(req, res) 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)] #[cfg(test)]
mod tests { 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, //! These are requests that a `hyper::Server` receives, and include its method,
//! target URI, headers, and message body. //! target URI, headers, and message body.
use std::io::{self, Read}; //use std::net::SocketAddr;
use std::net::SocketAddr;
use std::time::Duration;
use buffer::BufReader; use version::HttpVersion;
use net::NetworkStream;
use version::{HttpVersion};
use method::Method; use method::Method;
use header::{Headers, ContentLength, TransferEncoding}; use header::Headers;
use http::h1::{self, Incoming, HttpReader}; use http::{RequestHead, MessageHead, RequestLine};
use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader};
use uri::RequestUri; 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`. /// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
pub struct Request<'a, 'b: 'a> { #[derive(Debug)]
/// The IP address of the remote connection. pub struct Request {
pub remote_addr: SocketAddr, // The IP address of the remote connection.
/// The `Method`, such as `Get`, `Post`, etc. //remote_addr: SocketAddr,
pub method: Method, method: Method,
/// The headers of the incoming request. headers: Headers,
pub headers: Headers, uri: RequestUri,
/// The target request-uri for this request. version: HttpVersion,
pub uri: RequestUri,
/// The version of HTTP for this request.
pub version: HttpVersion,
body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>>
} }
impl<'a, 'b: 'a> Request<'a, 'b> { impl Request {
/// Create a new Request, reading the StartLine and Headers so they are /// The `Method`, such as `Get`, `Post`, etc.
/// 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.
#[inline] #[inline]
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { pub fn method(&self) -> &Method { &self.method }
self.body.get_ref().get_ref().set_read_timeout(timeout)
}
/// Get a reference to the underlying `NetworkStream`. /// The headers of the incoming request.
#[inline] #[inline]
pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> { pub fn headers(&self) -> &Headers { &self.headers }
self.body.get_ref().get_ref().downcast_ref()
}
/// Get a reference to the underlying Ssl stream, if connected /// The target request-uri for this request.
/// 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>>();
/// # }
/// ```
#[inline] #[inline]
pub fn ssl<T: NetworkStream>(&self) -> Option<&T> { pub fn uri(&self) -> &RequestUri { &self.uri }
use ::net::HttpsStream;
match self.downcast_ref() { /// The version of HTTP for this request.
Some(&HttpsStream::Https(ref s)) => Some(s), #[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 _ => 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] #[inline]
pub fn deconstruct(self) -> (SocketAddr, Method, Headers, pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers) {
RequestUri, HttpVersion, (self.method, self.uri, self.version, self.headers)
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());
} }
} }

View File

@@ -2,431 +2,49 @@
//! //!
//! These are responses sent by a `hyper::Server` to clients, after //! These are responses sent by a `hyper::Server` to clients, after
//! receiving a request. //! 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 header;
use http::h1::{LINE_ENDING, HttpWriter}; use http;
use http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter}; use status::StatusCode;
use status;
use net::{Fresh, Streaming};
use version; use version;
/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`. /// 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`. /// 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)] #[derive(Debug)]
pub struct Response<'a, W: Any = Fresh> { pub struct Response<'a> {
/// The HTTP version of this response. head: &'a mut http::MessageHead<StatusCode>,
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>
} }
impl<'a, W: Any> Response<'a, W> { impl<'a> Response<'a> {
/// The status of this response.
#[inline]
pub fn status(&self) -> status::StatusCode { self.status }
/// The headers of this response. /// The headers of this response.
#[inline] #[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] #[inline]
pub fn construct(version: version::HttpVersion, pub fn status(&self) -> &StatusCode {
body: HttpWriter<&'a mut (Write + 'a)>, &self.head.subject
status: status::StatusCode,
headers: &'a mut header::Headers) -> Response<'a, Fresh> {
Response {
status: status,
version: version,
body: body,
headers: headers,
_writing: PhantomData,
}
} }
/// Deconstruct this Response into its constituent parts. /// The HTTP version of this response.
#[inline] #[inline]
pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (Write + 'a)>, pub fn version(&self) -> &version::HttpVersion { &self.head.version }
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 }
/// Get a mutable reference to the Headers. /// Get a mutable reference to the Headers.
#[inline] #[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 }
}
/// Get a mutable reference to the status.
impl<'a> Response<'a, Streaming> {
/// Flushes all writing of a response to the client.
#[inline] #[inline]
pub fn end(self) -> io::Result<()> { pub fn set_status(&mut self, status: StatusCode) {
trace!("ending"); self.head.subject = status;
let (_, body, _, _) = self.deconstruct();
try!(body.end());
Ok(())
} }
} }
impl<'a> Write for Response<'a, Streaming> { /// Creates a new Response that can be used to write to a network stream.
#[inline] pub fn new<'a>(head: &'a mut http::MessageHead<StatusCode>) -> Response<'a> {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> { Response {
debug!("write {:?} bytes", msg.len()); head: head
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,
""
}
} }
} }

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`. /// The class of an HTTP `status-code`.
/// ///
/// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6): /// [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, Star,
} }
impl Default for RequestUri {
fn default() -> RequestUri {
RequestUri::Star
}
}
impl FromStr for RequestUri { impl FromStr for RequestUri {
type Err = Error; type Err = Error;
@@ -67,7 +73,7 @@ impl FromStr for RequestUri {
let mut temp = "http://".to_owned(); let mut temp = "http://".to_owned();
temp.push_str(s); temp.push_str(s);
try!(Url::parse(&temp[..])); try!(Url::parse(&temp[..]));
todo!("compare vs u.authority()"); //TODO: compare vs u.authority()?
Ok(RequestUri::Authority(s.to_owned())) Ok(RequestUri::Authority(s.to_owned()))
} }
} }

View File

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