feat(lib): redesign API to use Futures and Tokio

There are many changes involved with this, but let's just talk about
user-facing changes.

- Creating a `Client` and `Server` now needs a Tokio `Core` event loop
to attach to.
- `Request` and `Response` both no longer implement the
`std::io::{Read,Write}` traits, but instead represent their bodies as a
`futures::Stream` of items, where each item is a `Chunk`.
- The `Client.request` method now takes a `Request`, instead of being
used as a builder, and returns a `Future` that resolves to `Response`.
- The `Handler` trait for servers is no more, and instead the Tokio
`Service` trait is used. This allows interoperability with generic
middleware.

BREAKING CHANGE: A big sweeping set of breaking changes.
This commit is contained in:
Sean McArthur
2016-11-17 17:31:42 -08:00
parent e23689122a
commit 2d2d5574a6
43 changed files with 2775 additions and 5033 deletions

View File

@@ -2,15 +2,10 @@ language: rust
matrix:
fast_finish: true
include:
- os: osx
rust: stable
env: FEATURES="--no-default-features --features security-framework"
- rust: nightly
env: FEATURES="--features nightly"
- rust: beta
- rust: stable
- rust: stable
env: FEATURES="--no-default-features"
cache:
apt: true

View File

@@ -7,49 +7,34 @@ readme = "README.md"
documentation = "http://hyperium.github.io/hyper"
repository = "https://github.com/hyperium/hyper"
license = "MIT"
authors = ["Sean McArthur <sean.monstar@gmail.com>",
"Jonathan Reem <jonathan.reem@gmail.com>"]
authors = ["Sean McArthur <sean.monstar@gmail.com>"]
keywords = ["http", "hyper", "hyperium"]
[dependencies]
futures = "0.1.7"
futures-cpupool = "0.1"
httparse = "1.0"
language-tags = "0.2"
log = "0.3"
mime = "0.2"
rotor = "0.6"
relay = "0.1"
rustc-serialize = "0.3"
spmc = "0.2"
time = "0.1"
tokio-core = "0.1"
tokio-proto = "0.1"
tokio-service = "0.1"
unicase = "1.0"
url = "1.0"
vecio = "0.1"
[dependencies.cookie]
version = "0.3"
default-features = false
[dependencies.openssl]
version = "0.7"
optional = true
[dependencies.openssl-verify]
version = "0.1"
optional = true
[dependencies.security-framework]
version = "0.1.4"
optional = true
[dependencies.serde]
version = "0.8"
optional = true
[dev-dependencies]
env_logger = "0.3"
num_cpus = "1.0"
pretty_env_logger = "0.1"
spmc = "0.2"
[features]
default = ["ssl"]
ssl = ["openssl", "openssl-verify"]
serde-serialization = ["serde", "mime/serde"]
default = []
nightly = []

61
benches/end_to_end.rs Normal file
View File

@@ -0,0 +1,61 @@
#![feature(test)]
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate test;
use futures::{Future, Stream};
use tokio_core::reactor::Core;
use hyper::header::{ContentLength, ContentType};
use hyper::server::{Service, Request, Response};
#[bench]
fn one_request_at_a_time(b: &mut test::Bencher) {
extern crate pretty_env_logger;
let _ = pretty_env_logger::init();
let mut core = Core::new().unwrap();
let handle = core.handle();
let addr = hyper::Server::http(&"127.0.0.1:0".parse().unwrap(), &handle).unwrap()
.handle(|| Ok(Hello), &handle).unwrap();
let mut client = hyper::Client::new(&handle);
let url: hyper::Url = format!("http://{}/get", addr).parse().unwrap();
b.bytes = 160;
b.iter(move || {
let work = client.get(url.clone()).and_then(|res| {
res.body().for_each(|_chunk| {
Ok(())
})
});
core.run(work).unwrap();
});
}
static PHRASE: &'static [u8] = b"Hello, World!";
#[derive(Clone, Copy)]
struct Hello;
impl Service for Hello {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = ::futures::Finished<Response, hyper::Error>;
fn call(&mut self, _req: Request) -> Self::Future {
::futures::finished(
Response::new()
.with_header(ContentLength(PHRASE.len() as u64))
.with_header(ContentType::plaintext())
.with_body(PHRASE)
)
}
}

View File

@@ -1,387 +1,3 @@
% Server Guide
# Hello, World
Let's start off by creating a simple server to just serve a text response
of "Hello, World!" to every request.
```no_run
extern crate hyper;
use hyper::{Decoder, Encoder, HttpStream as Http, Next};
use hyper::server::{Server, Handler, Request, Response};
struct Text(&'static [u8]);
impl Handler<Http> for Text {
fn on_request(&mut self, _req: Request<Http>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<Http>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut Response) -> Next {
use hyper::header::ContentLength;
res.headers_mut().set(ContentLength(self.0.len() as u64));
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<Http>) -> Next {
encoder.write(self.0).unwrap(); // for now
Next::end()
}
}
fn main() {
let addr = "127.0.0.1:0".parse().unwrap();
let (listening, server) = Server::http(&addr).unwrap()
.handle(|_| Text(b"Hello, World")).unwrap();
println!("Listening on http://{}", listening);
server.run()
}
```
There is quite a few concepts here, so let's tackle them one by one.
## Handler
The [`Handler`][Handler] is how you define what should happen during the lifetime
of an HTTP message. We've implemented it for the `Text`, defining what should
happen at each event during an HTTP message.
## Next
Every event in the [`Handler`][Handler] returns a [`Next`][Next]. This signals
to hyper what the `Handler` would wishes to do next, and hyper will call the
appropriate method of the `Handler` when the action is ready again.
So, in our "Hello World" server, you'll notice that when a request comes in, we
have no interest in the `Request` or its body. We immediately just wish to write
"Hello, World!", and be done. So, in `on_request`, we return `Next::write()`,
which tells hyper we wish to write the response.
After `on_response` is called, we ask for `Next::write()` again, because we
still need to write the response body. hyper knows that the next time the
transport is ready to be written, since it already called `on_response`, it
will call `on_response_writable`, which is where we can write the text body.
Once we're all done with the response, we can tell hyper to finish by returning
`Next::end()`. hyper will try to finish flushing all the output, and if the
conditions are met, it may try to use the underlying transport for another
request. This is also known as "keep-alive".
## Server
In the `main` function, a [`Server`][Server] is created that will utilize our
`Hello` handler. We use the default options, though you may wish to peruse
them, especially the `max_sockets` option, as it is conservative by default.
We pass a constructor closure to `Server.handle`, which constructs a `Handler`
to be used for each incoming request.
# Non-blocking IO
## Don't Panic
There is actually a very bad practice in the "Hello, World" example. The usage
of `decoder.write(x).unwrap()` will panic if the write operation fails. A panic
will take down the whole thread, which means the event loop and all other
in-progress requests. So don't do it. It's bad.
What makes it worse, is that the write operation is much more likely to fail
when using non-blocking IO. If the write would block the thread, instead of
doing so, it will return an `io::Error` with the `kind` of `WouldBlock`. These
are expected errors.
## WouldBlock
Instead, we should inspect when there is a read or write error to see if the
`kind` was a `WouldBlock` error. Since `WouldBlock` is so common when using
non-blocking IO, the `Encoder` and `Decoder` provide `try_` methods that will
special case `WouldBlock`, allowing you to treat all `Err` cases as actual
errors.
Additionally, it's possible there was a partial write of the response body, so
we should probably change the example to keep track of it's progress. Can you
see how we should change the example to better handle these conditions?
This will just show the updated `on_response_writable` method, the rest stays
the same:
```no_run
# extern crate hyper;
# use hyper::{Encoder, HttpStream as Http, Next};
# struct Text(&'static [u8]);
# impl Text {
fn on_response_writable(&mut self, encoder: &mut Encoder<Http>) -> Next {
match encoder.try_write(self.0) {
Ok(Some(n)) => {
if n == self.0.len() {
// all done!
Next::end()
} else {
// a partial write!
// with a static array, we can just move our pointer
// another option could be to store a separate index field
self.0 = &self.0[n..];
// there's still more to write, so ask to write again
Next::write()
}
},
Ok(None) => {
// would block, ask to write again
Next::write()
},
Err(e) => {
println!("oh noes, we cannot say hello! {}", e);
// remove (kill) this transport
Next::remove()
}
}
}
# }
# fn main() {}
```
# Routing
What if we wanted to serve different messages depending on the URL of the
request? Say, we wanted to respond with "Hello, World!" to `/hello`, but
"Good-bye" with `/bye`. Let's adjust our example to do that.
```no_run
extern crate hyper;
use hyper::{Decoder, Encoder, HttpStream as Http, Next, StatusCode};
use hyper::server::{Server, Handler, Request, Response};
struct Text(StatusCode, &'static [u8]);
impl Handler<Http> for Text {
fn on_request(&mut self, req: Request<Http>) -> Next {
use hyper::RequestUri;
let path = match *req.uri() {
RequestUri::AbsolutePath { path: ref p, .. } => p,
RequestUri::AbsoluteUri(ref url) => url.path(),
// other 2 forms are for CONNECT and OPTIONS methods
_ => ""
};
match path {
"/hello" => {
self.1 = b"Hello, World!";
},
"/bye" => {
self.1 = b"Good-bye";
},
_ => {
self.0 = StatusCode::NotFound;
self.1 = b"Not Found";
}
}
Next::write()
}
# fn on_request_readable(&mut self, _decoder: &mut Decoder<Http>) -> Next {
# Next::write()
# }
fn on_response(&mut self, res: &mut Response) -> Next {
use hyper::header::ContentLength;
// we send an HTTP Status Code, 200 OK, or 404 Not Found
res.set_status(self.0);
res.headers_mut().set(ContentLength(self.1.len() as u64));
Next::write()
}
# fn on_response_writable(&mut self, encoder: &mut Encoder<Http>) -> Next {
# match encoder.try_write(self.1) {
# Ok(Some(n)) => {
# if n == self.1.len() {
# Next::end()
# } else {
# self.1 = &self.1[n..];
# Next::write()
# }
# },
# Ok(None) => {
# Next::write()
# },
# Err(e) => {
# println!("oh noes, we cannot say hello! {}", e);
# Next::remove()
# }
# }
# }
}
fn main() {
let addr = "127.0.0.1:0".parse().unwrap();
let (listening, server) = Server::http(&addr).unwrap()
.handle(|_| Text(StatusCode::Ok, b"")).unwrap();
println!("Listening on http://{}", listening);
server.run()
}
```
# Waiting
More often than not, a server needs to something "expensive" before it can
provide a response to a request. This may be talking to a database, reading
a file, processing an image, sending its own HTTP request to another server,
or anything else that would impede the event loop thread. These sorts of actions
should be done off the event loop thread, when complete, should notify hyper
that it can now proceed. This is done by combining `Next::wait()` and the
[`Control`][Control].
## Control
The `Control` is provided to the `Handler` constructor; it is the argument we
have so far been ignoring. It's not needed if we don't ever need to wait a
transport. The `Control` is usually sent to a queue, or another thread, or
wherever makes sense to be able to use it when the "blocking" operations are
complete.
To focus on hyper instead of obscure blocking operations, we'll use this useless
sleeping thread to show it works.
```no_run
extern crate hyper;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use hyper::{Control, Next};
fn calculate_ultimate_question(rx: mpsc::Receiver<(Control, mpsc::Sender<&'static [u8]>)>) {
thread::spawn(move || {
while let Ok((ctrl, tx)) = rx.recv() {
thread::sleep(Duration::from_millis(500));
tx.send(b"42").unwrap();
ctrl.ready(Next::write()).unwrap();
}
});
}
# fn main() {}
```
Our worker will spawn a thread that waits on messages. When receiving a message,
after a short nap, it will send back the "result" of the work, and wake up the
waiting transport with a `Next::write()` desire.
## Wait
Finally, let's tie in our worker thread into our `Text` handler:
```no_run
extern crate hyper;
use hyper::{Control, Decoder, Encoder, HttpStream as Http, Next, StatusCode};
use hyper::server::{Server, Handler, Request, Response};
use std::sync::mpsc;
struct Text {
status: StatusCode,
text: &'static [u8],
control: Option<Control>,
worker_tx: mpsc::Sender<(Control, mpsc::Sender<&'static [u8]>)>,
worker_rx: Option<mpsc::Receiver<&'static [u8]>>,
}
impl Handler<Http> for Text {
fn on_request(&mut self, req: Request<Http>) -> Next {
use hyper::RequestUri;
let path = match *req.uri() {
RequestUri::AbsolutePath { path: ref p, .. } => p,
RequestUri::AbsoluteUri(ref url) => url.path(),
_ => ""
};
match path {
"/hello" => {
self.text = b"Hello, World!";
},
"/bye" => {
self.text = b"Good-bye";
},
"/question" => {
let (tx, rx) = mpsc::channel();
// queue work on our worker
self.worker_tx.send((self.control.take().unwrap(), tx)).unwrap();
// save receive channel for response handling
self.worker_rx = Some(rx);
// tell hyper we need to wait until we can continue
return Next::wait();
}
_ => {
self.status = StatusCode::NotFound;
self.text = b"Not Found";
}
}
Next::write()
}
# fn on_request_readable(&mut self, _decoder: &mut Decoder<Http>) -> Next {
# Next::write()
# }
#
fn on_response(&mut self, res: &mut Response) -> Next {
use hyper::header::ContentLength;
res.set_status(self.status);
if let Some(rx) = self.worker_rx.take() {
self.text = rx.recv().unwrap();
}
res.headers_mut().set(ContentLength(self.text.len() as u64));
Next::write()
}
#
# fn on_response_writable(&mut self, encoder: &mut Encoder<Http>) -> Next {
# unimplemented!()
# }
}
# fn calculate_ultimate_question(rx: mpsc::Receiver<(Control, mpsc::Sender<&'static [u8]>)>) {
# use std::sync::mpsc;
# use std::thread;
# use std::time::Duration;
# thread::spawn(move || {
# while let Ok((ctrl, tx)) = rx.recv() {
# thread::sleep(Duration::from_millis(500));
# tx.send(b"42").unwrap();
# ctrl.ready(Next::write()).unwrap();
# }
# });
# }
fn main() {
let (tx, rx) = mpsc::channel();
calculate_ultimate_question(rx);
let addr = "127.0.0.1:0".parse().unwrap();
let (listening, server) = Server::http(&addr).unwrap()
.handle(move |ctrl| Text {
status: StatusCode::Ok,
text: b"",
control: Some(ctrl),
worker_tx: tx.clone(),
worker_rx: None,
}).unwrap();
println!("Listening on http://{}", listening);
server.run()
}
```
[Control]: ../hyper/struct.Control.html
[Handler]: ../hyper/server/trait.Handler.html
[Next]: ../hyper/struct.Next.html
[Server]: ../hyper/server/struct.Server.html

View File

@@ -1,68 +1,20 @@
#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate env_logger;
extern crate pretty_env_logger;
use std::env;
use std::io;
use std::sync::mpsc;
use std::time::Duration;
use std::io::{self, Write};
use hyper::client::{Client, Request, Response, DefaultTransport as HttpStream};
use hyper::header::Connection;
use hyper::{Decoder, Encoder, Next};
use futures::Future;
use futures::stream::Stream;
#[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:example: {}", e);
Next::end()
}
}
}
}
fn on_error(&mut self, err: hyper::Error) -> Next {
println!("ERROR:example: {}", err);
Next::remove()
}
}
use hyper::Client;
fn main() {
env_logger::init().unwrap();
pretty_env_logger::init().unwrap();
let url = match env::args().nth(1) {
Some(url) => url,
@@ -72,11 +24,26 @@ fn main() {
}
};
let (tx, rx) = mpsc::channel();
let client = Client::new().expect("Failed to create a Client");
client.request(url.parse().unwrap(), Dump(tx)).unwrap();
let url = hyper::Url::parse(&url).unwrap();
if url.scheme() != "http" {
println!("This example only works with 'http' URLs.");
return;
}
// wait till done
let _ = rx.recv();
client.close();
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
let client = Client::new(&handle);
let work = client.get(url).and_then(|res| {
println!("Response: {}", res.status());
println!("Headers: \n{}", res.headers());
res.body().for_each(|chunk| {
io::stdout().write_all(&chunk).map_err(From::from)
})
}).map(|_| {
println!("\n\nDone.");
});
core.run(work).unwrap();
}

View File

@@ -1,50 +1,39 @@
#![deny(warnings)]
extern crate hyper;
extern crate env_logger;
extern crate num_cpus;
extern crate futures;
extern crate pretty_env_logger;
//extern crate num_cpus;
use hyper::{Decoder, Encoder, Next, HttpStream};
use hyper::server::{Server, Handler, Request, Response, HttpListener};
use hyper::header::{ContentLength, ContentType};
use hyper::server::{Server, Service, Request, Response};
static PHRASE: &'static [u8] = b"Hello World!";
#[derive(Clone, Copy)]
struct Hello;
impl Handler<HttpStream> for Hello {
fn on_request(&mut self, _: Request<HttpStream>) -> 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()
impl Service for Hello {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = ::futures::Finished<Response, hyper::Error>;
fn call(&self, _req: Request) -> Self::Future {
::futures::finished(
Response::new()
.with_header(ContentLength(PHRASE.len() as u64))
.with_header(ContentType::plaintext())
.with_body(PHRASE)
)
}
}
fn main() {
env_logger::init().unwrap();
let listener = HttpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
let mut handles = Vec::new();
for _ in 0..num_cpus::get() {
let listener = listener.try_clone().unwrap();
handles.push(::std::thread::spawn(move || {
Server::new(listener)
.handle(|_| Hello).unwrap();
}));
}
println!("Listening on http://127.0.0.1:3000");
for handle in handles {
handle.join().unwrap();
}
pretty_env_logger::init().unwrap();
let addr = "127.0.0.1:3000".parse().unwrap();
let _server = Server::standalone(|tokio| {
Server::http(&addr, tokio)?
.handle(|| Ok(Hello), tokio)
}).unwrap();
println!("Listening on http://{}", addr);
}

View File

@@ -1,164 +1,57 @@
#![deny(warnings)]
//#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate env_logger;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;
use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, HttpStream, Next};
use hyper::{Get, Post, StatusCode};
use hyper::header::ContentLength;
use hyper::server::{Server, Handler, Request, Response};
use hyper::server::{Server, Service, Request, Response};
struct Echo {
buf: Vec<u8>,
read_pos: usize,
write_pos: usize,
eof: bool,
route: Route,
}
enum Route {
NotFound,
Index,
Echo(Body),
}
#[derive(Clone, Copy)]
enum Body {
Len(u64),
Chunked
}
static INDEX: &'static [u8] = b"Try POST /echo";
impl Echo {
fn new() -> Echo {
Echo {
buf: vec![0; 4096],
read_pos: 0,
write_pos: 0,
eof: false,
route: Route::NotFound,
}
}
}
#[derive(Clone, Copy)]
struct Echo;
impl Handler<HttpStream> for Echo {
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
match *req.uri() {
RequestUri::AbsolutePath { ref path, .. } => match (req.method(), &path[..]) {
(&Get, "/") | (&Get, "/echo") => {
info!("GET Index");
self.route = Route::Index;
Next::write()
}
(&Post, "/echo") => {
info!("POST Echo");
let mut is_more = true;
self.route = if let Some(len) = req.headers().get::<ContentLength>() {
is_more = **len > 0;
Route::Echo(Body::Len(**len))
} else {
Route::Echo(Body::Chunked)
};
if is_more {
Next::read_and_write()
} else {
Next::write()
}
}
_ => Next::write(),
impl Service for Echo {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = ::futures::Finished<Response, hyper::Error>;
fn call(&self, req: Request) -> Self::Future {
::futures::finished(match (req.method(), req.path()) {
(&Get, Some("/")) | (&Get, Some("/echo")) => {
Response::new()
.with_header(ContentLength(INDEX.len() as u64))
.with_body(INDEX)
},
_ => Next::write()
}
}
fn on_request_readable(&mut self, transport: &mut Decoder<HttpStream>) -> Next {
match self.route {
Route::Echo(ref body) => {
if self.read_pos < self.buf.len() {
match transport.try_read(&mut self.buf[self.read_pos..]) {
Ok(Some(0)) => {
debug!("Read 0, eof");
self.eof = true;
Next::write()
},
Ok(Some(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()
}
}
Ok(None) => Next::read_and_write(),
Err(e) => {
println!("read error {:?}", e);
Next::end()
}
}
} else {
Next::write()
(&Post, Some("/echo")) => {
let mut res = Response::new();
if let Some(len) = req.headers().get::<ContentLength>() {
res.headers_mut().set(len.clone());
}
res.with_body(req.body())
},
_ => {
Response::new()
.with_status(StatusCode::NotFound)
}
_ => unreachable!()
}
})
}
fn on_response(&mut self, res: &mut Response) -> Next {
match self.route {
Route::NotFound => {
res.set_status(StatusCode::NotFound);
Next::end()
}
Route::Index => {
res.headers_mut().set(ContentLength(INDEX.len() as u64));
Next::write()
}
Route::Echo(body) => {
if let Body::Len(len) = body {
res.headers_mut().set(ContentLength(len));
}
Next::read_and_write()
}
}
}
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
match self.route {
Route::Index => {
transport.write(INDEX).unwrap();
Next::end()
}
Route::Echo(..) => {
if self.write_pos < self.read_pos {
match transport.try_write(&self.buf[self.write_pos..self.read_pos]) {
Ok(Some(0)) => panic!("write ZERO"),
Ok(Some(n)) => {
self.write_pos += n;
Next::write()
}
Ok(None) => Next::write(),
Err(e) => {
println!("write error {:?}", e);
Next::end()
}
}
} else if !self.eof {
Next::read()
} else {
Next::end()
}
}
_ => unreachable!()
}
}
}
fn main() {
env_logger::init().unwrap();
let server = Server::http(&"127.0.0.1:1337".parse().unwrap()).unwrap();
let (listening, server) = server.handle(|_| Echo::new()).unwrap();
pretty_env_logger::init().unwrap();
let addr = "127.0.0.1:1337".parse().unwrap();
let (listening, server) = Server::standalone(|tokio| {
Server::http(&addr, tokio)?
.handle(|| Ok(Echo), tokio)
}).unwrap();
println!("Listening on http://{}", listening);
server.run();
}

View File

@@ -1,59 +1,59 @@
use std::collections::hash_map::{HashMap, Entry};
use std::hash::Hash;
use std::fmt;
use std::io;
use std::net::SocketAddr;
//use std::net::SocketAddr;
use rotor::mio::tcp::TcpStream;
use futures::{Future, Poll, Async};
use tokio::io::Io;
use tokio::reactor::Handle;
use tokio::net::{TcpStream, TcpStreamNew};
use tokio_service::Service;
use url::Url;
use net::{HttpStream, HttpsStream, Transport, SslClient};
use super::dns::Dns;
use super::Registration;
use super::dns;
/// 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 + fmt::Debug;
/// Returns the key based off the Url.
fn key(&self, &Url) -> Option<Self::Key>;
/// A connector creates an Io to a remote address..
///
/// This trait is not implemented directly, and only exists to make
/// the intent clearer. A connector should implement `Service` with
/// `Request=Url` and `Response: Io` instead.
pub trait Connect: Service<Request=Url, Error=io::Error> + 'static {
/// The connected Io Stream.
type Output: Io + 'static;
/// A Future that will resolve to the connected Stream.
type Future: Future<Item=Self::Output, Error=io::Error> + 'static;
/// 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)]
/// Configure number of dns workers to use.
fn dns_workers(&mut self, usize);
#[doc(hidden)]
fn register(&mut self, Registration);
fn connect(&self, Url) -> <Self as Connect>::Future;
}
/// A connector for the `http` scheme.
pub struct HttpConnector {
dns: Option<Dns>,
threads: usize,
resolving: HashMap<String, Vec<(&'static str, String, u16)>>,
}
impl<T> Connect for T
where T: Service<Request=Url, Error=io::Error> + 'static,
T::Response: Io,
T::Future: Future<Error=io::Error>,
{
type Output = T::Response;
type Future = T::Future;
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
fn connect(&self, url: Url) -> <Self as Connect>::Future {
self.call(url)
}
}
impl Default for HttpConnector {
fn default() -> HttpConnector {
/// A connector for the `http` scheme.
#[derive(Clone)]
pub struct HttpConnector {
dns: dns::Dns,
handle: Handle,
}
impl HttpConnector {
/// Construct a new HttpConnector.
///
/// Takes number of DNS worker threads.
pub fn new(threads: usize, handle: &Handle) -> HttpConnector {
HttpConnector {
dns: None,
threads: 4,
resolving: HashMap::new(),
dns: dns::Dns::new(threads),
handle: handle.clone(),
}
}
}
@@ -61,79 +61,115 @@ impl Default for HttpConnector {
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);
impl Service for HttpConnector {
type Request = Url;
type Response = TcpStream;
type Error = io::Error;
type Future = HttpConnecting;
fn dns_workers(&mut self, count: usize) {
self.threads = count;
}
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> {
fn call(&self, url: Url) -> Self::Future {
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_with(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, addrs) = match self.dns.as_ref().expect("dns workers lost").resolved() {
Ok(res) => res,
Err(_) => return None
let host = match url.host_str() {
Some(s) => s,
None => return HttpConnecting {
state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, "invalid url"))),
handle: self.handle.clone(),
},
};
//TODO: try all addrs
let addr = addrs.and_then(|mut addrs| Ok(addrs.next().unwrap()));
debug!("Http::resolved <- ({:?}, {:?})", host, addr);
if let Entry::Occupied(mut entry) = self.resolving.entry(host) {
let resolved = entry.get_mut().remove(0);
if entry.get().is_empty() {
entry.remove();
}
let port = resolved.2;
Some((resolved, addr.and_then(|addr| TcpStream::connect(&SocketAddr::new(addr, port))
.map(HttpStream))
))
} else {
trace!("^-- resolved but not in hashmap?");
None
let port = url.port_or_known_default().unwrap_or(80);
HttpConnecting {
state: State::Resolving(self.dns.resolve(host.into(), port)),
handle: self.handle.clone(),
}
}
fn register(&mut self, reg: Registration) {
self.dns = Some(Dns::new(reg.notify, self.threads));
}
/// A Future representing work to connect to a URL.
pub struct HttpConnecting {
state: State,
handle: Handle,
}
enum State {
Resolving(dns::Query),
Connecting(ConnectingTcp),
Error(Option<io::Error>),
}
impl Future for HttpConnecting {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
let state;
match self.state {
State::Resolving(ref mut query) => {
match try!(query.poll()) {
Async::NotReady => return Ok(Async::NotReady),
Async::Ready(addrs) => {
state = State::Connecting(ConnectingTcp {
addrs: addrs,
current: None,
})
}
};
},
State::Connecting(ref mut c) => return c.poll(&self.handle).map_err(From::from),
State::Error(ref mut e) => return Err(e.take().expect("polled more than once")),
}
self.state = state;
}
}
}
/// A connector that can protect HTTP streams using SSL.
#[derive(Debug, Default)]
pub struct HttpsConnector<S: SslClient> {
http: HttpConnector,
ssl: S
impl fmt::Debug for HttpConnecting {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("HttpConnecting")
}
}
struct ConnectingTcp {
addrs: dns::IpAddrs,
current: Option<TcpStreamNew>,
}
impl ConnectingTcp {
// not a Future, since passing a &Handle to poll
fn poll(&mut self, handle: &Handle) -> Poll<TcpStream, io::Error> {
let mut err = None;
loop {
if let Some(ref mut current) = self.current {
match current.poll() {
Ok(ok) => return Ok(ok),
Err(e) => {
trace!("connect error {:?}", e);
err = Some(e);
if let Some(addr) = self.addrs.next() {
debug!("connecting to {:?}", addr);
*current = TcpStream::connect(&addr, handle);
continue;
}
}
}
} else if let Some(addr) = self.addrs.next() {
debug!("connecting to {:?}", addr);
self.current = Some(TcpStream::connect(&addr, handle));
continue;
}
return Err(err.take().expect("missing connect error"));
}
}
}
/*
impl<S: SslClient> HttpsConnector<S> {
/// Create a new connector using the provided SSL implementation.
pub fn new(s: S) -> HttpsConnector<S> {
@@ -143,80 +179,22 @@ impl<S: SslClient> HttpsConnector<S> {
}
}
}
*/
impl<S: SslClient> Connect for HttpsConnector<S> {
type Output = HttpsStream<S::Stream>;
type Key = (&'static str, String, u16);
#[cfg(test)]
mod tests {
use std::io;
use tokio::reactor::Core;
use url::Url;
use super::{Connect, HttpConnector};
fn dns_workers(&mut self, count: usize) {
self.http.dns_workers(count)
#[test]
fn test_non_http_url() {
let mut core = Core::new().unwrap();
let url = Url::parse("file:///home/sean/foo.txt").unwrap();
let connector = HttpConnector::new(1, &core.handle());
assert_eq!(core.run(connector.connect(url)).unwrap_err().kind(), io::ErrorKind::InvalidInput);
}
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_with(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>();
}

View File

@@ -1,96 +1,53 @@
use std::io;
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::thread;
use std::net::{SocketAddr, ToSocketAddrs};
use std::vec;
use ::spmc;
use http::channel;
use ::futures::{Future, Poll};
use ::futures_cpupool::{CpuPool, CpuFuture};
#[derive(Clone)]
pub struct Dns {
tx: spmc::Sender<String>,
rx: channel::Receiver<Answer>,
pool: CpuPool,
}
pub type Answer = (String, io::Result<IpAddrs>);
impl Dns {
pub fn new(threads: usize) -> Dns {
Dns {
pool: CpuPool::new(threads)
}
}
pub fn resolve(&self, host: String, port: u16) -> Query {
Query(self.pool.spawn_fn(move || work(host, port)))
}
}
pub struct Query(CpuFuture<IpAddrs, io::Error>);
impl Future for Query {
type Item = IpAddrs;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll()
}
}
pub struct IpAddrs {
iter: vec::IntoIter<SocketAddr>,
}
impl Iterator for IpAddrs {
type Item = IpAddr;
type Item = SocketAddr;
#[inline]
fn next(&mut self) -> Option<IpAddr> {
self.iter.next().map(|addr| addr.ip())
fn next(&mut self) -> Option<SocketAddr> {
self.iter.next()
}
}
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 type Answer = io::Result<IpAddrs>;
pub fn resolve<T: Into<String>>(&self, hostname: T) {
self.tx.send(hostname.into()).expect("DNS workers all died unexpectedly");
}
pub fn resolved(&self) -> Result<Answer, channel::TryRecvError> {
self.rx.try_recv()
}
}
fn work(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) {
thread::Builder::new().name(String::from("hyper-dns")).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(|i| IpAddrs{ iter: i }) {
Ok(addrs) => (host, Ok(addrs)),
Err(e) => (host, Err(e))
};
if let Err(_) = notify.send(res) {
break;
}
}
worker.shutdown = true;
}).expect("spawn dns thread");
}
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");
}
}
fn work(hostname: String, port: u16) -> Answer {
debug!("resolve {:?}:{:?}", hostname, port);
(&*hostname, port).to_socket_addrs().map(|i| IpAddrs { iter: i })
}

View File

@@ -3,51 +3,49 @@
//! The HTTP `Client` uses asynchronous IO, and utilizes the `Handler` trait
//! to convey when IO events are available for a given request.
use std::collections::{VecDeque, HashMap};
use std::cell::RefCell;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::sync::mpsc;
use std::thread;
use std::rc::Rc;
use std::time::Duration;
use rotor::{self, Scope, EventSet, PollOpt};
use futures::{Poll, Async, Future};
use relay;
use tokio::io::Io;
use tokio::reactor::Handle;
use tokio_proto::BindClient;
use tokio_proto::streaming::Message;
use tokio_proto::streaming::pipeline::ClientProto;
use tokio_proto::util::client_proxy::ClientProxy;
pub use tokio_service::Service;
use header::Host;
use http::{self, Next, RequestHead, ReadyResult};
use net::Transport;
use header::{Headers, Host};
use http::{self, TokioBody};
use method::Method;
use self::pool::{Pool, Pooled};
use uri::RequestUri;
use {Url};
pub use self::connect::{Connect, DefaultConnector, HttpConnector, HttpsConnector, DefaultTransport};
pub use self::connect::{HttpConnector, Connect};
pub use self::request::Request;
pub use self::response::Response;
mod connect;
mod dns;
mod pool;
mod request;
mod response;
/// A Client to make outgoing HTTP requests.
pub struct Client<H> {
tx: http::channel::Sender<Notify<H>>,
// If the Connector is clone, then the Client can be clone easily.
#[derive(Clone)]
pub struct Client<C> {
connector: C,
handle: Handle,
pool: Pool<TokioClient>,
}
impl<H> Clone for Client<H> {
fn clone(&self) -> Client<H> {
Client {
tx: self.tx.clone()
}
}
}
impl<H> fmt::Debug for Client<H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("Client")
}
}
impl<H> Client<H> {
impl Client<HttpConnector> {
/// Configure a Client.
///
/// # Example
@@ -56,116 +54,218 @@ impl<H> Client<H> {
/// # use hyper::Client;
/// let client = Client::configure()
/// .keep_alive(true)
/// .max_sockets(10_000)
/// .build().unwrap();
/// ```
#[inline]
pub fn configure() -> Config<DefaultConnector> {
pub fn configure() -> Config<UseDefaultConnector> {
Config::default()
}
}
impl<H: Handler<<DefaultConnector as Connect>::Output>> Client<H> {
impl Client<HttpConnector> {
/// Create a new Client with the default config.
#[inline]
pub fn new() -> ::Result<Client<H>> {
Client::<H>::configure().build()
pub fn new(handle: &Handle) -> Client<HttpConnector> {
Client::configure().build(handle)
}
}
impl<H: Send> Client<H> {
impl<C: Connect> Client<C> {
/// Create a new client with a specific connector.
fn configured<T, C>(config: Config<C>) -> ::Result<Client<H>>
where H: Handler<T>,
T: Transport,
C: Connect<Output=T> + Send + 'static {
let mut rotor_config = rotor::Config::new();
rotor_config.slab_capacity(config.max_sockets);
rotor_config.mio().notify_capacity(config.max_sockets);
let keep_alive = config.keep_alive;
let connect_timeout = config.connect_timeout;
let mut loop_ = try!(rotor::Loop::new(&rotor_config));
let mut notifier = None;
let mut connector = config.connector;
connector.dns_workers(config.dns_workers);
{
let not = &mut notifier;
loop_.add_machine_with(move |scope| {
let (tx, rx) = http::channel::new(scope.notifier());
let (dns_tx, dns_rx) = http::channel::share(&tx);
*not = Some(tx);
connector.register(Registration {
notify: (dns_tx, dns_rx),
});
rotor::Response::ok(ClientFsm::Connector(connector, rx))
}).unwrap();
#[inline]
fn configured(config: Config<C>, handle: &Handle) -> Client<C> {
Client {
connector: config.connector,
handle: handle.clone(),
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
}
let notifier = notifier.expect("loop.add_machine_with failed");
let _handle = try!(thread::Builder::new().name("hyper-client".to_owned()).spawn(move || {
loop_.run(Context {
connect_timeout: connect_timeout,
keep_alive: keep_alive,
idle_conns: HashMap::new(),
queue: HashMap::new(),
awaiting_slot: VecDeque::new(),
}).unwrap()
}));
Ok(Client {
//handle: Some(handle),
tx: notifier,
})
}
/// Build a new request using this Client.
///
/// ## Error
///
/// If the event loop thread has died, or the queue is full, a `ClientError`
/// will be returned.
pub fn request(&self, url: Url, handler: H) -> Result<(), ClientError<H>> {
self.tx.send(Notify::Connect(url, handler)).map_err(|e| {
match e.0 {
Some(Notify::Connect(url, handler)) => ClientError(Some((url, handler))),
_ => ClientError(None)
/// Send a GET Request using this Client.
#[inline]
pub fn get(&self, url: Url) -> FutureResponse {
self.request(Request::new(Method::Get, url))
}
/// Send a constructed Request using this Client.
#[inline]
pub fn request(&self, req: Request) -> FutureResponse {
self.call(req)
}
}
/// A `Future` that will resolve to an HTTP Response.
pub struct FutureResponse(Box<Future<Item=Response, Error=::Error> + 'static>);
impl fmt::Debug for FutureResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("Future<Response>")
}
}
impl Future for FutureResponse {
type Item = Response;
type Error = ::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll()
}
}
impl<C: Connect> Service for Client<C> {
type Request = Request;
type Response = Response;
type Error = ::Error;
type Future = FutureResponse;
fn call(&self, req: Request) -> Self::Future {
let url = req.url().clone();
let (mut head, body) = request::split(req);
let mut headers = Headers::new();
headers.set(Host {
hostname: url.host_str().unwrap().to_owned(),
port: url.port().or(None),
});
headers.extend(head.headers.iter());
head.subject.1 = RequestUri::AbsolutePath {
path: url.path().to_owned(),
query: url.query().map(ToOwned::to_owned),
};
head.headers = headers;
let checkout = self.pool.checkout(&url[..::url::Position::BeforePath]);
let connect = {
let handle = self.handle.clone();
let pool = self.pool.clone();
let pool_key = Rc::new(url[..::url::Position::BeforePath].to_owned());
self.connector.connect(url)
.map(move |io| {
let (tx, rx) = relay::channel();
let client = HttpClient {
client_rx: RefCell::new(Some(rx)),
}.bind_client(&handle, io);
let pooled = pool.pooled(pool_key, client);
tx.complete(pooled.clone());
pooled
})
};
let race = checkout.select(connect)
.map(|(client, _work)| client)
.map_err(|(e, _work)| {
// the Pool Checkout cannot error, so the only error
// is from the Connector
// XXX: should wait on the Checkout? Problem is
// that if the connector is failing, it may be that we
// never had a pooled stream at all
e.into()
});
let req = race.and_then(move |client| {
let msg = match body {
Some(body) => {
Message::WithBody(head, body.into())
},
None => Message::WithoutBody(head),
};
client.call(msg)
});
FutureResponse(Box::new(req.map(|msg| {
match msg {
Message::WithoutBody(head) => response::new(head, None),
Message::WithBody(head, body) => response::new(head, Some(body.into())),
}
})
})))
}
/// Close the Client loop.
pub fn close(self) {
// Most errors mean that the Receivers are already dead, which would
// imply the EventLoop panicked.
let _ = self.tx.send(Notify::Shutdown);
}
impl<C> fmt::Debug for Client<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("Client")
}
}
type TokioClient = ClientProxy<Message<http::RequestHead, TokioBody>, Message<http::ResponseHead, TokioBody>, ::Error>;
struct HttpClient {
client_rx: RefCell<Option<relay::Receiver<Pooled<TokioClient>>>>,
}
impl<T: Io + 'static> ClientProto<T> for HttpClient {
type Request = http::RequestHead;
type RequestBody = http::Chunk;
type Response = http::ResponseHead;
type ResponseBody = http::Chunk;
type Error = ::Error;
type Transport = http::Conn<T, http::ClientTransaction, Pooled<TokioClient>>;
type BindTransport = BindingClient<T>;
fn bind_transport(&self, io: T) -> Self::BindTransport {
BindingClient {
rx: self.client_rx.borrow_mut().take().expect("client_rx was lost"),
io: Some(io),
}
}
}
struct BindingClient<T> {
rx: relay::Receiver<Pooled<TokioClient>>,
io: Option<T>,
}
impl<T: Io + 'static> Future for BindingClient<T> {
type Item = http::Conn<T, http::ClientTransaction, Pooled<TokioClient>>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.rx.poll() {
Ok(Async::Ready(client)) => Ok(Async::Ready(
http::Conn::new(self.io.take().expect("binding client io lost"), client)
)),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(_canceled) => unreachable!(),
}
}
}
/// Configuration for a Client
#[derive(Debug, Clone)]
pub struct Config<C> {
connect_timeout: Duration,
//connect_timeout: Duration,
connector: C,
keep_alive: bool,
keep_alive_timeout: Option<Duration>,
//TODO: make use of max_idle config
max_idle: usize,
max_sockets: usize,
dns_workers: usize,
}
impl<C> Config<C> where C: Connect + Send + 'static {
/// Phantom type used to signal that `Config` should create a `HttpConnector`.
#[derive(Debug, Clone, Copy)]
pub struct UseDefaultConnector(());
impl Config<UseDefaultConnector> {
fn default() -> Config<UseDefaultConnector> {
Config {
//connect_timeout: Duration::from_secs(10),
connector: UseDefaultConnector(()),
keep_alive: true,
keep_alive_timeout: Some(Duration::from_secs(90)),
max_idle: 5,
}
}
}
impl<C> Config<C> {
/// Set the `Connect` type to be used.
#[inline]
pub fn connector<CC: Connect>(self, val: CC) -> Config<CC> {
Config {
connect_timeout: self.connect_timeout,
//connect_timeout: self.connect_timeout,
connector: val,
keep_alive: self.keep_alive,
keep_alive_timeout: Some(Duration::from_secs(60 * 2)),
keep_alive_timeout: self.keep_alive_timeout,
max_idle: self.max_idle,
max_sockets: self.max_sockets,
dns_workers: self.dns_workers,
}
}
@@ -189,15 +289,7 @@ impl<C> Config<C> where C: Connect + Send + 'static {
self
}
/// Set the max table size allocated for holding on to live sockets.
///
/// Default is 1024.
#[inline]
pub fn max_sockets(mut self, val: usize) -> Config<C> {
self.max_sockets = val;
self
}
/*
/// Set the timeout for connecting to a URL.
///
/// Default is 10 seconds.
@@ -206,584 +298,25 @@ impl<C> Config<C> where C: Connect + Send + 'static {
self.connect_timeout = val;
self
}
*/
}
/// Set number of Dns workers to use for this client
///
/// Default is 4
#[inline]
pub fn dns_workers(mut self, workers: usize) -> Config<C> {
self.dns_workers = workers;
self
}
impl<C: Connect> Config<C> {
/// Construct the Client with this configuration.
#[inline]
pub fn build<H: Handler<C::Output>>(self) -> ::Result<Client<H>> {
Client::configured(self)
pub fn build(self, handle: &Handle) -> Client<C> {
Client::configured(self, handle)
}
}
impl Default for Config<DefaultConnector> {
fn default() -> Config<DefaultConnector> {
Config {
connect_timeout: Duration::from_secs(10),
connector: DefaultConnector::default(),
keep_alive: true,
keep_alive_timeout: Some(Duration::from_secs(60 * 2)),
max_idle: 5,
max_sockets: 1024,
dns_workers: 4,
}
impl Config<UseDefaultConnector> {
/// Construct the Client with this configuration.
#[inline]
pub fn build(self, handle: &Handle) -> Client<HttpConnector> {
self.connector(HttpConnector::new(4, handle)).build(handle)
}
}
/// An error that can occur when trying to queue a request.
#[derive(Debug)]
pub struct ClientError<H>(Option<(Url, H)>);
impl<H> ClientError<H> {
/// If the event loop was down, the `Url` and `Handler` can be recovered
/// from this method.
pub fn recover(self) -> Option<(Url, H)> {
self.0
}
}
impl<H: fmt::Debug + ::std::any::Any> ::std::error::Error for ClientError<H> {
fn description(&self) -> &str {
"Cannot queue request"
}
}
impl<H> fmt::Display for ClientError<H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Cannot queue request")
}
}
/// A trait to react to client events that happen for each message.
///
/// Each event handler returns it's desired `Next` action.
pub trait Handler<T: Transport>: Send + 'static {
/// This event occurs first, triggering when a `Request` head can be written..
fn on_request(&mut self, request: &mut Request) -> http::Next;
/// This event occurs each time the `Request` is ready to be written to.
fn on_request_writable(&mut self, request: &mut http::Encoder<T>) -> http::Next;
/// This event occurs after the first time this handler signals `Next::read()`,
/// and a Response has been parsed.
fn on_response(&mut self, response: Response) -> http::Next;
/// This event occurs each time the `Response` is ready to be read from.
fn on_response_readable(&mut self, response: &mut http::Decoder<T>) -> http::Next;
/// This event occurs whenever an `Error` occurs outside of the other events.
///
/// This could IO errors while waiting for events, or a timeout, etc.
fn on_error(&mut self, err: ::Error) -> http::Next {
debug!("default Handler.on_error({:?})", err);
http::Next::remove()
}
/// This event occurs when this Handler has requested to remove the Transport.
fn on_remove(self, _transport: T) where Self: Sized {
debug!("default Handler.on_remove");
}
/// Receive a `Control` to manage waiting for this request.
fn on_control(&mut self, _: http::Control) {
debug!("default Handler.on_control()");
}
}
struct Message<H: Handler<T>, T: Transport> {
handler: H,
url: Option<Url>,
_marker: PhantomData<T>,
}
impl<H: Handler<T>, T: Transport> http::MessageHandler<T> for Message<H, T> {
type Message = http::ClientMessage;
fn on_outgoing(&mut self, head: &mut RequestHead) -> Next {
let url = self.url.take().expect("Message.url is missing");
if let Some(host) = url.host_str() {
head.headers.set(Host {
hostname: host.to_owned(),
port: url.port(),
});
}
head.subject.1 = RequestUri::AbsolutePath {
path: url.path().to_owned(),
query: url.query().map(|q| q.to_owned()),
};
let mut req = self::request::new(head);
self.handler.on_request(&mut req)
}
fn on_encode(&mut self, transport: &mut http::Encoder<T>) -> Next {
self.handler.on_request_writable(transport)
}
fn on_incoming(&mut self, head: http::ResponseHead, _: &T) -> Next {
trace!("on_incoming {:?}", head);
let resp = response::new(head);
self.handler.on_response(resp)
}
fn on_decode(&mut self, transport: &mut http::Decoder<T>) -> Next {
self.handler.on_response_readable(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);
}
}
struct Context<K, H, C: Connect> {
connect_timeout: Duration,
keep_alive: bool,
idle_conns: HashMap<K, VecDeque<http::Control>>,
queue: HashMap<K, VecDeque<Queued<H>>>,
awaiting_slot: VecDeque<(C::Key, C::Output)>,
}
/// Macro for advancing state of a ClientFsm::Socket
///
/// This was previously a method on Context, but due to eviction needs, this
/// block now needs access to the registration APIs on rotor::Scope.
macro_rules! conn_response {
($scope:expr, $conn:expr, $time:expr) => {{
match $conn {
Some((conn, timeout)) => {
//TODO: HTTP2: a connection doesn't need to be idle to be used for a second stream
if conn.is_idle() {
$scope.idle_conns.entry(conn.key().clone()).or_insert_with(VecDeque::new)
.push_back(conn.control());
}
match timeout {
Some(dur) => rotor::Response::ok(ClientFsm::Socket(conn))
.deadline($time + dur),
None => rotor::Response::ok(ClientFsm::Socket(conn)),
}
}
None => {
if let Some((key, socket)) = $scope.awaiting_slot.pop_front() {
rotor_try!($scope.register(&socket, EventSet::writable() | EventSet::hup(), PollOpt::level()));
rotor::Response::ok(ClientFsm::Connecting((key, socket)))
} else {
rotor::Response::done()
}
}
}
}}
}
impl<K: http::Key, H, C: Connect> Context<K, H, C> {
fn pop_queue(&mut self, key: &K) -> Option<Queued<H>> {
let mut should_remove = false;
let queued = {
self.queue.get_mut(key).and_then(|vec| {
let queued = vec.pop_front();
if vec.is_empty() {
should_remove = true;
}
queued
})
};
if should_remove {
self.queue.remove(key);
}
queued
}
}
impl<K, H, T, C> http::MessageHandlerFactory<K, T> for Context<K, H, C>
where K: http::Key,
H: Handler<T>,
T: Transport,
C: Connect
{
type Output = Message<H, T>;
fn create(&mut self, seed: http::Seed<K>) -> Option<Self::Output> {
let key = seed.key();
self.pop_queue(key).map(|queued| {
let (url, mut handler) = (queued.url, queued.handler);
handler.on_control(seed.control());
Message {
handler: handler,
url: Some(url),
_marker: PhantomData,
}
})
}
fn keep_alive_interest(&self) -> Next {
Next::wait()
}
}
enum Notify<T> {
Connect(Url, T),
Shutdown,
}
enum ClientFsm<C, H>
where C: Connect,
C::Output: Transport,
H: Handler<C::Output> {
Connector(C, http::channel::Receiver<Notify<H>>),
Connecting((C::Key, C::Output)),
Socket(http::Conn<C::Key, C::Output, Message<H, C::Output>>)
}
unsafe impl<C, H> Send for ClientFsm<C, H>
where
C: Connect + Send,
//C::Key, // Key doesn't need to be Send
C::Output: Transport, // Tranport doesn't need to be Send
H: Handler<C::Output> + Send
{}
impl<C, H> rotor::Machine for ClientFsm<C, H>
where C: Connect,
C::Key: fmt::Debug,
C::Output: Transport,
H: Handler<C::Output> {
type Context = Context<C::Key, H, C>;
type Seed = (C::Key, C::Output);
fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, rotor::Void> {
rotor_try!(scope.register(&seed.1, EventSet::writable() | EventSet::hup(), PollOpt::level()));
rotor::Response::ok(ClientFsm::Connecting(seed))
}
fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ClientFsm::Socket(conn) => {
let mut conn = Some(conn);
loop {
match conn.take().unwrap().ready(events, scope) {
ReadyResult::Done(res) => {
let now = scope.now();
return conn_response!(scope, res, now);
},
ReadyResult::Continue(c) => conn = Some(c),
}
}
},
ClientFsm::Connecting(mut seed) => {
if events.is_error() || events.is_hup() {
if let Some(err) = seed.1.take_socket_error().err() {
debug!("error while connecting: {:?}", err);
scope.pop_queue(&seed.0).map(move |mut queued| queued.handler.on_error(::Error::Io(err)));
} else {
trace!("connecting is_error, but no socket error");
}
rotor::Response::done()
} else if events.is_writable() {
if scope.queue.contains_key(&seed.0) {
trace!("connected and writable {:?}", seed.0);
rotor::Response::ok(
ClientFsm::Socket(
http::Conn::new(
seed.0,
seed.1,
Next::write().timeout(scope.connect_timeout),
scope.notifier(),
scope.now()
).keep_alive(scope.keep_alive)
)
)
} else {
trace!("connected, but queued handler is gone: {:?}", seed.0); // probably took too long connecting
rotor::Response::done()
}
} else {
// spurious?
rotor::Response::ok(ClientFsm::Connecting(seed))
}
}
ClientFsm::Connector(..) => {
unreachable!("Connector can never be ready")
},
}
}
fn spawned(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ClientFsm::Connector(..) => self.connect(scope),
other => rotor::Response::ok(other)
}
}
fn spawn_error(
self,
scope: &mut Scope<Self::Context>,
error: rotor::SpawnError<Self::Seed>
) -> rotor::Response<Self, Self::Seed> {
// see if there's an idle connections that can be terminated. If yes, put this seed on a
// list waiting for empty slot.
if let rotor::SpawnError::NoSlabSpace((key, socket)) = error {
if let Some(mut queued) = scope.pop_queue(&key) {
trace!("attempting to remove an idle socket");
// Remove an idle connection. Any connection. Just make some space
// for the new request.
let mut remove_keys = Vec::new();
let mut found_idle = false;
// Check all idle connections regardless of origin
for (key, idle) in scope.idle_conns.iter_mut() {
// Pop from the front since those are lease recently used
while let Some(ctrl) = idle.pop_front() {
// Signal connection to close. An err here means the
// socket is already dead can should be tossed.
if ctrl.ready(Next::remove()).is_ok() {
found_idle = true;
break;
}
}
// This list is empty, mark it for removal
if idle.is_empty() {
remove_keys.push(key.to_owned());
}
// if found, stop looking for an idle connection.
if found_idle {
break;
}
}
trace!("idle conns: {:?}", scope.idle_conns);
// Remove empty idle lists.
for key in &remove_keys {
scope.idle_conns.remove(&key);
}
if found_idle {
// A socket should be evicted soon; put it on a queue to
// consume newly freed slot. Also need to put the Queued<H>
// back onto front of queue.
scope.awaiting_slot.push_back((key.clone(), socket));
scope.queue
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(queued);
} else {
// Couldn't evict a socket, just run the error handler.
debug!("Error spawning state machine; slab full and no sockets idle");
let _ = queued.handler.on_error(::Error::Full);
}
}
}
self.connect(scope)
}
fn timeout(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
trace!("timeout now = {:?}", scope.now());
match self {
ClientFsm::Connector(..) => {
let now = scope.now();
let mut empty_keys = Vec::new();
{
for (key, mut vec) in &mut scope.queue {
while !vec.is_empty() && vec[0].deadline <= now {
vec.pop_front()
.map(|mut queued| queued.handler.on_error(::Error::Timeout));
}
if vec.is_empty() {
empty_keys.push(key.clone());
}
}
}
for key in &empty_keys {
scope.queue.remove(key);
}
match self.deadline(scope) {
Some(deadline) => {
rotor::Response::ok(self).deadline(deadline)
},
None => rotor::Response::ok(self)
}
}
ClientFsm::Connecting(..) => unreachable!(),
ClientFsm::Socket(conn) => {
let res = conn.timeout(scope);
let now = scope.now();
conn_response!(scope, res, now)
}
}
}
fn wakeup(self, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ClientFsm::Connector(..) => {
self.connect(scope)
},
ClientFsm::Socket(conn) => {
let res = conn.wakeup(scope);
let now = scope.now();
conn_response!(scope, res, now)
},
ClientFsm::Connecting(..) => unreachable!("connecting sockets should not be woken up")
}
}
}
impl<C, H> ClientFsm<C, H>
where C: Connect,
C::Key: fmt::Debug,
C::Output: Transport,
H: Handler<C::Output> {
fn connect(self, scope: &mut rotor::Scope<<Self as rotor::Machine>::Context>) -> rotor::Response<Self, <Self as rotor::Machine>::Seed> {
match self {
ClientFsm::Connector(mut connector, rx) => {
if let Some((key, res)) = connector.connected() {
match res {
Ok(socket) => {
trace!("connecting {:?}", key);
return rotor::Response::spawn(ClientFsm::Connector(connector, rx), (key, socket));
},
Err(e) => {
trace!("connect error = {:?}", e);
scope.pop_queue(&key).map(|mut queued| queued.handler.on_error(::Error::Io(e)));
}
}
}
loop {
match rx.try_recv() {
Ok(Notify::Connect(url, mut handler)) => {
// check pool for sockets to this domain
if let Some(key) = connector.key(&url) {
let mut remove_idle = false;
let mut woke_up = false;
if let Some(mut idle) = scope.idle_conns.get_mut(&key) {
// Pop from back since those are most recently used. Connections
// at the front are allowed to expire.
while let Some(ctrl) = idle.pop_back() {
// err means the socket has since died
if ctrl.ready(Next::write()).is_ok() {
woke_up = true;
break;
}
}
remove_idle = idle.is_empty();
}
if remove_idle {
scope.idle_conns.remove(&key);
}
if woke_up {
trace!("woke up idle conn for '{}'", url);
let deadline = scope.now() + scope.connect_timeout;
scope.queue
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(Queued {
deadline: deadline,
handler: handler,
url: url
});
continue;
}
} else {
// this connector cannot handle this url anyways
let _ = handler.on_error(io::Error::new(io::ErrorKind::InvalidInput, "invalid url for connector").into());
continue;
}
// no exist connection, call connector
match connector.connect(&url) {
Ok(key) => {
let deadline = scope.now() + scope.connect_timeout;
scope.queue
.entry(key)
.or_insert_with(VecDeque::new)
.push_back(Queued {
deadline: deadline,
handler: handler,
url: url
});
}
Err(e) => {
let _todo = handler.on_error(e.into());
trace!("Connect error, next={:?}", _todo);
continue;
}
}
}
Ok(Notify::Shutdown) => {
scope.shutdown_loop();
return rotor::Response::done()
},
Err(mpsc::TryRecvError::Disconnected) => {
// if there is no way to send additional requests,
// what more can the loop do? i suppose we should
// shutdown.
scope.shutdown_loop();
return rotor::Response::done()
}
Err(mpsc::TryRecvError::Empty) => {
// spurious wakeup or loop is done
let fsm = ClientFsm::Connector(connector, rx);
return match fsm.deadline(scope) {
Some(deadline) => {
rotor::Response::ok(fsm).deadline(deadline)
},
None => rotor::Response::ok(fsm)
};
}
}
}
},
other => rotor::Response::ok(other)
}
}
fn deadline(&self, scope: &mut rotor::Scope<<Self as rotor::Machine>::Context>) -> Option<rotor::Time> {
match *self {
ClientFsm::Connector(..) => {
let mut earliest = None;
for vec in scope.queue.values() {
for queued in vec {
match earliest {
Some(ref mut earliest) => {
if queued.deadline < *earliest {
*earliest = queued.deadline;
}
}
None => earliest = Some(queued.deadline)
}
}
}
trace!("deadline = {:?}, now = {:?}", earliest, scope.now());
earliest
}
_ => None
}
}
}
struct Queued<H> {
deadline: rotor::Time,
handler: H,
url: Url,
}
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct Registration {
notify: (http::channel::Sender<self::dns::Answer>, http::channel::Receiver<self::dns::Answer>),
}
#[cfg(test)]
mod tests {

353
src/client/pool.rs Normal file
View File

@@ -0,0 +1,353 @@
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::io;
use std::ops::{Deref, DerefMut, BitAndAssign};
use std::rc::Rc;
use std::time::{Duration, Instant};
use futures::{Future, Async, Poll};
use relay;
use http::{KeepAlive, KA};
pub struct Pool<T> {
inner: Rc<RefCell<PoolInner<T>>>,
}
struct PoolInner<T> {
enabled: bool,
idle: HashMap<Rc<String>, Vec<Entry<T>>>,
parked: HashMap<Rc<String>, VecDeque<relay::Sender<Entry<T>>>>,
timeout: Option<Duration>,
}
impl<T: Clone> Pool<T> {
pub fn new(enabled: bool, timeout: Option<Duration>) -> Pool<T> {
Pool {
inner: Rc::new(RefCell::new(PoolInner {
enabled: enabled,
idle: HashMap::new(),
parked: HashMap::new(),
timeout: timeout,
})),
}
}
pub fn checkout(&self, key: &str) -> Checkout<T> {
Checkout {
key: Rc::new(key.to_owned()),
pool: self.clone(),
parked: None,
}
}
fn put(&mut self, key: Rc<String>, entry: Entry<T>) {
trace!("Pool::put {:?}", key);
let mut remove_parked = false;
let tx = self.inner.borrow_mut().parked.get_mut(&key).and_then(|parked| {
let mut ret = None;
while let Some(tx) = parked.pop_front() {
if !tx.is_canceled() {
ret = Some(tx);
break;
}
trace!("Pool::put removing canceled parked {:?}", key);
}
remove_parked = parked.is_empty();
ret
});
if remove_parked {
self.inner.borrow_mut().parked.remove(&key);
}
if let Some(tx) = tx {
trace!("Pool::put found parked {:?}", key);
tx.complete(entry);
} else {
self.inner.borrow_mut()
.idle.entry(key)
.or_insert(Vec::new())
.push(entry);
}
}
pub fn pooled(&self, key: Rc<String>, value: T) -> Pooled<T> {
trace!("Pool::pooled {:?}", key);
Pooled {
entry: Entry {
value: value,
is_reused: false,
status: Rc::new(Cell::new(KA::Busy)),
},
key: key,
pool: self.clone(),
}
}
fn is_enabled(&self) -> bool {
self.inner.borrow().enabled
}
fn reuse(&self, key: Rc<String>, mut entry: Entry<T>) -> Pooled<T> {
trace!("Pool::reuse {:?}", key);
entry.is_reused = true;
entry.status.set(KA::Busy);
Pooled {
entry: entry,
key: key,
pool: self.clone(),
}
}
fn park(&mut self, key: Rc<String>, tx: relay::Sender<Entry<T>>) {
trace!("Pool::park {:?}", key);
self.inner.borrow_mut()
.parked.entry(key)
.or_insert(VecDeque::new())
.push_back(tx);
}
}
impl<T> Clone for Pool<T> {
fn clone(&self) -> Pool<T> {
Pool {
inner: self.inner.clone(),
}
}
}
#[derive(Clone)]
pub struct Pooled<T> {
entry: Entry<T>,
key: Rc<String>,
pool: Pool<T>,
}
impl<T> Deref for Pooled<T> {
type Target = T;
fn deref(&self) -> &T {
&self.entry.value
}
}
impl<T> DerefMut for Pooled<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.entry.value
}
}
impl<T: Clone> KeepAlive for Pooled<T> {
fn busy(&mut self) {
self.entry.status.set(KA::Busy);
}
fn disable(&mut self) {
self.entry.status.set(KA::Disabled);
}
fn idle(&mut self) {
let previous = self.status();
self.entry.status.set(KA::Idle(Instant::now()));
if let KA::Idle(..) = previous {
trace!("Pooled::idle already idle");
return;
}
self.entry.is_reused = true;
if self.pool.is_enabled() {
self.pool.put(self.key.clone(), self.entry.clone());
}
}
fn status(&self) -> KA {
self.entry.status.get()
}
}
impl<T> fmt::Debug for Pooled<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Pooled")
.field("status", &self.entry.status.get())
.field("key", &self.key)
.finish()
}
}
impl<T: Clone> BitAndAssign<bool> for Pooled<T> {
fn bitand_assign(&mut self, enabled: bool) {
if !enabled {
self.disable();
}
}
}
#[derive(Clone)]
struct Entry<T> {
value: T,
is_reused: bool,
status: Rc<Cell<KA>>,
}
pub struct Checkout<T> {
key: Rc<String>,
pool: Pool<T>,
parked: Option<relay::Receiver<Entry<T>>>,
}
impl<T: Clone> Future for Checkout<T> {
type Item = Pooled<T>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
trace!("Checkout::poll");
let mut drop_parked = false;
if let Some(ref mut rx) = self.parked {
match rx.poll() {
Ok(Async::Ready(entry)) => {
trace!("Checkout::poll found client in relay for {:?}", self.key);
return Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry)));
},
Ok(Async::NotReady) => (),
Err(_canceled) => drop_parked = true,
}
}
if drop_parked {
self.parked.take();
}
let expiration = Expiration::new(self.pool.inner.borrow().timeout);
let key = &self.key;
trace!("Checkout::poll url = {:?}, expiration = {:?}", key, expiration.0);
let mut should_remove = false;
let entry = self.pool.inner.borrow_mut().idle.get_mut(key).and_then(|list| {
trace!("Checkout::poll key found {:?}", key);
while let Some(entry) = list.pop() {
match entry.status.get() {
KA::Idle(idle_at) if !expiration.expires(idle_at) => {
trace!("Checkout::poll found idle client for {:?}", key);
should_remove = list.is_empty();
return Some(entry);
},
_ => {
trace!("Checkout::poll removing unacceptable pooled {:?}", key);
// every other case the Entry should just be dropped
// 1. Idle but expired
// 2. Busy (something else somehow took it?)
// 3. Disabled don't reuse of course
}
}
}
should_remove = true;
None
});
if should_remove {
self.pool.inner.borrow_mut().idle.remove(key);
}
match entry {
Some(entry) => Ok(Async::Ready(self.pool.reuse(self.key.clone(), entry))),
None => {
if self.parked.is_none() {
let (tx, mut rx) = relay::channel();
let _ = rx.poll(); // park this task
self.pool.park(self.key.clone(), tx);
self.parked = Some(rx);
}
Ok(Async::NotReady)
},
}
}
}
struct Expiration(Option<Instant>);
impl Expiration {
fn new(dur: Option<Duration>) -> Expiration {
Expiration(dur.map(|dur| Instant::now() - dur))
}
fn expires(&self, instant: Instant) -> bool {
match self.0 {
Some(expire) => expire > instant,
None => false,
}
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use std::time::Duration;
use futures::{Async, Future};
use http::KeepAlive;
use super::Pool;
#[test]
fn test_pool_checkout_smoke() {
let pool = Pool::new(true, Some(Duration::from_secs(5)));
let key = Rc::new("foo".to_string());
let mut pooled = pool.pooled(key.clone(), 41);
pooled.idle();
match pool.checkout(&key).poll().unwrap() {
Async::Ready(pooled) => assert_eq!(*pooled, 41),
_ => panic!("not ready"),
}
}
#[test]
fn test_pool_checkout_returns_none_if_expired() {
::futures::lazy(|| {
let pool = Pool::new(true, Some(Duration::from_secs(1)));
let key = Rc::new("foo".to_string());
let mut pooled = pool.pooled(key.clone(), 41);
pooled.idle();
::std::thread::sleep(pool.inner.borrow().timeout.unwrap());
assert!(pool.checkout(&key).poll().unwrap().is_not_ready());
::futures::future::ok::<(), ()>(())
}).wait().unwrap();
}
#[test]
fn test_pool_removes_expired() {
let pool = Pool::new(true, Some(Duration::from_secs(1)));
let key = Rc::new("foo".to_string());
let mut pooled1 = pool.pooled(key.clone(), 41);
pooled1.idle();
let mut pooled2 = pool.pooled(key.clone(), 5);
pooled2.idle();
let mut pooled3 = pool.pooled(key.clone(), 99);
pooled3.idle();
assert_eq!(pool.inner.borrow().idle.get(&key).map(|entries| entries.len()), Some(3));
::std::thread::sleep(pool.inner.borrow().timeout.unwrap());
pooled1.idle();
pooled2.idle(); // idle after sleep, not expired
pool.checkout(&key).poll().unwrap();
assert_eq!(pool.inner.borrow().idle.get(&key).map(|entries| entries.len()), Some(1));
pool.checkout(&key).poll().unwrap();
assert!(pool.inner.borrow().idle.get(&key).is_none());
}
#[test]
fn test_pool_checkout_task_unparked() {
let pool = Pool::new(true, Some(Duration::from_secs(10)));
let key = Rc::new("foo".to_string());
let pooled1 = pool.pooled(key.clone(), 41);
let mut pooled = pooled1.clone();
let checkout = pool.checkout(&key).join(::futures::lazy(move || {
// the checkout future will park first,
// and then this lazy future will be polled, which will insert
// the pooled back into the pool
//
// this test makes sure that doing so will unpark the checkout
pooled.idle();
Ok(())
})).map(|(entry, _)| entry);
assert_eq!(*checkout.wait().unwrap(), *pooled1);
}
}

View File

@@ -1,55 +1,90 @@
//! Client Requests
use std::fmt;
use Url;
use header::Headers;
use http::RequestHead;
use http::{Body, RequestHead};
use method::Method;
use uri::RequestUri;
use version::HttpVersion;
/// A client request to a remote server.
#[derive(Debug)]
pub struct Request<'a> {
head: &'a mut RequestHead
pub struct Request {
method: Method,
url: Url,
version: HttpVersion,
headers: Headers,
body: Option<Body>,
}
impl<'a> Request<'a> {
impl Request {
/// Construct a new Request.
#[inline]
pub fn new(method: Method, url: Url) -> Request {
Request {
method: method,
url: url,
version: HttpVersion::default(),
headers: Headers::new(),
body: None,
}
}
/// Read the Request Url.
#[inline]
pub fn uri(&self) -> &RequestUri { &self.head.subject.1 }
pub fn url(&self) -> &Url { &self.url }
/// Readthe Request Version.
#[inline]
pub fn version(&self) -> &HttpVersion { &self.head.version }
pub fn version(&self) -> &HttpVersion { &self.version }
/// Read the Request headers.
#[inline]
pub fn headers(&self) -> &Headers { &self.head.headers }
pub fn headers(&self) -> &Headers { &self.headers }
/// Read the Request method.
#[inline]
pub fn method(&self) -> &Method { &self.head.subject.0 }
pub fn method(&self) -> &Method { &self.method }
/// Set the Method of this request.
#[inline]
pub fn set_method(&mut self, method: Method) { self.head.subject.0 = method; }
pub fn set_method(&mut self, method: Method) { self.method = method; }
/// Get a mutable reference to the Request headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.head.headers }
pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
/// Set the `RequestUri` of this request.
/// Set the `Url` of this request.
#[inline]
pub fn set_uri(&mut self, uri: RequestUri) { self.head.subject.1 = uri; }
pub fn set_url(&mut self, url: Url) { self.url = url; }
/// Set the `HttpVersion` of this request.
#[inline]
pub fn set_version(&mut self, version: HttpVersion) { self.head.version = version; }
pub fn set_version(&mut self, version: HttpVersion) { self.version = version; }
/// Set the body of the request.
#[inline]
pub fn set_body<T: Into<Body>>(&mut self, body: T) { self.body = Some(body.into()); }
}
pub fn new(head: &mut RequestHead) -> Request {
Request { head: head }
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Request")
.field("method", &self.method)
.field("url", &self.url)
.field("version", &self.version)
.field("headers", &self.headers)
.finish()
}
}
pub fn split(req: Request) -> (RequestHead, Option<Body>) {
let head = RequestHead {
subject: ::http::RequestLine(req.method, RequestUri::AbsoluteUri(req.url)),
headers: req.headers,
version: req.version,
};
(head, req.body)
}
#[cfg(test)]

View File

@@ -1,11 +1,11 @@
//! Client Responses
use std::fmt;
use header;
//use net::NetworkStream;
use http::{self, RawStatus};
use http::{self, RawStatus, Body};
use status;
use version;
pub fn new(incoming: http::ResponseHead) -> Response {
pub fn new(incoming: http::ResponseHead, body: Option<Body>) -> Response {
trace!("Response::new");
let status = status::StatusCode::from_u16(incoming.subject.0);
debug!("version={:?}, status={:?}", incoming.version, status);
@@ -16,17 +16,18 @@ pub fn new(incoming: http::ResponseHead) -> Response {
version: incoming.version,
headers: incoming.headers,
status_raw: incoming.subject,
body: body,
}
}
/// A response for a client request to a remote server.
#[derive(Debug)]
pub struct Response {
status: status::StatusCode,
headers: header::Headers,
version: version::HttpVersion,
status_raw: RawStatus,
body: Option<Body>,
}
impl Response {
@@ -42,170 +43,23 @@ impl Response {
#[inline]
pub fn status_raw(&self) -> &RawStatus { &self.status_raw }
/// Get the final URL of this response.
#[inline]
//pub fn url(&self) -> &Url { &self.url }
/// Get the HTTP version of this response from the server.
#[inline]
pub fn version(&self) -> &version::HttpVersion { &self.version }
}
/*
impl Drop for Response {
fn drop(&mut self) {
// if not drained, theres old bits in the Reader. we can't reuse this,
// since those old bits would end up in new Responses
//
// otherwise, the response has been drained. we should check that the
// server has agreed to keep the connection open
let is_drained = !self.message.has_body();
trace!("Response.drop is_drained={}", is_drained);
if !(is_drained && http::should_keep_alive(self.version, &self.headers)) {
trace!("Response.drop closing connection");
if let Err(e) = self.message.close_connection() {
error!("Response.drop error closing connection: {}", e);
}
}
/// Take the `Body` of this response.
#[inline]
pub fn body(mut self) -> Body {
self.body.take().unwrap_or(Body::empty())
}
}
*/
#[cfg(test)]
mod tests {
/*
use std::io::{self, Read};
use url::Url;
use header::TransferEncoding;
use header::Encoding;
use http::HttpMessage;
use mock::MockStream;
use status;
use version;
use http::h1::Http11Message;
use super::Response;
fn read_to_string(mut r: Response) -> io::Result<String> {
let mut s = String::new();
try!(r.read_to_string(&mut s));
Ok(s)
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("status", &self.status)
.field("version", &self.version)
.field("headers", &self.headers)
.finish()
}
#[test]
fn test_into_inner() {
let message: Box<HttpMessage> = Box::new(
Http11Message::with_stream(Box::new(MockStream::new())));
let message = message.downcast::<Http11Message>().ok().unwrap();
let b = message.into_inner().downcast::<MockStream>().ok().unwrap();
assert_eq!(b, Box::new(MockStream::new()));
}
#[test]
fn test_parse_chunked_response() {
let stream = MockStream::with_input(b"\
HTTP/1.1 200 OK\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"
);
let url = Url::parse("http://hyper.rs").unwrap();
let res = Response::new(url, Box::new(stream)).unwrap();
// The status line is correct?
assert_eq!(res.status, status::StatusCode::Ok);
assert_eq!(res.version, version::HttpVersion::Http11);
// The header is correct?
match res.headers.get::<TransferEncoding>() {
Some(encodings) => {
assert_eq!(1, encodings.len());
assert_eq!(Encoding::Chunked, encodings[0]);
},
None => panic!("Transfer-Encoding: chunked expected!"),
};
// The body is correct?
assert_eq!(read_to_string(res).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 stream = MockStream::with_input(b"\
HTTP/1.1 200 OK\r\n\
Transfer-Encoding: chunked\r\n\
\r\n\
X\r\n\
1\r\n\
0\r\n\
\r\n"
);
let url = Url::parse("http://hyper.rs").unwrap();
let res = Response::new(url, Box::new(stream)).unwrap();
assert!(read_to_string(res).is_err());
}
/// Tests that when a chunk size contains an invalid extension, an error is
/// returned.
#[test]
fn test_invalid_chunk_size_extension() {
let stream = MockStream::with_input(b"\
HTTP/1.1 200 OK\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"
);
let url = Url::parse("http://hyper.rs").unwrap();
let res = Response::new(url, Box::new(stream)).unwrap();
assert!(read_to_string(res).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 stream = MockStream::with_input(b"\
HTTP/1.1 200 OK\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"
);
let url = Url::parse("http://hyper.rs").unwrap();
let res = Response::new(url, Box::new(stream)).unwrap();
assert_eq!(read_to_string(res).unwrap(), "1".to_owned());
}
#[test]
fn test_parse_error_closes() {
let url = Url::parse("http://hyper.rs").unwrap();
let stream = MockStream::with_input(b"\
definitely not http
");
assert!(Response::new(url, Box::new(stream)).is_err());
}
*/
}

View File

@@ -8,9 +8,6 @@ use std::string::FromUtf8Error;
use httparse;
use url;
#[cfg(feature = "openssl")]
use openssl::ssl::error::SslError;
use self::Error::{
Method,
Uri,
@@ -19,7 +16,6 @@ use self::Error::{
Status,
Timeout,
Io,
Ssl,
TooLarge,
Incomplete,
Utf8
@@ -49,12 +45,8 @@ pub enum Error {
Status,
/// A timeout occurred waiting for an IO event.
Timeout,
/// Event loop is full and cannot process request
Full,
/// An `io::Error` that occurred while trying to read or write to a network stream.
Io(IoError),
/// An error from a SSL library.
Ssl(Box<StdError + Send + Sync>),
/// Parsing a field as string failed
Utf8(Utf8Error),
@@ -76,7 +68,6 @@ impl fmt::Display for Error {
match *self {
Uri(ref e) => fmt::Display::fmt(e, f),
Io(ref e) => fmt::Display::fmt(e, f),
Ssl(ref e) => fmt::Display::fmt(e, f),
Utf8(ref e) => fmt::Display::fmt(e, f),
ref e => f.write_str(e.description()),
}
@@ -93,10 +84,8 @@ impl StdError for Error {
Status => "Invalid Status provided",
Incomplete => "Message is incomplete",
Timeout => "Timeout",
Error::Full => "Event loop is full",
Uri(ref e) => e.description(),
Io(ref e) => e.description(),
Ssl(ref e) => e.description(),
Utf8(ref e) => e.description(),
Error::__Nonexhaustive(ref void) => match *void {}
}
@@ -105,8 +94,9 @@ impl StdError for Error {
fn cause(&self) -> Option<&StdError> {
match *self {
Io(ref error) => Some(error),
Ssl(ref error) => Some(&**error),
Uri(ref error) => Some(error),
Utf8(ref error) => Some(error),
Error::__Nonexhaustive(ref void) => match *void {},
_ => None,
}
}
@@ -124,16 +114,6 @@ impl From<url::ParseError> for Error {
}
}
#[cfg(feature = "openssl")]
impl From<SslError> for Error {
fn from(err: SslError) -> Error {
match err {
SslError::StreamError(err) => Io(err),
err => Ssl(Box::new(err)),
}
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Error {
Utf8(err)
@@ -181,9 +161,9 @@ mod tests {
($from:expr => $error:pat) => {
match Error::from($from) {
e @ $error => {
assert!(e.description().len() > 5);
assert!(e.description().len() >= 5);
} ,
_ => panic!("{:?}", $from)
e => panic!("{:?}", e)
}
}
}

View File

@@ -19,7 +19,7 @@ header! {
/// # Examples
/// ```
/// use hyper::header::{Headers, AccessControlAllowMethods};
/// use hyper::method::Method;
/// use hyper::Method;
///
/// let mut headers = Headers::new();
/// headers.set(
@@ -28,7 +28,7 @@ header! {
/// ```
/// ```
/// use hyper::header::{Headers, AccessControlAllowMethods};
/// use hyper::method::Method;
/// use hyper::Method;
///
/// let mut headers = Headers::new();
/// headers.set(

View File

@@ -17,7 +17,7 @@ header! {
/// # Examples
/// ```
/// use hyper::header::{Headers, AccessControlRequestMethod};
/// use hyper::method::Method;
/// use hyper::Method;
///
/// let mut headers = Headers::new();
/// headers.set(AccessControlRequestMethod(Method::Get));

View File

@@ -21,7 +21,7 @@ header! {
/// # Examples
/// ```
/// use hyper::header::{Headers, Allow};
/// use hyper::method::Method;
/// use hyper::Method;
///
/// let mut headers = Headers::new();
/// headers.set(
@@ -30,7 +30,7 @@ header! {
/// ```
/// ```
/// use hyper::header::{Headers, Allow};
/// use hyper::method::Method;
/// use hyper::Method;
///
/// let mut headers = Headers::new();
/// headers.set(

View File

@@ -72,7 +72,6 @@ impl fmt::Display for ContentLength {
}
__hyper__deref!(ContentLength => u64);
__hyper_generate_header_serialization!(ContentLength);
__hyper__tm!(ContentLength, tests {
// Testcase from RFC

View File

@@ -182,31 +182,6 @@ macro_rules! test_header {
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __hyper_generate_header_serialization {
($id:ident) => {
#[cfg(feature = "serde-serialization")]
impl ::serde::Serialize for $id {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: ::serde::Serializer {
format!("{}", self).serialize(serializer)
}
}
#[cfg(feature = "serde-serialization")]
impl ::serde::Deserialize for $id {
fn deserialize<D>(deserializer: &mut D) -> Result<$id, D::Error>
where D: ::serde::Deserializer {
let string_representation: String =
try!(::serde::Deserialize::deserialize(deserializer));
let raw = string_representation.into_bytes().into();
Ok($crate::header::Header::parse_header(&raw).unwrap())
}
}
}
}
#[macro_export]
macro_rules! header {
// $a:meta: Attributes associated with the header item (usually docs)
@@ -238,8 +213,6 @@ macro_rules! header {
self.fmt_header(f)
}
}
__hyper_generate_header_serialization!($id);
};
// List header, one or more items
($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+) => {
@@ -265,7 +238,6 @@ macro_rules! header {
self.fmt_header(f)
}
}
__hyper_generate_header_serialization!($id);
};
// Single value header
($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => {
@@ -290,7 +262,6 @@ macro_rules! header {
::std::fmt::Display::fmt(&**self, f)
}
}
__hyper_generate_header_serialization!($id);
};
// List header, one or more items with "*" option
($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+}) => {
@@ -330,7 +301,6 @@ macro_rules! header {
self.fmt_header(f)
}
}
__hyper_generate_header_serialization!($id);
};
// optional test module
@@ -421,4 +391,4 @@ mod transfer_encoding;
mod upgrade;
mod user_agent;
mod vary;
mod warning;
mod warning;

View File

@@ -85,11 +85,6 @@ use unicase::UniCase;
use self::internals::{Item, VecMap, Entry};
#[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde-serialization")]
use serde::de;
pub use self::shared::*;
pub use self::common::*;
pub use self::raw::Raw;
@@ -437,44 +432,6 @@ impl fmt::Debug for Headers {
}
}
#[cfg(feature = "serde-serialization")]
impl Serialize for Headers {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer
{
let mut state = try!(serializer.serialize_map(Some(self.len())));
for header in self.iter() {
try!(serializer.serialize_map_key(&mut state, header.name()));
try!(serializer.serialize_map_value(&mut state, header.value_string()));
}
serializer.serialize_map_end(state)
}
}
#[cfg(feature = "serde-serialization")]
impl Deserialize for Headers {
fn deserialize<D>(deserializer: &mut D) -> Result<Headers, D::Error> where D: Deserializer {
struct HeadersVisitor;
impl de::Visitor for HeadersVisitor {
type Value = Headers;
fn visit_map<V>(&mut self, mut visitor: V) -> Result<Headers, V::Error>
where V: de::MapVisitor {
let mut result = Headers::new();
while let Some((key, value)) = try!(visitor.visit()) {
let (key, value): (String, String) = (key, value);
result.set_raw(key, vec![value.into_bytes()]);
}
try!(visitor.end());
Ok(result)
}
}
deserializer.deserialize_map(HeadersVisitor)
}
}
/// An `Iterator` over the fields in a `Headers` map.
#[allow(missing_debug_implementations)]
pub struct HeadersItems<'a> {

97
src/http/body.rs Normal file
View File

@@ -0,0 +1,97 @@
use std::convert::From;
use std::sync::Arc;
use tokio_proto;
use http::Chunk;
use futures::{Poll, Stream};
use futures::sync::mpsc;
pub type TokioBody = tokio_proto::streaming::Body<Chunk, ::Error>;
/// A `Stream` for `Chunk`s used in requests and responses.
#[derive(Debug)]
pub struct Body(TokioBody);
impl Body {
/// Return an empty body stream
pub fn empty() -> Body {
Body(TokioBody::empty())
}
/// Return a body stream with an associated sender half
pub fn pair() -> (mpsc::Sender<Result<Chunk, ::Error>>, Body) {
let (tx, rx) = TokioBody::pair();
let rx = Body(rx);
(tx, rx)
}
}
impl Stream for Body {
type Item = Chunk;
type Error = ::Error;
fn poll(&mut self) -> Poll<Option<Chunk>, ::Error> {
self.0.poll()
}
}
impl From<Body> for tokio_proto::streaming::Body<Chunk, ::Error> {
fn from(b: Body) -> tokio_proto::streaming::Body<Chunk, ::Error> {
b.0
}
}
impl From<tokio_proto::streaming::Body<Chunk, ::Error>> for Body {
fn from(tokio_body: tokio_proto::streaming::Body<Chunk, ::Error>) -> Body {
Body(tokio_body)
}
}
impl From<mpsc::Receiver<Result<Chunk, ::Error>>> for Body {
fn from(src: mpsc::Receiver<Result<Chunk, ::Error>>) -> Body {
Body(src.into())
}
}
impl From<Chunk> for Body {
fn from (chunk: Chunk) -> Body {
Body(TokioBody::from(chunk))
}
}
impl From<Vec<u8>> for Body {
fn from (vec: Vec<u8>) -> Body {
Body(TokioBody::from(Chunk::from(vec)))
}
}
impl From<Arc<Vec<u8>>> for Body {
fn from (vec: Arc<Vec<u8>>) -> Body {
Body(TokioBody::from(Chunk::from(vec)))
}
}
impl From<&'static [u8]> for Body {
fn from (slice: &'static [u8]) -> Body {
Body(TokioBody::from(Chunk::from(slice)))
}
}
impl From<String> for Body {
fn from (s: String) -> Body {
Body(TokioBody::from(Chunk::from(s.into_bytes())))
}
}
impl From<&'static str> for Body {
fn from (slice: &'static str) -> Body {
Body(TokioBody::from(Chunk::from(slice.as_bytes())))
}
}
fn _assert_send() {
fn _assert<T: Send>() {}
_assert::<Body>();
_assert::<Chunk>();
}

View File

@@ -1,16 +1,16 @@
use std::cmp;
use std::io::{self, Read};
use std::io::{self, Read, Write};
use std::ptr;
const INIT_BUFFER_SIZE: usize = 4096;
const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
pub const MAX_BUFFER_SIZE: usize = 8192 + 4096 * 100;
#[derive(Debug, Default)]
pub struct Buffer {
vec: Vec<u8>,
read_pos: usize,
write_pos: usize,
tail: usize,
head: usize,
}
impl Buffer {
@@ -24,7 +24,17 @@ impl Buffer {
#[inline]
pub fn len(&self) -> usize {
self.read_pos - self.write_pos
self.tail - self.head
}
#[inline]
fn available(&self) -> usize {
self.vec.len() - self.tail
}
#[inline]
pub fn is_max_size(&self) -> bool {
self.len() >= MAX_BUFFER_SIZE
}
#[inline]
@@ -34,45 +44,88 @@ impl Buffer {
#[inline]
pub fn bytes(&self) -> &[u8] {
&self.vec[self.write_pos..self.read_pos]
&self.vec[self.head..self.tail]
}
#[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;
debug_assert!(self.tail >= self.head + pos);
self.head += pos;
if self.head == self.tail {
self.head = 0;
self.tail = 0;
}
}
pub fn consume_leading_lines(&mut self) {
while !self.is_empty() {
match self.vec[self.head] {
b'\r' | b'\n' => {
self.consume(1);
},
_ => return
}
}
}
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;
self.maybe_reserve(1);
let n = try!(r.read(&mut self.vec[self.tail..]));
self.tail += n;
self.maybe_reset();
Ok(n)
}
pub fn write_into<W: Write>(&mut self, w: &mut W) -> io::Result<usize> {
if self.is_empty() {
Ok(0)
} else {
let n = try!(w.write(&mut self.vec[self.head..self.tail]));
self.head += n;
self.maybe_reset();
Ok(n)
}
}
pub fn write(&mut self, data: &[u8]) -> usize {
trace!("Buffer::write len = {:?}", data.len());
self.maybe_reserve(data.len());
let len = cmp::min(self.available(), data.len());
assert!(self.available() >= len);
unsafe {
// in rust 1.9, we could use slice::copy_from_slice
ptr::copy(
data.as_ptr(),
self.vec.as_mut_ptr().offset(self.tail as isize),
len
);
}
self.tail += len;
len
}
#[inline]
fn maybe_reserve(&mut self) {
fn maybe_reserve(&mut self, needed: usize) {
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;
// first reserve
let init = cmp::max(INIT_BUFFER_SIZE, needed);
trace!("reserving initial {}", init);
self.vec = vec![0; init];
} else if self.head > 0 && self.tail == cap && self.head >= needed {
// there is space to shift over
let count = self.tail - self.head;
trace!("moving buffer bytes over by {}", count);
unsafe {
ptr::copy(
self.vec.as_ptr().offset(self.write_pos as isize),
self.vec.as_ptr().offset(self.head 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.tail -= count;
self.head = 0;
} else if self.tail == 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);
@@ -80,36 +133,11 @@ impl Buffer {
}
}
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 + 'a> BufReader<'a, R> {
pub fn get_ref(&self) -> &R {
self.reader
}
}
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]
fn maybe_reset(&mut self) {
if self.tail != 0 && self.tail == self.head {
self.tail = 0;
self.head = 0;
}
}
}

View File

@@ -1,96 +0,0 @@
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()
}
}

77
src/http/chunk.rs Normal file
View File

@@ -0,0 +1,77 @@
use std::borrow::Borrow;
use std::fmt;
use std::sync::Arc;
/// A piece of a message body.
pub struct Chunk(Inner);
enum Inner {
Owned(Vec<u8>),
Referenced(Arc<Vec<u8>>),
Static(&'static [u8]),
}
impl From<Vec<u8>> for Chunk {
#[inline]
fn from(v: Vec<u8>) -> Chunk {
Chunk(Inner::Owned(v))
}
}
impl From<Arc<Vec<u8>>> for Chunk {
#[inline]
fn from(v: Arc<Vec<u8>>) -> Chunk {
Chunk(Inner::Referenced(v))
}
}
impl From<&'static [u8]> for Chunk {
#[inline]
fn from(slice: &'static [u8]) -> Chunk {
Chunk(Inner::Static(slice))
}
}
impl From<String> for Chunk {
#[inline]
fn from(s: String) -> Chunk {
s.into_bytes().into()
}
}
impl From<&'static str> for Chunk {
#[inline]
fn from(slice: &'static str) -> Chunk {
slice.as_bytes().into()
}
}
impl ::std::ops::Deref for Chunk {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl AsRef<[u8]> for Chunk {
#[inline]
fn as_ref(&self) -> &[u8] {
match self.0 {
Inner::Owned(ref vec) => vec,
Inner::Referenced(ref vec) => {
let v: &Vec<u8> = vec.borrow();
v.as_slice()
}
Inner::Static(slice) => slice,
}
}
}
impl fmt::Debug for Chunk {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_ref(), f)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -277,7 +277,7 @@ mod tests {
use std::io::Write;
use super::Decoder;
use super::ChunkedState;
use mock::Async;
use mock::AsyncIo;
#[test]
fn test_read_chunk_size() {
@@ -422,7 +422,7 @@ mod tests {
-> String {
let content_len = content.len();
let mock_buf = io::Cursor::new(content.clone());
let mut ins = Async::new(mock_buf, block_at);
let mut ins = AsyncIo::new(mock_buf, block_at);
let mut outs = vec![];
loop {
let mut buf = vec![0; read_buffer_size];

View File

@@ -1,15 +1,12 @@
use std::borrow::Cow;
use std::cmp;
use std::io::{self, Write};
use http::internal::{AtomicWrite, WriteBuf};
use http::io::AtomicWrite;
/// Encoders to handle different Transfer-Encodings.
#[derive(Debug, Clone)]
pub struct Encoder {
kind: Kind,
prefix: Prefix,
is_closed: bool,
}
#[derive(Debug, PartialEq, Clone)]
@@ -26,27 +23,16 @@ impl Encoder {
pub fn chunked() -> Encoder {
Encoder {
kind: Kind::Chunked(Chunked::Init),
prefix: Prefix(None),
is_closed: false,
}
}
pub fn length(len: u64) -> Encoder {
Encoder {
kind: Kind::Length(len),
prefix: Prefix(None),
is_closed: false,
}
}
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,
@@ -54,71 +40,26 @@ impl Encoder {
}
}
/// User has called `encoder.close()` in a `Handler`.
pub fn is_closed(&self) -> bool {
self.is_closed
}
pub fn close(&mut self) {
self.is_closed = true;
}
pub fn finish(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)
chunked.encode(w, msg)
},
Kind::Length(ref mut remaining) => {
let mut n = {
let n = {
let max = cmp::min(*remaining as usize, msg.len());
trace!("sized write, len = {}", max);
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]))
try!(w.write_atomic(&[slice]))
};
n = self.prefix.update(n);
if n == 0 {
return Err(io::Error::new(io::ErrorKind::WouldBlock, "would block"));
}
*remaining -= n as u64;
trace!("sized write complete, remaining = {}", remaining);
Ok(n)
},
}
@@ -138,7 +79,7 @@ enum Chunked {
}
impl Chunked {
fn encode<W: AtomicWrite>(&mut self, w: &mut W, prefix: &mut Prefix, msg: &[u8]) -> io::Result<usize> {
fn encode<W: AtomicWrite>(&mut self, w: &mut W, msg: &[u8]) -> io::Result<usize> {
match *self {
Chunked::Init => {
let mut size = ChunkSize {
@@ -158,28 +99,24 @@ impl Chunked {
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..],
@@ -189,14 +126,12 @@ impl Chunked {
&b""[..],
&b""[..],
&b""[..],
&b""[..],
&b"\r\n"[..],
],
Chunked::BodyLf => [
&b""[..],
&b""[..],
&b""[..],
&b""[..],
&b"\n"[..],
],
Chunked::End => unreachable!("Chunked::End shouldn't write more")
@@ -204,9 +139,6 @@ impl Chunked {
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"),
@@ -321,30 +253,10 @@ impl io::Write for ChunkSize {
}
}
#[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};
use mock::{AsyncIo, Buf};
#[test]
fn test_chunked_encode_sync() {
@@ -359,7 +271,7 @@ mod tests {
#[test]
fn test_chunked_encode_async() {
let mut dst = Async::new(Buf::new(), 7);
let mut dst = AsyncIo::new(Buf::new(), 7);
let mut encoder = Encoder::chunked();
assert!(encoder.encode(&mut dst, b"foo bar").is_err());

View File

@@ -1,21 +1,3 @@
/*
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;
@@ -23,7 +5,7 @@ pub use self::parse::parse;
mod decode;
mod encode;
mod parse;
pub mod parse;
/*
fn should_have_response_body(method: &Method, status: u16) -> bool {

View File

@@ -4,7 +4,7 @@ use std::fmt::{self, Write};
use httparse;
use header::{self, Headers, ContentLength, TransferEncoding};
use http::{MessageHead, RawStatus, Http1Message, ParseResult, ServerMessage, ClientMessage, RequestLine};
use http::{MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine};
use http::h1::{Encoder, Decoder};
use method::Method;
use status::StatusCode;
@@ -13,17 +13,15 @@ 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> {
pub fn parse<T: Http1Transaction<Incoming=I>, I>(buf: &[u8]) -> ParseResult<I> {
if buf.len() == 0 {
return Ok(None);
}
trace!("parse({:?})", buf);
<T as Http1Message>::parse(buf)
<T as Http1Transaction>::parse(buf)
}
impl Http1Message for ServerMessage {
impl Http1Transaction for ServerTransaction {
type Incoming = RequestLine;
type Outgoing = StatusCode;
@@ -60,7 +58,7 @@ impl Http1Message for ServerMessage {
}
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
use ::header;
trace!("writing head: {:?}", head);
@@ -103,9 +101,14 @@ impl Http1Message for ServerMessage {
}
body
}
fn should_set_length(_head: &MessageHead<Self::Outgoing>) -> bool {
//TODO: pass method, check if method == HEAD
true
}
}
impl Http1Message for ClientMessage {
impl Http1Transaction for ClientTransaction {
type Incoming = RawStatus;
type Outgoing = RequestLine;
@@ -162,7 +165,7 @@ impl Http1Message for ClientMessage {
}
}
fn encode(mut head: MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> Encoder {
trace!("writing head: {:?}", head);
@@ -203,6 +206,14 @@ impl Http1Message for ClientMessage {
body
}
fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool {
match &head.subject.0 {
&Method::Get | &Method::Head => false,
_ => true
}
}
}
struct FastWrite<'a>(&'a mut Vec<u8>);
@@ -238,17 +249,17 @@ mod tests {
#[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();
parse::<http::ServerTransaction, _>(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();
let (res, _) = parse::<http::ClientTransaction, _>(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();
let (res, _) = parse::<http::ClientTransaction, _>(raw).unwrap().unwrap();
assert_eq!(res.subject.1, "Howdy");
}
@@ -260,7 +271,7 @@ mod tests {
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()
parse::<http::ServerTransaction, _>(raw).unwrap()
});
}

207
src/http/io.rs Normal file
View File

@@ -0,0 +1,207 @@
use std::fmt;
use std::io::{self, Read, Write};
use futures::Async;
use tokio::io::Io;
use http::{Http1Transaction, h1, MessageHead, ParseResult};
use http::buffer::Buffer;
pub struct Buffered<T> {
io: T,
read_buf: Buffer,
write_buf: Buffer,
}
impl<T> fmt::Debug for Buffered<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Buffered")
.field("read_buf", &self.read_buf)
.field("write_buf", &self.write_buf)
.finish()
}
}
impl<T: Io> Buffered<T> {
pub fn new(io: T) -> Buffered<T> {
Buffered {
io: io,
read_buf: Buffer::new(),
write_buf: Buffer::new(),
}
}
pub fn read_buf(&self) -> &[u8] {
self.read_buf.bytes()
}
pub fn consume_leading_lines(&mut self) {
self.read_buf.consume_leading_lines();
}
pub fn poll_read(&mut self) -> Async<()> {
self.io.poll_read()
}
pub fn parse<S: Http1Transaction>(&mut self) -> ::Result<Option<MessageHead<S::Incoming>>> {
match self.read_buf.read_from(&mut self.io) {
Ok(0) => {
trace!("parse eof");
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "parse eof").into());
}
Ok(_) => {},
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => {},
_ => return Err(e.into())
}
}
match try!(parse::<S, _>(self.read_buf.bytes())) {
Some((head, len)) => {
trace!("parsed {} bytes out of {}", len, self.read_buf.len());
self.read_buf.consume(len);
Ok(Some(head))
},
None => {
if self.read_buf.is_max_size() {
debug!("MAX_BUFFER_SIZE reached, closing");
Err(::Error::TooLarge)
} else {
Ok(None)
}
},
}
}
pub fn buffer<B: AsRef<[u8]>>(&mut self, buf: B) {
self.write_buf.write(buf.as_ref());
}
#[cfg(test)]
pub fn io_mut(&mut self) -> &mut T {
&mut self.io
}
}
impl<T: Read> Read for Buffered<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
trace!("Buffered.read self={}, buf={}", self.read_buf.len(), buf.len());
let n = try!(self.read_buf.bytes().read(buf));
self.read_buf.consume(n);
if n == 0 {
self.read_buf.reset();
self.io.read(&mut buf[n..])
} else {
Ok(n)
}
}
}
impl<T: Write> Write for Buffered<T> {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
Ok(self.write_buf.write(data))
}
fn flush(&mut self) -> io::Result<()> {
self.write_buf.write_into(&mut self.io).and_then(|_n| {
if self.write_buf.is_empty() {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::WouldBlock, "wouldblock"))
}
})
}
}
fn parse<T: Http1Transaction<Incoming=I>, I>(rdr: &[u8]) -> ParseResult<I> {
h1::parse::<T, I>(rdr)
}
#[derive(Clone)]
pub struct Cursor<T: AsRef<[u8]>> {
bytes: T,
pos: usize,
}
impl<T: AsRef<[u8]>> Cursor<T> {
pub fn new(bytes: T) -> Cursor<T> {
Cursor {
bytes: bytes,
pos: 0,
}
}
pub fn is_written(&self) -> bool {
trace!("Cursor::is_written pos = {}, len = {}", self.pos, self.bytes.as_ref().len());
self.pos >= self.bytes.as_ref().len()
}
/*
pub fn write_to<W: Write>(&mut self, dst: &mut W) -> io::Result<usize> {
dst.write(&self.bytes.as_ref()[self.pos..]).map(|n| {
self.pos += n;
n
})
}
*/
#[inline]
pub fn buf(&self) -> &[u8] {
&self.bytes.as_ref()[self.pos..]
}
#[inline]
pub fn consume(&mut self, num: usize) {
trace!("Cursor::consume({})", num);
self.pos = ::std::cmp::min(self.bytes.as_ref().len(), self.pos + num);
}
}
impl<T: AsRef<[u8]>> fmt::Debug for Cursor<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.buf();
let reasonable_max = ::std::cmp::min(bytes.len(), 32);
write!(f, "Cursor({:?})", &bytes[..reasonable_max])
}
}
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> {
if cfg!(not(windows)) {
warn!("write_atomic not using writev");
}
let vec = bufs.concat();
self.write(&vec)
}
}
//}
#[test]
fn test_iobuf_write_empty_slice() {
use mock::{AsyncIo, Buf as MockBuf};
let mut mock = AsyncIo::new(MockBuf::new(), 256);
mock.error(io::Error::new(io::ErrorKind::Other, "logic error"));
let mut io_buf = Buffered::new(mock);
// underlying io will return the logic error upon write,
// so we are testing that the io_buf does not trigger a write
// when there is nothing to flush
io_buf.flush().expect("should short-circuit flush");
}

View File

@@ -1,227 +1,44 @@
//! Pieces pertaining to the HTTP message protocol.
use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::time::Duration;
use header::Connection;
use header::ConnectionOption::{KeepAlive, Close};
use header::{Connection, ConnectionOption};
use header::Headers;
use method::Method;
use net::Transport;
use status::StatusCode;
use uri::RequestUri;
use version::HttpVersion;
use version::HttpVersion::{Http10, Http11};
#[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use self::conn::{Conn, MessageHandler, MessageHandlerFactory, Seed, Key, ReadyResult};
pub use self::conn::{Conn, KeepAlive, KA};
pub use self::body::{Body, TokioBody};
pub use self::chunk::Chunk;
mod body;
//mod buf;
mod buffer;
pub mod channel;
mod chunk;
mod conn;
mod io;
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> Trans<'a, T> {
fn get_ref(&self) -> &T {
match *self {
Trans::Port(ref t) => &*t,
Trans::Buf(ref buf) => buf.get_ref()
}
}
}
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))
}
/// Read from the `Transport`.
#[inline]
pub 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)
}
}
}
/// Try to read from the `Transport`.
///
/// This method looks for the `WouldBlock` error. If the read did not block,
/// a return value would be `Ok(Some(x))`. If the read would block,
/// this method would return `Ok(None)`.
#[inline]
pub fn try_read(&mut self, buf: &mut [u8]) -> io::Result<Option<usize>> {
match self.read(buf) {
/*
macro_rules! nonblocking {
($e:expr) => ({
match $e {
Ok(n) => Ok(Some(n)),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Ok(None),
stdio::ErrorKind::WouldBlock => Ok(None),
_ => Err(e)
}
}
}
/// Get a reference to the transport.
pub fn get_ref(&self) -> &T {
match self.0 {
DecoderImpl::H1(_, ref transport) => transport.get_ref()
}
}
}
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))
}
/// Write to the `Transport`.
#[inline]
pub 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) => {
if encoder.is_closed() {
Ok(0)
} else {
encoder.encode(*transport, data)
}
}
}
}
/// Try to write to the `Transport`.
///
/// This method looks for the `WouldBlock` error. If the write did not block,
/// a return value would be `Ok(Some(x))`. If the write would block,
/// this method would return `Ok(None)`.
#[inline]
pub fn try_write(&mut self, data: &[u8]) -> io::Result<Option<usize>> {
match self.write(data) {
Ok(n) => Ok(Some(n)),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Ok(None),
_ => Err(e)
}
}
}
/// Closes an encoder, signaling that no more writing will occur.
///
/// This is needed for encodings that don't know the length of the content
/// beforehand. Most common instance would be usage of
/// `Transfer-Enciding: chunked`. You would call `close()` to signal
/// the `Encoder` should write the end chunk, or `0\r\n\r\n`.
pub fn close(&mut self) {
match self.0 {
EncoderImpl::H1(ref mut encoder, _) => encoder.close()
}
}
/// Get a reference to the transport.
pub fn get_ref(&self) -> &T {
match self.0 {
EncoderImpl::H1(_, ref transport) => &*transport
}
}
}
impl<'a, T: Read> Read for Decoder<'a, T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read(buf)
}
}
impl<'a, T: Transport> Write for Encoder<'a, T> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.write(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)]
#[derive(Debug, Default, PartialEq)]
pub struct MessageHead<S> {
/// HTTP version of the message.
pub version: HttpVersion,
@@ -234,7 +51,7 @@ pub struct MessageHead<S> {
/// An incoming request message.
pub type RequestHead = MessageHead<RequestLine>;
#[derive(Debug, Default)]
#[derive(Debug, Default, PartialEq)]
pub struct RequestLine(pub Method, pub RequestUri);
impl fmt::Display for RequestLine {
@@ -274,18 +91,13 @@ impl Default for RawStatus {
}
}
#[cfg(feature = "serde-serialization")]
impl Serialize for RawStatus {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
(self.0, &self.1).serialize(serializer)
}
}
#[cfg(feature = "serde-serialization")]
impl Deserialize for RawStatus {
fn deserialize<D>(deserializer: &mut D) -> Result<RawStatus, D::Error> where D: Deserializer {
let representation: (u16, String) = try!(Deserialize::deserialize(deserializer));
Ok(RawStatus(representation.0, Cow::Owned(representation.1)))
impl From<MessageHead<::StatusCode>> for MessageHead<RawStatus> {
fn from(head: MessageHead<::StatusCode>) -> MessageHead<RawStatus> {
MessageHead {
subject: head.subject.into(),
version: head.version,
headers: head.headers,
}
}
}
@@ -294,174 +106,31 @@ impl Deserialize for RawStatus {
pub fn should_keep_alive(version: HttpVersion, headers: &Headers) -> bool {
let ret = match (version, headers.get::<Connection>()) {
(Http10, None) => false,
(Http10, Some(conn)) if !conn.contains(&KeepAlive) => false,
(Http11, Some(conn)) if conn.contains(&Close) => false,
(Http10, Some(conn)) if !conn.contains(&ConnectionOption::KeepAlive) => false,
(Http11, Some(conn)) if conn.contains(&ConnectionOption::Close) => false,
_ => true
};
trace!("should_keep_alive(version={:?}, header={:?}) = {:?}", version, headers.get::<Connection>(), ret);
ret
}
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 {}
pub enum ServerTransaction {}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ClientMessage {}
pub enum ClientTransaction {}
pub trait Http1Message {
pub trait Http1Transaction {
type Incoming;
type Outgoing: Default;
//type KeepAlive: KeepAlive;
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;
fn encode(head: &mut MessageHead<Self::Outgoing>, dst: &mut Vec<u8>) -> h1::Encoder;
fn should_set_length(head: &MessageHead<Self::Outgoing>) -> bool;
}
/// 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(())
}
}
}
// Internal enum for `Next`
#[derive(Debug, Clone, Copy)]
enum Next_ {
Read,
Write,
ReadWrite,
Wait,
End,
Remove,
}
// An enum representing all the possible actions to taken when registering
// with the event loop.
#[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 reg(&self) -> Reg {
self.interest.register()
}
*/
/// 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
}
}
impl Next_ {
fn register(&self) -> Reg {
match *self {
Next_::Read => Reg::Read,
Next_::Write => Reg::Write,
Next_::ReadWrite => Reg::ReadWrite,
Next_::Wait => Reg::Wait,
Next_::End => Reg::Remove,
Next_::Remove => Reg::Remove,
}
}
}
type ParseResult<T> = ::Result<Option<(MessageHead<T>, usize)>>;
#[test]
fn test_should_keep_alive() {

View File

@@ -13,34 +13,22 @@
//! Hyper provides both a [Client](client/index.html) and a
//! [Server](server/index.html), along with a
//! [typed Headers system](header/index.html).
//!
//! If just getting started, consider looking over the **[Guide](../guide/)**.
extern crate cookie;
extern crate futures;
extern crate futures_cpupool;
extern crate httparse;
#[macro_use] extern crate language_tags;
#[macro_use] extern crate log;
#[macro_use] pub extern crate mime;
extern crate relay;
extern crate rustc_serialize as serialize;
extern crate time;
#[macro_use] extern crate url;
#[cfg(feature = "openssl")]
extern crate openssl;
#[cfg(feature = "openssl-verify")]
extern crate openssl_verify;
#[cfg(feature = "security-framework")]
extern crate security_framework;
#[cfg(feature = "serde-serialization")]
extern crate serde;
extern crate cookie;
#[macro_use] extern crate tokio_core as tokio;
extern crate tokio_proto;
extern crate tokio_service;
extern crate unicase;
extern crate httparse;
extern crate rotor;
extern crate spmc;
extern crate vecio;
#[macro_use]
extern crate language_tags;
#[macro_use]
extern crate mime as mime_crate;
#[macro_use]
extern crate log;
#[macro_use] extern crate url;
#[cfg(all(test, feature = "nightly"))]
extern crate test;
@@ -50,20 +38,22 @@ pub use url::Url;
pub use client::Client;
pub use error::{Result, Error};
pub use header::Headers;
pub use http::{Next, Encoder, Decoder, Control, ControlError};
pub use http::{Body, Chunk};
pub use method::Method::{self, Get, Head, Post, Delete};
pub use net::{HttpStream, Transport};
pub use status::StatusCode::{self, Ok, BadRequest, NotFound};
pub use server::Server;
pub use uri::RequestUri;
pub use version::HttpVersion;
macro_rules! rotor_try {
($e:expr) => ({
match $e {
Ok(v) => v,
Err(e) => return ::rotor::Response::error(e.into())
}
macro_rules! unimplemented {
() => ({
panic!("unimplemented")
});
($msg:expr) => ({
unimplemented!("{}", $msg)
});
($fmt:expr, $($arg:tt)*) => ({
panic!(concat!("unimplemented: ", $fmt), $($arg)*)
});
}
@@ -71,32 +61,10 @@ macro_rules! rotor_try {
mod mock;
pub mod client;
pub mod error;
pub mod method;
mod method;
pub mod header;
mod http;
pub mod net;
pub mod server;
pub mod status;
pub mod uri;
pub mod version;
/// Re-exporting the mime crate, for convenience.
pub mod mime {
pub use mime_crate::*;
}
/*
#[allow(unconditional_recursion)]
fn _assert_send<T: Send>() {
_assert_send::<Client>();
_assert_send::<client::Request<net::Fresh>>();
_assert_send::<client::Response>();
_assert_send::<error::Error>();
}
#[allow(unconditional_recursion)]
fn _assert_sync<T: Sync>() {
_assert_sync::<Client>();
_assert_sync::<error::Error>();
}
*/
mod uri;
mod version;

View File

@@ -7,8 +7,6 @@ use error::Error;
use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch,
Extension};
#[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// The Request Method (VERB)
///
@@ -134,21 +132,6 @@ impl Default for Method {
}
}
#[cfg(feature = "serde-serialization")]
impl Serialize for Method {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
format!("{}", self).serialize(serializer)
}
}
#[cfg(feature = "serde-serialization")]
impl Deserialize for Method {
fn deserialize<D>(deserializer: &mut D) -> Result<Method, D::Error> where D: Deserializer {
let string_representation: String = try!(Deserialize::deserialize(deserializer));
Ok(FromStr::from_str(&string_representation[..]).unwrap())
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;

View File

@@ -1,6 +1,9 @@
use std::cmp;
use std::io::{self, Read, Write};
use futures::Async;
use tokio::io::Io;
#[derive(Debug)]
pub struct Buf {
vec: Vec<u8>
@@ -8,8 +11,12 @@ pub struct Buf {
impl Buf {
pub fn new() -> Buf {
Buf::wrap(vec![])
}
pub fn wrap(vec: Vec<u8>) -> Buf {
Buf {
vec: vec![]
vec: vec,
}
}
}
@@ -58,27 +65,41 @@ impl ::vecio::Writev for Buf {
}
#[derive(Debug)]
pub struct Async<T> {
pub struct AsyncIo<T> {
inner: T,
bytes_until_block: usize,
error: Option<io::Error>,
}
impl<T> Async<T> {
pub fn new(inner: T, bytes: usize) -> Async<T> {
Async {
impl<T> AsyncIo<T> {
pub fn new(inner: T, bytes: usize) -> AsyncIo<T> {
AsyncIo {
inner: inner,
bytes_until_block: bytes
bytes_until_block: bytes,
error: None,
}
}
pub fn block_in(&mut self, bytes: usize) {
self.bytes_until_block = bytes;
}
pub fn error(&mut self, err: io::Error) {
self.error = Some(err);
}
}
impl<T: Read> Read for Async<T> {
impl AsyncIo<Buf> {
pub fn new_buf<T: Into<Vec<u8>>>(buf: T, bytes: usize) -> AsyncIo<Buf> {
AsyncIo::new(Buf::wrap(buf.into()), bytes)
}
}
impl<T: Read> Read for AsyncIo<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.bytes_until_block == 0 {
if let Some(err) = self.error.take() {
Err(err)
} else 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());
@@ -89,9 +110,11 @@ impl<T: Read> Read for Async<T> {
}
}
impl<T: Write> Write for Async<T> {
impl<T: Write> Write for AsyncIo<T> {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
if self.bytes_until_block == 0 {
if let Some(err) = self.error.take() {
Err(err)
} else 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());
@@ -106,7 +129,25 @@ impl<T: Write> Write for Async<T> {
}
}
impl<T: Write> ::vecio::Writev for Async<T> {
impl<T: Read + Write> Io for AsyncIo<T> {
fn poll_read(&mut self) -> Async<()> {
if self.bytes_until_block == 0 {
Async::NotReady
} else {
Async::Ready(())
}
}
fn poll_write(&mut self) -> Async<()> {
if self.bytes_until_block == 0 {
Async::NotReady
} else {
Async::Ready(())
}
}
}
impl<T: Write> ::vecio::Writev for AsyncIo<T> {
fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let cap = bufs.iter().map(|buf| buf.len()).fold(0, |total, next| total + next);
let mut vec = Vec::with_capacity(cap);
@@ -118,7 +159,7 @@ impl<T: Write> ::vecio::Writev for Async<T> {
}
}
impl ::std::ops::Deref for Async<Buf> {
impl ::std::ops::Deref for AsyncIo<Buf> {
type Target = [u8];
fn deref(&self) -> &[u8] {

View File

@@ -3,77 +3,12 @@ use std::io::{self, Read, Write};
use std::net::{SocketAddr};
use std::option;
use rotor::mio::tcp::{TcpStream, TcpListener};
use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt, TryAccept};
use std::net::{TcpStream, TcpListener};
#[cfg(feature = "openssl")]
pub use self::openssl::{Openssl, OpensslStream};
#[cfg(feature = "security-framework")]
pub use self::security_framework::{SecureTransport, SecureTransportClient, SecureTransportServer};
/// A trait representing a socket transport that can be used in a Client or Server.
#[cfg(not(windows))]
pub trait Transport: Read + Write + Evented + ::vecio::Writev {
/// Takes a socket error when event polling notices an `events.is_error()`.
fn take_socket_error(&mut self) -> io::Result<()>;
/// Returns if the this transport is blocked on read or write.
///
/// By default, the user will declare whether they wish to wait on read
/// or write events. However, some transports, such as those protected by
/// TLS, may be blocked on reading before it can write, or vice versa.
fn blocked(&self) -> Option<Blocked> {
None
}
}
/// A trait representing a socket transport that can be used in a Client or Server.
#[cfg(windows)]
pub trait Transport: Read + Write + Evented {
/// Takes a socket error when event polling notices an `events.is_error()`.
fn take_socket_error(&mut self) -> io::Result<()>;
/// Returns if the this transport is blocked on read or write.
///
/// By default, the user will declare whether they wish to wait on read
/// or write events. However, some transports, such as those protected by
/// TLS, may be blocked on reading before it can write, or vice versa.
fn blocked(&self) -> Option<Blocked> {
None
}
}
/// Declares when a transport is blocked from any further action, until the
/// corresponding event has occured.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Blocked {
/// Blocked on reading
Read,
/// blocked on writing
Write,
}
/// Accepts sockets asynchronously.
pub trait Accept: Evented {
/// The transport type that is accepted.
type Output: Transport;
/// Accept a socket from the listener, if it doesn not block.
fn accept(&self) -> io::Result<Option<Self::Output>>;
/// Return the local `SocketAddr` of this listener.
fn local_addr(&self) -> io::Result<SocketAddr>;
}
/// An alias to `mio::tcp::TcpStream`.
#[derive(Debug)]
pub struct HttpStream(pub TcpStream);
impl Transport for HttpStream {
fn take_socket_error(&mut self) -> io::Result<()> {
self.0.take_socket_error()
}
}
//#[derive(Debug)]
pub struct HttpStream(pub ::tokio::net::TcpStream);
impl Read for HttpStream {
#[inline]
@@ -94,23 +29,7 @@ impl Write for HttpStream {
}
}
impl Evented for HttpStream {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.register(selector, token, interest, opts)
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.reregister(selector, token, interest, opts)
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
self.0.deregister(selector)
}
}
/*
#[cfg(not(windows))]
impl ::vecio::Writev for HttpStream {
#[inline]
@@ -119,6 +38,7 @@ impl ::vecio::Writev for HttpStream {
self.0.writev(bufs)
}
}
*/
/// An alias to `mio::tcp::TcpListener`.
#[derive(Debug)]
@@ -137,59 +57,7 @@ impl HttpListener {
}
}
impl Accept for HttpListener {
type Output = HttpStream;
#[inline]
fn accept(&self) -> io::Result<Option<HttpStream>> {
TryAccept::accept(&self.0).map(|ok| ok.map(HttpStream))
}
#[inline]
fn local_addr(&self) -> io::Result<SocketAddr> {
self.0.local_addr()
}
}
impl Evented for HttpListener {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.register(selector, token, interest, opts)
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.reregister(selector, token, interest, opts)
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
self.0.deregister(selector)
}
}
impl IntoIterator for HttpListener {
type Item = Self;
type IntoIter = option::IntoIter<Self>;
fn into_iter(self) -> Self::IntoIter {
Some(self).into_iter()
}
}
/// Deprecated
///
/// Use `SslClient` and `SslServer` instead.
pub trait Ssl {
/// The protected stream.
type Stream: Transport;
/// Wrap a client stream with SSL.
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream>;
/// Wrap a server stream with SSL.
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>;
}
/*
/// An abstraction to allow any SSL implementation to be used with client-side `HttpsStream`s.
pub trait SslClient {
/// The protected stream.
@@ -206,21 +74,6 @@ pub trait SslServer {
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>;
}
impl<S: Ssl> SslClient for S {
type Stream = <S as Ssl>::Stream;
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
Ssl::wrap_client(self, stream, host)
}
}
impl<S: Ssl> SslServer for S {
type Stream = <S as Ssl>::Stream;
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
Ssl::wrap_server(self, stream)
}
}
/// A stream over the HTTP protocol, possibly protected by TLS.
#[derive(Debug)]
@@ -259,6 +112,7 @@ impl<S: Transport> Write for HttpsStream<S> {
}
}
/*
#[cfg(not(windows))]
impl<S: Transport> ::vecio::Writev for HttpsStream<S> {
#[inline]
@@ -269,8 +123,10 @@ impl<S: Transport> ::vecio::Writev for HttpsStream<S> {
}
}
}
*/
/*
#[cfg(unix)]
impl ::std::os::unix::io::AsRawFd for HttpStream {
#[inline]
@@ -289,50 +145,7 @@ impl<S: Transport + ::std::os::unix::io::AsRawFd> ::std::os::unix::io::AsRawFd f
}
}
}
impl<S: Transport> Evented for HttpsStream<S> {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
match *self {
HttpsStream::Http(ref s) => s.register(selector, token, interest, opts),
HttpsStream::Https(ref s) => s.register(selector, token, interest, opts),
}
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
match *self {
HttpsStream::Http(ref s) => s.reregister(selector, token, interest, opts),
HttpsStream::Https(ref s) => s.reregister(selector, token, interest, opts),
}
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
match *self {
HttpsStream::Http(ref s) => s.deregister(selector),
HttpsStream::Https(ref s) => s.deregister(selector),
}
}
}
impl<S: Transport> Transport for HttpsStream<S> {
#[inline]
fn take_socket_error(&mut self) -> io::Result<()> {
match *self {
HttpsStream::Http(ref mut s) => s.take_socket_error(),
HttpsStream::Https(ref mut s) => s.take_socket_error(),
}
}
#[inline]
fn blocked(&self) -> Option<Blocked> {
match *self {
HttpsStream::Http(ref s) => s.blocked(),
HttpsStream::Https(ref s) => s.blocked(),
}
}
}
*/
/// An `HttpListener` over SSL.
#[derive(Debug)]
@@ -360,6 +173,7 @@ impl<S: SslServer> HttpsListener<S> {
}
}
/*
impl<S: SslServer> Accept for HttpsListener<S> {
type Output = S::Stream;
@@ -382,401 +196,6 @@ impl<S: SslServer> Accept for HttpsListener<S> {
self.listener.local_addr()
}
}
impl<S: SslServer> Evented for HttpsListener<S> {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.listener.register(selector, token, interest, opts)
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.listener.reregister(selector, token, interest, opts)
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
self.listener.deregister(selector)
}
}
impl<S: SslServer> IntoIterator for HttpsListener<S> {
type Item = Self;
type IntoIter = option::IntoIter<Self>;
fn into_iter(self) -> Self::IntoIter {
Some(self).into_iter()
}
}
fn _assert_transport() {
fn _assert<T: Transport>() {}
_assert::<HttpsStream<HttpStream>>();
}
/*
#[cfg(all(not(feature = "openssl"), not(feature = "security-framework")))]
#[doc(hidden)]
pub type DefaultConnector = HttpConnector;
#[cfg(feature = "openssl")]
#[doc(hidden)]
pub type DefaultConnector = HttpsConnector<self::openssl::OpensslClient>;
#[cfg(all(feature = "security-framework", not(feature = "openssl")))]
pub type DefaultConnector = HttpsConnector<self::security_framework::ClientWrapper>;
#[doc(hidden)]
pub type DefaultTransport = <DefaultConnector as Connect>::Output;
*/
#[cfg(feature = "openssl")]
mod openssl {
use std::io::{self, Write};
use std::path::Path;
use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt};
use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_PEER, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use openssl::ssl::error::StreamError as SslIoError;
use openssl::ssl::error::SslError;
use openssl::ssl::error::Error as OpensslError;
use openssl::x509::X509FileType;
use super::{HttpStream, Blocked};
/// An implementation of `Ssl` for OpenSSL.
///
/// # Example
///
/// ```no_run
/// use hyper::Server;
/// use hyper::net::Openssl;
///
/// let ssl = Openssl::with_cert_and_key("/home/foo/cert", "/home/foo/key").unwrap();
/// Server::https(&"0.0.0.0:443".parse().unwrap(), ssl).unwrap();
/// ```
///
/// For complete control, create a `SslContext` with the options you desire
/// and then create `Openssl { context: ctx }
#[derive(Debug, Clone)]
pub struct Openssl {
/// The `SslContext` from openssl crate.
pub context: SslContext
}
/// A client-specific implementation of OpenSSL.
#[derive(Debug, Clone)]
pub struct OpensslClient(SslContext);
impl Default for OpensslClient {
fn default() -> OpensslClient {
let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
ctx.set_default_verify_paths().unwrap();
ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
// cipher list taken from curl:
// https://github.com/curl/curl/blob/5bf5f6ebfcede78ef7c2b16daa41c4b7ba266087/lib/vtls/openssl.h#L120
ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH").unwrap();
OpensslClient::new(ctx)
}
}
impl OpensslClient {
/// Creates a new OpensslClient with a custom SslContext
pub fn new(ctx: SslContext) -> OpensslClient {
OpensslClient(ctx)
}
}
impl super::SslClient for OpensslClient {
type Stream = OpensslStream<HttpStream>;
#[cfg(not(windows))]
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
let mut ssl = try!(Ssl::new(&self.0));
try!(ssl.set_hostname(host));
let host = host.to_owned();
ssl.set_verify_callback(SSL_VERIFY_PEER, move |p, x| ::openssl_verify::verify_callback(&host, p, x));
SslStream::connect(ssl, stream)
.map(openssl_stream)
.map_err(From::from)
}
#[cfg(windows)]
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
let mut ssl = try!(Ssl::new(&self.0));
try!(ssl.set_hostname(host));
SslStream::connect(ssl, stream)
.map(openssl_stream)
.map_err(From::from)
}
}
impl Default for Openssl {
fn default() -> Openssl {
Openssl {
context: SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| {
// if we cannot create a SslContext, that's because of a
// serious problem. just crash.
panic!("{}", e)
})
}
}
}
impl Openssl {
/// Ease creating an `Openssl` with a certificate and key.
pub fn with_cert_and_key<C, K>(cert: C, key: K) -> Result<Openssl, SslError>
where C: AsRef<Path>, K: AsRef<Path> {
let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH"));
try!(ctx.set_certificate_file(cert.as_ref(), X509FileType::PEM));
try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM));
Ok(Openssl { context: ctx })
}
}
impl super::Ssl for Openssl {
type Stream = OpensslStream<HttpStream>;
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
let ssl = try!(Ssl::new(&self.context));
try!(ssl.set_hostname(host));
SslStream::connect(ssl, stream)
.map(openssl_stream)
.map_err(From::from)
}
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
match SslStream::accept(&self.context, stream) {
Ok(ssl_stream) => Ok(openssl_stream(ssl_stream)),
Err(SslIoError(e)) => {
Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into())
},
Err(e) => Err(e.into())
}
}
}
/// A transport protected by OpenSSL.
#[derive(Debug)]
pub struct OpensslStream<T> {
stream: SslStream<T>,
blocked: Option<Blocked>,
}
fn openssl_stream<T>(inner: SslStream<T>) -> OpensslStream<T> {
OpensslStream {
stream: inner,
blocked: None,
}
}
impl<T: super::Transport> io::Read for OpensslStream<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.blocked = None;
self.stream.ssl_read(buf).or_else(|e| match e {
OpensslError::ZeroReturn => Ok(0),
OpensslError::WantWrite(e) => {
self.blocked = Some(Blocked::Write);
Err(e)
},
OpensslError::WantRead(e) | OpensslError::Stream(e) => Err(e),
e => Err(io::Error::new(io::ErrorKind::Other, e))
})
}
}
impl<T: super::Transport> io::Write for OpensslStream<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.blocked = None;
self.stream.ssl_write(buf).or_else(|e| match e {
OpensslError::ZeroReturn => Ok(0),
OpensslError::WantRead(e) => {
self.blocked = Some(Blocked::Read);
Err(e)
},
OpensslError::WantWrite(e) | OpensslError::Stream(e) => Err(e),
e => Err(io::Error::new(io::ErrorKind::Other, e))
})
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
impl<T: super::Transport> Evented for OpensslStream<T> {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.stream.get_ref().register(selector, token, interest, opts)
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.stream.get_ref().reregister(selector, token, interest, opts)
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
self.stream.get_ref().deregister(selector)
}
}
impl<T: super::Transport> ::vecio::Writev for OpensslStream<T> {
fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let vec = bufs.concat();
self.write(&vec)
}
}
impl<T: super::Transport> super::Transport for OpensslStream<T> {
fn take_socket_error(&mut self) -> io::Result<()> {
self.stream.get_mut().take_socket_error()
}
fn blocked(&self) -> Option<super::Blocked> {
self.blocked
}
}
}
#[cfg(feature = "security-framework")]
mod security_framework {
use std::io::{self, Read, Write};
use error::Error;
use net::{SslClient, SslServer, HttpStream, Transport, Blocked};
use security_framework::secure_transport::SslStream;
pub use security_framework::secure_transport::{ClientBuilder as SecureTransportClient, ServerBuilder as SecureTransportServer};
use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt};
impl SslClient for SecureTransportClient {
type Stream = SecureTransport<HttpStream>;
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
match self.handshake(host, journal(stream)) {
Ok(s) => Ok(SecureTransport(s)),
Err(e) => Err(Error::Ssl(e.into())),
}
}
}
impl SslServer for SecureTransportServer {
type Stream = SecureTransport<HttpStream>;
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
match self.handshake(journal(stream)) {
Ok(s) => Ok(SecureTransport(s)),
Err(e) => Err(Error::Ssl(e.into())),
}
}
}
/// A transport protected by Security Framework.
#[derive(Debug)]
pub struct SecureTransport<T>(SslStream<Journal<T>>);
impl<T: Transport> io::Read for SecureTransport<T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
}
impl<T: Transport> io::Write for SecureTransport<T> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl<T: Transport> Evented for SecureTransport<T> {
#[inline]
fn register(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.get_ref().inner.register(selector, token, interest, opts)
}
#[inline]
fn reregister(&self, selector: &mut Selector, token: Token, interest: EventSet, opts: PollOpt) -> io::Result<()> {
self.0.get_ref().inner.reregister(selector, token, interest, opts)
}
#[inline]
fn deregister(&self, selector: &mut Selector) -> io::Result<()> {
self.0.get_ref().inner.deregister(selector)
}
}
impl<T: Transport> ::vecio::Writev for SecureTransport<T> {
fn writev(&mut self, bufs: &[&[u8]]) -> io::Result<usize> {
let vec = bufs.concat();
self.write(&vec)
}
}
impl<T: Transport> Transport for SecureTransport<T> {
fn take_socket_error(&mut self) -> io::Result<()> {
self.0.get_mut().inner.take_socket_error()
}
fn blocked(&self) -> Option<super::Blocked> {
self.0.get_ref().blocked
}
}
// Records if this object was blocked on reading or writing.
#[derive(Debug)]
struct Journal<T> {
inner: T,
blocked: Option<Blocked>,
}
fn journal<T: Read + Write>(inner: T) -> Journal<T> {
Journal {
inner: inner,
blocked: None,
}
}
impl<T: Read> Read for Journal<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.blocked = None;
self.inner.read(buf).map_err(|e| match e.kind() {
io::ErrorKind::WouldBlock => {
self.blocked = Some(Blocked::Read);
e
},
_ => e
})
}
}
impl<T: Write> Write for Journal<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.blocked = None;
self.inner.write(buf).map_err(|e| match e.kind() {
io::ErrorKind::WouldBlock => {
self.blocked = Some(Blocked::Write);
e
},
_ => e
})
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
}
*/

View File

@@ -1,57 +0,0 @@
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, transport: &T) -> Next {
trace!("on_incoming {:?}", head);
let req = request::new(head, transport);
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,65 +1,55 @@
//! HTTP Server
//!
//! A `Server` is created to listen on a port, parse HTTP requests, and hand
//! them off to a `Handler`.
//! them off to a `Service`.
use std::fmt;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::io;
use std::net::{SocketAddr, TcpListener as StdTcpListener};
use rotor::mio::{EventSet, PollOpt};
use rotor::{self, Scope};
use futures::{Future, Map};
use futures::stream::{Stream};
use futures::sync::oneshot;
use tokio::io::Io;
use tokio::net::TcpListener;
use tokio::reactor::{Core, Handle};
use tokio_proto::BindServer;
use tokio_proto::streaming::Message;
use tokio_proto::streaming::pipeline::ServerProto;
pub use tokio_service::{NewService, Service};
pub use self::accept::Accept;
pub use self::request::Request;
pub use self::response::Response;
use http::{self, Next, ReadyResult};
pub use net::{Accept, HttpListener, HttpsListener};
use net::{SslServer, Transport};
use http;
mod request;
mod response;
mod message;
/// A configured `Server` ready to run.
pub struct ServerLoop<A, H> where A: Accept, H: HandlerFactory<A::Output> {
inner: Option<(rotor::Loop<ServerFsm<A, H>>, Context<H>)>,
}
impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("ServerLoop")
}
}
type HttpIncoming = ::tokio::net::Incoming;
/// A Server that can accept incoming network requests.
#[derive(Debug)]
pub struct Server<A> {
lead_listener: A,
other_listeners: Vec<A>,
accepter: A,
addr: SocketAddr,
keep_alive: bool,
idle_timeout: Option<Duration>,
max_sockets: usize,
//idle_timeout: Option<Duration>,
//max_sockets: usize,
}
impl<A: Accept> Server<A> {
/// Creates a new Server from one or more Listeners.
/// Creates a new Server from a Stream of Ios.
///
/// Panics if listeners is an empty iterator.
pub fn new<I: IntoIterator<Item = A>>(listeners: I) -> Server<A> {
let mut listeners = listeners.into_iter();
let lead_listener = listeners.next().expect("Server::new requires at least 1 listener");
let other_listeners = listeners.collect::<Vec<_>>();
/// The addr is the socket address the accepter is listening on.
pub fn new(accepter: A, addr: SocketAddr) -> Server<A> {
Server {
lead_listener: lead_listener,
other_listeners: other_listeners,
accepter: accepter,
addr: addr,
keep_alive: true,
idle_timeout: Some(Duration::from_secs(10)),
max_sockets: 4096,
//idle_timeout: Some(Duration::from_secs(75)),
//max_sockets: 4096,
}
}
@@ -71,14 +61,17 @@ impl<A: Accept> Server<A> {
self
}
/*
/// Sets how long an idle connection will be kept before closing.
///
/// Default is 10 seconds.
/// Default is 75 seconds.
pub fn idle_timeout(mut self, val: Option<Duration>) -> Server<A> {
self.idle_timeout = val;
self
}
*/
/*
/// Sets the maximum open sockets for this Server.
///
/// Default is 4096, but most servers can handle much more than this.
@@ -86,20 +79,21 @@ impl<A: Accept> Server<A> {
self.max_sockets = val;
self
}
*/
}
impl Server<HttpListener> { //<H: HandlerFactory<<HttpListener as Accept>::Output>> Server<HttpListener, H> {
impl Server<HttpIncoming> {
/// Creates a new HTTP server config listening on the provided address.
pub fn http(addr: &SocketAddr) -> ::Result<Server<HttpListener>> {
use ::rotor::mio::tcp::TcpListener;
TcpListener::bind(addr)
.map(HttpListener)
.map(Server::new)
.map_err(From::from)
pub fn http(addr: &SocketAddr, handle: &Handle) -> ::Result<Server<HttpIncoming>> {
let listener = try!(StdTcpListener::bind(addr));
let addr = try!(listener.local_addr());
let listener = try!(TcpListener::from_listener(listener, &addr, handle));
Ok(Server::new(listener.incoming(), addr))
}
}
/*
impl<S: SslServer> Server<HttpsListener<S>> {
/// Creates a new server config that will handle `HttpStream`s over SSL.
///
@@ -110,304 +104,227 @@ impl<S: SslServer> Server<HttpsListener<S>> {
.map_err(From::from)
}
}
*/
impl<A: Accept> Server<A> {
/// Binds to a socket and starts handling connections.
pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)>
where H: HandlerFactory<A::Output> {
let shutdown = Arc::new(AtomicBool::new(false));
let mut config = rotor::Config::new();
config.slab_capacity(self.max_sockets);
config.mio().notify_capacity(self.max_sockets);
let keep_alive = self.keep_alive;
let idle_timeout = self.idle_timeout;
let mut loop_ = rotor::Loop::new(&config).unwrap();
let mut addrs = Vec::with_capacity(1 + self.other_listeners.len());
// Add the lead listener. This one handles shutdown messages.
let mut notifier = None;
{
let notifier = &mut notifier;
let listener = self.lead_listener;
addrs.push(try!(listener.local_addr()));
let shutdown_rx = shutdown.clone();
loop_.add_machine_with(move |scope| {
*notifier = Some(scope.notifier());
rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level()));
rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx))
}).unwrap();
}
let notifier = notifier.expect("loop.add_machine failed");
// Add the other listeners.
for listener in self.other_listeners {
addrs.push(try!(listener.local_addr()));
let shutdown_rx = shutdown.clone();
loop_.add_machine_with(move |scope| {
rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level()));
rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx))
}).unwrap();
}
let listening = Listening {
addrs: addrs,
shutdown: (shutdown, notifier),
pub fn handle<H>(self, factory: H, handle: &Handle) -> ::Result<SocketAddr>
where H: NewService<Request=Request, Response=Response, Error=::Error> + 'static {
let binder = HttpServer {
keep_alive: self.keep_alive,
};
let server = ServerLoop {
inner: Some((loop_, Context {
factory: factory,
idle_timeout: idle_timeout,
keep_alive: keep_alive,
}))
};
Ok((listening, server))
let inner_handle = handle.clone();
handle.spawn(self.accepter.accept().for_each(move |(socket, remote_addr)| {
let service = HttpService {
inner: try!(factory.new_service()),
remote_addr: remote_addr,
};
binder.bind_server(&inner_handle, socket, service);
Ok(())
}).map_err(|e| {
error!("listener io error: {:?}", e);
()
}));
Ok(self.addr)
}
}
impl Server<()> {
/// Create a server that owns its event loop.
///
/// The returned `ServerLoop` can be used to run the loop forever in the
/// thread. The returned `Listening` can be sent to another thread, and
/// used to shutdown the `ServerLoop`.
pub fn standalone<F>(closure: F) -> ::Result<(Listening, ServerLoop)>
where F: FnOnce(&Handle) -> ::Result<SocketAddr> {
let core = try!(Core::new());
let handle = core.handle();
let addr = try!(closure(&handle));
let (shutdown_tx, shutdown_rx) = oneshot::channel();
Ok((
Listening {
addr: addr,
shutdown: shutdown_tx,
},
ServerLoop {
inner: Some((core, shutdown_rx)),
}
))
impl<A: Accept, H: HandlerFactory<A::Output>> ServerLoop<A, H> {
}
}
/// A configured `Server` ready to run.
pub struct ServerLoop {
inner: Option<(Core, oneshot::Receiver<()>)>,
}
impl fmt::Debug for ServerLoop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("ServerLoop")
}
}
impl ServerLoop {
/// Runs the server forever in this loop.
///
/// This will block the current thread.
pub fn run(self) {
// drop will take care of it.
trace!("ServerLoop::run()");
}
}
impl<A: Accept, H: HandlerFactory<A::Output>> Drop for ServerLoop<A, H> {
impl Drop for ServerLoop {
fn drop(&mut self) {
self.inner.take().map(|(loop_, ctx)| {
let _ = loop_.run(ctx);
self.inner.take().map(|(mut loop_, shutdown)| {
debug!("ServerLoop::drop running");
let _ = loop_.run(shutdown.or_else(|_dropped| ::futures::future::empty::<(), oneshot::Canceled>()));
debug!("Server closed");
});
}
}
struct Context<F> {
factory: F,
idle_timeout: Option<Duration>,
keep_alive: bool,
}
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<()>) -> Option<Self::Output> {
Some(message::Message::new(self.factory.create(seed.control())))
}
fn keep_alive_interest(&self) -> Next {
if let Some(dur) = self.idle_timeout {
Next::read().timeout(dur)
} else {
Next::read()
}
}
}
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, Next::read(), scope.notifier(), scope.now())
.keep_alive(scope.keep_alive)
)
)
}
fn ready(self, events: EventSet, scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(listener, rx) => {
match listener.accept() {
Ok(Some(conn)) => {
rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn)
},
Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)),
Err(e) => {
error!("listener accept error {}", e);
// usually fine, just keep listening
rotor::Response::ok(ServerFsm::Listener(listener, rx))
}
}
},
ServerFsm::Conn(conn) => {
let mut conn = Some(conn);
loop {
match conn.take().unwrap().ready(events, scope) {
ReadyResult::Continue(c) => conn = Some(c),
ReadyResult::Done(res) => {
return match res {
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 spawned(self, _scope: &mut Scope<Self::Context>) -> rotor::Response<Self, Self::Seed> {
match self {
ServerFsm::Listener(listener, rx) => {
match listener.accept() {
Ok(Some(conn)) => {
rotor::Response::spawn(ServerFsm::Listener(listener, rx), conn)
},
Ok(None) => rotor::Response::ok(ServerFsm::Listener(listener, rx)),
Err(e) => {
error!("listener accept error {}", e);
// usually fine, just keep listening
rotor::Response::ok(ServerFsm::Listener(listener, rx))
}
}
},
sock => rotor::Response::ok(sock)
}
}
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 handle of the running server.
pub struct Listening {
addrs: Vec<SocketAddr>,
shutdown: (Arc<AtomicBool>, rotor::Notifier),
addr: SocketAddr,
shutdown: ::futures::sync::oneshot::Sender<()>,
}
impl fmt::Debug for Listening {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Listening")
.field("addrs", &self.addrs)
.field("closed", &self.shutdown.0.load(Ordering::Relaxed))
.field("addr", &self.addr)
.finish()
}
}
impl fmt::Display for Listening {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, addr) in self.addrs().iter().enumerate() {
if i > 1 {
try!(f.write_str(", "));
}
try!(fmt::Display::fmt(addr, f));
}
Ok(())
fmt::Display::fmt(&self.addr, f)
}
}
impl Listening {
/// The addresses this server is listening on.
pub fn addrs(&self) -> &[SocketAddr] {
&self.addrs
pub fn addr(&self) -> &SocketAddr {
&self.addr
}
/// Stop the server from listening to its socket address.
pub fn close(self) {
debug!("closing server {}", self);
self.shutdown.0.store(true, Ordering::Release);
self.shutdown.1.wakeup().unwrap();
self.shutdown.complete(());
}
}
/// A trait to react to server events that happen for each message.
///
/// Each event handler returns its desired `Next` action.
pub trait Handler<T: Transport> {
/// This event occurs first, triggering when a `Request` has been parsed.
fn on_request(&mut self, request: Request<T>) -> 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;
struct HttpServer {
keep_alive: bool,
}
/// This event occurs whenever an `Error` occurs outside of the other events.
impl<T: Io + 'static> ServerProto<T> for HttpServer {
type Request = http::RequestHead;
type RequestBody = http::Chunk;
type Response = ResponseHead;
type ResponseBody = http::Chunk;
type Error = ::Error;
type Transport = http::Conn<T, http::ServerTransaction>;
type BindTransport = io::Result<http::Conn<T, http::ServerTransaction>>;
fn bind_transport(&self, io: T) -> Self::BindTransport {
let ka = if self.keep_alive {
http::KA::Busy
} else {
http::KA::Disabled
};
Ok(http::Conn::new(io, ka))
}
}
struct HttpService<T> {
inner: T,
remote_addr: SocketAddr,
}
fn map_response_to_message(res: Response) -> Message<ResponseHead, http::TokioBody> {
let (head, body) = response::split(res);
if let Some(body) = body {
Message::WithBody(head, body.into())
} else {
Message::WithoutBody(head)
}
}
type ResponseHead = http::MessageHead<::StatusCode>;
impl<T> Service for HttpService<T>
where T: Service<Request=Request, Response=Response, Error=::Error>,
{
type Request = Message<http::RequestHead, http::TokioBody>;
type Response = Message<ResponseHead, http::TokioBody>;
type Error = ::Error;
type Future = Map<T::Future, fn(Response) -> Message<ResponseHead, http::TokioBody>>;
fn call(&self, message: Self::Request) -> Self::Future {
let (head, body) = match message {
Message::WithoutBody(head) => (head, http::Body::empty()),
Message::WithBody(head, body) => (head, body.into()),
};
let req = request::new(self.remote_addr, head, body);
self.inner.call(req).map(map_response_to_message)
}
}
//private so the `Acceptor` type can stay internal
mod accept {
use std::io;
use std::net::SocketAddr;
use futures::{Stream, Poll};
use tokio::io::Io;
/// An Acceptor is an incoming Stream of Io.
///
/// This could IO errors while waiting for events, or a timeout, etc.
fn on_error(&mut self, err: ::Error) -> Next where Self: Sized {
debug!("default Handler.on_error({:?})", err);
http::Next::remove()
/// This trait is not implemented directly, and only exists to make the
/// intent clearer. A `Stream<Item=(Io, SocketAddr), Error=io::Error>`
/// should be implemented instead.
pub trait Accept: Stream<Error=io::Error> {
#[doc(hidden)]
type Output: Io + 'static;
#[doc(hidden)]
type Stream: Stream<Item=(Self::Output, SocketAddr), Error=io::Error> + 'static;
#[doc(hidden)]
fn accept(self) -> Accepter<Self::Stream, Self::Output>
where Self: Sized;
}
/// This event occurs when this Handler has requested to remove the Transport.
fn on_remove(self, _transport: T) where Self: Sized {
debug!("default Handler.on_remove");
}
}
/// Used to create a `Handler` when a new message is received by the server.
pub trait HandlerFactory<T: Transport> {
/// The `Handler` to use for the incoming message.
type Output: Handler<T>;
/// Creates the associated `Handler`.
fn create(&mut self, ctrl: http::Control) -> Self::Output;
}
impl<F, H, T> HandlerFactory<T> for F
where F: FnMut(http::Control) -> H, H: Handler<T>, T: Transport {
type Output = H;
fn create(&mut self, ctrl: http::Control) -> H {
self(ctrl)
#[allow(missing_debug_implementations)]
pub struct Accepter<T: Stream<Item=(I, SocketAddr), Error=io::Error> + 'static, I: Io + 'static>(T, ::std::marker::PhantomData<I>);
impl<T, I> Stream for Accepter<T, I>
where T: Stream<Item=(I, SocketAddr), Error=io::Error>,
I: Io + 'static,
{
type Item = T::Item;
type Error = io::Error;
#[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.0.poll()
}
}
impl<T, I> Accept for T
where T: Stream<Item=(I, SocketAddr), Error=io::Error> + 'static,
I: Io + 'static,
{
type Output = I;
type Stream = T;
fn accept(self) -> Accepter<Self, I> {
Accepter(self, ::std::marker::PhantomData)
}
}
}

View File

@@ -4,48 +4,25 @@
//! target URI, headers, and message body.
use std::fmt;
use std::net::SocketAddr;
use version::HttpVersion;
use method::Method;
use header::Headers;
use http::{RequestHead, MessageHead, RequestLine};
use http::{RequestHead, MessageHead, RequestLine, Body};
use uri::RequestUri;
pub fn new<'a, T>(incoming: RequestHead, transport: &'a T) -> Request<'a, T> {
let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming;
debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
debug!("{:#?}", headers);
Request {
method: method,
uri: uri,
headers: headers,
version: version,
transport: transport,
}
}
/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
pub struct Request<'a, T: 'a> {
pub struct Request {
method: Method,
uri: RequestUri,
version: HttpVersion,
headers: Headers,
transport: &'a T,
remote_addr: SocketAddr,
body: Body,
}
impl<'a, T> fmt::Debug for Request<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Request")
.field("method", &self.method)
.field("uri", &self.uri)
.field("version", &self.version)
.field("headers", &self.headers)
.finish()
}
}
impl<'a, T> Request<'a, T> {
impl Request {
/// The `Method`, such as `Get`, `Post`, etc.
#[inline]
pub fn method(&self) -> &Method { &self.method }
@@ -54,10 +31,6 @@ impl<'a, T> Request<'a, T> {
#[inline]
pub fn headers(&self) -> &Headers { &self.headers }
/// The underlying `Transport` of this request.
#[inline]
pub fn transport(&self) -> &'a T { self.transport }
/// The target request-uri for this request.
#[inline]
pub fn uri(&self) -> &RequestUri { &self.uri }
@@ -66,6 +39,10 @@ impl<'a, T> Request<'a, T> {
#[inline]
pub fn version(&self) -> &HttpVersion { &self.version }
/// The remote socket address of this request
#[inline]
pub fn remote_addr(&self) -> &SocketAddr { &self.remote_addr }
/// The target path of this Request.
#[inline]
pub fn path(&self) -> Option<&str> {
@@ -86,12 +63,44 @@ impl<'a, T> Request<'a, T> {
}
}
/// Take the `Body` of this `Request`.
#[inline]
pub fn body(self) -> Body {
self.body
}
/// Deconstruct this Request into its pieces.
///
/// Modifying these pieces will have no effect on how hyper behaves.
#[inline]
pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers) {
(self.method, self.uri, self.version, self.headers)
pub fn deconstruct(self) -> (Method, RequestUri, HttpVersion, Headers, Body) {
(self.method, self.uri, self.version, self.headers, self.body)
}
}
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Request")
.field("method", &self.method)
.field("uri", &self.uri)
.field("version", &self.version)
.field("remote_addr", &self.remote_addr)
.field("headers", &self.headers)
.finish()
}
}
pub fn new(addr: SocketAddr, incoming: RequestHead, body: Body) -> Request {
let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming;
debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
debug!("{:#?}", headers);
Request {
method: method,
uri: uri,
headers: headers,
version: version,
remote_addr: addr,
body: body,
}
}

View File

@@ -1,22 +1,26 @@
//! Server Responses
//!
//! These are responses sent by a `hyper::Server` to clients, after
//! receiving a request.
use std::fmt;
use header;
use http;
use http::{self, Body};
use status::StatusCode;
use version;
/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
/// The Response sent to a client after receiving a Request in a Service.
///
/// The default `StatusCode` for a `Response` is `200 OK`.
#[derive(Debug)]
pub struct Response<'a> {
head: &'a mut http::MessageHead<StatusCode>,
#[derive(Default)]
pub struct Response {
head: http::MessageHead<StatusCode>,
body: Option<Body>,
}
impl<'a> Response<'a> {
impl Response {
/// Create a new Response.
#[inline]
pub fn new() -> Response {
Response::default()
}
/// The headers of this response.
#[inline]
pub fn headers(&self) -> &header::Headers { &self.head.headers }
@@ -35,16 +39,65 @@ impl<'a> Response<'a> {
#[inline]
pub fn headers_mut(&mut self) -> &mut header::Headers { &mut self.head.headers }
/// Set the status of this response.
/// Set the `StatusCode` for this response.
#[inline]
pub fn set_status(&mut self, status: StatusCode) {
self.head.subject = status;
}
}
/// Creates a new Response that can be used to write to a network stream.
pub fn new(head: &mut http::MessageHead<StatusCode>) -> Response {
Response {
head: head
/// Set the body.
#[inline]
pub fn set_body<T: Into<Body>>(&mut self, body: T) {
self.body = Some(body.into());
}
/// Set the status and move the Response.
///
/// Useful for the "builder-style" pattern.
#[inline]
pub fn with_status(mut self, status: StatusCode) -> Self {
self.set_status(status);
self
}
/// Set a header and move the Response.
///
/// Useful for the "builder-style" pattern.
#[inline]
pub fn with_header<H: header::Header>(mut self, header: H) -> Self {
self.head.headers.set(header);
self
}
/// Set the headers and move the Response.
///
/// Useful for the "builder-style" pattern.
#[inline]
pub fn with_headers(mut self, headers: header::Headers) -> Self {
self.head.headers = headers;
self
}
/// Set the body and move the Response.
///
/// Useful for the "builder-style" pattern.
#[inline]
pub fn with_body<T: Into<Body>>(mut self, body: T) -> Self {
self.set_body(body);
self
}
}
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("status", &self.head.subject)
.field("version", &self.head.version)
.field("headers", &self.head.headers)
.finish()
}
}
pub fn split(res: Response) -> (http::MessageHead<StatusCode>, Option<Body>) {
(res.head, res.body)
}

View File

@@ -66,7 +66,7 @@ impl FromStr for RequestUri {
fn from_str(s: &str) -> Result<RequestUri, Error> {
let bytes = s.as_bytes();
if bytes == [] {
if bytes.len() == 0 {
Err(Error::Uri(UrlError::RelativeUrlWithoutBase))
} else if bytes == b"*" {
Ok(RequestUri::Star)

View File

@@ -19,6 +19,8 @@ pub enum HttpVersion {
H2,
/// `HTTP/2.0` over cleartext
H2c,
#[doc(hidden)]
__DontMatchMe,
}
impl fmt::Display for HttpVersion {
@@ -29,6 +31,7 @@ impl fmt::Display for HttpVersion {
Http11 => "HTTP/1.1",
H2 => "h2",
H2c => "h2c",
HttpVersion::__DontMatchMe => unreachable!(),
})
}
}

View File

@@ -1,189 +1,27 @@
#![deny(warnings)]
extern crate hyper;
extern crate futures;
extern crate tokio_core;
use std::io::{self, Read, Write};
use std::net::TcpListener;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use hyper::client::{Handler, Request, Response, HttpConnector};
use hyper::{Method, StatusCode, Next, Encoder, Decoder};
use hyper::header::Headers;
use hyper::net::HttpStream;
use hyper::client::{Client, Request, HttpConnector};
use hyper::{Method, StatusCode};
fn s(bytes: &[u8]) -> &str {
::std::str::from_utf8(bytes.as_ref()).unwrap()
use futures::Future;
use futures::sync::oneshot;
use tokio_core::reactor::{Core, Handle};
fn client(handle: &Handle) -> Client<HttpConnector> {
Client::new(handle)
}
#[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());
req.headers_mut().extend(self.opts.headers.iter());
if self.opts.body.is_some() {
Next::write()
} else {
read(&self.opts)
}
}
fn on_request_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
if let Some(ref mut body) = self.opts.body {
let n = encoder.write(body).unwrap();
*body = &body[n..];
if !body.is_empty() {
return Next::write()
}
}
encoder.close();
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 {
body: Option<&'static [u8]>,
method: Method,
headers: Headers,
read_timeout: Option<Duration>,
}
impl Default for Opts {
fn default() -> Opts {
Opts {
body: None,
method: Method::Get,
headers: Headers::new(),
read_timeout: None,
}
}
}
fn opts() -> Opts {
Opts::default()
}
impl Opts {
fn method(mut self, method: Method) -> Opts {
self.method = method;
self
}
fn header<H: ::hyper::header::Header>(mut self, header: H) -> Opts {
self.headers.set(header);
self
}
fn body(mut self, body: Option<&'static [u8]>) -> Opts {
self.body = body;
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),
}
fn s(buf: &[u8]) -> &str {
::std::str::from_utf8(buf).unwrap()
}
macro_rules! test {
@@ -206,43 +44,50 @@ macro_rules! test {
) => (
#[test]
fn $name() {
#[allow(unused)]
#![allow(unused)]
use hyper::header::*;
let server = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = server.local_addr().unwrap();
let client = client();
let opts = opts()
.method(Method::$client_method)
.body($request_body);
let mut core = Core::new().unwrap();
let client = client(&core.handle());
let mut req = Request::new(Method::$client_method, format!($client_url, addr=addr).parse().unwrap());
$(
let opts = opts.header($request_headers);
req.headers_mut().set($request_headers);
)*
let res = client.request(format!($client_url, addr=addr), opts);
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 expected = format!($server_expected, addr=addr);
let mut buf = [0; 4096];
let mut n = 0;
while n < buf.len() && n < expected.len() {
n += inc.read(&mut buf[n..]).unwrap();
if let Some(body) = $request_body {
let body: &'static str = body;
req.set_body(body);
}
assert_eq!(s(&buf[..n]), expected);
let res = client.request(req);
inc.write_all($server_reply.as_ref()).unwrap();
let (tx, rx) = oneshot::channel();
if let Msg::Head(head) = res.recv().unwrap() {
assert_eq!(head.status(), &StatusCode::$client_status);
$(
assert_eq!(head.headers().get(), Some(&$response_headers));
)*
} else {
panic!("we lost the head!");
}
//drop(inc);
thread::spawn(move || {
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 expected = format!($server_expected, addr=addr);
let mut buf = [0; 4096];
let mut n = 0;
while n < buf.len() && n < expected.len() {
n += inc.read(&mut buf[n..]).unwrap();
}
assert_eq!(s(&buf[..n]), expected);
assert!(res.recv().is_err());
inc.write_all($server_reply.as_ref()).unwrap();
tx.complete(());
});
let rx = rx.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
let work = res.join(rx).map(|r| r.0);
let res = core.run(work).unwrap();
assert_eq!(res.status(), &StatusCode::$client_status);
$(
assert_eq!(res.headers().get(), Some(&$response_headers));
)*
}
);
}
@@ -311,7 +156,7 @@ test! {
headers: [
ContentLength(7),
],
body: Some(b"foo bar"),
body: Some("foo bar"),
response:
status: Ok,
headers: [],
@@ -340,51 +185,86 @@ test! {
headers: [
TransferEncoding::chunked(),
],
body: Some(b"foo bar baz"),
body: Some("foo bar baz"),
response:
status: Ok,
headers: [],
body: None,
}
#[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)
}
}
#[test]
fn client_keep_alive() {
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://{}/a", addr), opts());
let mut core = Core::new().unwrap();
let client = client(&core.handle());
let mut sock = server.accept().unwrap().0;
sock.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
sock.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
let mut buf = [0; 4096];
sock.read(&mut buf).expect("read 1");
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 1");
while let Ok(_) = res.recv() {}
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
thread::spawn(move || {
let mut sock = server.accept().unwrap().0;
sock.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
sock.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
let mut buf = [0; 4096];
sock.read(&mut buf).expect("read 1");
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 1");
tx1.complete(());
let res = client.request(format!("http://{}/b", addr), opts());
sock.read(&mut buf).expect("read 2");
let second_get = b"GET /b HTTP/1.1\r\n";
assert_eq!(&buf[..second_get.len()], second_get);
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 2");
sock.read(&mut buf).expect("read 2");
let second_get = b"GET /b HTTP/1.1\r\n";
assert_eq!(&buf[..second_get.len()], second_get);
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 2");
tx2.complete(());
});
while let Ok(_) = res.recv() {}
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
let res = client.get(format!("http://{}/a", addr).parse().unwrap());
core.run(res.join(rx).map(|r| r.0)).unwrap();
let rx = rx2.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
let res = client.get(format!("http://{}/b", addr).parse().unwrap());
core.run(res.join(rx).map(|r| r.0)).unwrap();
}
#[test]
fn client_pooled_socket_disconnected() {
let server = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = server.local_addr().unwrap();
let mut core = Core::new().unwrap();
let client = client(&core.handle());
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
thread::spawn(move || {
let mut sock = server.accept().unwrap().0;
sock.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
sock.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
let mut buf = [0; 4096];
sock.read(&mut buf).expect("read 1");
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 1");
drop(sock);
tx1.complete(());
let mut sock = server.accept().unwrap().0;
sock.read(&mut buf).expect("read 2");
let second_get = b"GET /b HTTP/1.1\r\n";
assert_eq!(&buf[..second_get.len()], second_get);
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").expect("write 2");
tx2.complete(());
});
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
let res = client.get(format!("http://{}/a", addr).parse().unwrap());
core.run(res.join(rx).map(|r| r.0)).unwrap();
let rx = rx2.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
let res = client.get(format!("http://{}/b", addr).parse().unwrap());
core.run(res.join(rx).map(|r| r.0)).unwrap();
}

View File

@@ -1,30 +1,28 @@
#![deny(warnings)]
extern crate hyper;
extern crate futures;
extern crate spmc;
use futures::Future;
use futures::stream::Stream;
use std::net::{TcpStream, SocketAddr};
use std::io::{self, Read, Write};
use std::io::{Read, Write};
use std::sync::mpsc;
use std::time::Duration;
use hyper::{Next, Encoder, Decoder};
use hyper::net::{HttpListener, HttpStream};
use hyper::server::{Server, Handler, Request, Response};
use hyper::server::{Server, Request, Response, Service, NewService};
struct Serve {
listening: Option<hyper::server::Listening>,
msg_rx: mpsc::Receiver<Msg>,
reply_tx: mpsc::Sender<Reply>,
reply_tx: spmc::Sender<Reply>,
spawn_rx: mpsc::Receiver<()>,
}
impl Serve {
fn addrs(&self) -> &[SocketAddr] {
self.listening.as_ref().unwrap().addrs()
}
fn addr(&self) -> &SocketAddr {
let addrs = self.addrs();
assert!(addrs.len() == 1);
&addrs[0]
self.listening.as_ref().unwrap().addr()
}
/*
@@ -49,7 +47,7 @@ impl Serve {
}
struct ReplyBuilder<'a> {
tx: &'a mpsc::Sender<Reply>,
tx: &'a spmc::Sender<Reply>,
}
impl<'a> ReplyBuilder<'a> {
@@ -73,16 +71,18 @@ impl<'a> ReplyBuilder<'a> {
impl Drop for Serve {
fn drop(&mut self) {
self.listening.take().unwrap().close();
self.spawn_rx.recv().expect("server thread should shutdown cleanly");
}
}
struct TestHandler {
#[derive(Clone)]
struct TestService {
tx: mpsc::Sender<Msg>,
reply: Vec<Reply>,
peeked: Option<Vec<u8>>,
timeout: Option<Duration>,
reply: spmc::Receiver<Reply>,
_timeout: Option<Duration>,
}
#[derive(Clone, Debug)]
enum Reply {
Status(hyper::StatusCode),
Headers(hyper::Headers),
@@ -94,72 +94,57 @@ enum Msg {
Chunk(Vec<u8>),
}
impl TestHandler {
fn next(&self, next: Next) -> Next {
if let Some(dur) = self.timeout {
next.timeout(dur)
} else {
next
}
impl NewService for TestService {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Instance = TestService;
fn new_service(&self) -> std::io::Result<TestService> {
Ok(self.clone())
}
}
impl Handler<HttpStream> for TestHandler {
fn on_request(&mut self, _req: Request<HttpStream>) -> Next {
//self.tx.send(Msg::Head(req)).unwrap();
self.next(Next::read())
impl Service for TestService {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item=Response, Error=hyper::Error>>;
fn call(&self, req: Request) -> Self::Future {
let tx = self.tx.clone();
let replies = self.reply.clone();
req.body().for_each(move |chunk| {
tx.send(Msg::Chunk(chunk.to_vec())).unwrap();
Ok(())
}).map(move |_| {
let mut res = Response::new();
while let Ok(reply) = replies.try_recv() {
match reply {
Reply::Status(s) => {
res.set_status(s);
},
Reply::Headers(headers) => {
*res.headers_mut() = headers;
},
Reply::Body(body) => {
res.set_body(body);
},
}
}
res
}).boxed()
}
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 {
for reply in self.reply.drain(..) {
match reply {
Reply::Status(s) => {
res.set_status(s);
},
Reply::Headers(headers) => {
use std::iter::Extend;
res.headers_mut().extend(headers.iter());
},
Reply::Body(body) => {
self.peeked = Some(body);
},
}
}
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 connect(addr: &SocketAddr) -> TcpStream {
let req = TcpStream::connect(addr).unwrap();
req.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
req.set_write_timeout(Some(Duration::from_secs(1))).unwrap();
req
}
fn serve() -> Serve {
@@ -167,45 +152,37 @@ fn serve() -> Serve {
}
fn serve_with_timeout(dur: Option<Duration>) -> Serve {
serve_n_with_timeout(1, dur)
}
fn serve_n(n: u32) -> Serve {
serve_n_with_timeout(n, None)
}
fn serve_n_with_timeout(n: u32, dur: Option<Duration>) -> Serve {
use std::thread;
let (thread_tx, thread_rx) = mpsc::channel();
let (spawn_tx, spawn_rx) = mpsc::channel();
let (msg_tx, msg_rx) = mpsc::channel();
let (reply_tx, reply_rx) = mpsc::channel();
let (reply_tx, reply_rx) = spmc::channel();
let addr = "127.0.0.1:0".parse().unwrap();
let listeners = (0..n).map(|_| HttpListener::bind(&addr).unwrap());
let (listening, server) = Server::new(listeners)
.handle(move |_| {
let mut replies = Vec::new();
while let Ok(reply) = reply_rx.try_recv() {
replies.push(reply);
}
TestHandler {
tx: msg_tx.clone(),
timeout: dur,
reply: replies,
peeked: None,
}
}).unwrap();
let thread_name = format!("test-server-{}: {:?}", listening, dur);
let thread_name = format!("test-server-{:?}", dur);
thread::Builder::new().name(thread_name).spawn(move || {
let (listening, server) = Server::standalone(move |tokio| {
Server::http(&addr, tokio).unwrap()
.handle(TestService {
tx: msg_tx.clone(),
_timeout: dur,
reply: reply_rx,
}, tokio)
}).unwrap();
thread_tx.send(listening).unwrap();
server.run();
spawn_tx.send(()).unwrap();
}).unwrap();
let listening = thread_rx.recv().unwrap();
Serve {
listening: Some(listening),
msg_rx: msg_rx,
reply_tx: reply_tx,
spawn_rx: spawn_rx,
}
}
@@ -213,10 +190,12 @@ fn serve_n_with_timeout(n: u32, dur: Option<Duration>) -> Serve {
fn server_get_should_ignore_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
let mut req = connect(server.addr());
// Connection: close = don't try to parse the body as a new request
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
\r\n\
I shouldn't be read.\r\n\
").unwrap();
@@ -228,7 +207,7 @@ fn server_get_should_ignore_body() {
#[test]
fn server_get_with_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
@@ -244,17 +223,18 @@ fn server_get_with_body() {
#[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();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n
Connection: close\r\n\
\r\n\
").unwrap();
let mut body = String::new();
@@ -272,7 +252,7 @@ fn server_get_chunked_response() {
.status(hyper::Ok)
.header(hyper::header::TransferEncoding::chunked())
.body(foo_bar);
let mut req = TcpStream::connect(server.addr()).unwrap();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
@@ -286,10 +266,67 @@ fn server_get_chunked_response() {
assert_eq!(&body[n..], "B\r\nfoo bar baz\r\n0\r\n\r\n");
}
#[test]
fn server_get_chunked_response_with_ka() {
let foo_bar = b"foo bar baz";
let foo_bar_chunk = b"\r\nfoo bar baz\r\n0\r\n\r\n";
let server = serve();
server.reply()
.status(hyper::Ok)
.header(hyper::header::TransferEncoding::chunked())
.body(foo_bar);
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: keep-alive\r\n\
\r\n\
").expect("writing 1");
let mut buf = [0; 1024 * 4];
let mut ntotal = 0;
loop {
let n = req.read(&mut buf[ntotal..]).expect("reading 1");
ntotal = ntotal + n;
assert!(ntotal < buf.len());
if &buf[ntotal - foo_bar_chunk.len()..ntotal] == foo_bar_chunk {
break;
}
}
// try again!
let quux = b"zar quux";
server.reply()
.status(hyper::Ok)
.header(hyper::header::ContentLength(quux.len() as u64))
.body(quux);
req.write_all(b"\
GET /quux HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\r\n\
\r\n\
").expect("writing 2");
let mut buf = [0; 1024 * 8];
loop {
let n = req.read(&mut buf[..]).expect("reading 2");
assert!(n > 0, "n = {}", n);
if n < buf.len() && n > 0 {
if &buf[n - quux.len()..n] == quux {
break;
}
}
}
}
#[test]
fn server_post_with_chunked_body() {
let server = serve();
let mut req = TcpStream::connect(server.addr()).unwrap();
let mut req = connect(server.addr());
req.write_all(b"\
POST / HTTP/1.1\r\n\
Host: example.domain\r\n\
@@ -302,56 +339,31 @@ fn server_post_with_chunked_body() {
2\r\n\
rt\r\n\
0\r\n\
\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();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Content-Length: 0\r\n
Connection: close\r\n
\r\n\
").unwrap();
let mut response = String::new();
req.read_to_string(&mut response).unwrap();
@@ -369,49 +381,44 @@ fn server_empty_response_chunked() {
}
#[test]
fn server_empty_response_chunked_without_calling_write() {
fn server_empty_response_chunked_without_body_should_set_content_length() {
extern crate pretty_env_logger;
let _ = pretty_env_logger::init();
let server = serve();
server.reply()
.status(hyper::Ok)
.header(hyper::header::TransferEncoding::chunked());
let mut req = TcpStream::connect(server.addr()).unwrap();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
Connection: close\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"));
assert!(!response.contains("Transfer-Encoding: chunked\r\n"));
assert!(response.contains("Content-Length: 0\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_keep_alive() {
extern crate env_logger;
env_logger::init().unwrap();
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.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
req.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
let mut req = connect(server.addr());
req.write_all(b"\
GET / HTTP/1.1\r\n\
Host: example.domain\r\n\
@@ -426,7 +433,6 @@ fn server_keep_alive() {
if &buf[n - foo_bar.len()..n] == foo_bar {
break;
} else {
println!("{:?}", ::std::str::from_utf8(&buf[..n]));
}
}
}
@@ -457,6 +463,7 @@ fn server_keep_alive() {
}
}
/*
#[test]
fn server_get_with_body_three_listeners() {
let server = serve_n(3);
@@ -479,3 +486,4 @@ fn server_get_with_body_three_listeners() {
assert_eq!(server.body(), comparison);
}
}
*/