From a12f7beed99604eafb0cbcb38fbc105ee68a5445 Mon Sep 17 00:00:00 2001 From: Sam Rijs Date: Fri, 30 Mar 2018 07:32:44 +1100 Subject: [PATCH] refactor(lib): convert to futures 0.2.0-beta (#1470) --- Cargo.toml | 16 +- benches/end_to_end.rs | 34 ++-- benches/server.rs | 6 +- examples/client.rs | 15 +- examples/hello.rs | 8 +- examples/multi_server.rs | 19 +- examples/params.rs | 12 +- examples/send_file.rs | 10 +- examples/server.rs | 10 +- examples/web_api.rs | 17 +- src/client/conn.rs | 43 ++--- src/client/connect.rs | 98 ++++------- src/client/dispatch.rs | 57 +++--- src/client/dns.rs | 41 +++-- src/client/mod.rs | 214 +++++++---------------- src/client/pool.rs | 128 +++++++------- src/client/tests.rs | 37 ++-- src/common/mod.rs | 2 - src/error.rs | 6 +- src/executor.rs | 17 ++ src/headers.rs | 2 +- src/lib.rs | 6 +- src/mock.rs | 191 +++++++++----------- src/proto/body.rs | 49 +++--- src/proto/h1/conn.rs | 103 ++++++----- src/proto/h1/decode.rs | 243 ++++++++++++++------------ src/proto/h1/dispatch.rs | 155 +++++++++-------- src/proto/h1/io.rs | 367 +++++++++++++++++++++------------------ src/server/conn.rs | 11 +- src/server/mod.rs | 143 +++++++-------- src/server/service.rs | 64 ------- src/service.rs | 116 +++++++++++++ tests/client.rs | 341 ++++++++++++++++++------------------ tests/server.rs | 132 +++++++------- 34 files changed, 1366 insertions(+), 1347 deletions(-) delete mode 100644 src/common/mod.rs create mode 100644 src/executor.rs delete mode 100644 src/server/service.rs create mode 100644 src/service.rs diff --git a/Cargo.toml b/Cargo.toml index ed5e1052..a1527674 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,20 +22,17 @@ include = [ [dependencies] bytes = "0.4.4" -futures = "0.1.17" -futures-cpupool = "0.1.6" -futures-timer = "0.1.0" +futures = "0.2.0-beta" +futures-timer = { git = "https://github.com/alexcrichton/futures-timer.git" } http = "0.1.5" httparse = "1.0" iovec = "0.1" log = "0.4" net2 = "0.2.32" time = "0.1" -tokio = "0.1.3" -tokio-executor = "0.1.0" -tokio-service = "0.1" -tokio-io = "0.1" -want = "0.0.2" +tokio = { git = "https://github.com/seanmonstar/tokio.git", branch = "futures2-use-after-free", features = ["unstable-futures"] } +tokio-executor = { git = "https://github.com/seanmonstar/tokio.git", branch = "futures2-use-after-free", features = ["unstable-futures"] } +want = { git = "https://github.com/srijs/want.git", branch = "futures-0.2" } [dev-dependencies] num_cpus = "1.0" @@ -45,3 +42,6 @@ url = "1.0" [features] nightly = [] + +[replace] +"futures:0.2.0-beta" = { git = "https://github.com/srijs/futures-rs.git", branch = "with-executor" } diff --git a/benches/end_to_end.rs b/benches/end_to_end.rs index 8c9aeecf..fe803b7d 100644 --- a/benches/end_to_end.rs +++ b/benches/end_to_end.rs @@ -8,7 +8,8 @@ extern crate tokio; use std::net::SocketAddr; -use futures::{Future, Stream}; +use futures::{FutureExt, StreamExt}; +use futures::executor::block_on; use tokio::runtime::Runtime; use tokio::net::TcpListener; @@ -22,19 +23,20 @@ fn get_one_at_a_time(b: &mut test::Bencher) { let addr = spawn_hello(&mut rt); let client = hyper::Client::configure() - .build_with_executor(&rt.handle(), rt.executor()); + .build(&rt.handle()); let url: hyper::Uri = format!("http://{}/get", addr).parse().unwrap(); b.bytes = 160 * 2 + PHRASE.len() as u64; b.iter(move || { - client.get(url.clone()) + block_on(client.get(url.clone()) + .with_executor(rt.executor()) .and_then(|res| { res.into_body().into_stream().for_each(|_chunk| { Ok(()) - }) + }).map(|_| ()) }) - .wait().expect("client wait"); + ).expect("client wait"); }); } @@ -44,7 +46,7 @@ fn post_one_at_a_time(b: &mut test::Bencher) { let addr = spawn_hello(&mut rt); let client = hyper::Client::configure() - .build_with_executor(&rt.handle(), rt.executor()); + .build(&rt.handle()); let url: hyper::Uri = format!("http://{}/post", addr).parse().unwrap(); @@ -54,11 +56,14 @@ fn post_one_at_a_time(b: &mut test::Bencher) { let mut req = Request::new(post.into()); *req.method_mut() = Method::POST; *req.uri_mut() = url.clone(); - client.request(req).and_then(|res| { - res.into_body().into_stream().for_each(|_chunk| { - Ok(()) + block_on(client.request(req) + .with_executor(rt.executor()) + .and_then(|res| { + res.into_body().into_stream().for_each(|_chunk| { + Ok(()) + }).map(|_| ()) }) - }).wait().expect("client wait"); + ).expect("client wait"); }); } @@ -76,21 +81,22 @@ fn spawn_hello(rt: &mut Runtime) -> SocketAddr { let service = const_service(service_fn(|req: Request| { req.into_body() .into_stream() - .concat2() + .concat() .map(|_| { Response::new(Body::from(PHRASE)) }) })); let srv = listener.incoming() - .into_future() + .next() .map_err(|(e, _inc)| panic!("accept error: {}", e)) .and_then(move |(accepted, _inc)| { let socket = accepted.expect("accepted socket"); http.serve_connection(socket, service.new_service().expect("new_service")) .map(|_| ()) .map_err(|_| ()) - }); - rt.spawn(srv); + }) + .map_err(|_| panic!("server error")); + rt.spawn2(srv); return addr } diff --git a/benches/server.rs b/benches/server.rs index 158bdb1f..25c98da3 100644 --- a/benches/server.rs +++ b/benches/server.rs @@ -11,8 +11,8 @@ use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::sync::mpsc; -use futures::{future, stream, Future, Stream}; -use futures::sync::oneshot; +use futures::{future, stream, FutureExt, StreamExt}; +use futures::channel::oneshot; use hyper::{Body, Request, Response}; use hyper::server::Service; @@ -31,7 +31,7 @@ macro_rules! bench_server { })).unwrap(); let addr = srv.local_addr().unwrap(); addr_tx.send(addr).unwrap(); - tokio::run(srv.run_until(until_rx.map_err(|_| ())).map_err(|e| panic!("server error: {}", e))); + tokio::runtime::run2(srv.run_until(until_rx.map_err(|_| ())).map_err(|e| panic!("server error: {}", e))); }); addr_rx.recv().unwrap() diff --git a/examples/client.rs b/examples/client.rs index e5d9a286..53507e81 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -8,7 +8,7 @@ extern crate pretty_env_logger; use std::env; use std::io::{self, Write}; -use futures::{Future, Stream}; +use futures::{FutureExt, StreamExt}; use futures::future::lazy; use hyper::{Body, Client, Request}; @@ -30,7 +30,7 @@ fn main() { return; } - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let client = Client::default(); let mut req = Request::new(Body::empty()); @@ -43,10 +43,13 @@ fn main() { res.into_parts().1.into_stream().for_each(|chunk| { io::stdout().write_all(&chunk).map_err(From::from) }) - }).map(|_| { - println!("\n\nDone."); - }).map_err(|err| { - eprintln!("Error {}", err); + }).then(|result| { + if let Some(err) = result.err() { + eprintln!("Error {}", err); + } else { + println!("\n\nDone."); + } + Ok(()) }) })); } diff --git a/examples/hello.rs b/examples/hello.rs index 80d33542..b4195d5d 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -4,7 +4,7 @@ extern crate futures; extern crate pretty_env_logger; extern crate tokio; -use futures::Future; +use futures::FutureExt; use futures::future::lazy; use hyper::{Body, Response}; @@ -20,13 +20,13 @@ fn main() { Ok(Response::new(Body::from(PHRASE))) })); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let server = Http::new() .sleep_on_errors(true) .bind(&addr, new_service) .unwrap(); - println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); - server.run().map_err(|err| eprintln!("Server error {}", err)) + println!("Listening on http://{}", server.local_addr().unwrap()); + server.run().map_err(|err| panic!("Server error {}", err)) })); } diff --git a/examples/multi_server.rs b/examples/multi_server.rs index 239d7abb..a42fc5bf 100644 --- a/examples/multi_server.rs +++ b/examples/multi_server.rs @@ -4,8 +4,9 @@ extern crate futures; extern crate pretty_env_logger; extern crate tokio; -use futures::{Future, Stream}; +use futures::{FutureExt, StreamExt}; use futures::future::{FutureResult, lazy}; +use futures::executor::spawn; use hyper::{Body, Method, Request, Response, StatusCode}; use hyper::server::{Http, Service}; @@ -43,22 +44,20 @@ fn main() { let addr1 = "127.0.0.1:1337".parse().unwrap(); let addr2 = "127.0.0.1:1338".parse().unwrap(); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let srv1 = Http::new().serve_addr(&addr1, || Ok(Srv(INDEX1))).unwrap(); let srv2 = Http::new().serve_addr(&addr2, || Ok(Srv(INDEX2))).unwrap(); println!("Listening on http://{}", srv1.incoming_ref().local_addr()); println!("Listening on http://{}", srv2.incoming_ref().local_addr()); - tokio::spawn(srv1.for_each(move |conn| { - tokio::spawn(conn.map(|_| ()).map_err(|err| println!("srv1 error: {:?}", err))); - Ok(()) - }).map_err(|_| ())); + spawn(srv1.map_err(|err| panic!("srv1 error: {:?}", err)).for_each(move |conn| { + spawn(conn.map(|_| ()).map_err(|err| panic!("srv1 error: {:?}", err))) + }).map(|_| ())); - tokio::spawn(srv2.for_each(move |conn| { - tokio::spawn(conn.map(|_| ()).map_err(|err| println!("srv2 error: {:?}", err))); - Ok(()) - }).map_err(|_| ())); + spawn(srv2.map_err(|err| panic!("srv2 error: {:?}", err)).for_each(move |conn| { + spawn(conn.map(|_| ()).map_err(|err| panic!("srv2 error: {:?}", err))) + }).map(|_| ())); Ok(()) })); diff --git a/examples/params.rs b/examples/params.rs index d362840f..eb576cc1 100644 --- a/examples/params.rs +++ b/examples/params.rs @@ -5,7 +5,7 @@ extern crate pretty_env_logger; extern crate tokio; extern crate url; -use futures::{Future, Stream}; +use futures::{Future, FutureExt, StreamExt}; use futures::future::lazy; use hyper::{Body, Method, Request, Response, StatusCode}; @@ -32,7 +32,7 @@ impl Service for ParamExample { Box::new(futures::future::ok(Response::new(INDEX.into()))) }, (&Method::POST, "/post") => { - Box::new(req.into_parts().1.into_stream().concat2().map(|b| { + Box::new(req.into_parts().1.into_stream().concat().map(|b| { // Parse the request body. form_urlencoded::parse // always succeeds, but in general parsing may // fail (for example, an invalid post of json), so @@ -98,9 +98,11 @@ fn main() { pretty_env_logger::init(); let addr = "127.0.0.1:1337".parse().unwrap(); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let server = Http::new().bind(&addr, || Ok(ParamExample)).unwrap(); - println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); - server.run().map_err(|err| eprintln!("Server error {}", err)) + println!("Listening on http://{}", server.local_addr().unwrap()); + server.run().recover(|err| { + eprintln!("Server error {}", err) + }) })); } diff --git a/examples/send_file.rs b/examples/send_file.rs index 8e6fe917..ad0cdc12 100644 --- a/examples/send_file.rs +++ b/examples/send_file.rs @@ -4,9 +4,9 @@ extern crate hyper; extern crate pretty_env_logger; extern crate tokio; -use futures::{Future/*, Sink*/}; +use futures::{Future, FutureExt}; use futures::future::lazy; -use futures::sync::oneshot; +use futures::channel::oneshot; use hyper::{Body, /*Chunk,*/ Method, Request, Response, StatusCode}; use hyper::error::Error; @@ -141,9 +141,9 @@ fn main() { pretty_env_logger::init(); let addr = "127.0.0.1:1337".parse().unwrap(); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let server = Http::new().bind(&addr, || Ok(ResponseExamples)).unwrap(); - println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); - server.run().map_err(|err| eprintln!("Server error {}", err)) + println!("Listening on http://{}", server.local_addr().unwrap()); + server.run().map_err(|err| panic!("Server error {}", err)) })); } diff --git a/examples/server.rs b/examples/server.rs index b5d21539..9486ab8d 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -4,7 +4,7 @@ extern crate hyper; extern crate pretty_env_logger; extern crate tokio; -use futures::Future; +use futures::FutureExt; use futures::future::{FutureResult, lazy}; use hyper::{Body, Method, Request, Response, StatusCode}; @@ -43,9 +43,11 @@ fn main() { pretty_env_logger::init(); let addr = "127.0.0.1:1337".parse().unwrap(); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let server = Http::new().bind(&addr, || Ok(Echo)).unwrap(); - println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap()); - server.run().map_err(|err| eprintln!("Server error {}", err)) + println!("Listening on http://{}", server.local_addr().unwrap()); + server.run().recover(|err| { + eprintln!("Server error {}", err) + }) })); } diff --git a/examples/web_api.rs b/examples/web_api.rs index d3261e9a..d792eeaf 100644 --- a/examples/web_api.rs +++ b/examples/web_api.rs @@ -4,14 +4,15 @@ extern crate hyper; extern crate pretty_env_logger; extern crate tokio; -use futures::{Future, Stream}; +use futures::{Future, FutureExt, StreamExt}; +use futures::executor::spawn; use futures::future::lazy; use tokio::reactor::Handle; use hyper::{Body, Chunk, Client, Method, Request, Response, StatusCode}; use hyper::server::{Http, Service}; -#[allow(unused)] +#[allow(unused, deprecated)] use std::ascii::AsciiExt; static NOTFOUND: &[u8] = b"Not Found"; @@ -78,13 +79,15 @@ fn main() { pretty_env_logger::init(); let addr = "127.0.0.1:1337".parse().unwrap(); - tokio::run(lazy(move || { + tokio::runtime::run2(lazy(move |_| { let handle = Handle::current(); let serve = Http::new().serve_addr(&addr, move || Ok(ResponseExamples(handle.clone()))).unwrap(); - println!("Listening on http://{} with 1 thread.", serve.incoming_ref().local_addr()); + println!("Listening on http://{}", serve.incoming_ref().local_addr()); - serve.map_err(|_| ()).for_each(move |conn| { - tokio::spawn(conn.map(|_| ()).map_err(|err| println!("serve error: {:?}", err))) - }) + serve.map_err(|err| panic!("server error {:?}", err)).for_each(move |conn| { + spawn(conn.recover(|err| { + println!("connection error: {:?}", err); + })) + }).map(|_| ()) })); } diff --git a/src/client/conn.rs b/src/client/conn.rs index 9d43ec26..6dee97fa 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -11,9 +11,10 @@ use std::fmt; use std::marker::PhantomData; use bytes::Bytes; -use futures::{Async, Future, Poll}; +use futures::{Async, Future, FutureExt, Poll}; use futures::future::{self, Either}; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use proto; use proto::body::Entity; @@ -123,8 +124,8 @@ impl SendRequest /// Polls to determine whether this sender can be used yet for a request. /// /// If the associated connection is closed, this returns an Error. - pub fn poll_ready(&mut self) -> Poll<(), ::Error> { - self.dispatch.poll_ready() + pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.dispatch.poll_ready(cx) } pub(super) fn is_closed(&self) -> bool { @@ -162,7 +163,7 @@ where /// # use http::header::HOST; /// # use hyper::client::conn::SendRequest; /// # use hyper::Body; - /// use futures::Future; + /// use futures::FutureExt; /// use hyper::Request; /// /// # fn doc(mut tx: SendRequest) { @@ -186,19 +187,19 @@ where pub fn send_request(&mut self, req: Request) -> ResponseFuture { let inner = match self.dispatch.send(req) { Ok(rx) => { - Either::A(rx.then(move |res| { + rx.then(move |res| { match res { Ok(Ok(res)) => Ok(res), Ok(Err(err)) => Err(err), // this is definite bug if it happens, but it shouldn't happen! Err(_) => panic!("dispatch dropped without returning error"), } - })) + }).left() }, Err(_req) => { debug!("connection was not ready"); let err = ::Error::new_canceled(Some("connection was not ready")); - Either::B(future::err(err)) + future::err(err).right() } }; @@ -214,7 +215,7 @@ where { let inner = match self.dispatch.try_send(req) { Ok(rx) => { - Either::A(rx.then(move |res| { + Either::Left(rx.then(move |res| { match res { Ok(Ok(res)) => Ok(res), Ok(Err(err)) => Err(err), @@ -226,7 +227,7 @@ where Err(req) => { debug!("connection was not ready"); let err = ::Error::new_canceled(Some("connection was not ready")); - Either::B(future::err((err, Some(req)))) + Either::Right(future::err((err, Some(req)))) } }; Box::new(inner) @@ -277,8 +278,8 @@ where /// upgrade. Once the upgrade is completed, the connection would be "done", /// but it is not desired to actally shutdown the IO object. Instead you /// would take it back using `into_parts`. - pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> { - self.inner.poll_without_shutdown() + pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.inner.poll_without_shutdown(cx) } } @@ -290,8 +291,8 @@ where type Item = (); type Error = ::Error; - fn poll(&mut self) -> Poll { - self.inner.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.inner.poll(cx) } } @@ -363,8 +364,8 @@ where type Item = (SendRequest, Connection); type Error = ::Error; - fn poll(&mut self) -> Poll { - self.inner.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.inner.poll(cx) .map(|async| { async.map(|(tx, dispatch)| { (tx, Connection { inner: dispatch }) @@ -394,8 +395,8 @@ where >); type Error = ::Error; - fn poll(&mut self) -> Poll { - self.inner.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.inner.poll(cx) } } @@ -417,7 +418,7 @@ where >); type Error = ::Error; - fn poll(&mut self) -> Poll { + fn poll(&mut self, _cx: &mut task::Context) -> Poll { let io = self.io.take().expect("polled more than once"); let (tx, rx) = dispatch::channel(); let mut conn = proto::Conn::new(io); @@ -441,8 +442,8 @@ impl Future for ResponseFuture { type Error = ::Error; #[inline] - fn poll(&mut self) -> Poll { - self.inner.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.inner.poll(cx) } } diff --git a/src/client/connect.rs b/src/client/connect.rs index 33e636c0..ad5ab7b5 100644 --- a/src/client/connect.rs +++ b/src/client/connect.rs @@ -8,24 +8,21 @@ use std::error::Error as StdError; use std::fmt; use std::io; -use std::mem; use std::net::SocketAddr; -use std::sync::Arc; use std::time::Duration; -use futures::{Future, Poll, Async}; -use futures::future::{Executor, ExecuteError}; -use futures::sync::oneshot; -use futures_cpupool::{Builder as CpuPoolBuilder}; +use futures::{Future, Never, Poll, Async}; +use futures::executor::{Executor, SpawnError, ThreadPoolBuilder}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use http::Uri; use http::uri::Scheme; use net2::TcpBuilder; -use tokio_io::{AsyncRead, AsyncWrite}; use tokio::reactor::Handle; use tokio::net::{TcpStream, ConnectFuture}; +use executor::CloneBoxedExecutor; use super::dns; -use self::http_connector::HttpConnectorBlockingTask; /// Connect to a destination, returning an IO transport. /// @@ -174,7 +171,7 @@ impl HttpConnector { /// Takes number of DNS worker threads. #[inline] pub fn new(threads: usize, handle: &Handle) -> HttpConnector { - let pool = CpuPoolBuilder::new() + let pool = ThreadPoolBuilder::new() .name_prefix("hyper-dns") .pool_size(threads) .create(); @@ -186,10 +183,10 @@ impl HttpConnector { /// Takes an executor to run blocking tasks on. #[inline] pub fn new_with_executor(executor: E, handle: &Handle) -> HttpConnector - where E: Executor + Send + Sync + where E: Executor + Clone + Send + Sync { HttpConnector { - executor: HttpConnectExecutor(Arc::new(executor)), + executor: HttpConnectExecutor(Box::new(executor)), enforce_http: true, handle: handle.clone(), keep_alive_timeout: None, @@ -298,7 +295,7 @@ pub struct HttpConnecting { enum State { Lazy(HttpConnectExecutor, String, u16), - Resolving(oneshot::SpawnHandle), + Resolving(dns::Resolving), Connecting(ConnectingTcp), Error(Option), } @@ -307,11 +304,11 @@ impl Future for HttpConnecting { type Item = (TcpStream, Connected); type Error = io::Error; - fn poll(&mut self) -> Poll { + fn poll(&mut self, cx: &mut task::Context) -> Poll { loop { let state; match self.state { - State::Lazy(ref executor, ref mut host, port) => { + State::Lazy(ref mut executor, ref mut host, port) => { // If the host is already an IP addr (v4 or v6), // skip resolving the dns and start connecting right away. if let Some(addrs) = dns::IpAddrs::try_parse(host, port) { @@ -320,24 +317,19 @@ impl Future for HttpConnecting { current: None }) } else { - let host = mem::replace(host, String::new()); - let work = dns::Work::new(host, port); - state = State::Resolving(oneshot::spawn(work, executor)); + let host = ::std::mem::replace(host, String::new()); + state = State::Resolving(dns::Resolving::spawn(host, port, executor)); } }, State::Resolving(ref mut future) => { - match try!(future.poll()) { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(addrs) => { - state = State::Connecting(ConnectingTcp { - addrs: addrs, - current: None, - }) - } - }; + let addrs = try_ready!(future.poll(cx)); + state = State::Connecting(ConnectingTcp { + addrs: addrs, + current: None, + }); }, State::Connecting(ref mut c) => { - let sock = try_ready!(c.poll(&self.handle)); + let sock = try_ready!(c.poll(cx, &self.handle)); if let Some(dur) = self.keep_alive_timeout { sock.set_keepalive(Some(dur))?; @@ -365,11 +357,11 @@ struct ConnectingTcp { impl ConnectingTcp { // not a Future, since passing a &Handle to poll - fn poll(&mut self, handle: &Handle) -> Poll { + fn poll(&mut self, cx: &mut task::Context, handle: &Handle) -> Poll { let mut err = None; loop { if let Some(ref mut current) = self.current { - match current.poll() { + match current.poll(cx) { Ok(ok) => return Ok(ok), Err(e) => { trace!("connect error {:?}", e); @@ -392,37 +384,19 @@ impl ConnectingTcp { } } -// Make this Future unnameable outside of this crate. -mod http_connector { - use super::*; - // Blocking task to be executed on a thread pool. - pub struct HttpConnectorBlockingTask { - pub(super) work: oneshot::Execute - } - - impl fmt::Debug for HttpConnectorBlockingTask { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad("HttpConnectorBlockingTask") - } - } - - impl Future for HttpConnectorBlockingTask { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - self.work.poll() - } - } -} - #[derive(Clone)] -struct HttpConnectExecutor(Arc + Send + Sync>); +struct HttpConnectExecutor(Box); -impl Executor> for HttpConnectExecutor { - fn execute(&self, future: oneshot::Execute) -> Result<(), ExecuteError>> { - self.0.execute(HttpConnectorBlockingTask { work: future }) - .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work)) +impl Executor for HttpConnectExecutor { + fn spawn( + &mut self, + f: Box + 'static + Send> + ) -> Result<(), SpawnError> { + self.0.spawn(f) + } + + fn status(&self) -> Result<(), SpawnError> { + self.0.status() } } @@ -430,7 +404,7 @@ impl Executor> for HttpConnectExecutor { mod tests { #![allow(deprecated)] use std::io; - use futures::Future; + use futures::executor::block_on; use tokio::runtime::Runtime; use super::{Connect, Destination, HttpConnector}; @@ -443,7 +417,7 @@ mod tests { }; let connector = HttpConnector::new(1, runtime.handle()); - assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); + assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); } #[test] @@ -455,7 +429,7 @@ mod tests { }; let connector = HttpConnector::new(1, runtime.handle()); - assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); + assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); } @@ -468,6 +442,6 @@ mod tests { }; let connector = HttpConnector::new(1, runtime.handle()); - assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); + assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); } } diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index 51eae1d4..77402afe 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -1,9 +1,8 @@ -use futures::{Async, Poll, Stream}; -use futures::sync::{mpsc, oneshot}; +use futures::{Async, Never, Poll, Stream}; +use futures::channel::{mpsc, oneshot}; +use futures::task; use want; -use common::Never; - //pub type Callback = oneshot::Sender)>>; pub type RetryPromise = oneshot::Receiver)>>; pub type Promise = oneshot::Receiver>; @@ -33,15 +32,15 @@ pub struct Sender { } impl Sender { - pub fn poll_ready(&mut self) -> Poll<(), ::Error> { - match self.inner.poll_ready() { + pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + match self.inner.poll_ready(cx) { Ok(Async::Ready(())) => { // there's room in the queue, but does the Connection // want a message yet? - self.giver.poll_want() + self.giver.poll_want(cx) .map_err(|_| ::Error::Closed) }, - Ok(Async::NotReady) => Ok(Async::NotReady), + Ok(Async::Pending) => Ok(Async::Pending), Err(_) => Err(::Error::Closed), } } @@ -75,16 +74,15 @@ impl Stream for Receiver { type Item = (T, Callback); type Error = Never; - fn poll(&mut self) -> Poll, Self::Error> { - match self.inner.poll() { - Ok(Async::Ready(item)) => Ok(Async::Ready(item.map(|mut env| { + fn poll_next(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { + match self.inner.poll_next(cx)? { + Async::Ready(item) => Ok(Async::Ready(item.map(|mut env| { env.0.take().expect("envelope not dropped") }))), - Ok(Async::NotReady) => { + Async::Pending => { self.taker.want(); - Ok(Async::NotReady) + Ok(Async::Pending) } - Err(()) => unreachable!("mpsc never errors"), } } } @@ -107,11 +105,11 @@ impl Drop for Receiver { // This poll() is safe to call in `Drop`, because we've // called, `close`, which promises that no new messages // will arrive, and thus, once we reach the end, we won't - // see a `NotReady` (and try to park), but a Ready(None). + // see a `Pending` (and try to park), but a Ready(None). // // All other variants: // - Ready(None): the end. we want to stop looping - // - NotReady: unreachable + // - Pending: unreachable // - Err: unreachable while let Ok(Async::Ready(Some((val, cb)))) = self.inner.poll() { let _ = cb.send(Err((::Error::new_canceled(None::<::Error>), Some(val)))); @@ -137,10 +135,10 @@ pub enum Callback { } impl Callback { - pub fn poll_cancel(&mut self) -> Poll<(), ()> { + pub fn poll_cancel(&mut self, cx: &mut task::Context) -> Poll<(), Never> { match *self { - Callback::Retry(ref mut tx) => tx.poll_cancel(), - Callback::NoRetry(ref mut tx) => tx.poll_cancel(), + Callback::Retry(ref mut tx) => tx.poll_cancel(cx), + Callback::NoRetry(ref mut tx) => tx.poll_cancel(cx), } } @@ -162,7 +160,8 @@ mod tests { #[cfg(feature = "nightly")] extern crate test; - use futures::{future, Future}; + use futures::{future, FutureExt}; + use futures::executor::block_on; #[cfg(feature = "nightly")] use futures::{Stream}; @@ -171,7 +170,7 @@ mod tests { fn drop_receiver_sends_cancel_errors() { let _ = pretty_env_logger::try_init(); - future::lazy(|| { + block_on(future::lazy(|_| { #[derive(Debug)] struct Custom(i32); let (mut tx, rx) = super::channel::(); @@ -188,7 +187,7 @@ mod tests { Ok::<(), ()>(()) }) - }).wait().unwrap(); + })).unwrap(); } #[cfg(feature = "nightly")] @@ -197,18 +196,18 @@ mod tests { let (mut tx, mut rx) = super::channel::(); b.iter(move || { - ::futures::future::lazy(|| { + block_on(future::lazy(|cx| { let _ = tx.send(1).unwrap(); loop { - let async = rx.poll().unwrap(); - if async.is_not_ready() { + let async = rx.poll_next(cx).unwrap(); + if async.is_pending() { break; } } Ok::<(), ()>(()) - }).wait().unwrap(); + })).unwrap(); }) } @@ -218,11 +217,11 @@ mod tests { let (_tx, mut rx) = super::channel::(); b.iter(move || { - ::futures::future::lazy(|| { - assert!(rx.poll().unwrap().is_not_ready()); + block_on(future::lazy(|cx| { + assert!(rx.poll_next(cx).unwrap().is_pending()); Ok::<(), ()>(()) - }).wait().unwrap(); + })).unwrap(); }) } diff --git a/src/client/dns.rs b/src/client/dns.rs index 182481d3..6d8a7518 100644 --- a/src/client/dns.rs +++ b/src/client/dns.rs @@ -6,27 +6,44 @@ use std::net::{ }; use std::vec; -use ::futures::{Async, Future, Poll}; +use futures::{Async, Future, Poll}; +use futures::task; +use futures::future::lazy; +use futures::executor::Executor; +use futures::channel::oneshot; -pub struct Work { - host: String, - port: u16 +pub struct Resolving { + receiver: oneshot::Receiver> } -impl Work { - pub fn new(host: String, port: u16) -> Work { - Work { host: host, port: port } +impl Resolving { + pub fn spawn(host: String, port: u16, executor: &mut Executor) -> Resolving { + let (sender, receiver) = oneshot::channel(); + // The `Resolving` future will return an error when the sender is dropped, + // so we can just ignore the spawn error here + executor.spawn(Box::new(lazy(move |_| { + debug!("resolving host={:?}, port={:?}", host, port); + let result = (host.as_ref(), port).to_socket_addrs() + .map(|i| IpAddrs { iter: i }); + sender.send(result).ok(); + Ok(()) + }))).ok(); + Resolving { receiver } } } -impl Future for Work { +impl Future for Resolving { type Item = IpAddrs; type Error = io::Error; - fn poll(&mut self) -> Poll { - debug!("resolving host={:?}, port={:?}", self.host, self.port); - (&*self.host, self.port).to_socket_addrs() - .map(|i| Async::Ready(IpAddrs { iter: i })) + fn poll(&mut self, cx: &mut task::Context) -> Poll { + match self.receiver.poll(cx) { + Ok(Async::Pending) => Ok(Async::Pending), + Ok(Async::Ready(Ok(ips))) => Ok(Async::Ready(ips)), + Ok(Async::Ready(Err(err))) => Err(err), + Err(_) => + Err(io::Error::new(io::ErrorKind::Other, "dns task was cancelled")) + } } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 94c96f63..4d81d11d 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,22 +6,21 @@ use std::marker::PhantomData; use std::sync::Arc; use std::time::Duration; -use futures::{Async, Future, Poll}; -use futures::future::{self, Executor}; +use futures::{Async, Future, FutureExt, Never, Poll}; +use futures::future; +use futures::task; use http::{Method, Request, Response, Uri, Version}; use http::header::{Entry, HeaderValue, HOST}; use http::uri::Scheme; use tokio::reactor::Handle; -use tokio_executor::spawn; -pub use tokio_service::Service; +pub use service::Service; use proto::body::{Body, Entity}; use proto; use self::pool::Pool; pub use self::connect::{Connect, HttpConnector}; -use self::background::{bg, Background}; use self::connect::Destination; pub mod conn; @@ -35,7 +34,6 @@ mod tests; /// A Client to make outgoing HTTP requests. pub struct Client { connector: Arc, - executor: Exec, h1_writev: bool, pool: Pool>, retry_canceled_requests: bool, @@ -82,10 +80,9 @@ impl Client { impl Client { #[inline] - fn configured(config: Config, exec: Exec) -> Client { + fn configured(config: Config) -> Client { Client { connector: Arc::new(config.connector), - executor: exec, h1_writev: config.h1_writev, pool: Pool::new(config.keep_alive, config.keep_alive_timeout), retry_canceled_requests: config.retry_canceled_requests, @@ -125,14 +122,6 @@ where C: Connect + Sync + 'static, /// Send a constructed Request using this Client. pub fn request(&self, mut req: Request) -> FutureResponse { - // TODO(0.12): do this at construction time. - // - // It cannot be done in the constructor because the Client::configured - // does not have `B: 'static` bounds, which are required to spawn - // the interval. In 0.12, add a static bounds to the constructor, - // and move this. - self.schedule_pool_timer(); - match req.version() { Version::HTTP_10 | Version::HTTP_11 => (), @@ -175,7 +164,6 @@ where C: Connect + Sync + 'static, } } - let client = self.clone(); let uri = req.uri().clone(); let fut = RetryableSendRequest { @@ -192,7 +180,6 @@ where C: Connect + Sync + 'static, let url = req.uri().clone(); let checkout = self.pool.checkout(domain); let connect = { - let executor = self.executor.clone(); let pool = self.pool.clone(); let pool_key = Arc::new(domain.to_string()); let h1_writev = self.h1_writev; @@ -200,36 +187,39 @@ where C: Connect + Sync + 'static, let dst = Destination { uri: url, }; - future::lazy(move || { + future::lazy(move |_| { connector.connect(dst) - .from_err() + .err_into() .and_then(move |(io, connected)| { conn::Builder::new() .h1_writev(h1_writev) .handshake_no_upgrades(io) .and_then(move |(tx, conn)| { - executor.execute(conn.map_err(|e| debug!("client connection error: {}", e)))?; - Ok(pool.pooled(pool_key, PoolClient { - is_proxied: connected.is_proxied, - tx: tx, - })) + future::lazy(move |cx| { + execute(conn.recover(|e| { + debug!("client connection error: {}", e); + }), cx)?; + Ok(pool.pooled(pool_key, PoolClient { + is_proxied: connected.is_proxied, + tx: tx, + })) + }) }) }) }) }; - let race = checkout.select(connect) - .map(|(pooled, _work)| pooled) - .map_err(|(e, _checkout)| { - // 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 - ClientError::Normal(e) - }); + let race = checkout.select(connect).map(|either| { + either.either(|(pooled, _)| pooled, |(pooled, _)| pooled) + }).map_err(|either| { + // 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 + ClientError::Normal(either.either(|(e, _)| e, |(e, _)| e)) + }); - let executor = self.executor.clone(); let resp = race.and_then(move |mut pooled| { let conn_reused = pooled.is_reused(); set_relative_uri(req.uri_mut(), pooled.is_proxied); @@ -245,24 +235,26 @@ where C: Connect + Sync + 'static, ClientError::Normal(err) } }) - .map(move |res| { - // when pooled is dropped, it will try to insert back into the - // pool. To delay that, spawn a future that completes once the - // sender is ready again. - // - // This *should* only be once the related `Connection` has polled - // for a new request to start. - // - // It won't be ready if there is a body to stream. - if let Ok(Async::NotReady) = pooled.tx.poll_ready() { - // If the executor doesn't have room, oh well. Things will likely - // be blowing up soon, but this specific task isn't required. - let _ = executor.execute(future::poll_fn(move || { - pooled.tx.poll_ready().map_err(|_| ()) - })); - } + .and_then(move |res| { + future::lazy(move |cx| { + // when pooled is dropped, it will try to insert back into the + // pool. To delay that, spawn a future that completes once the + // sender is ready again. + // + // This *should* only be once the related `Connection` has polled + // for a new request to start. + // + // It won't be ready if there is a body to stream. + if let Ok(Async::Pending) = pooled.tx.poll_ready(cx) { + // If the executor doesn't have room, oh well. Things will likely + // be blowing up soon, but this specific task isn't required. + execute(future::poll_fn(move |cx| { + pooled.tx.poll_ready(cx).or(Ok(Async::Ready(()))) + }), cx).ok(); + } - res + Ok(res) + }) }); @@ -271,10 +263,6 @@ where C: Connect + Sync + 'static, Box::new(resp) } - - fn schedule_pool_timer(&self) { - self.pool.spawn_expired_interval(&self.executor); - } } impl Service for Client @@ -297,7 +285,6 @@ impl Clone for Client { fn clone(&self) -> Client { Client { connector: self.connector.clone(), - executor: self.executor.clone(), h1_writev: self.h1_writev, pool: self.pool.clone(), retry_canceled_requests: self.retry_canceled_requests, @@ -327,8 +314,8 @@ impl Future for FutureResponse { type Item = Response; type Error = ::Error; - fn poll(&mut self) -> Poll { - self.0.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.0.poll(cx) } } @@ -349,11 +336,11 @@ where type Item = Response; type Error = ::Error; - fn poll(&mut self) -> Poll { + fn poll(&mut self, cx: &mut task::Context) -> Poll { loop { - match self.future.poll() { + match self.future.poll(cx) { Ok(Async::Ready(resp)) => return Ok(Async::Ready(resp)), - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(ClientError::Normal(err)) => return Err(err), Err(ClientError::Canceled { connection_reused, @@ -561,18 +548,7 @@ where C: Connect, /// Construct the Client with this configuration. #[inline] pub fn build(self) -> Client { - Client::configured(self, Exec::Default) - } - - /// Construct a Client with this configuration and an executor. - /// - /// The executor will be used to spawn "background" connection tasks - /// to drive requests and responses. - pub fn executor(self, executor: E) -> Client - where - E: Executor + Send + Sync + 'static, - { - Client::configured(self, Exec::new(executor)) + Client::configured(self) } } @@ -590,21 +566,6 @@ where } self.connector(connector).build() } - - /// Construct a Client with this configuration and an executor. - /// - /// The executor will be used to spawn "background" connection tasks - /// to drive requests and responses. - pub fn build_with_executor(self, handle: &Handle, executor: E) -> Client - where - E: Executor + Send + Sync + 'static, - { - let mut connector = HttpConnector::new(4, handle); - if self.keep_alive { - connector.set_keepalive(self.keep_alive_timeout); - } - self.connector(connector).executor(executor) - } } impl fmt::Debug for Config { @@ -629,68 +590,15 @@ impl Clone for Config { } -// ===== impl Exec ===== - -#[derive(Clone)] -enum Exec { - Default, - Executor(Arc + Send + Sync>), -} - - -impl Exec { - pub(crate) fn new + Send + Sync + 'static>(executor: E) -> Exec { - Exec::Executor(Arc::new(executor)) - } - - fn execute(&self, fut: F) -> io::Result<()> - where - F: Future + Send + 'static, - { - match *self { - Exec::Default => spawn(fut), - Exec::Executor(ref e) => { - e.execute(bg(Box::new(fut))) - .map_err(|err| { - debug!("executor error: {:?}", err.kind()); - io::Error::new( - io::ErrorKind::Other, - "executor error", - ) - })? - }, - } - Ok(()) +fn execute(fut: F, cx: &mut task::Context) -> Result<(), ::Error> + where F: Future + Send + 'static, +{ + if let Some(executor) = cx.executor() { + executor.spawn(Box::new(fut)).map_err(|err| { + debug!("executor error: {:?}", err); + ::Error::Executor + }) + } else { + Err(::Error::Executor) } } - -// ===== impl Background ===== - -// The types inside this module are not exported out of the crate, -// so they are in essence un-nameable. -mod background { - use futures::{Future, Poll}; - - // This is basically `impl Future`, since the type is un-nameable, - // and only implementeds `Future`. - #[allow(missing_debug_implementations)] - pub struct Background { - inner: Box + Send>, - } - - pub fn bg(fut: Box + Send>) -> Background { - Background { - inner: fut, - } - } - - impl Future for Background { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll { - self.inner.poll() - } - } -} - diff --git a/src/client/pool.rs b/src/client/pool.rs index de4024b0..f4d6f637 100644 --- a/src/client/pool.rs +++ b/src/client/pool.rs @@ -4,14 +4,13 @@ use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex, Weak}; use std::time::{Duration, Instant}; -use futures::{Future, Async, Poll, Stream}; -use futures::sync::oneshot; +use futures::{Future, Async, Never, Poll, Stream}; +use futures::channel::oneshot; +use futures::task; use futures_timer::Interval; -use super::Exec; - -pub struct Pool { - inner: Arc>>, +pub(super) struct Pool { + inner: Arc>> } // Before using a pooled connection, make sure the sender is not dead. @@ -48,7 +47,7 @@ struct PoolInner { } impl Pool { - pub fn new(enabled: bool, timeout: Option) -> Pool { + pub(super) fn new(enabled: bool, timeout: Option) -> Pool { Pool { inner: Arc::new(Mutex::new(PoolInner { enabled: enabled, @@ -56,7 +55,7 @@ impl Pool { parked: HashMap::new(), timeout: timeout, expired_timer_spawned: false, - })), + })) } } } @@ -67,6 +66,7 @@ impl Pool { key: Arc::new(key.to_owned()), pool: self.clone(), parked: None, + spawned_expired_interval: false } } @@ -221,38 +221,38 @@ impl PoolInner { impl Pool { - pub(super) fn spawn_expired_interval(&self, exec: &Exec) { + fn spawn_expired_interval(&mut self, cx: &mut task::Context) -> Result<(), ::Error> { let dur = { let mut inner = self.inner.lock().unwrap(); if !inner.enabled { - return; + return Ok(()); } if inner.expired_timer_spawned { - return; + return Ok(()); } inner.expired_timer_spawned = true; if let Some(dur) = inner.timeout { dur } else { - return + return Ok(()); } }; let interval = Interval::new(dur); - exec.execute(IdleInterval { + super::execute(IdleInterval { interval: interval, pool: Arc::downgrade(&self.inner), - }).unwrap(); + }, cx) } } impl Clone for Pool { fn clone(&self) -> Pool { Pool { - inner: self.inner.clone(), + inner: self.inner.clone() } } } @@ -322,22 +322,23 @@ pub struct Checkout { key: Arc, pool: Pool, parked: Option>, + spawned_expired_interval: bool } struct NotParked; impl Checkout { - fn poll_parked(&mut self) -> Poll, NotParked> { + fn poll_parked(&mut self, cx: &mut task::Context) -> Poll, NotParked> { let mut drop_parked = false; if let Some(ref mut rx) = self.parked { - match rx.poll() { + match rx.poll(cx) { Ok(Async::Ready(value)) => { if !value.is_closed() { return Ok(Async::Ready(self.pool.reuse(&self.key, value))); } drop_parked = true; }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(_canceled) => drop_parked = true, } } @@ -347,22 +348,27 @@ impl Checkout { Err(NotParked) } - fn park(&mut self) { + fn park(&mut self, cx: &mut task::Context) { if self.parked.is_none() { let (tx, mut rx) = oneshot::channel(); - let _ = rx.poll(); // park this task + let _ = rx.poll(cx); // park this task self.pool.park(self.key.clone(), tx); self.parked = Some(rx); } } } -impl Future for Checkout { +impl Future for Checkout { type Item = Pooled; type Error = ::Error; - fn poll(&mut self) -> Poll { - match self.poll_parked() { + fn poll(&mut self, cx: &mut task::Context) -> Poll { + if !self.spawned_expired_interval { + self.pool.spawn_expired_interval(cx)?; + self.spawned_expired_interval = true; + } + + match self.poll_parked(cx) { Ok(async) => return Ok(async), Err(_not_parked) => (), } @@ -372,8 +378,8 @@ impl Future for Checkout { if let Some(pooled) = entry { Ok(Async::Ready(pooled)) } else { - self.park(); - Ok(Async::NotReady) + self.park(cx); + Ok(Async::Pending) } } } @@ -409,11 +415,11 @@ struct IdleInterval { impl Future for IdleInterval { type Item = (); - type Error = (); + type Error = Never; - fn poll(&mut self) -> Poll { + fn poll(&mut self, cx: &mut task::Context) -> Poll { loop { - try_ready!(self.interval.poll().map_err(|_| unreachable!("interval cannot error"))); + try_ready!(self.interval.poll_next(cx).map_err(|_| unreachable!("interval cannot error"))); if let Some(inner) = self.pool.upgrade() { if let Ok(mut inner) = inner.lock() { @@ -430,9 +436,10 @@ impl Future for IdleInterval { mod tests { use std::sync::Arc; use std::time::Duration; - use futures::{Async, Future}; + use futures::{Async, Future, FutureExt}; use futures::future; - use super::{Closed, Pool, Exec}; + use futures::executor::block_on; + use super::{Closed, Pool}; impl Closed for i32 { fn is_closed(&self) -> bool { @@ -442,34 +449,37 @@ mod tests { #[test] fn test_pool_checkout_smoke() { - let pool = Pool::new(true, Some(Duration::from_secs(5))); - let key = Arc::new("foo".to_string()); - let pooled = pool.pooled(key.clone(), 41); + block_on(future::lazy(|cx| { + let pool = Pool::new(true, Some(Duration::from_secs(5))); + let key = Arc::new("foo".to_string()); + let pooled = pool.pooled(key.clone(), 41); - drop(pooled); + drop(pooled); - match pool.checkout(&key).poll().unwrap() { - Async::Ready(pooled) => assert_eq!(*pooled, 41), - _ => panic!("not ready"), - } + match pool.checkout(&key).poll(cx).unwrap() { + Async::Ready(pooled) => assert_eq!(*pooled, 41), + _ => panic!("not ready"), + } + Ok::<_, ()>(()) + })).unwrap(); } #[test] fn test_pool_checkout_returns_none_if_expired() { - future::lazy(|| { + block_on(future::lazy(|cx| { let pool = Pool::new(true, Some(Duration::from_millis(100))); let key = Arc::new("foo".to_string()); let pooled = pool.pooled(key.clone(), 41); drop(pooled); ::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap()); - assert!(pool.checkout(&key).poll().unwrap().is_not_ready()); - ::futures::future::ok::<(), ()>(()) - }).wait().unwrap(); + assert!(pool.checkout(&key).poll(cx).unwrap().is_pending()); + Ok::<_, ()>(()) + })).unwrap(); } #[test] fn test_pool_checkout_removes_expired() { - future::lazy(|| { + block_on(future::lazy(|cx| { let pool = Pool::new(true, Some(Duration::from_millis(100))); let key = Arc::new("foo".to_string()); @@ -481,20 +491,20 @@ mod tests { ::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap()); // checkout.poll() should clean out the expired - pool.checkout(&key).poll().unwrap(); + pool.checkout(&key).poll(cx).unwrap(); assert!(pool.inner.lock().unwrap().idle.get(&key).is_none()); - Ok::<(), ()>(()) - }).wait().unwrap(); + Ok::<_, ()>(()) + })).unwrap(); } #[test] fn test_pool_timer_removes_expired() { - let runtime = ::tokio::runtime::Runtime::new().unwrap(); - let pool = Pool::new(true, Some(Duration::from_millis(100))); + let mut pool = Pool::new(true, Some(Duration::from_millis(100))); - let executor = runtime.executor(); - pool.spawn_expired_interval(&Exec::new(executor)); + block_on(future::lazy(|cx| { + pool.spawn_expired_interval(cx) + })).unwrap(); let key = Arc::new("foo".to_string()); pool.pooled(key.clone(), 41); @@ -503,9 +513,9 @@ mod tests { assert_eq!(pool.inner.lock().unwrap().idle.get(&key).map(|entries| entries.len()), Some(3)); - ::futures_timer::Delay::new( + block_on(::futures_timer::Delay::new( Duration::from_millis(400) // allow for too-good resolution - ).wait().unwrap(); + )).unwrap(); assert!(pool.inner.lock().unwrap().idle.get(&key).is_none()); } @@ -516,7 +526,7 @@ mod tests { let key = Arc::new("foo".to_string()); let pooled = pool.pooled(key.clone(), 41); - let checkout = pool.checkout(&key).join(future::lazy(move || { + let checkout = pool.checkout(&key).join(future::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 @@ -525,12 +535,12 @@ mod tests { drop(pooled); Ok(()) })).map(|(entry, _)| entry); - assert_eq!(*checkout.wait().unwrap(), 41); + assert_eq!(*block_on(checkout).unwrap(), 41); } #[test] fn test_pool_checkout_drop_cleans_up_parked() { - future::lazy(|| { + block_on(future::lazy(|cx| { let pool = Pool::::new(true, Some(Duration::from_secs(10))); let key = Arc::new("localhost:12345".to_string()); @@ -538,9 +548,9 @@ mod tests { let mut checkout2 = pool.checkout(&key); // first poll needed to get into Pool's parked - checkout1.poll().unwrap(); + checkout1.poll(cx).unwrap(); assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 1); - checkout2.poll().unwrap(); + checkout2.poll(cx).unwrap(); assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 2); // on drop, clean up Pool @@ -550,7 +560,7 @@ mod tests { drop(checkout2); assert!(pool.inner.lock().unwrap().parked.get(&key).is_none()); - ::futures::future::ok::<(), ()>(()) - }).wait().unwrap(); + Ok::<_, ()>(()) + })).unwrap(); } } diff --git a/src/client/tests.rs b/src/client/tests.rs index 2030c3e8..2edf2d11 100644 --- a/src/client/tests.rs +++ b/src/client/tests.rs @@ -5,6 +5,7 @@ use std::time::Duration; use futures::Async; use futures::future::poll_fn; +use futures::executor::block_on; use tokio::executor::thread_pool::{Builder as ThreadPoolBuilder}; use mock::MockConnector; @@ -22,7 +23,7 @@ fn retryable_request() { let client = Client::configure() .connector(connector) - .executor(executor.sender().clone()); + .build(); { @@ -30,13 +31,13 @@ fn retryable_request() { .uri("http://mock.local/a") .body(Default::default()) .unwrap(); - let res1 = client.request(req); - let srv1 = poll_fn(|| { - try_ready!(sock1.read(&mut [0u8; 512])); + let res1 = client.request(req).with_executor(executor.sender().clone()); + let srv1 = poll_fn(|cx| { + try_ready!(sock1.read(cx, &mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }); - res1.join(srv1).wait().expect("res1"); + block_on(res1.join(srv1)).expect("res1"); } drop(sock1); @@ -44,17 +45,17 @@ fn retryable_request() { .uri("http://mock.local/b") .body(Default::default()) .unwrap(); - let res2 = client.request(req) + let res2 = client.request(req).with_executor(executor.sender().clone()) .map(|res| { assert_eq!(res.status().as_u16(), 222); }); - let srv2 = poll_fn(|| { - try_ready!(sock2.read(&mut [0u8; 512])); + let srv2 = poll_fn(|cx| { + try_ready!(sock2.read(cx, &mut [0u8; 512])); try_ready!(sock2.write(b"HTTP/1.1 222 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }); - res2.join(srv2).wait().expect("res2"); + block_on(res2.join(srv2)).expect("res2"); } #[test] @@ -68,7 +69,7 @@ fn conn_reset_after_write() { let client = Client::configure() .connector(connector) - .executor(executor.sender().clone()); + .build(); { let req = Request::builder() @@ -77,13 +78,13 @@ fn conn_reset_after_write() { .header("content-length", "0") .body(Default::default()) .unwrap(); - let res1 = client.request(req); - let srv1 = poll_fn(|| { - try_ready!(sock1.read(&mut [0u8; 512])); + let res1 = client.request(req).with_executor(executor.sender().clone()); + let srv1 = poll_fn(|cx| { + try_ready!(sock1.read(cx, &mut [0u8; 512])); try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); Ok(Async::Ready(())) }); - res1.join(srv1).wait().expect("res1"); + block_on(res1.join(srv1)).expect("res1"); } // sleep to allow some time for the connection to return to the pool @@ -93,20 +94,20 @@ fn conn_reset_after_write() { .uri("http://mock.local/a") .body(Default::default()) .unwrap(); - let res2 = client.request(req); + let res2 = client.request(req).with_executor(executor.sender().clone()); let mut sock1 = Some(sock1); - let srv2 = poll_fn(|| { + let srv2 = poll_fn(|cx| { // We purposefully keep the socket open until the client // has written the second request, and THEN disconnect. // // Not because we expect servers to be jerks, but to trigger // state where we write on an assumedly good connetion, and // only reset the close AFTER we wrote bytes. - try_ready!(sock1.as_mut().unwrap().read(&mut [0u8; 512])); + try_ready!(sock1.as_mut().unwrap().read(cx, &mut [0u8; 512])); sock1.take(); Ok(Async::Ready(())) }); - let err = res2.join(srv2).wait().expect_err("res2"); + let err = block_on(res2.join(srv2)).expect_err("res2"); match err { ::Error::Incomplete => (), other => panic!("expected Incomplete, found {:?}", other) diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index a627be7d..00000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[derive(Debug)] -pub enum Never {} diff --git a/src/error.rs b/src/error.rs index e3f14afa..45b15d12 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,8 @@ use self::Error::{ Io, TooLarge, Incomplete, - Utf8 + Utf8, + Executor }; /// Result type often returned from methods that can have hyper `Error`s. @@ -56,6 +57,8 @@ pub enum Error { Io(IoError), /// Parsing a field as string failed Utf8(Utf8Error), + /// Executing a future failed + Executor, #[doc(hidden)] __Nonexhaustive(Void) @@ -127,6 +130,7 @@ impl StdError for Error { Cancel(ref e) => e.description(), Io(ref e) => e.description(), Utf8(ref e) => e.description(), + Executor => "executor is missing or failed to spawn", Error::__Nonexhaustive(..) => unreachable!(), } } diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 00000000..09b8cab5 --- /dev/null +++ b/src/executor.rs @@ -0,0 +1,17 @@ +use futures::executor::Executor; + +pub(crate) trait CloneBoxedExecutor: Executor + Send + Sync { + fn clone_boxed(&self) -> Box; +} + +impl CloneBoxedExecutor for E { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_boxed() + } +} diff --git a/src/headers.rs b/src/headers.rs index 47e13032..204ec04a 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -129,7 +129,7 @@ fn eq_ascii(left: &str, right: &str) -> bool { // compiler says this trait is unused. // // Once our minimum Rust compiler version is >=1.23, this can be removed. - #[allow(unused)] + #[allow(unused, deprecated)] use std::ascii::AsciiExt; left.eq_ignore_ascii_case(right) diff --git a/src/lib.rs b/src/lib.rs index 9ef8176e..05747d00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,6 @@ extern crate bytes; #[macro_use] extern crate futures; -extern crate futures_cpupool; extern crate futures_timer; extern crate http; extern crate httparse; @@ -28,8 +27,6 @@ extern crate net2; extern crate time; extern crate tokio; extern crate tokio_executor; -#[macro_use] extern crate tokio_io; -extern crate tokio_service; extern crate want; #[cfg(all(test, feature = "nightly"))] @@ -49,7 +46,8 @@ pub use error::{Result, Error}; pub use proto::{body, Body, Chunk}; pub use server::Server; -mod common; +mod executor; +mod service; #[cfg(test)] mod mock; pub mod client; diff --git a/src/mock.rs b/src/mock.rs index b4bdef18..223fa79b 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -3,10 +3,10 @@ use std::cmp; use std::io::{self, Read, Write}; use std::sync::{Arc, Mutex}; -use bytes::Buf; use futures::{Async, Poll}; -use futures::task::{self, Task}; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; +use iovec::IoVec; use ::client::connect::{Connect, Connected, Destination}; @@ -77,7 +77,7 @@ pub struct AsyncIo { max_read_vecs: usize, num_writes: usize, park_tasks: bool, - task: Option, + task: Option, } impl AsyncIo { @@ -99,7 +99,7 @@ impl AsyncIo { self.bytes_until_block = bytes; if let Some(task) = self.task.take() { - task.notify(); + task.wake(); } } @@ -130,12 +130,12 @@ impl AsyncIo { self.num_writes } - fn would_block(&mut self) -> io::Error { + fn would_block(&mut self, cx: &mut task::Context) -> Poll { self.blocked = true; if self.park_tasks { - self.task = Some(task::current()); + self.task = Some(cx.waker().clone()); } - io::ErrorKind::WouldBlock.into() + Ok(Async::Pending) } } @@ -159,118 +159,101 @@ impl AsyncIo { } } -impl AsyncIo { - fn write_no_vecs(&mut self, buf: &mut B) -> Poll { - if !buf.has_remaining() { - return Ok(Async::Ready(0)); - } - - let n = try_nb!(self.write(buf.bytes())); - buf.advance(n); - Ok(Async::Ready(n)) - } -} - impl, T: AsRef<[u8]>> PartialEq for AsyncIo { fn eq(&self, other: &S) -> bool { self.inner.as_ref() == other.as_ref() } } - -impl Read for AsyncIo { - fn read(&mut self, buf: &mut [u8]) -> io::Result { +impl AsyncRead for AsyncIo { + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll { self.blocked = false; if let Some(err) = self.error.take() { Err(err) } else if self.bytes_until_block == 0 { - Err(self.would_block()) + self.would_block(cx) } else { let n = cmp::min(self.bytes_until_block, buf.len()); let n = try!(self.inner.read(&mut buf[..n])); self.bytes_until_block -= n; - Ok(n) + Ok(Async::Ready(n)) } } } -impl Write for AsyncIo { - fn write(&mut self, data: &[u8]) -> io::Result { +impl AsyncIo { + fn write_no_vecs(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll { + if buf.len() == 0 { + return Ok(Async::Ready(0)); + } + + self.poll_write(cx, buf) + } +} + +impl AsyncWrite for AsyncIo { + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll { self.num_writes += 1; if let Some(err) = self.error.take() { trace!("AsyncIo::write error"); Err(err) } else if self.bytes_until_block == 0 { trace!("AsyncIo::write would block"); - Err(self.would_block()) + self.would_block(cx) } else { - trace!("AsyncIo::write; {} bytes", data.len()); + trace!("AsyncIo::write; {} bytes", buf.len()); self.flushed = false; - let n = cmp::min(self.bytes_until_block, data.len()); - let n = try!(self.inner.write(&data[..n])); + let n = cmp::min(self.bytes_until_block, buf.len()); + let n = try!(self.inner.write(&buf[..n])); self.bytes_until_block -= n; - Ok(n) + Ok(Async::Ready(n)) } } - fn flush(&mut self) -> io::Result<()> { + fn poll_flush(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> { self.flushed = true; - self.inner.flush() + try!(self.inner.flush()); + Ok(Async::Ready(())) } -} -impl AsyncRead for AsyncIo { -} - -impl AsyncWrite for AsyncIo { - fn shutdown(&mut self) -> Poll<(), io::Error> { + fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> { Ok(().into()) } - fn write_buf(&mut self, buf: &mut B) -> Poll { + fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) -> Poll { if self.max_read_vecs == 0 { - return self.write_no_vecs(buf); + if let Some(ref first_iovec) = vec.get(0) { + return self.write_no_vecs(cx, &*first_iovec) + } else { + return Ok(Async::Ready(0)); + } } - let r = { - static DUMMY: &[u8] = &[0]; - let mut bufs = [From::from(DUMMY); READ_VECS_CNT]; - let i = Buf::bytes_vec(&buf, &mut bufs[..self.max_read_vecs]); - let mut n = 0; - let mut ret = Ok(0); - // each call to write() will increase our count, but we assume - // that if iovecs are used, its really only 1 write call. - let num_writes = self.num_writes; - for iovec in &bufs[..i] { - match self.write(iovec) { - Ok(num) => { - n += num; - ret = Ok(n); - }, - Err(e) => { - if e.kind() == io::ErrorKind::WouldBlock { - if let Ok(0) = ret { - ret = Err(e); - } - } else { - ret = Err(e); - } - break; + + let mut n = 0; + let mut ret = Ok(Async::Ready(0)); + // each call to poll_write() will increase our count, but we assume + // that if iovecs are used, its really only 1 write call. + let num_writes = self.num_writes; + for buf in vec { + match self.poll_write(cx, &buf) { + Ok(Async::Ready(num)) => { + n += num; + ret = Ok(Async::Ready(n)); + }, + Ok(Async::Pending) => { + if let Ok(Async::Ready(0)) = ret { + ret = Ok(Async::Pending); } + break; + }, + Err(err) => { + ret = Err(err); + break; } } - self.num_writes = num_writes + 1; - ret - }; - match r { - Ok(n) => { - Buf::advance(buf, n); - Ok(Async::Ready(n)) - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - Ok(Async::NotReady) - } - Err(e) => Err(e), } + self.num_writes = num_writes + 1; + ret } } @@ -287,47 +270,33 @@ pub struct Duplex { } struct DuplexInner { - handle_read_task: Option, + handle_read_task: Option, read: AsyncIo, write: AsyncIo, } -impl Read for Duplex { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.lock().unwrap().read.read(buf) - } -} - -impl Write for Duplex { - fn write(&mut self, buf: &[u8]) -> io::Result { - let mut inner = self.inner.lock().unwrap(); - if let Some(task) = inner.handle_read_task.take() { - trace!("waking DuplexHandle read"); - task.notify(); - } - inner.write.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.lock().unwrap().write.flush() - } -} - impl AsyncRead for Duplex { - + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll { + self.inner.lock().unwrap().read.poll_read(cx, buf) + } } impl AsyncWrite for Duplex { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(().into()) - } - - fn write_buf(&mut self, buf: &mut B) -> Poll { + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll { let mut inner = self.inner.lock().unwrap(); if let Some(task) = inner.handle_read_task.take() { - task.notify(); + trace!("waking DuplexHandle read"); + task.wake(); } - inner.write.write_buf(buf) + inner.write.poll_write(cx, buf) + } + + fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + self.inner.lock().unwrap().write.poll_flush(cx) + } + + fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> { + Ok(().into()) } } @@ -336,13 +305,13 @@ pub struct DuplexHandle { } impl DuplexHandle { - pub fn read(&self, buf: &mut [u8]) -> Poll { + pub fn read(&self, cx: &mut task::Context, buf: &mut [u8]) -> Poll { let mut inner = self.inner.lock().unwrap(); assert!(buf.len() >= inner.write.inner.len()); if inner.write.inner.is_empty() { trace!("DuplexHandle read parking"); - inner.handle_read_task = Some(task::current()); - return Ok(Async::NotReady); + inner.handle_read_task = Some(cx.waker().clone()); + return Ok(Async::Pending); } inner.write.inner.vec.truncate(0); Ok(Async::Ready(inner.write.inner.len())) diff --git a/src/proto/body.rs b/src/proto/body.rs index 23eea59e..236e6f2e 100644 --- a/src/proto/body.rs +++ b/src/proto/body.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; use std::fmt; use bytes::Bytes; -use futures::{Async, Future, Poll, Stream}; -use futures::sync::{mpsc, oneshot}; +use futures::{Async, Future, Poll, Stream, StreamExt}; +use futures::task; +use futures::channel::{mpsc, oneshot}; use http::HeaderMap; use super::Chunk; @@ -24,14 +25,14 @@ pub trait Entity { /// /// Similar to `Stream::poll_next`, this yields `Some(Data)` until /// the body ends, when it yields `None`. - fn poll_data(&mut self) -> Poll, Self::Error>; + fn poll_data(&mut self, cx: &mut task::Context) -> Poll, Self::Error>; /// Poll for an optional **single** `HeaderMap` of trailers. /// /// This should **only** be called after `poll_data` has ended. /// /// Note: Trailers aren't currently used for HTTP/1, only for HTTP/2. - fn poll_trailers(&mut self) -> Poll, Self::Error> { + fn poll_trailers(&mut self, _cx: &mut task::Context) -> Poll, Self::Error> { Ok(Async::Ready(None)) } @@ -67,12 +68,12 @@ impl Entity for Box { type Data = E::Data; type Error = E::Error; - fn poll_data(&mut self) -> Poll, Self::Error> { - (**self).poll_data() + fn poll_data(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { + (**self).poll_data(cx) } - fn poll_trailers(&mut self) -> Poll, Self::Error> { - (**self).poll_trailers() + fn poll_trailers(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { + (**self).poll_trailers(cx) } fn is_end_stream(&self) -> bool { @@ -96,10 +97,10 @@ impl Stream for EntityStream { type Item = E::Data; type Error = E::Error; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { loop { if self.is_data_eof { - return self.entity.poll_trailers() + return self.entity.poll_trailers(cx) .map(|async| { async.map(|_opt| { // drop the trailers and return that Stream is done @@ -108,7 +109,7 @@ impl Stream for EntityStream { }); } - let opt = try_ready!(self.entity.poll_data()); + let opt = try_ready!(self.entity.poll_data(cx)); if let Some(data) = opt { return Ok(Async::Ready(Some(data))); } else { @@ -211,14 +212,14 @@ impl Body { /// ``` /// # extern crate futures; /// # extern crate hyper; - /// # use futures::{Future, Stream}; + /// # use futures::{FutureExt, StreamExt}; /// # use hyper::{Body, Request}; /// # fn request_concat(some_req: Request) { /// let req: Request = some_req; /// let body = req.into_body(); /// /// let stream = body.into_stream(); - /// stream.concat2() + /// stream.concat() /// .map(|buf| { /// println!("body length: {}", buf.len()); /// }); @@ -267,15 +268,15 @@ impl Entity for Body { type Data = Chunk; type Error = ::Error; - fn poll_data(&mut self) -> Poll, Self::Error> { + fn poll_data(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { match self.kind { - Kind::Chan { ref mut rx, .. } => match rx.poll().expect("mpsc cannot error") { + Kind::Chan { ref mut rx, .. } => match rx.poll_next(cx).expect("mpsc cannot error") { Async::Ready(Some(Ok(chunk))) => Ok(Async::Ready(Some(chunk))), Async::Ready(Some(Err(err))) => Err(err), Async::Ready(None) => Ok(Async::Ready(None)), - Async::NotReady => Ok(Async::NotReady), + Async::Pending => Ok(Async::Pending), }, - Kind::Wrapped(ref mut s) => s.poll(), + Kind::Wrapped(ref mut s) => s.poll_next(cx), Kind::Once(ref mut val) => Ok(Async::Ready(val.take())), Kind::Empty => Ok(Async::Ready(None)), } @@ -310,13 +311,13 @@ impl fmt::Debug for Body { impl Sender { /// Check to see if this `Sender` can send more data. - pub fn poll_ready(&mut self) -> Poll<(), ()> { - match self.close_rx.poll() { + pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()> { + match self.close_rx.poll(cx) { Ok(Async::Ready(())) | Err(_) => return Err(()), - Ok(Async::NotReady) => (), + Ok(Async::Pending) => (), } - self.tx.poll_ready().map_err(|_| ()) + self.tx.poll_ready(cx).map_err(|_| ()) } /// Sends data on this channel. @@ -413,13 +414,11 @@ fn _assert_send_sync() { #[test] fn test_body_stream_concat() { - use futures::{Stream, Future}; + use futures::{StreamExt}; let body = Body::from("hello world"); - let total = body.into_stream() - .concat2() - .wait() + let total = ::futures::executor::block_on(body.into_stream().concat()) .unwrap(); assert_eq!(total.as_ref(), b"hello world"); diff --git a/src/proto/h1/conn.rs b/src/proto/h1/conn.rs index 9a3419f6..b3a287ba 100644 --- a/src/proto/h1/conn.rs +++ b/src/proto/h1/conn.rs @@ -3,10 +3,10 @@ use std::io::{self}; use std::marker::PhantomData; use bytes::Bytes; -use futures::{Async, AsyncSink, Poll, StartSend}; -use futures::task::Task; +use futures::{Async, Poll}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use http::{Method, Version}; -use tokio_io::{AsyncRead, AsyncWrite}; use proto::{BodyLength, Chunk, Decode, Http1Transaction, MessageHead}; use super::io::{Cursor, Buffered}; @@ -113,14 +113,14 @@ where I: AsyncRead + AsyncWrite, T::should_error_on_parse_eof() && !self.state.is_idle() } - pub fn read_head(&mut self) -> Poll, bool)>, ::Error> { + pub fn read_head(&mut self, cx: &mut task::Context) -> Poll, bool)>, ::Error> { debug_assert!(self.can_read_head()); trace!("Conn::read_head"); loop { - let (version, head) = match self.io.parse::() { + let (version, head) = match self.io.parse::(cx) { Ok(Async::Ready(head)) => (head.version, head), - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(e) => { // If we are currently waiting on a message, then an empty // message should be reported as an error. If not, it is just @@ -132,7 +132,7 @@ where I: AsyncRead + AsyncWrite, return if was_mid_parse || must_error { debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len()); self.on_parse_error(e) - .map(|()| Async::NotReady) + .map(|()| Async::Pending) } else { debug!("read eof"); Ok(Async::Ready(None)) @@ -169,7 +169,7 @@ where I: AsyncRead + AsyncWrite, debug!("decoder error = {:?}", e); self.state.close_read(); return self.on_parse_error(e) - .map(|()| Async::NotReady); + .map(|()| Async::Pending); } }; @@ -193,20 +193,20 @@ where I: AsyncRead + AsyncWrite, self.state.reading = reading; } if !body { - self.try_keep_alive(); + self.try_keep_alive(cx); } return Ok(Async::Ready(Some((head, body)))); } } - pub fn read_body(&mut self) -> Poll, io::Error> { + pub fn read_body(&mut self, cx: &mut task::Context) -> Poll, io::Error> { debug_assert!(self.can_read_body()); trace!("Conn::read_body"); let (reading, ret) = match self.state.reading { Reading::Body(ref mut decoder) => { - match decoder.decode(&mut self.io) { + match decoder.decode(&mut self.io, cx) { Ok(Async::Ready(slice)) => { let (reading, chunk) = if !slice.is_empty() { return Ok(Async::Ready(Some(Chunk::from(slice)))); @@ -222,7 +222,7 @@ where I: AsyncRead + AsyncWrite, }; (reading, Ok(Async::Ready(chunk))) }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(e) => { trace!("decode stream error: {}", e); (Reading::Closed, Err(e)) @@ -233,19 +233,19 @@ where I: AsyncRead + AsyncWrite, }; self.state.reading = reading; - self.try_keep_alive(); + self.try_keep_alive(cx); ret } - pub fn read_keep_alive(&mut self) -> Result<(), ::Error> { + pub fn read_keep_alive(&mut self, cx: &mut task::Context) -> Result<(), ::Error> { debug_assert!(!self.can_read_head() && !self.can_read_body()); trace!("read_keep_alive; is_mid_message={}", self.is_mid_message()); if self.is_mid_message() { - self.maybe_park_read(); + self.maybe_park_read(cx); } else { - self.require_empty_read()?; + self.require_empty_read(cx)?; } Ok(()) } @@ -257,18 +257,19 @@ where I: AsyncRead + AsyncWrite, } } - fn maybe_park_read(&mut self) { + fn maybe_park_read(&mut self, cx: &mut task::Context) { if !self.io.is_read_blocked() { // the Io object is ready to read, which means it will never alert // us that it is ready until we drain it. However, we're currently // finished reading, so we need to park the task to be able to // wake back up later when more reading should happen. + let current_waker = cx.waker(); let park = self.state.read_task.as_ref() - .map(|t| !t.will_notify_current()) + .map(|t| !t.will_wake(current_waker)) .unwrap_or(true); if park { trace!("parking current task"); - self.state.read_task = Some(::futures::task::current()); + self.state.read_task = Some(current_waker.clone()); } } } @@ -277,14 +278,14 @@ where I: AsyncRead + AsyncWrite, // // This should only be called for Clients wanting to enter the idle // state. - fn require_empty_read(&mut self) -> io::Result<()> { + fn require_empty_read(&mut self, cx: &mut task::Context) -> io::Result<()> { assert!(!self.can_read_head() && !self.can_read_body()); if !self.io.read_buf().is_empty() { debug!("received an unexpected {} bytes", self.io.read_buf().len()); Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected bytes after message ended")) } else { - match self.try_io_read()? { + match self.try_io_read(cx)? { Async::Ready(0) => { // case handled in try_io_read Ok(()) @@ -298,15 +299,15 @@ where I: AsyncRead + AsyncWrite, }; Err(io::Error::new(io::ErrorKind::InvalidData, desc)) }, - Async::NotReady => { + Async::Pending => { Ok(()) }, } } } - fn try_io_read(&mut self) -> Poll { - match self.io.read_from_io() { + fn try_io_read(&mut self, cx: &mut task::Context) -> Poll { + match self.io.read_from_io(cx) { Ok(Async::Ready(0)) => { trace!("try_io_read; found EOF on connection: {:?}", self.state); let must_error = self.should_error_on_eof(); @@ -328,8 +329,8 @@ where I: AsyncRead + AsyncWrite, Ok(Async::Ready(n)) => { Ok(Async::Ready(n)) }, - Ok(Async::NotReady) => { - Ok(Async::NotReady) + Ok(Async::Pending) => { + Ok(Async::Pending) }, Err(e) => { self.state.close(); @@ -339,8 +340,8 @@ where I: AsyncRead + AsyncWrite, } - fn maybe_notify(&mut self) { - // its possible that we returned NotReady from poll() without having + fn maybe_notify(&mut self, cx: &mut task::Context) { + // its possible that we returned Pending from poll() without having // exhausted the underlying Io. We would have done this when we // determined we couldn't keep reading until we knew how writing // would finish. @@ -366,9 +367,9 @@ where I: AsyncRead + AsyncWrite, if !self.io.is_read_blocked() { if wants_read && self.io.read_buf().is_empty() { - match self.io.read_from_io() { + match self.io.read_from_io(cx) { Ok(Async::Ready(_)) => (), - Ok(Async::NotReady) => { + Ok(Async::Pending) => { trace!("maybe_notify; read_from_io blocked"); return }, @@ -380,16 +381,16 @@ where I: AsyncRead + AsyncWrite, } if let Some(ref task) = self.state.read_task { trace!("maybe_notify; notifying task"); - task.notify(); + task.wake(); } else { trace!("maybe_notify; no task to notify"); } } } - fn try_keep_alive(&mut self) { + fn try_keep_alive(&mut self, cx: &mut task::Context) { self.state.try_keep_alive(); - self.maybe_notify(); + self.maybe_notify(cx); } pub fn can_write_head(&self) -> bool { @@ -475,17 +476,15 @@ where I: AsyncRead + AsyncWrite, } } - pub fn write_body(&mut self, chunk: Option) -> StartSend, io::Error> { + pub fn write_body(&mut self, _cx: &mut task::Context, chunk: Option) -> Poll<(), io::Error> { debug_assert!(self.can_write_body()); if !self.can_buffer_body() { - if let Async::NotReady = self.flush()? { - // if chunk is Some(&[]), aka empty, whatever, just skip it - if chunk.as_ref().map(|c| c.as_ref().is_empty()).unwrap_or(false) { - return Ok(AsyncSink::Ready); - } else { - return Ok(AsyncSink::NotReady(chunk)); - } + // if chunk is Some(&[]), aka empty, whatever, just skip it + if chunk.as_ref().map(|c| c.as_ref().is_empty()).unwrap_or(true) { + return Ok(Async::Ready(())); + } else { + return Err(io::Error::new(io::ErrorKind::Other, "tried to write chunk when body can't buffer")); } } @@ -493,7 +492,7 @@ where I: AsyncRead + AsyncWrite, Writing::Body(ref mut encoder) => { if let Some(chunk) = chunk { if chunk.as_ref().is_empty() { - return Ok(AsyncSink::Ready); + return Ok(Async::Ready(())); } let encoded = encoder.encode(Cursor::new(chunk)); @@ -506,7 +505,7 @@ where I: AsyncRead + AsyncWrite, Writing::KeepAlive } } else { - return Ok(AsyncSink::Ready); + return Ok(Async::Ready(())); } } else { // end of stream, that means we should try to eof @@ -529,7 +528,7 @@ where I: AsyncRead + AsyncWrite, }; self.state.writing = state; - Ok(AsyncSink::Ready) + Ok(Async::Ready(())) } // When we get a parse error, depending on what side we are, we might be able @@ -553,16 +552,16 @@ where I: AsyncRead + AsyncWrite, Err(err) } - pub fn flush(&mut self) -> Poll<(), io::Error> { - try_ready!(self.io.flush()); - self.try_keep_alive(); + pub fn flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + try_ready!(self.io.flush(cx)); + self.try_keep_alive(cx); trace!("flushed {:?}", self.state); Ok(Async::Ready(())) } - pub fn shutdown(&mut self) -> Poll<(), io::Error> { - match self.io.io_mut().shutdown() { - Ok(Async::NotReady) => Ok(Async::NotReady), + pub fn shutdown(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + match self.io.io_mut().poll_close(cx) { + Ok(Async::Pending) => Ok(Async::Pending), Ok(Async::Ready(())) => { trace!("shut down IO"); Ok(Async::Ready(())) @@ -612,7 +611,7 @@ struct State { error: Option<::Error>, keep_alive: KA, method: Option, - read_task: Option, + read_task: Option, reading: Reading, writing: Writing, version: Version, @@ -964,7 +963,7 @@ mod tests { } match conn.poll() { - Ok(Async::NotReady) => (), + Ok(Async::Pending) => (), other => panic!("unexpected frame: {:?}", other) } Ok(()) diff --git a/src/proto/h1/decode.rs b/src/proto/h1/decode.rs index 4521547f..03967a28 100644 --- a/src/proto/h1/decode.rs +++ b/src/proto/h1/decode.rs @@ -4,6 +4,7 @@ use std::usize; use std::io; use futures::{Async, Poll}; +use futures::task; use bytes::Bytes; use super::io::MemRead; @@ -84,7 +85,7 @@ impl Decoder { } } - pub fn decode(&mut self, body: &mut R) -> Poll { + pub fn decode(&mut self, body: &mut R, cx: &mut task::Context) -> Poll { trace!("decode; state={:?}", self.kind); match self.kind { Length(ref mut remaining) => { @@ -92,7 +93,7 @@ impl Decoder { Ok(Async::Ready(Bytes::new())) } else { let to_read = *remaining as usize; - let buf = try_ready!(body.read_mem(to_read)); + let buf = try_ready!(body.read_mem(cx, to_read)); let num = buf.as_ref().len() as u64; if num > *remaining { *remaining = 0; @@ -108,7 +109,7 @@ impl Decoder { loop { let mut buf = None; // advances the chunked state - *state = try_ready!(state.step(body, size, &mut buf)); + *state = try_ready!(state.step(body, cx, size, &mut buf)); if *state == ChunkedState::End { trace!("end of chunked"); return Ok(Async::Ready(Bytes::new())); @@ -125,7 +126,7 @@ impl Decoder { // 8192 chosen because its about 2 packets, there probably // won't be that much available, so don't have MemReaders // allocate buffers to big - let slice = try_ready!(body.read_mem(8192)); + let slice = try_ready!(body.read_mem(cx, 8192)); *is_eof = slice.is_empty(); Ok(Async::Ready(slice)) } @@ -152,8 +153,8 @@ impl fmt::Display for Decoder { } macro_rules! byte ( - ($rdr:ident) => ({ - let buf = try_ready!($rdr.read_mem(1)); + ($rdr:ident, $cx:ident) => ({ + let buf = try_ready!($rdr.read_mem($cx, 1)); if !buf.is_empty() { buf[0] } else { @@ -166,27 +167,28 @@ macro_rules! byte ( impl ChunkedState { fn step(&self, body: &mut R, + cx: &mut task::Context, size: &mut u64, buf: &mut Option) -> Poll { use self::ChunkedState::*; match *self { - Size => ChunkedState::read_size(body, size), - SizeLws => ChunkedState::read_size_lws(body), - Extension => ChunkedState::read_extension(body), - SizeLf => ChunkedState::read_size_lf(body, *size), - Body => ChunkedState::read_body(body, size, buf), - BodyCr => ChunkedState::read_body_cr(body), - BodyLf => ChunkedState::read_body_lf(body), - EndCr => ChunkedState::read_end_cr(body), - EndLf => ChunkedState::read_end_lf(body), + Size => ChunkedState::read_size(body, cx, size), + SizeLws => ChunkedState::read_size_lws(body, cx), + Extension => ChunkedState::read_extension(body, cx), + SizeLf => ChunkedState::read_size_lf(body, cx, *size), + Body => ChunkedState::read_body(body, cx, size, buf), + BodyCr => ChunkedState::read_body_cr(body, cx), + BodyLf => ChunkedState::read_body_lf(body, cx), + EndCr => ChunkedState::read_end_cr(body, cx), + EndLf => ChunkedState::read_end_lf(body, cx), End => Ok(Async::Ready(ChunkedState::End)), } } - fn read_size(rdr: &mut R, size: &mut u64) -> Poll { + fn read_size(rdr: &mut R, cx: &mut task::Context, size: &mut u64) -> Poll { trace!("Read chunk hex size"); let radix = 16; - match byte!(rdr) { + match byte!(rdr, cx) { b @ b'0'...b'9' => { *size *= radix; *size += (b - b'0') as u64; @@ -209,9 +211,9 @@ impl ChunkedState { } Ok(Async::Ready(ChunkedState::Size)) } - fn read_size_lws(rdr: &mut R) -> Poll { + fn read_size_lws(rdr: &mut R, cx: &mut task::Context) -> Poll { trace!("read_size_lws"); - match byte!(rdr) { + match byte!(rdr, cx) { // LWS can follow the chunk size, but no more digits can come b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)), b';' => Ok(Async::Ready(ChunkedState::Extension)), @@ -222,16 +224,16 @@ impl ChunkedState { } } } - fn read_extension(rdr: &mut R) -> Poll { + fn read_extension(rdr: &mut R, cx: &mut task::Context) -> Poll { trace!("read_extension"); - match byte!(rdr) { + match byte!(rdr, cx) { b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), _ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions } } - fn read_size_lf(rdr: &mut R, size: u64) -> Poll { + fn read_size_lf(rdr: &mut R, cx: &mut task::Context, size: u64) -> Poll { trace!("Chunk size is {:?}", size); - match byte!(rdr) { + match byte!(rdr, cx) { b'\n' => { if size == 0 { Ok(Async::Ready(ChunkedState::EndCr)) @@ -244,7 +246,7 @@ impl ChunkedState { } } - fn read_body(rdr: &mut R, + fn read_body(rdr: &mut R, cx: &mut task::Context, rem: &mut u64, buf: &mut Option) -> Poll { @@ -257,7 +259,7 @@ impl ChunkedState { }; let to_read = rem_cap; - let slice = try_ready!(rdr.read_mem(to_read)); + let slice = try_ready!(rdr.read_mem(cx, to_read)); let count = slice.len(); if count == 0 { @@ -273,27 +275,27 @@ impl ChunkedState { Ok(Async::Ready(ChunkedState::BodyCr)) } } - fn read_body_cr(rdr: &mut R) -> Poll { - match byte!(rdr) { + fn read_body_cr(rdr: &mut R, cx: &mut task::Context) -> Poll { + match byte!(rdr, cx) { b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR")), } } - fn read_body_lf(rdr: &mut R) -> Poll { - match byte!(rdr) { + fn read_body_lf(rdr: &mut R, cx: &mut task::Context) -> Poll { + match byte!(rdr, cx) { b'\n' => Ok(Async::Ready(ChunkedState::Size)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF")), } } - fn read_end_cr(rdr: &mut R) -> Poll { - match byte!(rdr) { + fn read_end_cr(rdr: &mut R, cx: &mut task::Context) -> Poll { + match byte!(rdr, cx) { b'\r' => Ok(Async::Ready(ChunkedState::EndLf)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR")), } } - fn read_end_lf(rdr: &mut R) -> Poll { - match byte!(rdr) { + fn read_end_lf(rdr: &mut R, cx: &mut task::Context) -> Poll { + match byte!(rdr, cx) { b'\n' => Ok(Async::Ready(ChunkedState::End)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF")), } @@ -323,11 +325,14 @@ mod tests { use super::ChunkedState; use super::super::io::MemRead; use futures::{Async, Poll}; + use futures::task; + use futures::future::lazy; + use futures::executor::block_on; use bytes::{BytesMut, Bytes}; use mock::AsyncIo; impl<'a> MemRead for &'a [u8] { - fn read_mem(&mut self, len: usize) -> Poll { + fn read_mem(&mut self, _cx: &mut task::Context, len: usize) -> Poll { let n = ::std::cmp::min(len, self.len()); if n > 0 { let (a, b) = self.split_at(n); @@ -347,7 +352,7 @@ mod tests { fn unwrap(self) -> Bytes { match self { Async::Ready(bytes) => bytes, - Async::NotReady => panic!(), + Async::Pending => panic!(), } } } @@ -355,7 +360,7 @@ mod tests { fn unwrap(self) -> ChunkedState { match self { Async::Ready(state) => state, - Async::NotReady => panic!(), + Async::Pending => panic!(), } } } @@ -364,12 +369,12 @@ mod tests { fn test_read_chunk_size() { use std::io::ErrorKind::{UnexpectedEof, InvalidInput}; - fn read(s: &str) -> u64 { + fn read(cx: &mut task::Context, s: &str) -> u64 { let mut state = ChunkedState::Size; let rdr = &mut s.as_bytes(); let mut size = 0; loop { - let result = state.step(rdr, &mut size, &mut None); + let result = state.step(rdr, cx, &mut size, &mut None); let desc = format!("read_size failed for {:?}", s); state = result.expect(desc.as_str()).unwrap(); if state == ChunkedState::Body || state == ChunkedState::EndCr { @@ -379,12 +384,12 @@ mod tests { size } - fn read_err(s: &str, expected_err: io::ErrorKind) { + fn read_err(cx: &mut task::Context, s: &str, expected_err: io::ErrorKind) { let mut state = ChunkedState::Size; let rdr = &mut s.as_bytes(); let mut size = 0; loop { - let result = state.step(rdr, &mut size, &mut None); + let result = state.step(rdr, cx, &mut size, &mut None); state = match result { Ok(s) => s.unwrap(), Err(e) => { @@ -399,90 +404,111 @@ mod tests { } } - assert_eq!(1, read("1\r\n")); - assert_eq!(1, read("01\r\n")); - assert_eq!(0, read("0\r\n")); - assert_eq!(0, read("00\r\n")); - assert_eq!(10, read("A\r\n")); - assert_eq!(10, read("a\r\n")); - assert_eq!(255, read("Ff\r\n")); - assert_eq!(255, read("Ff \r\n")); - // Missing LF or CRLF - read_err("F\rF", InvalidInput); - read_err("F", UnexpectedEof); - // Invalid hex digit - read_err("X\r\n", InvalidInput); - read_err("1X\r\n", InvalidInput); - read_err("-\r\n", InvalidInput); - read_err("-1\r\n", InvalidInput); - // Acceptable (if not fully valid) extensions do not influence the size - assert_eq!(1, read("1;extension\r\n")); - assert_eq!(10, read("a;ext name=value\r\n")); - assert_eq!(1, read("1;extension;extension2\r\n")); - assert_eq!(1, read("1;;; ;\r\n")); - assert_eq!(2, read("2; extension...\r\n")); - assert_eq!(3, read("3 ; extension=123\r\n")); - assert_eq!(3, read("3 ;\r\n")); - assert_eq!(3, read("3 ; \r\n")); - // Invalid extensions cause an error - read_err("1 invalid extension\r\n", InvalidInput); - read_err("1 A\r\n", InvalidInput); - read_err("1;no CRLF", UnexpectedEof); + block_on(lazy(|cx| { + assert_eq!(1, read(cx, "1\r\n")); + assert_eq!(1, read(cx, "01\r\n")); + assert_eq!(0, read(cx, "0\r\n")); + assert_eq!(0, read(cx, "00\r\n")); + assert_eq!(10, read(cx, "A\r\n")); + assert_eq!(10, read(cx, "a\r\n")); + assert_eq!(255, read(cx, "Ff\r\n")); + assert_eq!(255, read(cx, "Ff \r\n")); + // Missing LF or CRLF + read_err(cx, "F\rF", InvalidInput); + read_err(cx, "F", UnexpectedEof); + // Invalid hex digit + read_err(cx, "X\r\n", InvalidInput); + read_err(cx, "1X\r\n", InvalidInput); + read_err(cx, "-\r\n", InvalidInput); + read_err(cx, "-1\r\n", InvalidInput); + // Acceptable (if not fully valid) extensions do not influence the size + assert_eq!(1, read(cx, "1;extension\r\n")); + assert_eq!(10, read(cx, "a;ext name=value\r\n")); + assert_eq!(1, read(cx, "1;extension;extension2\r\n")); + assert_eq!(1, read(cx, "1;;; ;\r\n")); + assert_eq!(2, read(cx, "2; extension...\r\n")); + assert_eq!(3, read(cx, "3 ; extension=123\r\n")); + assert_eq!(3, read(cx, "3 ;\r\n")); + assert_eq!(3, read(cx, "3 ; \r\n")); + // Invalid extensions cause an error + read_err(cx, "1 invalid extension\r\n", InvalidInput); + read_err(cx, "1 A\r\n", InvalidInput); + read_err(cx, "1;no CRLF", UnexpectedEof); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn test_read_sized_early_eof() { - let mut bytes = &b"foo bar"[..]; - let mut decoder = Decoder::length(10); - assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); - let e = decoder.decode(&mut bytes).unwrap_err(); - assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); + block_on(lazy(|cx| { + let mut bytes = &b"foo bar"[..]; + let mut decoder = Decoder::length(10); + assert_eq!(decoder.decode(&mut bytes, cx).unwrap().unwrap().len(), 7); + let e = decoder.decode(&mut bytes, cx).unwrap_err(); + assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn test_read_chunked_early_eof() { - let mut bytes = &b"\ - 9\r\n\ - foo bar\ - "[..]; - let mut decoder = Decoder::chunked(); - assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7); - let e = decoder.decode(&mut bytes).unwrap_err(); - assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); + block_on(lazy(|cx| { + let mut bytes = &b"\ + 9\r\n\ + foo bar\ + "[..]; + let mut decoder = Decoder::chunked(); + assert_eq!(decoder.decode(&mut bytes, cx).unwrap().unwrap().len(), 7); + let e = decoder.decode(&mut bytes, cx).unwrap_err(); + assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn test_read_chunked_single_read() { - let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; - let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode").unwrap(); - assert_eq!(16, buf.len()); - let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); - assert_eq!("1234567890abcdef", &result); + block_on(lazy(|cx| { + let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..]; + let buf = Decoder::chunked().decode(&mut mock_buf, cx).expect("decode").unwrap(); + assert_eq!(16, buf.len()); + let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); + assert_eq!("1234567890abcdef", &result); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn test_read_chunked_after_eof() { - let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..]; - let mut decoder = Decoder::chunked(); + block_on(lazy(|cx| { + let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..]; + let mut decoder = Decoder::chunked(); - // normal read - let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); - assert_eq!(16, buf.len()); - let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); - assert_eq!("1234567890abcdef", &result); + // normal read + let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap(); + assert_eq!(16, buf.len()); + let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String"); + assert_eq!("1234567890abcdef", &result); - // eof read - let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); - assert_eq!(0, buf.len()); + // eof read + let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap(); + assert_eq!(0, buf.len()); - // ensure read after eof also returns eof - let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap(); - assert_eq!(0, buf.len()); + // ensure read after eof also returns eof + let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap(); + assert_eq!(0, buf.len()); + + Ok::<_, ()>(()) + })).unwrap() } // perform an async read using a custom buffer size and causing a blocking // read at the specified byte fn read_async(mut decoder: Decoder, + cx: &mut task::Context, content: &[u8], block_at: usize) -> String { @@ -490,14 +516,14 @@ mod tests { let mut ins = AsyncIo::new(content, block_at); let mut outs = Vec::new(); loop { - match decoder.decode(&mut ins).expect("unexpected decode error: {}") { + match decoder.decode(&mut ins, cx).expect("unexpected decode error: {}") { Async::Ready(buf) => { if buf.is_empty() { break; // eof } outs.write(buf.as_ref()).expect("write buffer"); }, - Async::NotReady => { + Async::Pending => { ins.block_in(content_len); // we only block once } }; @@ -508,11 +534,14 @@ mod tests { // iterate over the different ways that this async read could go. // tests blocking a read at each byte along the content - The shotgun approach fn all_async_cases(content: &str, expected: &str, decoder: Decoder) { - let content_len = content.len(); - for block_at in 0..content_len { - let actual = read_async(decoder.clone(), content.as_bytes(), block_at); - assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at); - } + block_on(lazy(|cx| { + let content_len = content.len(); + for block_at in 0..content_len { + let actual = read_async(decoder.clone(), cx, content.as_bytes(), block_at); + assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at); + } + Ok::<_, ()>(()) + })).unwrap() } #[test] diff --git a/src/proto/h1/dispatch.rs b/src/proto/h1/dispatch.rs index dc77d45e..74036f12 100644 --- a/src/proto/h1/dispatch.rs +++ b/src/proto/h1/dispatch.rs @@ -2,13 +2,15 @@ use std::io; use bytes::Bytes; use futures::{Async, Future, Poll, Stream}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use http::{Request, Response, StatusCode}; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_service::Service; use proto::body::Entity; use proto::{Body, BodyLength, Conn, Http1Transaction, MessageHead, RequestHead, RequestLine, ResponseHead}; +use ::service::Service; + pub struct Dispatcher { conn: Conn, dispatch: D, @@ -21,9 +23,9 @@ pub trait Dispatch { type PollItem; type PollBody; type RecvItem; - fn poll_msg(&mut self) -> Poll)>, ::Error>; - fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()>; - fn poll_ready(&mut self) -> Poll<(), ()>; + fn poll_msg(&mut self, cx: &mut task::Context) -> Poll)>, ::Error>; + fn recv_msg(&mut self, cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()>; + fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()>; fn should_poll(&self) -> bool; } @@ -69,57 +71,57 @@ where /// The "Future" poll function. Runs this dispatcher until the /// connection is shutdown, or an error occurs. - pub fn poll_until_shutdown(&mut self) -> Poll<(), ::Error> { - self.poll_catch(true) + pub fn poll_until_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.poll_catch(cx, true) } /// Run this dispatcher until HTTP says this connection is done, /// but don't call `AsyncWrite::shutdown` on the underlying IO. /// /// This is useful for HTTP upgrades. - pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> { - self.poll_catch(false) + pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.poll_catch(cx, false) } - fn poll_catch(&mut self, should_shutdown: bool) -> Poll<(), ::Error> { - self.poll_inner(should_shutdown).or_else(|e| { + fn poll_catch(&mut self, cx: &mut task::Context, should_shutdown: bool) -> Poll<(), ::Error> { + self.poll_inner(cx, should_shutdown).or_else(|e| { // An error means we're shutting down either way. // We just try to give the error to the user, // and close the connection with an Ok. If we // cannot give it to the user, then return the Err. - self.dispatch.recv_msg(Err(e)).map(Async::Ready) + self.dispatch.recv_msg(cx, Err(e)).map(Async::Ready) }) } - fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> { - self.poll_read()?; - self.poll_write()?; - self.poll_flush()?; + fn poll_inner(&mut self, cx: &mut task::Context, should_shutdown: bool) -> Poll<(), ::Error> { + self.poll_read(cx)?; + self.poll_write(cx)?; + self.poll_flush(cx)?; if self.is_done() { if should_shutdown { - try_ready!(self.conn.shutdown()); + try_ready!(self.conn.shutdown(cx)); } self.conn.take_error()?; Ok(Async::Ready(())) } else { - Ok(Async::NotReady) + Ok(Async::Pending) } } - fn poll_read(&mut self) -> Poll<(), ::Error> { + fn poll_read(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { loop { if self.is_closing { return Ok(Async::Ready(())); } else if self.conn.can_read_head() { - try_ready!(self.poll_read_head()); + try_ready!(self.poll_read_head(cx)); } else if let Some(mut body) = self.body_tx.take() { if self.conn.can_read_body() { - match body.poll_ready() { + match body.poll_ready(cx) { Ok(Async::Ready(())) => (), - Ok(Async::NotReady) => { + Ok(Async::Pending) => { self.body_tx = Some(body); - return Ok(Async::NotReady); + return Ok(Async::Pending); }, Err(_canceled) => { // user doesn't care about the body @@ -129,7 +131,7 @@ where return Ok(Async::Ready(())); } } - match self.conn.read_body() { + match self.conn.read_body(cx) { Ok(Async::Ready(Some(chunk))) => { match body.send_data(chunk) { Ok(()) => { @@ -147,9 +149,9 @@ where Ok(Async::Ready(None)) => { // just drop, the body will close automatically }, - Ok(Async::NotReady) => { + Ok(Async::Pending) => { self.body_tx = Some(body); - return Ok(Async::NotReady); + return Ok(Async::Pending); } Err(e) => { body.send_error(::Error::Io(e)); @@ -159,16 +161,16 @@ where // just drop, the body will close automatically } } else { - return self.conn.read_keep_alive().map(Async::Ready); + return self.conn.read_keep_alive(cx).map(Async::Ready); } } } - fn poll_read_head(&mut self) -> Poll<(), ::Error> { + fn poll_read_head(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { // can dispatch receive, or does it still care about, an incoming message? - match self.dispatch.poll_ready() { + match self.dispatch.poll_ready(cx) { Ok(Async::Ready(())) => (), - Ok(Async::NotReady) => unreachable!("dispatch not ready when conn is"), + Ok(Async::Pending) => unreachable!("dispatch not ready when conn is"), Err(()) => { trace!("dispatch no longer receiving messages"); self.close(); @@ -176,27 +178,27 @@ where } } // dispatch is ready for a message, try to read one - match self.conn.read_head() { + match self.conn.read_head(cx) { Ok(Async::Ready(Some((head, has_body)))) => { let body = if has_body { let (mut tx, rx) = Body::channel(); - let _ = tx.poll_ready(); // register this task if rx is dropped + let _ = tx.poll_ready(cx); // register this task if rx is dropped self.body_tx = Some(tx); rx } else { Body::empty() }; - self.dispatch.recv_msg(Ok((head, body)))?; + self.dispatch.recv_msg(cx, Ok((head, body)))?; Ok(Async::Ready(())) }, Ok(Async::Ready(None)) => { // read eof, conn will start to shutdown automatically Ok(Async::Ready(())) } - Ok(Async::NotReady) => Ok(Async::NotReady), + Ok(Async::Pending) => Ok(Async::Pending), Err(err) => { debug!("read_head error: {}", err); - self.dispatch.recv_msg(Err(err))?; + self.dispatch.recv_msg(cx, Err(err))?; // if here, the dispatcher gave the user the error // somewhere else. we still need to shutdown, but // not as a second error. @@ -205,12 +207,12 @@ where } } - fn poll_write(&mut self) -> Poll<(), ::Error> { + fn poll_write(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { loop { if self.is_closing { return Ok(Async::Ready(())); } else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() { - if let Some((head, body)) = try_ready!(self.dispatch.poll_msg()) { + if let Some((head, body)) = try_ready!(self.dispatch.poll_msg(cx)) { let body_type = body.as_ref().map(|body| { body.content_length() .map(BodyLength::Known) @@ -223,27 +225,27 @@ where return Ok(Async::Ready(())); } } else if !self.conn.can_buffer_body() { - try_ready!(self.poll_flush()); + try_ready!(self.poll_flush(cx)); } else if let Some(mut body) = self.body_rx.take() { - let chunk = match body.poll_data()? { + let chunk = match body.poll_data(cx)? { Async::Ready(Some(chunk)) => { self.body_rx = Some(body); chunk }, Async::Ready(None) => { if self.conn.can_write_body() { - self.conn.write_body(None)?; + self.conn.write_body(cx, None)?; } continue; }, - Async::NotReady => { + Async::Pending => { self.body_rx = Some(body); - return Ok(Async::NotReady); + return Ok(Async::Pending); } }; if self.conn.can_write_body() { - assert!(self.conn.write_body(Some(chunk))?.is_ready()); + self.conn.write_body(cx, Some(chunk))?; // This allows when chunk is `None`, or `Some([])`. } else if chunk.as_ref().len() == 0 { // ok @@ -251,13 +253,13 @@ where warn!("unexpected chunk when body cannot write"); } } else { - return Ok(Async::NotReady); + return Ok(Async::Pending); } } } - fn poll_flush(&mut self) -> Poll<(), ::Error> { - self.conn.flush().map_err(|err| { + fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.conn.flush(cx).map_err(|err| { debug!("error writing: {}", err); err.into() }) @@ -300,8 +302,8 @@ where type Error = ::Error; #[inline] - fn poll(&mut self) -> Poll { - self.poll_until_shutdown() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.poll_until_shutdown(cx) } } @@ -325,13 +327,13 @@ where type PollBody = Bs; type RecvItem = RequestHead; - fn poll_msg(&mut self) -> Poll)>, ::Error> { + fn poll_msg(&mut self, cx: &mut task::Context) -> Poll)>, ::Error> { if let Some(mut fut) = self.in_flight.take() { - let resp = match fut.poll()? { + let resp = match fut.poll(cx)? { Async::Ready(res) => res, - Async::NotReady => { + Async::Pending => { self.in_flight = Some(fut); - return Ok(Async::NotReady); + return Ok(Async::Pending); } }; let (parts, body) = resp.into_parts(); @@ -351,7 +353,7 @@ where } } - fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> { + fn recv_msg(&mut self, _cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> { let (msg, body) = msg?; let mut req = Request::new(body); *req.method_mut() = msg.subject.0; @@ -362,9 +364,9 @@ where Ok(()) } - fn poll_ready(&mut self) -> Poll<(), ()> { + fn poll_ready(&mut self, _cx: &mut task::Context) -> Poll<(), ()> { if self.in_flight.is_some() { - Ok(Async::NotReady) + Ok(Async::Pending) } else { Ok(Async::Ready(())) } @@ -395,16 +397,16 @@ where type PollBody = B; type RecvItem = ResponseHead; - fn poll_msg(&mut self) -> Poll)>, ::Error> { - match self.rx.poll() { + fn poll_msg(&mut self, cx: &mut task::Context) -> Poll)>, ::Error> { + match self.rx.poll_next(cx) { Ok(Async::Ready(Some((req, mut cb)))) => { // check that future hasn't been canceled already - match cb.poll_cancel().expect("poll_cancel cannot error") { + match cb.poll_cancel(cx).expect("poll_cancel cannot error") { Async::Ready(()) => { trace!("request canceled"); Ok(Async::Ready(None)) }, - Async::NotReady => { + Async::Pending => { let (parts, body) = req.into_parts(); let head = RequestHead { version: parts.version, @@ -427,12 +429,12 @@ where // user has dropped sender handle Ok(Async::Ready(None)) }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(_) => unreachable!("receiver cannot error"), } } - fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> { + fn recv_msg(&mut self, cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> { match msg { Ok((msg, body)) => { if let Some(cb) = self.callback.take() { @@ -450,7 +452,7 @@ where if let Some(cb) = self.callback.take() { let _ = cb.send(Err((err, None))); Ok(()) - } else if let Ok(Async::Ready(Some((req, cb)))) = self.rx.poll() { + } else if let Ok(Async::Ready(Some((req, cb)))) = self.rx.poll_next(cx) { trace!("canceling queued request with connection error: {}", err); // in this case, the message was never even started, so it's safe to tell // the user that the request was completely canceled @@ -463,14 +465,14 @@ where } } - fn poll_ready(&mut self) -> Poll<(), ()> { + fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()> { match self.callback { - Some(ref mut cb) => match cb.poll_cancel() { + Some(ref mut cb) => match cb.poll_cancel(cx) { Ok(Async::Ready(())) => { trace!("callback receiver has dropped"); Err(()) }, - Ok(Async::NotReady) => Ok(Async::Ready(())), + Ok(Async::Pending) => Ok(Async::Ready(())), Err(_) => unreachable!("oneshot poll_cancel cannot error"), }, None => Err(()), @@ -487,31 +489,32 @@ mod tests { extern crate pretty_env_logger; use super::*; + use futures::executor::block_on; + use futures::future::lazy; use mock::AsyncIo; use proto::ClientTransaction; #[test] fn client_read_bytes_before_writing_request() { let _ = pretty_env_logger::try_init(); - ::futures::lazy(|| { + block_on(lazy(|cx| { let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), 100); let (mut tx, rx) = ::client::dispatch::channel(); let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io); let mut dispatcher = Dispatcher::new(Client::new(rx), conn); - let res_rx = tx.try_send(::Request::new(::Body::empty())).unwrap(); + let mut res_rx = tx.try_send(::Request::new(::Body::empty())).unwrap(); - let a1 = dispatcher.poll().expect("error should be sent on channel"); + let a1 = dispatcher.poll(cx).expect("error should be sent on channel"); assert!(a1.is_ready(), "dispatcher should be closed"); - let err = res_rx.wait() - .expect("callback poll") - .expect_err("callback response"); + let result = res_rx.poll(cx) + .expect("callback poll"); - match err { - (::Error::Cancel(_), Some(_)) => (), - other => panic!("expected Canceled, got {:?}", other), + match result { + Async::Ready(Err((::Error::Cancel(_), Some(_)))) => (), + other => panic!("expected Err(Canceled), got {:?}", other), } - Ok::<(), ()>(()) - }).wait().unwrap(); + Ok::<_, ()>(()) + })).unwrap(); } } diff --git a/src/proto/h1/io.rs b/src/proto/h1/io.rs index 4b112a37..426a10c4 100644 --- a/src/proto/h1/io.rs +++ b/src/proto/h1/io.rs @@ -1,12 +1,12 @@ -use std::cell::Cell; use std::collections::VecDeque; use std::fmt; use std::io; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures::{Async, Poll}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use iovec::IoVec; -use tokio_io::{AsyncRead, AsyncWrite}; use proto::{Http1Transaction, MessageHead}; @@ -108,7 +108,7 @@ where } } - pub fn parse(&mut self) -> Poll, ::Error> { + pub fn parse(&mut self, cx: &mut task::Context) -> Poll, ::Error> { loop { match try!(S::parse(&mut self.read_buf)) { Some((head, len)) => { @@ -122,7 +122,7 @@ where } }, } - match try_ready!(self.read_from_io()) { + match try_ready!(self.read_from_io(cx)) { 0 => { trace!("parse eof"); return Err(::Error::Incomplete); @@ -132,21 +132,21 @@ where } } - pub fn read_from_io(&mut self) -> Poll { + pub fn read_from_io(&mut self, cx: &mut task::Context) -> Poll { use bytes::BufMut; self.read_blocked = false; if self.read_buf.remaining_mut() < INIT_BUFFER_SIZE { self.read_buf.reserve(INIT_BUFFER_SIZE); } - self.io.read_buf(&mut self.read_buf).map(|ok| { + read_buf(&mut self.io, cx, &mut self.read_buf).map(|ok| { match ok { Async::Ready(n) => { debug!("read {} bytes", n); Async::Ready(n) }, - Async::NotReady => { + Async::Pending => { self.read_blocked = true; - Async::NotReady + Async::Pending } } }) @@ -164,14 +164,14 @@ where self.read_blocked } - pub fn flush(&mut self) -> Poll<(), io::Error> { + pub fn flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { if self.flush_pipeline && !self.read_buf.is_empty() { //Ok(()) } else if self.write_buf.remaining() == 0 { - try_nb!(self.io.flush()); + try_ready!(self.io.poll_flush(cx)); } else { loop { - let n = try_ready!(self.io.write_buf(&mut self.write_buf.auto())); + let n = try_ready!(self.write_buf.poll_flush_into(&mut self.io, cx)); debug!("flushed {} bytes", n); if self.write_buf.remaining() == 0 { break; @@ -180,14 +180,33 @@ where return Err(io::ErrorKind::WriteZero.into()) } } - try_nb!(self.io.flush()) + try_ready!(self.io.poll_flush(cx)) } Ok(Async::Ready(())) } } +fn read_buf(io: &mut I, cx: &mut task::Context, buf: &mut B) -> Poll { + if !buf.has_remaining_mut() { + return Ok(Async::Ready(0)); + } + + unsafe { + let n = { + let b = buf.bytes_mut(); + + io.initializer().initialize(b); + + try_ready!(io.poll_read(cx, b)) + }; + + buf.advance_mut(n); + Ok(Async::Ready(n)) + } +} + pub trait MemRead { - fn read_mem(&mut self, len: usize) -> Poll; + fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll; } impl MemRead for Buffered @@ -195,12 +214,12 @@ where T: AsyncRead + AsyncWrite, B: Buf, { - fn read_mem(&mut self, len: usize) -> Poll { + fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll { if !self.read_buf.is_empty() { let n = ::std::cmp::min(len, self.read_buf.len()); Ok(Async::Ready(self.read_buf.split_to(n).freeze())) } else { - let n = try_ready!(self.read_from_io()); + let n = try_ready!(self.read_from_io(cx)); Ok(Async::Ready(self.read_buf.split_to(::std::cmp::min(len, n)).freeze())) } } @@ -294,11 +313,6 @@ where self.strategy = strategy; } - #[inline] - fn auto(&mut self) -> WriteBufAuto { - WriteBufAuto::new(self) - } - fn buffer(&mut self, buf: B) { match self.strategy { Strategy::Flatten => { @@ -343,6 +357,48 @@ where unreachable!("head_buf just pushed on back"); } } + + fn poll_flush_into(&mut self, io: &mut I, cx: &mut task::Context) -> Poll { + if !self.has_remaining() { + return Ok(Async::Ready(0)); + } + + let (num_bufs_avail, num_bytes_written, len_first_buf) = { + static PLACEHOLDER: &[u8] = &[0]; + let mut bufs = [From::from(PLACEHOLDER); 64]; + let num_bufs_avail = self.bytes_vec(&mut bufs[..]); + let num_bytes_written = try_ready!(io.poll_vectored_write(cx, &bufs[..num_bufs_avail])); + (num_bufs_avail, num_bytes_written, bufs[0].len()) + }; + self.advance(num_bytes_written); + + if let Strategy::Auto = self.strategy { + if num_bufs_avail > 1 { + // If there's more than one IoVec available, attempt to + // determine the best buffering strategy based on whether + // the underlying AsyncWrite object supports vectored I/O. + if num_bytes_written == len_first_buf { + // If only the first of many IoVec was written, we can assume + // with some certainty that vectored I/O _is not_ supported. + // + // Switch to a flattening strategy for buffering data. + trace!("detected no usage of vectored write, flattening"); + let mut vec = Vec::new(); + vec.put(&mut self.buf); + self.buf.bufs.push_back(VecOrBuf::Vec(Cursor::new(vec))); + self.strategy = Strategy::Flatten; + } else if num_bytes_written > len_first_buf { + // If more than the first IoVec was written, we can assume + // with some certainty that vectored I/O _is_ supported. + // + // Switch to a queuing strategy for buffering data. + self.strategy = Strategy::Queue; + } + } + } + + Ok(Async::Ready(num_bytes_written)) + } } impl fmt::Debug for WriteBuf { @@ -376,65 +432,6 @@ impl Buf for WriteBuf { } } -/// Detects when wrapped `WriteBuf` is used for vectored IO, and -/// adjusts the `WriteBuf` strategy if not. -struct WriteBufAuto<'a, B: Buf + 'a> { - bytes_called: Cell, - bytes_vec_called: Cell, - inner: &'a mut WriteBuf, -} - -impl<'a, B: Buf> WriteBufAuto<'a, B> { - fn new(inner: &'a mut WriteBuf) -> WriteBufAuto<'a, B> { - WriteBufAuto { - bytes_called: Cell::new(false), - bytes_vec_called: Cell::new(false), - inner: inner, - } - } -} - -impl<'a, B: Buf> Buf for WriteBufAuto<'a, B> { - #[inline] - fn remaining(&self) -> usize { - self.inner.remaining() - } - - #[inline] - fn bytes(&self) -> &[u8] { - self.bytes_called.set(true); - self.inner.bytes() - } - - #[inline] - fn advance(&mut self, cnt: usize) { - self.inner.advance(cnt) - } - - #[inline] - fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize { - self.bytes_vec_called.set(true); - self.inner.bytes_vec(dst) - } -} - -impl<'a, B: Buf + 'a> Drop for WriteBufAuto<'a, B> { - fn drop(&mut self) { - if let Strategy::Auto = self.inner.strategy { - if self.bytes_vec_called.get() { - self.inner.strategy = Strategy::Queue; - } else if self.bytes_called.get() { - trace!("detected no usage of vectored write, flattening"); - self.inner.strategy = Strategy::Flatten; - let mut vec = Vec::new(); - vec.put(&mut self.inner.buf); - self.inner.buf.bufs.push_back(VecOrBuf::Vec(Cursor::new(vec))); - } - } - } -} - - #[derive(Debug)] enum Strategy { Auto, @@ -568,51 +565,68 @@ impl Buf for BufDeque { mod tests { use super::*; use std::io::Read; + use futures::task; + use futures::future; + use futures::executor::block_on; + use futures::io::AsyncRead; use mock::AsyncIo; #[cfg(test)] impl MemRead for ::mock::AsyncIo { - fn read_mem(&mut self, len: usize) -> Poll { + fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll { let mut v = vec![0; len]; - let n = try_nb!(self.read(v.as_mut_slice())); + let n = try_ready!(self.poll_read(cx, v.as_mut_slice())); Ok(Async::Ready(BytesMut::from(&v[..n]).freeze())) } } #[test] fn iobuf_write_empty_slice() { - let mut mock = AsyncIo::new_buf(vec![], 256); - mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); + block_on(future::lazy(|cx| { + let mut mock = AsyncIo::new_buf(vec![], 256); + mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); - let mut io_buf = Buffered::<_, Cursor>>::new(mock); + let mut io_buf = Buffered::<_, Cursor>>::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"); + // 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(cx).expect("should short-circuit flush"); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn parse_reads_until_blocked() { - // missing last line ending - let raw = "HTTP/1.1 200 OK\r\n"; + block_on(future::lazy(|cx| { + // missing last line ending + let raw = "HTTP/1.1 200 OK\r\n"; - let mock = AsyncIo::new_buf(raw, raw.len()); - let mut buffered = Buffered::<_, Cursor>>::new(mock); - assert_eq!(buffered.parse::<::proto::ClientTransaction>().unwrap(), Async::NotReady); - assert!(buffered.io.blocked()); + let mock = AsyncIo::new_buf(raw, raw.len()); + let mut buffered = Buffered::<_, Cursor>>::new(mock); + + assert_eq!(buffered.parse::<::proto::ClientTransaction>(cx).unwrap(), Async::Pending); + assert!(buffered.io.blocked()); + + Ok::<_, ()>(()) + })).unwrap() } #[test] fn write_buf_skips_empty_bufs() { - let mut mock = AsyncIo::new_buf(vec![], 1024); - mock.max_read_vecs(0); // disable vectored IO - let mut buffered = Buffered::<_, Cursor>>::new(mock); + block_on(future::lazy(|cx| { + let mut mock = AsyncIo::new_buf(vec![], 1024); + mock.max_read_vecs(0); // disable vectored IO + let mut buffered = Buffered::<_, Cursor>>::new(mock); - buffered.buffer(Cursor::new(Vec::new())); - buffered.buffer(Cursor::new(b"hello".to_vec())); - buffered.flush().unwrap(); - assert_eq!(buffered.io, b"hello"); + buffered.buffer(Cursor::new(Vec::new())); + buffered.buffer(Cursor::new(b"hello".to_vec())); + buffered.flush(cx).unwrap(); + assert_eq!(buffered.io, b"hello"); + + Ok::<_, ()>(()) + })).unwrap() } #[test] @@ -620,17 +634,22 @@ mod tests { extern crate pretty_env_logger; let _ = pretty_env_logger::try_init(); - let mock = AsyncIo::new_buf(vec![], 1024); - let mut buffered = Buffered::<_, Cursor>>::new(mock); + block_on(future::lazy(|cx| { + let mock = AsyncIo::new_buf(vec![], 1024); + let mut buffered = Buffered::<_, Cursor>>::new(mock); - buffered.write_buf_mut().extend(b"hello "); - buffered.buffer(Cursor::new(b"world, ".to_vec())); - buffered.write_buf_mut().extend(b"it's "); - buffered.buffer(Cursor::new(b"hyper!".to_vec())); - buffered.flush().unwrap(); + buffered.write_buf_mut().extend(b"hello "); + buffered.buffer(Cursor::new(b"world, ".to_vec())); + buffered.write_buf_mut().extend(b"it's "); + buffered.buffer(Cursor::new(b"hyper!".to_vec())); + assert_eq!(buffered.io.num_writes(), 0); + buffered.flush(cx).unwrap(); - assert_eq!(buffered.io, b"hello world, it's hyper!"); - assert_eq!(buffered.io.num_writes(), 1); + assert_eq!(buffered.io, b"hello world, it's hyper!"); + assert_eq!(buffered.io.num_writes(), 1); + + Ok::<_, ()>(()) + })).unwrap() } #[test] @@ -638,27 +657,31 @@ mod tests { extern crate pretty_env_logger; let _ = pretty_env_logger::try_init(); - let mock = AsyncIo::new_buf(vec![], 1024); - let mut buffered = Buffered::<_, Cursor>>::new(mock); + block_on(future::lazy(|cx| { + let mock = AsyncIo::new_buf(vec![], 1024); + let mut buffered = Buffered::<_, Cursor>>::new(mock); - buffered.write_buf_mut().extend(b"hello "); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); - buffered.write_buf_mut().extend(b"world, "); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + buffered.write_buf_mut().extend(b"hello "); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + buffered.write_buf_mut().extend(b"world, "); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); - // after flushing, reclaim the Vec - buffered.flush().unwrap(); - assert_eq!(buffered.write_buf.remaining(), 0); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + // after flushing, reclaim the Vec + buffered.flush(cx).unwrap(); + assert_eq!(buffered.write_buf.remaining(), 0); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); - // add a user buf in the way - buffered.buffer(Cursor::new(b"it's ".to_vec())); - // and then add more hyper bytes - buffered.write_buf_mut().extend(b"hyper!"); - buffered.flush().unwrap(); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + // add a user buf in the way + buffered.buffer(Cursor::new(b"it's ".to_vec())); + // and then add more hyper bytes + buffered.write_buf_mut().extend(b"hyper!"); + buffered.flush(cx).unwrap(); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); - assert_eq!(buffered.io, b"hello world, it's hyper!"); + assert_eq!(buffered.io, b"hello world, it's hyper!"); + + Ok::<_, ()>(()) + })).unwrap() } #[test] @@ -666,21 +689,25 @@ mod tests { extern crate pretty_env_logger; let _ = pretty_env_logger::try_init(); - let mock = AsyncIo::new_buf(vec![], 1024); - let mut buffered = Buffered::<_, Cursor>>::new(mock); - buffered.write_buf.set_strategy(Strategy::Flatten); + block_on(future::lazy(|cx| { + let mock = AsyncIo::new_buf(vec![], 1024); + let mut buffered = Buffered::<_, Cursor>>::new(mock); + buffered.write_buf.set_strategy(Strategy::Flatten); - buffered.write_buf_mut().extend(b"hello "); - buffered.buffer(Cursor::new(b"world, ".to_vec())); - buffered.write_buf_mut().extend(b"it's "); - buffered.buffer(Cursor::new(b"hyper!".to_vec())); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + buffered.write_buf_mut().extend(b"hello "); + buffered.buffer(Cursor::new(b"world, ".to_vec())); + buffered.write_buf_mut().extend(b"it's "); + buffered.buffer(Cursor::new(b"hyper!".to_vec())); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); - buffered.flush().unwrap(); + buffered.flush(cx).unwrap(); - assert_eq!(buffered.io, b"hello world, it's hyper!"); - assert_eq!(buffered.io.num_writes(), 1); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + assert_eq!(buffered.io, b"hello world, it's hyper!"); + assert_eq!(buffered.io.num_writes(), 1); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + + Ok::<_, ()>(()) + })).unwrap() } #[test] @@ -688,22 +715,26 @@ mod tests { extern crate pretty_env_logger; let _ = pretty_env_logger::try_init(); - let mut mock = AsyncIo::new_buf(vec![], 1024); - mock.max_read_vecs(0); // disable vectored IO - let mut buffered = Buffered::<_, Cursor>>::new(mock); + block_on(future::lazy(|cx| { + let mut mock = AsyncIo::new_buf(vec![], 1024); + mock.max_read_vecs(0); // disable vectored IO + let mut buffered = Buffered::<_, Cursor>>::new(mock); - // we have 4 buffers, but hope to detect that vectored IO isn't - // being used, and switch to flattening automatically, - // resulting in only 2 writes - buffered.write_buf_mut().extend(b"hello "); - buffered.buffer(Cursor::new(b"world, ".to_vec())); - buffered.write_buf_mut().extend(b"it's hyper!"); - //buffered.buffer(Cursor::new(b"hyper!".to_vec())); - buffered.flush().unwrap(); + // we have 4 buffers, but hope to detect that vectored IO isn't + // being used, and switch to flattening automatically, + // resulting in only 2 writes + buffered.write_buf_mut().extend(b"hello "); + buffered.buffer(Cursor::new(b"world, ".to_vec())); + buffered.write_buf_mut().extend(b"it's hyper!"); + //buffered.buffer(Cursor::new(b"hyper!".to_vec())); + buffered.flush(cx).unwrap(); - assert_eq!(buffered.io, b"hello world, it's hyper!"); - assert_eq!(buffered.io.num_writes(), 2); - assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + assert_eq!(buffered.io, b"hello world, it's hyper!"); + assert_eq!(buffered.io.num_writes(), 2); + assert_eq!(buffered.write_buf.buf.bufs.len(), 1); + + Ok::<_, ()>(()) + })).unwrap() } #[test] @@ -711,20 +742,24 @@ mod tests { extern crate pretty_env_logger; let _ = pretty_env_logger::try_init(); - let mut mock = AsyncIo::new_buf(vec![], 1024); - mock.max_read_vecs(0); // disable vectored IO - let mut buffered = Buffered::<_, Cursor>>::new(mock); - buffered.write_buf.set_strategy(Strategy::Queue); + block_on(future::lazy(move |cx| { + let mut mock = AsyncIo::new_buf(vec![], 1024); + mock.max_read_vecs(0); // disable vectored IO + let mut buffered = Buffered::<_, Cursor>>::new(mock); + buffered.write_buf.set_strategy(Strategy::Queue); - // we have 4 buffers, and vec IO disabled, but explicitly said - // don't try to auto detect (via setting strategy above) - buffered.write_buf_mut().extend(b"hello "); - buffered.buffer(Cursor::new(b"world, ".to_vec())); - buffered.write_buf_mut().extend(b"it's "); - buffered.buffer(Cursor::new(b"hyper!".to_vec())); - buffered.flush().unwrap(); + // we have 4 buffers, and vec IO disabled, but explicitly said + // don't try to auto detect (via setting strategy above) + buffered.write_buf_mut().extend(b"hello "); + buffered.buffer(Cursor::new(b"world, ".to_vec())); + buffered.write_buf_mut().extend(b"it's "); + buffered.buffer(Cursor::new(b"hyper!".to_vec())); + buffered.flush(cx).unwrap(); - assert_eq!(buffered.io, b"hello world, it's hyper!"); - assert_eq!(buffered.io.num_writes(), 4); + assert_eq!(buffered.io, b"hello world, it's hyper!"); + assert_eq!(buffered.io.num_writes(), 4); + + Ok::<_, ()>(()) + })).unwrap() } } diff --git a/src/server/conn.rs b/src/server/conn.rs index 4b34351c..22475b02 100644 --- a/src/server/conn.rs +++ b/src/server/conn.rs @@ -12,7 +12,8 @@ use std::fmt; use bytes::Bytes; use futures::{Future, Poll}; -use tokio_io::{AsyncRead, AsyncWrite}; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use proto; use proto::body::{Body, Entity}; @@ -89,8 +90,8 @@ where S: Service, Response = Response, Error = ::Erro /// upgrade. Once the upgrade is completed, the connection would be "done", /// but it is not desired to actally shutdown the IO object. Instead you /// would take it back using `into_parts`. - pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> { - try_ready!(self.conn.poll_without_shutdown()); + pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + try_ready!(self.conn.poll_without_shutdown(cx)); Ok(().into()) } } @@ -103,8 +104,8 @@ where S: Service, Response = Response, Error = ::Erro type Item = (); type Error = ::Error; - fn poll(&mut self) -> Poll { - self.conn.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll { + self.conn.poll(cx) } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 30ca9ebb..e4ddf247 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,7 +4,6 @@ //! them off to a `Service`. pub mod conn; -mod service; use std::fmt; use std::io; @@ -13,16 +12,15 @@ use std::net::{SocketAddr, TcpListener as StdTcpListener}; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; -use futures::task::{self, Task}; +use futures::task::{self, Waker}; use futures::future::{self}; -use futures::{Future, Stream, Poll, Async}; +use futures::{Future, FutureExt, Stream, StreamExt, Poll, Async}; +use futures::io::{AsyncRead, AsyncWrite}; +use futures::executor::spawn; use futures_timer::Delay; use http::{Request, Response}; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio::spawn; use tokio::reactor::Handle; use tokio::net::TcpListener; -pub use tokio_service::{NewService, Service}; use proto::body::{Body, Entity}; use proto; @@ -30,7 +28,8 @@ use self::addr_stream::AddrStream; use self::hyper_service::HyperService; pub use self::conn::Connection; -pub use self::service::{const_service, service_fn}; +pub use super::service::{NewService, Service}; +pub use super::service::{const_service, service_fn}; /// A configuration of the HTTP protocol. /// @@ -255,11 +254,10 @@ impl + 'static> Http { /// # extern crate futures; /// # extern crate hyper; /// # extern crate tokio; - /// # extern crate tokio_io; - /// # use futures::Future; + /// # use futures::FutureExt; + /// # use futures::io::{AsyncRead, AsyncWrite}; /// # use hyper::{Body, Request, Response}; /// # use hyper::server::{Http, Service}; - /// # use tokio_io::{AsyncRead, AsyncWrite}; /// # use tokio::reactor::Handle; /// # fn run(some_io: I, some_service: S) /// # where @@ -272,9 +270,9 @@ impl + 'static> Http { /// /// let fut = conn /// .map(|_| ()) - /// .map_err(|e| eprintln!("server connection error: {}", e)); + /// .map_err(|e| panic!("server connection error: {}", e)); /// - /// tokio::spawn(fut); + /// tokio::spawn2(fut); /// # } /// # fn main() {} /// ``` @@ -334,8 +332,8 @@ impl Future for Run { type Item = (); type Error = ::Error; - fn poll(&mut self) -> Poll<(), ::Error> { - self.0.poll() + fn poll(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> { + self.0.poll(cx) } } @@ -412,17 +410,21 @@ impl Server let addr = socket.remote_addr; debug!("accepted new connection ({})", addr); - let service = new_service.new_service()?; + let service = match new_service.new_service() { + Ok(service) => service, + Err(err) => return future::err(err).left() + }; + let s = NotifyService { inner: service, info: Arc::downgrade(&info_cloned), }; info_cloned.lock().unwrap().active += 1; - let fut = protocol.serve_connection(socket, s) - .map(|_| ()) - .map_err(move |err| error!("server connection error: ({}) {}", addr, err)); - spawn(fut); - Ok(()) + let fut = protocol.serve_connection(socket, s).recover(move |err| { + error!("server connection error: ({}) {}", addr, err); + }); + + spawn(fut).map_err(|err| err.never_into()).right() }); // for now, we don't care if the shutdown signal succeeds or errors @@ -438,8 +440,11 @@ impl Server // stop accepting incoming connections. let main_execution = shutdown_signal.select(srv).then(move |result| { match result { - Ok(((), _incoming)) => {}, - Err((e, _other)) => return future::Either::A(future::err(e.into())) + Ok(_) => {}, + Err(future::Either::Left((e, _other))) => + return future::Either::Left(future::err(e)), + Err(future::Either::Right((e, _other))) => + return future::Either::Left(future::err(e.into())), } // Ok we've stopped accepting new connections at this point, but we want @@ -451,10 +456,11 @@ impl Server // here have been destroyed. let timeout = Delay::new(shutdown_timeout); let wait = WaitUntilZero { info: info.clone() }; - future::Either::B(wait.select(timeout).then(|result| { + future::Either::Right(wait.select(timeout).then(|result| { match result { Ok(_) => Ok(()), - Err((e, _)) => Err(e.into()) + Err(future::Either::Left((e, _))) => Err(e.into()), + Err(future::Either::Right((e, _))) => Err(e.into()) } })) }); @@ -505,8 +511,8 @@ where type Item = Connection; type Error = ::Error; - fn poll(&mut self) -> Poll, Self::Error> { - if let Some(io) = try_ready!(self.incoming.poll()) { + fn poll_next(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { + if let Some(io) = try_ready!(self.incoming.poll_next(cx)) { let service = self.new_service.new_service()?; Ok(Async::Ready(Some(self.protocol.serve_connection(io, service)))) } else { @@ -586,17 +592,17 @@ impl Stream for AddrIncoming { type Item = AddrStream; type Error = ::std::io::Error; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(&mut self, cx: &mut task::Context) -> Poll, Self::Error> { // Check if a previous timeout is active that was set by IO errors. if let Some(ref mut to) = self.timeout { - match to.poll().expect("timeout never fails") { + match to.poll(cx).expect("timeout never fails") { Async::Ready(_) => {} - Async::NotReady => return Ok(Async::NotReady), + Async::Pending => return Ok(Async::Pending), } } self.timeout = None; loop { - match self.listener.poll_accept() { + match self.listener.poll_accept2(cx) { Ok(Async::Ready((socket, addr))) => { if let Some(dur) = self.keep_alive_timeout { if let Err(e) = socket.set_keepalive(Some(dur)) { @@ -605,7 +611,7 @@ impl Stream for AddrIncoming { } return Ok(Async::Ready(Some(AddrStream::new(socket, addr)))); }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Ok(Async::Pending) => return Ok(Async::Pending), Err(ref e) if self.sleep_on_errors => { // Connection errors can be ignored directly, continue by // accepting the next request. @@ -617,13 +623,13 @@ impl Stream for AddrIncoming { debug!("accept error: {}; sleeping {:?}", e, delay); let mut timeout = Delay::new(delay); - let result = timeout.poll() + let result = timeout.poll(cx) .expect("timeout never fails"); match result { Async::Ready(()) => continue, - Async::NotReady => { + Async::Pending => { self.timeout = Some(timeout); - return Ok(Async::NotReady); + return Ok(Async::Pending); } } }, @@ -647,12 +653,13 @@ fn connection_error(e: &io::Error) -> bool { } mod addr_stream { - use std::io::{self, Read, Write}; + use std::io; use std::net::SocketAddr; - use bytes::{Buf, BufMut}; use futures::Poll; + use futures::task; + use futures::io::{AsyncRead, AsyncWrite, Initializer}; + use iovec::IoVec; use tokio::net::TcpStream; - use tokio_io::{AsyncRead, AsyncWrite}; #[derive(Debug)] @@ -670,46 +677,42 @@ mod addr_stream { } } - impl Read for AddrStream { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - } - - impl Write for AddrStream { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - - #[inline] - fn flush(&mut self ) -> io::Result<()> { - self.inner.flush() - } - } - impl AsyncRead for AddrStream { #[inline] - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll { + self.inner.poll_read(cx, buf) } #[inline] - fn read_buf(&mut self, buf: &mut B) -> Poll { - self.inner.read_buf(buf) + unsafe fn initializer(&self) -> Initializer { + AsyncRead::initializer(&self.inner) + } + + #[inline] + fn poll_vectored_read(&mut self, cx: &mut task::Context, vec: &mut [&mut IoVec]) -> Poll { + self.inner.poll_vectored_read(cx, vec) } } impl AsyncWrite for AddrStream { #[inline] - fn shutdown(&mut self) -> Poll<(), io::Error> { - AsyncWrite::shutdown(&mut self.inner) + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll { + self.inner.poll_write(cx, buf) } #[inline] - fn write_buf(&mut self, buf: &mut B) -> Poll { - self.inner.write_buf(buf) + fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + self.inner.poll_flush(cx) + } + + #[inline] + fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + self.inner.poll_close(cx) + } + + #[inline] + fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) -> Poll { + self.inner.poll_vectored_write(cx, vec) } } } @@ -727,7 +730,7 @@ struct WaitUntilZero { struct Info { active: usize, - blocker: Option, + blocker: Option, } impl Service for NotifyService { @@ -750,8 +753,8 @@ impl Drop for NotifyService { let mut info = info.lock().unwrap(); info.active -= 1; if info.active == 0 { - if let Some(task) = info.blocker.take() { - task.notify(); + if let Some(waker) = info.blocker.take() { + waker.wake(); } } } @@ -761,13 +764,13 @@ impl Future for WaitUntilZero { type Item = (); type Error = io::Error; - fn poll(&mut self) -> Poll<(), io::Error> { + fn poll(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { let mut info = self.info.lock().unwrap(); if info.active == 0 { Ok(().into()) } else { - info.blocker = Some(task::current()); - Ok(Async::NotReady) + info.blocker = Some(cx.waker().clone()); + Ok(Async::Pending) } } } diff --git a/src/server/service.rs b/src/server/service.rs deleted file mode 100644 index 20c9812e..00000000 --- a/src/server/service.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::marker::PhantomData; -use std::sync::Arc; - -use futures::IntoFuture; -use tokio_service::{NewService, Service}; - -/// Create a `Service` from a function. -pub fn service_fn(f: F) -> ServiceFn -where - F: Fn(R) -> S, - S: IntoFuture, -{ - ServiceFn { - f: f, - _req: PhantomData, - } -} - -/// Create a `NewService` by sharing references of `service. -pub fn const_service(service: S) -> ConstService { - ConstService { - svc: Arc::new(service), - } -} - -#[derive(Debug)] -pub struct ServiceFn { - f: F, - _req: PhantomData R>, -} - -impl Service for ServiceFn -where - F: Fn(R) -> S, - S: IntoFuture, -{ - type Request = R; - type Response = S::Item; - type Error = S::Error; - type Future = S::Future; - - fn call(&self, req: Self::Request) -> Self::Future { - (self.f)(req).into_future() - } -} - -#[derive(Debug)] -pub struct ConstService { - svc: Arc, -} - -impl NewService for ConstService -where - S: Service, -{ - type Request = S::Request; - type Response = S::Response; - type Error = S::Error; - type Instance = Arc; - - fn new_service(&self) -> ::std::io::Result { - Ok(self.svc.clone()) - } -} diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 00000000..383152e8 --- /dev/null +++ b/src/service.rs @@ -0,0 +1,116 @@ +use std::marker::PhantomData; +use std::sync::Arc; + +use futures::{Future, IntoFuture}; + +/// An asynchronous function from `Request` to a `Response`. +pub trait Service { + /// Requests handled by the service. + type Request; + /// Responses given by the service. + type Response; + /// Errors produced by the service. + type Error; + /// The future response value. + type Future: Future; + /// Process the request and return the response asynchronously. + fn call(&self, req: Self::Request) -> Self::Future; +} + +impl Service for Arc { + type Request = S::Request; + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn call(&self, request: S::Request) -> S::Future { + (**self).call(request) + } +} + +/// Creates new `Service` values. +pub trait NewService { + /// Requests handled by the service. + type Request; + /// Responses given by the service. + type Response; + /// Errors produced by the service. + type Error; + /// The `Service` value created by this factory. + type Instance: Service; + /// Create and return a new service value. + fn new_service(&self) -> ::std::io::Result; +} + +impl NewService for F + where F: Fn() -> ::std::io::Result, + R: Service, +{ + type Request = R::Request; + type Response = R::Response; + type Error = R::Error; + type Instance = R; + + fn new_service(&self) -> ::std::io::Result { + (*self)() + } +} + +/// Create a `Service` from a function. +pub fn service_fn(f: F) -> ServiceFn +where + F: Fn(R) -> S, + S: IntoFuture, +{ + ServiceFn { + f: f, + _req: PhantomData, + } +} + +/// Create a `NewService` by sharing references of `service. +pub fn const_service(service: S) -> ConstService { + ConstService { + svc: Arc::new(service), + } +} + +#[derive(Debug)] +pub struct ServiceFn { + f: F, + _req: PhantomData R>, +} + +impl Service for ServiceFn +where + F: Fn(R) -> S, + S: IntoFuture, +{ + type Request = R; + type Response = S::Item; + type Error = S::Error; + type Future = S::Future; + + fn call(&self, req: Self::Request) -> Self::Future { + (self.f)(req).into_future() + } +} + +#[derive(Debug)] +pub struct ConstService { + svc: Arc, +} + +impl NewService for ConstService +where + S: Service, +{ + type Request = S::Request; + type Response = S::Response; + type Error = S::Error; + type Instance = Arc; + + fn new_service(&self) -> ::std::io::Result { + Ok(self.svc.clone()) + } +} diff --git a/tests/client.rs b/tests/client.rs index 33f0f2e3..bc5d82fe 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -5,7 +5,6 @@ extern crate futures; extern crate futures_timer; extern crate net2; extern crate tokio; -extern crate tokio_io; extern crate pretty_env_logger; use std::io::{self, Read, Write}; @@ -15,8 +14,9 @@ use std::time::Duration; use hyper::{Body, Client, Method, Request, StatusCode}; -use futures::{Future, Stream}; -use futures::sync::oneshot; +use futures::{FutureExt, StreamExt}; +use futures::channel::oneshot; +use futures::executor::block_on; use tokio::reactor::Handle; use tokio::runtime::Runtime; use tokio::net::{ConnectFuture, TcpStream}; @@ -111,11 +111,7 @@ macro_rules! test { assert_eq!(res.headers()[$response_header_name], $response_header_val); )* - let body = res - .into_body() - .into_stream() - .concat2() - .wait() + let body = block_on(res.into_body().into_stream().concat()) .expect("body concat wait"); let expected_res_body = Option::<&[u8]>::from($response_body) @@ -186,7 +182,7 @@ macro_rules! test { if !$set_host { config = config.set_host(false); } - let client = config.build_with_executor(&runtime.handle(), runtime.executor()); + let client = config.build(&runtime.handle()); let body = if let Some(body) = $request_body { let body: &'static str = body; @@ -203,7 +199,7 @@ macro_rules! test { .body(body) .expect("request builder"); - let res = client.request(req); + let res = client.request(req).with_executor(runtime.executor()); let (tx, rx) = oneshot::channel(); @@ -230,7 +226,7 @@ macro_rules! test { let rx = rx.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait() + block_on(res.join(rx).map(|r| r.0)) }); } @@ -642,12 +638,15 @@ mod dispatch_impl { use std::thread; use std::time::Duration; - use futures::{self, Future}; - use futures::sync::{mpsc, oneshot}; + use futures::{self, Future, FutureExt, Stream, StreamExt}; + use futures::channel::{mpsc, oneshot}; + use futures::io::{AsyncRead, AsyncWrite}; + use futures::future::lazy; + use futures::executor::block_on; + use futures::task; use futures_timer::Delay; use tokio::net::TcpStream; use tokio::runtime::Runtime; - use tokio_io::{AsyncRead, AsyncWrite}; use hyper::client::connect::{Connect, Connected, Destination, HttpConnector}; use hyper::Client; @@ -666,7 +665,7 @@ mod dispatch_impl { let (closes_tx, closes) = mpsc::channel(10); let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &runtime.handle()), closes_tx)) - .executor(runtime.executor()); + .build(); let (tx1, rx1) = oneshot::channel(); @@ -686,15 +685,15 @@ mod dispatch_impl { .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); Delay::new(Duration::from_secs(1)) - .from_err() + .err_into() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); - closes.into_future().wait().unwrap().0.expect("closes"); + block_on(closes.next()).unwrap().0.expect("closes"); } #[test] @@ -725,25 +724,25 @@ mod dispatch_impl { let res = { let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - client.request(req).and_then(move |res| { + client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }).and_then(|_| { Delay::new(Duration::from_secs(1)) - .from_err() + .err_into() }) }; // client is dropped let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); - closes.into_future().wait().unwrap().0.expect("closes"); + block_on(closes.next()).unwrap().0.expect("closes"); } @@ -773,41 +772,41 @@ mod dispatch_impl { // prevent this thread from closing until end of test, so the connection // stays open and idle until Client is dropped - let _ = client_drop_rx.wait(); + let _ = block_on(client_drop_rx); }); let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); // not closed yet, just idle { - futures::future::poll_fn(|| { - assert!(closes.poll()?.is_not_ready()); - Ok::<_, ()>(().into()) - }).wait().unwrap(); + block_on(lazy(|cx| { + assert!(closes.poll_next(cx).unwrap().is_pending()); + Ok::<_, ()>(()) + })).unwrap(); } drop(client); let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } @@ -836,32 +835,32 @@ mod dispatch_impl { // prevent this thread from closing until end of test, so the connection // stays open and idle until Client is dropped - let _ = client_drop_rx.wait(); + let _ = block_on(client_drop_rx); }); let res = { let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - client.request(req) + client.request(req).with_executor(runtime.executor()) }; //let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.select2(rx1).wait().unwrap(); + block_on(res.select(rx1)).unwrap(); // res now dropped let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } #[test] @@ -888,33 +887,33 @@ mod dispatch_impl { // prevent this thread from closing until end of test, so the connection // stays open and idle until Client is dropped - let _ = client_drop_rx.wait(); + let _ = block_on(client_drop_rx); }); let res = { let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); // notably, havent read body yet - client.request(req) + client.request(req).with_executor(runtime.executor()) }; let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } #[test] @@ -939,33 +938,33 @@ mod dispatch_impl { sock.read(&mut buf).expect("read 1"); sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap(); let _ = tx1.send(()); - let _ = rx2.wait(); + let _ = block_on(rx2); }); let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) .keep_alive(false) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } #[test] @@ -993,28 +992,28 @@ mod dispatch_impl { let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } #[test] @@ -1026,11 +1025,13 @@ mod dispatch_impl { // See https://github.com/hyperium/hyper/issues/1429 use std::error::Error; + use tokio::prelude::Future; let _ = pretty_env_logger::try_init(); let server = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = server.local_addr().unwrap(); let runtime = Runtime::new().unwrap(); + let executor = runtime.executor(); let handle = runtime.handle().clone(); let (tx1, rx1) = oneshot::channel(); @@ -1042,27 +1043,27 @@ mod dispatch_impl { 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").unwrap(); - sock.read(&mut buf).expect("read 2"); + //sock.read(&mut buf).expect("read 2"); let _ = tx1.send(()); }); let uri = format!("http://{}/a", addr).parse::().unwrap(); - let client = Client::configure().build_with_executor(&handle, runtime.executor()); + let client = Client::configure().build(&handle); let req = Request::builder() .uri(uri.clone()) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(executor.clone()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); - res.wait().unwrap(); + block_on(res).unwrap(); - // drop previous runtime - drop(runtime); + // shut down runtime + runtime.shutdown_now().wait().unwrap(); let timeout = Delay::new(Duration::from_millis(200)); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); @@ -1071,10 +1072,10 @@ mod dispatch_impl { .uri(uri) .body(Body::empty()) .unwrap(); - let res = client.request(req); + let res = client.request(req).with_executor(executor); // this does trigger an 'event loop gone' error, but before, it would // panic internally on a `SendError`, which is what we're testing against. - let err = res.join(rx).map(|r| r.0).wait().unwrap_err(); + let err = block_on(res.join(rx).map(|r| r.0)).unwrap_err(); assert_eq!(err.description(), "event loop gone"); } @@ -1100,31 +1101,31 @@ mod dispatch_impl { let client = Client::configure() .connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx)) - .executor(runtime.executor()); + .build(); let req = Request::builder() .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req).and_then(move |res| { + let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); let t = Delay::new(Duration::from_millis(100)) .map(|_| panic!("time out")); - let close = closes.into_future() + let close = closes.next() .map(|(opt, _)| { opt.expect("closes"); }) .map_err(|_| panic!("closes dropped")); - let _ = t.select(close).wait(); + let _ = block_on(t.select(close)); } #[test] @@ -1140,14 +1141,14 @@ mod dispatch_impl { let client = Client::configure() .connector(connector) - .executor(runtime.executor()); + .build(); assert_eq!(connects.load(Ordering::Relaxed), 0); let req = Request::builder() .uri("http://hyper.local/a") .body(Default::default()) .unwrap(); - let _fut = client.request(req); + let _fut = client.request(req).with_executor(runtime.executor()); // internal Connect::connect should have been lazy, and not // triggered an actual connect yet. assert_eq!(connects.load(Ordering::Relaxed), 0); @@ -1165,7 +1166,7 @@ mod dispatch_impl { let client = Client::configure() .connector(connector) - .executor(runtime.executor()); + .build(); let (tx1, rx1) = oneshot::channel(); let (tx2, rx2) = oneshot::channel(); @@ -1195,8 +1196,8 @@ mod dispatch_impl { .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req); - res.join(rx).map(|r| r.0).wait().unwrap(); + let res = client.request(req).with_executor(runtime.executor()); + block_on(res.join(rx).map(|r| r.0)).unwrap(); assert_eq!(connects.load(Ordering::SeqCst), 1); @@ -1209,8 +1210,8 @@ mod dispatch_impl { .uri(&*format!("http://{}/b", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req); - res.join(rx).map(|r| r.0).wait().unwrap(); + let res = client.request(req).with_executor(runtime.executor()); + block_on(res.join(rx).map(|r| r.0)).unwrap(); assert_eq!(connects.load(Ordering::SeqCst), 1, "second request should still only have 1 connect"); } @@ -1228,7 +1229,7 @@ mod dispatch_impl { let client = Client::configure() .connector(connector) - .executor(runtime.executor()); + .build(); let (tx1, rx1) = oneshot::channel(); let (tx2, rx2) = oneshot::channel(); @@ -1261,8 +1262,8 @@ mod dispatch_impl { .uri(&*format!("http://{}/a", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req); - res.join(rx).map(|r| r.0).wait().unwrap(); + let res = client.request(req).with_executor(runtime.executor()); + block_on(res.join(rx).map(|r| r.0)).unwrap(); assert_eq!(connects.load(Ordering::Relaxed), 1); @@ -1271,8 +1272,8 @@ mod dispatch_impl { .uri(&*format!("http://{}/b", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req); - res.join(rx).map(|r| r.0).wait().unwrap(); + let res = client.request(req).with_executor(runtime.executor()); + block_on(res.join(rx).map(|r| r.0)).unwrap(); assert_eq!(connects.load(Ordering::Relaxed), 2); } @@ -1289,7 +1290,7 @@ mod dispatch_impl { let client = Client::configure() .connector(connector) - .executor(runtime.executor()); + .build(); let (tx1, rx1) = oneshot::channel(); thread::spawn(move || { @@ -1312,8 +1313,8 @@ mod dispatch_impl { .uri(&*format!("http://{}/foo/bar", addr)) .body(Body::empty()) .unwrap(); - let res = client.request(req); - res.join(rx).map(|r| r.0).wait().unwrap(); + let res = client.request(req).with_executor(runtime.executor()); + block_on(res.join(rx).map(|r| r.0)).unwrap(); } @@ -1369,33 +1370,25 @@ mod dispatch_impl { } } - impl Write for DebugStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() - } - } - impl AsyncWrite for DebugStream { - fn shutdown(&mut self) -> futures::Poll<(), io::Error> { - AsyncWrite::shutdown(&mut self.0) + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> futures::Poll { + self.0.poll_write(cx, buf) } - fn write_buf(&mut self, buf: &mut B) -> futures::Poll { - self.0.write_buf(buf) + fn poll_flush(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> { + self.0.poll_flush(cx) + } + + fn poll_close(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> { + self.0.poll_close(cx) } } - impl Read for DebugStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) + impl AsyncRead for DebugStream { + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> futures::Poll { + self.0.poll_read(cx, buf) } } - - impl AsyncRead for DebugStream {} } mod conn { @@ -1404,13 +1397,15 @@ mod conn { use std::thread; use std::time::Duration; - use futures::{Async, Future, Poll, Stream}; - use futures::future::poll_fn; - use futures::sync::oneshot; + use futures::{FutureExt, Poll, StreamExt}; + use futures::future::{poll_fn, lazy}; + use futures::channel::oneshot; + use futures::task; + use futures::io::{AsyncRead, AsyncWrite}; + use futures::executor::block_on; use futures_timer::Delay; use tokio::runtime::Runtime; use tokio::net::TcpStream; - use tokio_io::{AsyncRead, AsyncWrite}; use hyper::{self, Request}; use hyper::client::conn; @@ -1442,11 +1437,11 @@ mod conn { let _ = tx1.send(()); }); - let tcp = tcp_connect(&addr).wait().unwrap(); + let tcp = block_on(tcp_connect(&addr)).unwrap(); - let (mut client, conn) = conn::handshake(tcp).wait().unwrap(); + let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap(); - runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); + runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); let req = Request::builder() .uri("/a") @@ -1454,13 +1449,13 @@ mod conn { .unwrap(); let res = client.send_request(req).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); } #[test] @@ -1487,11 +1482,11 @@ mod conn { let _ = tx1.send(()); }); - let tcp = tcp_connect(&addr).wait().unwrap(); + let tcp = block_on(tcp_connect(&addr)).unwrap(); - let (mut client, conn) = conn::handshake(tcp).wait().unwrap(); + let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap(); - runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); + runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); let req = Request::builder() .uri("http://hyper.local/a") @@ -1500,13 +1495,13 @@ mod conn { let res = client.send_request(req).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - res.join(rx).map(|r| r.0).wait().unwrap(); + block_on(res.join(rx).map(|r| r.0)).unwrap(); } #[test] @@ -1528,11 +1523,11 @@ mod conn { let _ = tx1.send(()); }); - let tcp = tcp_connect(&addr).wait().unwrap(); + let tcp = block_on(tcp_connect(&addr)).unwrap(); - let (mut client, conn) = conn::handshake(tcp).wait().unwrap(); + let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap(); - runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); + runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e))); let req = Request::builder() .uri("/a") @@ -1540,10 +1535,10 @@ mod conn { .unwrap(); let res1 = client.send_request(req).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); - // pipelined request will hit NotReady, and thus should return an Error::Cancel + // pipelined request will hit Pending, and thus should return an Error::Cancel let req = Request::builder() .uri("/b") .body(Default::default()) @@ -1562,12 +1557,12 @@ mod conn { let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - res1.join(res2).join(rx).map(|r| r.0).wait().unwrap(); + block_on(res1.join(res2).join(rx).map(|r| r.0)).unwrap(); } #[test] fn upgrade() { - use tokio_io::io::{read_to_end, write_all}; + use futures::io::{AsyncReadExt, AsyncWriteExt}; let _ = ::pretty_env_logger::try_init(); let server = TcpListener::bind("127.0.0.1:0").unwrap(); @@ -1595,18 +1590,18 @@ mod conn { sock.write_all(b"bar=foo").expect("write 2"); }); - let tcp = tcp_connect(&addr).wait().unwrap(); + let tcp = block_on(tcp_connect(&addr)).unwrap(); let io = DebugStream { tcp: tcp, shutdown_called: false, }; - let (mut client, mut conn) = conn::handshake(io).wait().unwrap(); + let (mut client, mut conn) = block_on(conn::handshake(io)).unwrap(); { - let until_upgrade = poll_fn(|| { - conn.poll_without_shutdown() + let until_upgrade = poll_fn(|cx| { + conn.poll_without_shutdown(cx) }); let req = Request::builder() @@ -1616,20 +1611,20 @@ mod conn { let res = client.send_request(req).and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::SWITCHING_PROTOCOLS); assert_eq!(res.headers()["Upgrade"], "foobar"); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }); let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked"))); let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - until_upgrade.join(res).join(rx).map(|r| r.0).wait().unwrap(); + block_on(until_upgrade.join(res).join(rx).map(|r| r.0)).unwrap(); // should not be ready now - poll_fn(|| { - assert!(client.poll_ready().unwrap().is_not_ready()); - Ok::<_, ()>(Async::Ready(())) - }).wait().unwrap(); + block_on(lazy(|cx| { + assert!(client.poll_ready(cx).unwrap().is_pending()); + Ok::<_, ()>(()) + })).unwrap(); } let parts = conn.into_parts(); @@ -1638,16 +1633,16 @@ mod conn { assert_eq!(buf, b"foobar=ready"[..]); assert!(!io.shutdown_called, "upgrade shouldn't shutdown AsyncWrite"); - assert!(client.poll_ready().is_err()); + assert!(block_on(poll_fn(|cx| client.poll_ready(cx))).is_err()); - let io = write_all(io, b"foo=bar").wait().unwrap().0; - let vec = read_to_end(io, vec![]).wait().unwrap().1; + let io = block_on(io.write_all(b"foo=bar")).unwrap().0; + let vec = block_on(io.read_to_end(vec![])).unwrap().1; assert_eq!(vec, b"bar=foo"); } #[test] fn connect_method() { - use tokio_io::io::{read_to_end, write_all}; + use futures::io::{AsyncReadExt, AsyncWriteExt}; let _ = ::pretty_env_logger::try_init(); let server = TcpListener::bind("127.0.0.1:0").unwrap(); @@ -1674,18 +1669,18 @@ mod conn { sock.write_all(b"bar=foo").expect("write 2"); }); - let tcp = tcp_connect(&addr).wait().unwrap(); + let tcp = block_on(tcp_connect(&addr)).unwrap(); let io = DebugStream { tcp: tcp, shutdown_called: false, }; - let (mut client, mut conn) = conn::handshake(io).wait().unwrap(); + let (mut client, mut conn) = block_on(conn::handshake(io)).unwrap(); { - let until_tunneled = poll_fn(|| { - conn.poll_without_shutdown() + let until_tunneled = poll_fn(|cx| { + conn.poll_without_shutdown(cx) }); let req = Request::builder() @@ -1696,7 +1691,7 @@ mod conn { let res = client.send_request(req) .and_then(move |res| { assert_eq!(res.status(), hyper::StatusCode::OK); - res.into_body().into_stream().concat2() + res.into_body().into_stream().concat() }) .map(|body| { assert_eq!(body.as_ref(), b""); @@ -1706,13 +1701,13 @@ mod conn { let timeout = Delay::new(Duration::from_millis(200)); let rx = rx.and_then(move |_| timeout.map_err(|e| e.into())); - until_tunneled.join(res).join(rx).map(|r| r.0).wait().unwrap(); + block_on(until_tunneled.join(res).join(rx).map(|r| r.0)).unwrap(); // should not be ready now - poll_fn(|| { - assert!(client.poll_ready().unwrap().is_not_ready()); - Ok::<_, ()>(Async::Ready(())) - }).wait().unwrap(); + block_on(lazy(|cx| { + assert!(client.poll_ready(cx).unwrap().is_pending()); + Ok::<_, ()>(()) + })).unwrap(); } let parts = conn.into_parts(); @@ -1721,10 +1716,10 @@ mod conn { assert_eq!(buf, b"foobar=ready"[..]); assert!(!io.shutdown_called, "tunnel shouldn't shutdown AsyncWrite"); - assert!(client.poll_ready().is_err()); + assert!(block_on(poll_fn(|cx| client.poll_ready(cx))).is_err()); - let io = write_all(io, b"foo=bar").wait().unwrap().0; - let vec = read_to_end(io, vec![]).wait().unwrap().1; + let io = block_on(io.write_all(b"foo=bar")).unwrap().0; + let vec = block_on(io.read_to_end(vec![])).unwrap().1; assert_eq!(vec, b"bar=foo"); } @@ -1733,28 +1728,24 @@ mod conn { shutdown_called: bool, } - impl Write for DebugStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.tcp.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.tcp.flush() - } - } - impl AsyncWrite for DebugStream { - fn shutdown(&mut self) -> Poll<(), io::Error> { + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll { + self.tcp.poll_write(cx, buf) + } + + fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { + self.tcp.poll_flush(cx) + } + + fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> { self.shutdown_called = true; - AsyncWrite::shutdown(&mut self.tcp) + self.tcp.poll_close(cx) } } - impl Read for DebugStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.tcp.read(buf) + impl AsyncRead for DebugStream { + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll { + self.tcp.poll_read(cx, buf) } } - - impl AsyncRead for DebugStream {} } diff --git a/tests/server.rs b/tests/server.rs index 548b3467..9ac7ca34 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -8,7 +8,6 @@ extern crate net2; extern crate spmc; extern crate pretty_env_logger; extern crate tokio; -extern crate tokio_io; use std::net::{TcpStream, Shutdown, SocketAddr}; use std::io::{self, Read, Write}; @@ -19,16 +18,18 @@ use std::net::{TcpListener as StdTcpListener}; use std::thread; use std::time::Duration; -use futures::{Future, Stream}; +use futures::{Future, FutureExt, StreamExt}; use futures::future::{self, FutureResult, Either}; -use futures::sync::oneshot; +use futures::channel::oneshot; +use futures::executor::block_on; +use futures::task; +use futures::io::{AsyncRead, AsyncWrite}; use futures_timer::Delay; use http::header::{HeaderName, HeaderValue}; //use net2::TcpBuilder; use tokio::net::TcpListener; use tokio::runtime::Runtime; use tokio::reactor::Handle; -use tokio_io::{AsyncRead, AsyncWrite}; use hyper::{Body, Request, Response, StatusCode}; @@ -91,14 +92,14 @@ fn get_implicitly_empty() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); Http::::new().serve_connection(socket, GetImplicitlyEmpty) }); - fut.wait().unwrap(); + block_on(fut).unwrap(); struct GetImplicitlyEmpty; @@ -111,7 +112,7 @@ fn get_implicitly_empty() { fn call(&self, req: Request) -> Self::Future { Box::new(req.into_body() .into_stream() - .concat2() + .concat() .map(|buf| { assert!(buf.is_empty()); Response::new(Body::empty()) @@ -765,34 +766,34 @@ fn disable_keep_alive_mid_request() { let mut req = connect(&addr); req.write_all(b"GET / HTTP/1.1\r\n").unwrap(); tx1.send(()).unwrap(); - rx2.wait().unwrap(); + block_on(rx2).unwrap(); req.write_all(b"Host: localhost\r\n\r\n").unwrap(); let mut buf = vec![]; req.read_to_end(&mut buf).unwrap(); }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); Http::::new().serve_connection(socket, HelloWorld) - .select2(rx1) + .select(rx1) .then(|r| { match r { - Ok(Either::A(_)) => panic!("expected rx first"), - Ok(Either::B(((), mut conn))) => { + Ok(Either::Left(_)) => panic!("expected rx first"), + Ok(Either::Right(((), mut conn))) => { conn.disable_keep_alive(); tx2.send(()).unwrap(); conn } - Err(Either::A((e, _))) => panic!("unexpected error {}", e), - Err(Either::B((e, _))) => panic!("unexpected error {}", e), + Err(Either::Left((e, _))) => panic!("unexpected error {}", e), + Err(Either::Right((e, _))) => panic!("unexpected error {}", e), } }) }); - fut.wait().unwrap(); + block_on(fut).unwrap(); child.join().unwrap(); } @@ -833,7 +834,7 @@ fn disable_keep_alive_post_request() { let dropped = Dropped::new(); let dropped2 = dropped.clone(); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.expect("accepted socket"); @@ -842,28 +843,28 @@ fn disable_keep_alive_post_request() { _debug: dropped2, }; Http::::new().serve_connection(transport, HelloWorld) - .select2(rx1) + .select(rx1) .then(|r| { match r { - Ok(Either::A(_)) => panic!("expected rx first"), - Ok(Either::B(((), mut conn))) => { + Ok(Either::Left(_)) => panic!("expected rx first"), + Ok(Either::Right(((), mut conn))) => { conn.disable_keep_alive(); conn } - Err(Either::A((e, _))) => panic!("unexpected error {}", e), - Err(Either::B((e, _))) => panic!("unexpected error {}", e), + Err(Either::Left((e, _))) => panic!("unexpected error {}", e), + Err(Either::Right((e, _))) => panic!("unexpected error {}", e), } }) }); assert!(!dropped.load()); - fut.wait().unwrap(); + block_on(fut).unwrap(); // we must poll the Core one more time in order for Windows to drop // the read-blocked socket. // // See https://github.com/carllerche/mio/issues/776 let timeout = Delay::new(Duration::from_millis(10)); - timeout.wait().unwrap(); + block_on(timeout).unwrap(); assert!(dropped.load()); child.join().unwrap(); } @@ -879,14 +880,14 @@ fn empty_parse_eof_does_not_return_error() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); Http::::new().serve_connection(socket, HelloWorld) }); - fut.wait().unwrap(); + block_on(fut).unwrap(); } #[test] @@ -901,7 +902,7 @@ fn nonempty_parse_eof_returns_error() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -909,7 +910,7 @@ fn nonempty_parse_eof_returns_error() { .map(|_| ()) }); - fut.wait().unwrap_err(); + block_on(fut).unwrap_err(); } #[test] @@ -929,7 +930,7 @@ fn returning_1xx_response_is_error() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -943,12 +944,12 @@ fn returning_1xx_response_is_error() { .map(|_| ()) }); - fut.wait().unwrap_err(); + block_on(fut).unwrap_err(); } #[test] fn upgrades() { - use tokio_io::io::{read_to_end, write_all}; + use futures::io::{AsyncReadExt, AsyncWriteExt}; let _ = pretty_env_logger::try_init(); let runtime = Runtime::new().unwrap(); let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap(), &runtime.handle()).unwrap(); @@ -977,7 +978,7 @@ fn upgrades() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| -> hyper::Error { unreachable!() }) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -992,24 +993,24 @@ fn upgrades() { })); let mut conn_opt = Some(conn); - future::poll_fn(move || { - try_ready!(conn_opt.as_mut().unwrap().poll_without_shutdown()); + future::poll_fn(move |cx| { + try_ready!(conn_opt.as_mut().unwrap().poll_without_shutdown(cx)); // conn is done with HTTP now Ok(conn_opt.take().unwrap().into()) }) }); - let conn = fut.wait().unwrap(); + let conn = block_on(fut).unwrap(); // wait so that we don't write until other side saw 101 response - rx.wait().unwrap(); + block_on(rx).unwrap(); let parts = conn.into_parts(); let io = parts.io; assert_eq!(parts.read_buf, "eagerly optimistic"); - let io = write_all(io, b"foo=bar").wait().unwrap().0; - let vec = read_to_end(io, vec![]).wait().unwrap().1; + let io = block_on(io.write_all(b"foo=bar")).unwrap().0; + let vec = block_on(io.read_to_end(vec![])).unwrap().1; assert_eq!(vec, b"bar=foo"); } @@ -1030,7 +1031,7 @@ fn parse_errors_send_4xx_response() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -1039,7 +1040,7 @@ fn parse_errors_send_4xx_response() { .map(|_| ()) }); - fut.wait().unwrap_err(); + block_on(fut).unwrap_err(); } #[test] @@ -1059,7 +1060,7 @@ fn illegal_request_length_returns_400_response() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -1068,7 +1069,7 @@ fn illegal_request_length_returns_400_response() { .map(|_| ()) }); - fut.wait().unwrap_err(); + block_on(fut).unwrap_err(); } #[test] @@ -1092,7 +1093,7 @@ fn max_buf_size() { }); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -1102,7 +1103,7 @@ fn max_buf_size() { .map(|_| ()) }); - fut.wait().unwrap_err(); + block_on(fut).unwrap_err(); } #[test] @@ -1136,7 +1137,7 @@ fn streaming_body() { let rx = rx.map_err(|_| panic!("thread panicked")); let fut = listener.incoming() - .into_future() + .next() .map_err(|_| unreachable!()) .and_then(|(item, _incoming)| { let socket = item.unwrap(); @@ -1152,7 +1153,7 @@ fn streaming_body() { .map(|_| ()) }); - fut.join(rx).wait().unwrap(); + block_on(fut.join(rx)).unwrap(); } // ------------------------------------------------- @@ -1292,7 +1293,7 @@ impl Service for TestService { Ok(()) }).then(move |result| { let msg = match result { - Ok(()) => Msg::End, + Ok(_) => Msg::End, Err(e) => Msg::Error(e), }; tx2.lock().unwrap().send(msg).unwrap(); @@ -1379,7 +1380,7 @@ fn serve_with_options(options: ServeOptions) -> Serve { let thread_name = format!("test-server-{:?}", dur); let thread = thread::Builder::new().name(thread_name).spawn(move || { - tokio::run(::futures::future::lazy(move || { + tokio::runtime::run2(::futures::future::lazy(move |_| { let srv = Http::new() .keep_alive(keep_alive) .pipeline(pipeline) @@ -1390,7 +1391,7 @@ fn serve_with_options(options: ServeOptions) -> Serve { }).unwrap(); addr_tx.send(srv.local_addr().unwrap()).unwrap(); srv.run_until(shutdown_rx.then(|_| Ok(()))) - .map_err(|err| println!("error {}", err)) + .map_err(|err| panic!("error {}", err)) })) }).unwrap(); @@ -1420,31 +1421,26 @@ struct DebugStream { _debug: D, } -impl Read for DebugStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.stream.read(buf) - } -} - -impl Write for DebugStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.stream.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.stream.flush() - } -} - - impl AsyncWrite for DebugStream { - fn shutdown(&mut self) -> futures::Poll<(), io::Error> { - self.stream.shutdown() + fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> futures::Poll { + self.stream.poll_write(cx, buf) + } + + fn poll_flush(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> { + self.stream.poll_flush(cx) + } + + fn poll_close(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> { + self.stream.poll_close(cx) } } -impl AsyncRead for DebugStream {} +impl AsyncRead for DebugStream { + fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> futures::Poll { + self.stream.poll_read(cx, buf) + } +} #[derive(Clone)] struct Dropped(Arc);