feat(lib): convert to use tokio 0.1
BREAKING CHANGE: All uses of `Handle` now need to be new-tokio `Handle`. Co-authored-by: Sean McArthur <sean@seanmonstar.com>
This commit is contained in:
		| @@ -208,7 +208,10 @@ where | ||||
|     } | ||||
|  | ||||
|     //TODO: replace with `impl Future` when stable | ||||
|     pub(crate) fn send_request_retryable(&mut self, req: Request<B>) -> Box<Future<Item=Response<Body>, Error=(::Error, Option<Request<B>>)>> { | ||||
|     pub(crate) fn send_request_retryable(&mut self, req: Request<B>) -> Box<Future<Item=Response<Body>, Error=(::Error, Option<Request<B>>)> + Send> | ||||
|     where | ||||
|         B: Send, | ||||
|     { | ||||
|         let inner = match self.dispatch.try_send(req) { | ||||
|             Ok(rx) => { | ||||
|                 Either::A(rx.then(move |res| { | ||||
|   | ||||
| @@ -9,6 +9,7 @@ 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; | ||||
|  | ||||
| @@ -18,9 +19,10 @@ use futures::sync::oneshot; | ||||
| use futures_cpupool::{Builder as CpuPoolBuilder}; | ||||
| use http::Uri; | ||||
| use http::uri::Scheme; | ||||
| use net2::TcpBuilder; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio::reactor::Handle; | ||||
| use tokio::net::{TcpStream, TcpStreamNew}; | ||||
| use tokio::net::{TcpStream, ConnectFuture}; | ||||
|  | ||||
| use super::dns; | ||||
| use self::http_connector::HttpConnectorBlockingTask; | ||||
| @@ -30,13 +32,13 @@ use self::http_connector::HttpConnectorBlockingTask; | ||||
| /// A connector receives a [`Destination`](Destination) describing how a | ||||
| /// connection should be estabilished, and returns a `Future` of the | ||||
| /// ready connection. | ||||
| pub trait Connect { | ||||
| pub trait Connect: Send + Sync { | ||||
|     /// The connected IO Stream. | ||||
|     type Transport: AsyncRead + AsyncWrite + 'static; | ||||
|     type Transport: AsyncRead + AsyncWrite + Send + 'static; | ||||
|     /// An error occured when trying to connect. | ||||
|     type Error; | ||||
|     /// A Future that will resolve to the connected Transport. | ||||
|     type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error>; | ||||
|     type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send; | ||||
|     /// Connect to a destination. | ||||
|     fn connect(&self, dst: Destination) -> Self::Future; | ||||
| } | ||||
| @@ -133,6 +135,28 @@ impl Connected { | ||||
|     */ | ||||
| } | ||||
|  | ||||
| fn connect(addr: &SocketAddr, handle: &Handle) -> io::Result<ConnectFuture> { | ||||
|     let builder = match addr { | ||||
|         &SocketAddr::V4(_) => TcpBuilder::new_v4()?, | ||||
|         &SocketAddr::V6(_) => TcpBuilder::new_v6()?, | ||||
|     }; | ||||
|  | ||||
|     if cfg!(windows) { | ||||
|         // Windows requires a socket be bound before calling connect | ||||
|         let any: SocketAddr = match addr { | ||||
|             &SocketAddr::V4(_) => { | ||||
|                 ([0, 0, 0, 0], 0).into() | ||||
|             }, | ||||
|             &SocketAddr::V6(_) => { | ||||
|                 ([0, 0, 0, 0, 0, 0, 0, 0], 0).into() | ||||
|             } | ||||
|         }; | ||||
|         builder.bind(any)?; | ||||
|     } | ||||
|  | ||||
|     Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, handle)) | ||||
| } | ||||
|  | ||||
| /// A connector for the `http` scheme. | ||||
| /// | ||||
| /// Performs DNS resolution in a thread pool, and then connects over TCP. | ||||
| @@ -162,7 +186,7 @@ impl HttpConnector { | ||||
|     /// Takes an executor to run blocking tasks on. | ||||
|     #[inline] | ||||
|     pub fn new_with_executor<E: 'static>(executor: E, handle: &Handle) -> HttpConnector | ||||
|         where E: Executor<HttpConnectorBlockingTask> | ||||
|         where E: Executor<HttpConnectorBlockingTask> + Send + Sync | ||||
|     { | ||||
|         HttpConnector { | ||||
|             executor: HttpConnectExecutor(Arc::new(executor)), | ||||
| @@ -336,7 +360,7 @@ impl fmt::Debug for HttpConnecting { | ||||
|  | ||||
| struct ConnectingTcp { | ||||
|     addrs: dns::IpAddrs, | ||||
|     current: Option<TcpStreamNew>, | ||||
|     current: Option<ConnectFuture>, | ||||
| } | ||||
|  | ||||
| impl ConnectingTcp { | ||||
| @@ -352,14 +376,14 @@ impl ConnectingTcp { | ||||
|                         err = Some(e); | ||||
|                         if let Some(addr) = self.addrs.next() { | ||||
|                             debug!("connecting to {}", addr); | ||||
|                             *current = TcpStream::connect(&addr, handle); | ||||
|                             *current = connect(&addr, handle)?; | ||||
|                             continue; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else if let Some(addr) = self.addrs.next() { | ||||
|                 debug!("connecting to {}", addr); | ||||
|                 self.current = Some(TcpStream::connect(&addr, handle)); | ||||
|                 self.current = Some(connect(&addr, handle)?); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
| @@ -393,7 +417,7 @@ mod http_connector { | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask>>); | ||||
| struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>); | ||||
|  | ||||
| impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor { | ||||
|     fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> { | ||||
| @@ -406,43 +430,44 @@ impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor { | ||||
| mod tests { | ||||
|     #![allow(deprecated)] | ||||
|     use std::io; | ||||
|     use tokio::reactor::Core; | ||||
|     use futures::Future; | ||||
|     use tokio::runtime::Runtime; | ||||
|     use super::{Connect, Destination, HttpConnector}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_errors_missing_authority() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let runtime = Runtime::new().unwrap(); | ||||
|         let uri = "/foo/bar?baz".parse().unwrap(); | ||||
|         let dst = Destination { | ||||
|             uri, | ||||
|         }; | ||||
|         let connector = HttpConnector::new(1, &core.handle()); | ||||
|         let connector = HttpConnector::new(1, runtime.handle()); | ||||
|  | ||||
|         assert_eq!(core.run(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_errors_enforce_http() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let runtime = Runtime::new().unwrap(); | ||||
|         let uri = "https://example.domain/foo/bar?baz".parse().unwrap(); | ||||
|         let dst = Destination { | ||||
|             uri, | ||||
|         }; | ||||
|         let connector = HttpConnector::new(1, &core.handle()); | ||||
|         let connector = HttpConnector::new(1, runtime.handle()); | ||||
|  | ||||
|         assert_eq!(core.run(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #[test] | ||||
|     fn test_errors_missing_scheme() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let runtime = Runtime::new().unwrap(); | ||||
|         let uri = "example.domain".parse().unwrap(); | ||||
|         let dst = Destination { | ||||
|             uri, | ||||
|         }; | ||||
|         let connector = HttpConnector::new(1, &core.handle()); | ||||
|         let connector = HttpConnector::new(1, runtime.handle()); | ||||
|  | ||||
|         assert_eq!(core.run(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,8 @@ pub struct Sender<T, U> { | ||||
|     // response have been fully processed, and a connection is ready | ||||
|     // for more. | ||||
|     giver: signal::Giver, | ||||
|     inner: mpsc::Sender<(T, Callback<T, U>)>, | ||||
|     //inner: mpsc::Sender<(T, Callback<T, U>)>, | ||||
|     inner: mpsc::Sender<Envelope<T, U>>, | ||||
| } | ||||
|  | ||||
| impl<T, U> Sender<T, U> { | ||||
| @@ -51,21 +52,22 @@ impl<T, U> Sender<T, U> { | ||||
|  | ||||
|     pub fn try_send(&mut self, val: T) -> Result<RetryPromise<T, U>, T> { | ||||
|         let (tx, rx) = oneshot::channel(); | ||||
|         self.inner.try_send((val, Callback::Retry(tx))) | ||||
|         self.inner.try_send(Envelope(Some((val, Callback::Retry(tx))))) | ||||
|             .map(move |_| rx) | ||||
|             .map_err(|e| e.into_inner().0) | ||||
|             .map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0) | ||||
|     } | ||||
|  | ||||
|     pub fn send(&mut self, val: T) -> Result<Promise<U>, T> { | ||||
|         let (tx, rx) = oneshot::channel(); | ||||
|         self.inner.try_send((val, Callback::NoRetry(tx))) | ||||
|         self.inner.try_send(Envelope(Some((val, Callback::NoRetry(tx))))) | ||||
|             .map(move |_| rx) | ||||
|             .map_err(|e| e.into_inner().0) | ||||
|             .map_err(|e| e.into_inner().0.take().expect("envelope not dropped").0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct Receiver<T, U> { | ||||
|     inner: mpsc::Receiver<(T, Callback<T, U>)>, | ||||
|     //inner: mpsc::Receiver<(T, Callback<T, U>)>, | ||||
|     inner: mpsc::Receiver<Envelope<T, U>>, | ||||
|     taker: signal::Taker, | ||||
| } | ||||
|  | ||||
| @@ -75,7 +77,9 @@ impl<T, U> Stream for Receiver<T, U> { | ||||
|  | ||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||
|         match self.inner.poll() { | ||||
|             Ok(Async::Ready(item)) => Ok(Async::Ready(item)), | ||||
|             Ok(Async::Ready(item)) => Ok(Async::Ready(item.map(|mut env| { | ||||
|                 env.0.take().expect("envelope not dropped") | ||||
|             }))), | ||||
|             Ok(Async::NotReady) => { | ||||
|                 self.taker.want(); | ||||
|                 Ok(Async::NotReady) | ||||
| @@ -85,6 +89,16 @@ impl<T, U> Stream for Receiver<T, U> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
| TODO: with futures 0.2, bring this Drop back and toss Envelope | ||||
|  | ||||
| The problem is, there is a bug in futures 0.1 mpsc channel, where | ||||
| even though you may call `rx.close()`, `rx.poll()` may still think | ||||
| there are messages and so should park the current task. In futures | ||||
| 0.2, we can use `try_next`, and not even risk such a bug. | ||||
|  | ||||
| For now, use an `Envelope` that has this drop guard logic instead. | ||||
|  | ||||
| impl<T, U> Drop for Receiver<T, U> { | ||||
|     fn drop(&mut self) { | ||||
|         self.taker.cancel(); | ||||
| @@ -105,6 +119,17 @@ impl<T, U> Drop for Receiver<T, U> { | ||||
|     } | ||||
|  | ||||
| } | ||||
| */ | ||||
|  | ||||
| struct Envelope<T, U>(Option<(T, Callback<T, U>)>); | ||||
|  | ||||
| impl<T, U> Drop for Envelope<T, U> { | ||||
|     fn drop(&mut self) { | ||||
|         if let Some((val, cb)) = self.0.take() { | ||||
|             let _ = cb.send(Err((::Error::new_canceled(None::<::Error>), Some(val)))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum Callback<T, U> { | ||||
|     Retry(oneshot::Sender<Result<U, (::Error, Option<T>)>>), | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| use std::fmt; | ||||
| use std::io; | ||||
| use std::marker::PhantomData; | ||||
| use std::rc::Rc; | ||||
| use std::sync::Arc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| @@ -13,6 +12,7 @@ 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; | ||||
|  | ||||
| use proto::body::{Body, Entity}; | ||||
| @@ -36,7 +36,7 @@ mod tests; | ||||
|  | ||||
| /// A Client to make outgoing HTTP requests. | ||||
| pub struct Client<C, B = proto::Body> { | ||||
|     connector: Rc<C>, | ||||
|     connector: Arc<C>, | ||||
|     executor: Exec, | ||||
|     h1_writev: bool, | ||||
|     pool: Pool<PoolClient<B>>, | ||||
| @@ -52,6 +52,12 @@ impl Client<HttpConnector, proto::Body> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for Client<HttpConnector, proto::Body> { | ||||
|     fn default() -> Client<HttpConnector, proto::Body> { | ||||
|         Client::new(&Handle::current()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Client<HttpConnector, proto::Body> { | ||||
|     /// Configure a Client. | ||||
|     /// | ||||
| @@ -59,11 +65,11 @@ impl Client<HttpConnector, proto::Body> { | ||||
|     /// | ||||
|     /// ```no_run | ||||
|     /// # extern crate hyper; | ||||
|     /// # extern crate tokio_core; | ||||
|     /// # extern crate tokio; | ||||
|     /// | ||||
|     /// # fn main() { | ||||
|     /// # let core = tokio_core::reactor::Core::new().unwrap(); | ||||
|     /// # let handle = core.handle(); | ||||
|     /// # let runtime = tokio::runtime::Runtime::new().unwrap(); | ||||
|     /// # let handle = runtime.handle(); | ||||
|     /// let client = hyper::Client::configure() | ||||
|     ///     .keep_alive(true) | ||||
|     ///     .build(&handle); | ||||
| @@ -77,22 +83,10 @@ impl Client<HttpConnector, proto::Body> { | ||||
| } | ||||
|  | ||||
| impl<C, B> Client<C, B> { | ||||
|     // Eventually, a Client won't really care about a tokio Handle, and only | ||||
|     // the executor used to spawn background tasks. Removing this method is | ||||
|     // a breaking change, so for now, it's just deprecated. | ||||
|     #[doc(hidden)] | ||||
|     #[deprecated] | ||||
|     pub fn handle(&self) -> &Handle { | ||||
|         match self.executor { | ||||
|             Exec::Handle(ref h) => h, | ||||
|             Exec::Executor(..) => panic!("Client not built with a Handle"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn configured(config: Config<C, B>, exec: Exec) -> Client<C, B> { | ||||
|         Client { | ||||
|             connector: Rc::new(config.connector), | ||||
|             connector: Arc::new(config.connector), | ||||
|             executor: exec, | ||||
|             h1_writev: config.h1_writev, | ||||
|             pool: Pool::new(config.keep_alive, config.keep_alive_timeout), | ||||
| @@ -103,10 +97,11 @@ impl<C, B> Client<C, B> { | ||||
| } | ||||
|  | ||||
| impl<C, B> Client<C, B> | ||||
| where C: Connect<Error=io::Error> + 'static, | ||||
| where C: Connect<Error=io::Error> + Sync + 'static, | ||||
|       C::Transport: 'static, | ||||
|       C::Future: 'static, | ||||
|       B: Entity<Error=::Error> + 'static, | ||||
|       B: Entity<Error=::Error> + Send + 'static, | ||||
|       B::Data: Send, | ||||
| { | ||||
|  | ||||
|     /// Send a `GET` request to the supplied `Uri`. | ||||
| @@ -195,7 +190,7 @@ where C: Connect<Error=io::Error> + 'static, | ||||
|     } | ||||
|  | ||||
|     //TODO: replace with `impl Future` when stable | ||||
|     fn send_request(&self, mut req: Request<B>, domain: &str) -> Box<Future<Item=Response<Body>, Error=ClientError<B>>> { | ||||
|     fn send_request(&self, mut req: Request<B>, domain: &str) -> Box<Future<Item=Response<Body>, Error=ClientError<B>> + Send> { | ||||
|         let url = req.uri().clone(); | ||||
|         let checkout = self.pool.checkout(domain); | ||||
|         let connect = { | ||||
| @@ -280,16 +275,15 @@ where C: Connect<Error=io::Error> + 'static, | ||||
|     } | ||||
|  | ||||
|     fn schedule_pool_timer(&self) { | ||||
|         if let Exec::Handle(ref h) = self.executor { | ||||
|             self.pool.spawn_expired_interval(h); | ||||
|         } | ||||
|         self.pool.spawn_expired_interval(&self.executor); | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<C, B> Service for Client<C, B> | ||||
| where C: Connect<Error=io::Error> + 'static, | ||||
|       C::Future: 'static, | ||||
|       B: Entity<Error=::Error> + 'static, | ||||
|       B: Entity<Error=::Error> + Send + 'static, | ||||
|       B::Data: Send, | ||||
| { | ||||
|     type Request = Request<B>; | ||||
|     type Response = Response<Body>; | ||||
| @@ -323,7 +317,7 @@ impl<C, B> fmt::Debug for Client<C, B> { | ||||
|  | ||||
| /// A `Future` that will resolve to an HTTP Response. | ||||
| #[must_use = "futures do nothing unless polled"] | ||||
| pub struct FutureResponse(Box<Future<Item=Response<Body>, Error=::Error> + 'static>); | ||||
| pub struct FutureResponse(Box<Future<Item=Response<Body>, Error=::Error> + Send + 'static>); | ||||
|  | ||||
| impl fmt::Debug for FutureResponse { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| @@ -343,7 +337,7 @@ impl Future for FutureResponse { | ||||
| struct RetryableSendRequest<C, B> { | ||||
|     client: Client<C, B>, | ||||
|     domain: String, | ||||
|     future: Box<Future<Item=Response<Body>, Error=ClientError<B>>>, | ||||
|     future: Box<Future<Item=Response<Body>, Error=ClientError<B>> + Send>, | ||||
|     uri: Uri, | ||||
| } | ||||
|  | ||||
| @@ -351,7 +345,8 @@ impl<C, B> Future for RetryableSendRequest<C, B> | ||||
| where | ||||
|     C: Connect<Error=io::Error> + 'static, | ||||
|     C::Future: 'static, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
|     B: Entity<Error=::Error> + Send + 'static, | ||||
|     B::Data: Send, | ||||
| { | ||||
|     type Item = Response<Body>; | ||||
|     type Error = ::Error; | ||||
| @@ -562,12 +557,13 @@ impl<C, B> Config<C, B> | ||||
| where C: Connect<Error=io::Error>, | ||||
|       C::Transport: 'static, | ||||
|       C::Future: 'static, | ||||
|       B: Entity<Error=::Error>, | ||||
|       B: Entity<Error=::Error> + Send, | ||||
|       B::Data: Send, | ||||
| { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
|     pub fn build(self, handle: &Handle) -> Client<C, B> { | ||||
|         Client::configured(self, Exec::Handle(handle.clone())) | ||||
|     pub fn build(self) -> Client<C, B> { | ||||
|         Client::configured(self, Exec::Default) | ||||
|     } | ||||
|  | ||||
|     /// Construct a Client with this configuration and an executor. | ||||
| @@ -576,14 +572,16 @@ where C: Connect<Error=io::Error>, | ||||
|     /// to drive requests and responses. | ||||
|     pub fn executor<E>(self, executor: E) -> Client<C, B> | ||||
|     where | ||||
|         E: Executor<Background> + 'static, | ||||
|         E: Executor<Background> + Send + Sync + 'static, | ||||
|     { | ||||
|         Client::configured(self, Exec::Executor(Rc::new(executor))) | ||||
|         Client::configured(self, Exec::new(executor)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<B> Config<UseDefaultConnector, B> | ||||
| where B: Entity<Error=::Error>, | ||||
| where | ||||
|     B: Entity<Error=::Error> + Send, | ||||
|     B::Data: Send, | ||||
| { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
| @@ -592,7 +590,22 @@ where B: Entity<Error=::Error>, | ||||
|         if self.keep_alive { | ||||
|             connector.set_keepalive(self.keep_alive_timeout); | ||||
|         } | ||||
|         self.connector(connector).build(handle) | ||||
|         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<E>(self, handle: &Handle, executor: E) -> Client<HttpConnector, B> | ||||
|     where | ||||
|         E: Executor<Background> + 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) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -622,18 +635,22 @@ impl<C: Clone, B> Clone for Config<C, B> { | ||||
|  | ||||
| #[derive(Clone)] | ||||
| enum Exec { | ||||
|     Handle(Handle), | ||||
|     Executor(Rc<Executor<Background>>), | ||||
|     Default, | ||||
|     Executor(Arc<Executor<Background> + Send + Sync>), | ||||
| } | ||||
|  | ||||
|  | ||||
| impl Exec { | ||||
|     pub(crate) fn new<E: Executor<Background> + Send + Sync + 'static>(executor: E) -> Exec { | ||||
|         Exec::Executor(Arc::new(executor)) | ||||
|     } | ||||
|  | ||||
|     fn execute<F>(&self, fut: F) -> io::Result<()> | ||||
|     where | ||||
|         F: Future<Item=(), Error=()> + 'static, | ||||
|         F: Future<Item=(), Error=()> + Send + 'static, | ||||
|     { | ||||
|         match *self { | ||||
|             Exec::Handle(ref h) => h.spawn(fut), | ||||
|             Exec::Default => spawn(fut), | ||||
|             Exec::Executor(ref e) => { | ||||
|                 e.execute(bg(Box::new(fut))) | ||||
|                     .map_err(|err| { | ||||
| @@ -660,10 +677,10 @@ mod background { | ||||
|     // and only implementeds `Future`. | ||||
|     #[allow(missing_debug_implementations)] | ||||
|     pub struct Background { | ||||
|         inner: Box<Future<Item=(), Error=()>>, | ||||
|         inner: Box<Future<Item=(), Error=()> + Send>, | ||||
|     } | ||||
|  | ||||
|     pub fn bg(fut: Box<Future<Item=(), Error=()>>) -> Background { | ||||
|     pub fn bg(fut: Box<Future<Item=(), Error=()> + Send>) -> Background { | ||||
|         Background { | ||||
|             inner: fut, | ||||
|         } | ||||
|   | ||||
| @@ -6,7 +6,9 @@ use std::time::{Duration, Instant}; | ||||
|  | ||||
| use futures::{Future, Async, Poll, Stream}; | ||||
| use futures::sync::oneshot; | ||||
| use tokio::reactor::{Handle, Interval}; | ||||
| use futures_timer::Interval; | ||||
|  | ||||
| use super::Exec; | ||||
|  | ||||
| pub struct Pool<T> { | ||||
|     inner: Arc<Mutex<PoolInner<T>>>, | ||||
| @@ -218,8 +220,8 @@ impl<T: Closed> PoolInner<T> { | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<T: Closed + 'static> Pool<T> { | ||||
|     pub(super) fn spawn_expired_interval(&self, handle: &Handle) { | ||||
| impl<T: Closed + Send + 'static> Pool<T> { | ||||
|     pub(super) fn spawn_expired_interval(&self, exec: &Exec) { | ||||
|         let dur = { | ||||
|             let mut inner = self.inner.lock().unwrap(); | ||||
|  | ||||
| @@ -239,12 +241,11 @@ impl<T: Closed + 'static> Pool<T> { | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         let interval = Interval::new(dur, handle) | ||||
|             .expect("reactor is gone"); | ||||
|         handle.spawn(IdleInterval { | ||||
|         let interval = Interval::new(dur); | ||||
|         exec.execute(IdleInterval { | ||||
|             interval: interval, | ||||
|             pool: Arc::downgrade(&self.inner), | ||||
|         }); | ||||
|         }).unwrap(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -431,7 +432,7 @@ mod tests { | ||||
|     use std::time::Duration; | ||||
|     use futures::{Async, Future}; | ||||
|     use futures::future; | ||||
|     use super::{Closed, Pool}; | ||||
|     use super::{Closed, Pool, Exec}; | ||||
|  | ||||
|     impl Closed for i32 { | ||||
|         fn is_closed(&self) -> bool { | ||||
| @@ -489,9 +490,11 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn test_pool_timer_removes_expired() { | ||||
|         let mut core = ::tokio::reactor::Core::new().unwrap(); | ||||
|         let runtime = ::tokio::runtime::Runtime::new().unwrap(); | ||||
|         let pool = Pool::new(true, Some(Duration::from_millis(100))); | ||||
|         pool.spawn_expired_interval(&core.handle()); | ||||
|  | ||||
|         let executor = runtime.executor(); | ||||
|         pool.spawn_expired_interval(&Exec::new(executor)); | ||||
|         let key = Arc::new("foo".to_string()); | ||||
|  | ||||
|         pool.pooled(key.clone(), 41); | ||||
| @@ -500,11 +503,9 @@ mod tests { | ||||
|  | ||||
|         assert_eq!(pool.inner.lock().unwrap().idle.get(&key).map(|entries| entries.len()), Some(3)); | ||||
|  | ||||
|         let timeout = ::tokio::reactor::Timeout::new( | ||||
|             Duration::from_millis(400), // allow for too-good resolution | ||||
|             &core.handle() | ||||
|         ).unwrap(); | ||||
|         core.run(timeout).unwrap(); | ||||
|         ::futures_timer::Delay::new( | ||||
|             Duration::from_millis(400) // allow for too-good resolution | ||||
|         ).wait().unwrap(); | ||||
|  | ||||
|         assert!(pool.inner.lock().unwrap().idle.get(&key).is_none()); | ||||
|     } | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| extern crate pretty_env_logger; | ||||
|  | ||||
| use std::thread; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use futures::Async; | ||||
| use futures::future::poll_fn; | ||||
| use tokio::reactor::Core; | ||||
| use tokio::executor::thread_pool::{Builder as ThreadPoolBuilder}; | ||||
|  | ||||
| use mock::MockConnector; | ||||
| use super::*; | ||||
| @@ -10,8 +13,8 @@ use super::*; | ||||
| #[test] | ||||
| fn retryable_request() { | ||||
|     let _ = pretty_env_logger::try_init(); | ||||
|     let mut core = Core::new().unwrap(); | ||||
|  | ||||
|     let executor = ThreadPoolBuilder::new().pool_size(1).build(); | ||||
|     let mut connector = MockConnector::new(); | ||||
|  | ||||
|     let sock1 = connector.mock("http://mock.local"); | ||||
| @@ -19,8 +22,7 @@ fn retryable_request() { | ||||
|  | ||||
|     let client = Client::configure() | ||||
|         .connector(connector) | ||||
|         .build(&core.handle()); | ||||
|  | ||||
|         .executor(executor.sender().clone()); | ||||
|  | ||||
|     { | ||||
|  | ||||
| @@ -34,7 +36,7 @@ fn retryable_request() { | ||||
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); | ||||
|             Ok(Async::Ready(())) | ||||
|         }); | ||||
|         core.run(res1.join(srv1)).expect("res1"); | ||||
|         res1.join(srv1).wait().expect("res1"); | ||||
|     } | ||||
|     drop(sock1); | ||||
|  | ||||
| @@ -52,22 +54,21 @@ fn retryable_request() { | ||||
|         Ok(Async::Ready(())) | ||||
|     }); | ||||
|  | ||||
|     core.run(res2.join(srv2)).expect("res2"); | ||||
|     res2.join(srv2).wait().expect("res2"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn conn_reset_after_write() { | ||||
|     let _ = pretty_env_logger::try_init(); | ||||
|     let mut core = Core::new().unwrap(); | ||||
|  | ||||
|     let executor = ThreadPoolBuilder::new().pool_size(1).build(); | ||||
|     let mut connector = MockConnector::new(); | ||||
|  | ||||
|     let sock1 = connector.mock("http://mock.local"); | ||||
|  | ||||
|     let client = Client::configure() | ||||
|         .connector(connector) | ||||
|         .build(&core.handle()); | ||||
|  | ||||
|         .executor(executor.sender().clone()); | ||||
|  | ||||
|     { | ||||
|         let req = Request::builder() | ||||
| @@ -82,9 +83,12 @@ fn conn_reset_after_write() { | ||||
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); | ||||
|             Ok(Async::Ready(())) | ||||
|         }); | ||||
|         core.run(res1.join(srv1)).expect("res1"); | ||||
|         res1.join(srv1).wait().expect("res1"); | ||||
|     } | ||||
|  | ||||
|     // sleep to allow some time for the connection to return to the pool | ||||
|     thread::sleep(Duration::from_secs(1)); | ||||
|  | ||||
|     let req = Request::builder() | ||||
|         .uri("http://mock.local/a") | ||||
|         .body(Default::default()) | ||||
| @@ -102,7 +106,7 @@ fn conn_reset_after_write() { | ||||
|         sock1.take(); | ||||
|         Ok(Async::Ready(())) | ||||
|     }); | ||||
|     let err = core.run(res2.join(srv2)).expect_err("res2"); | ||||
|     let err = res2.join(srv2).wait().expect_err("res2"); | ||||
|     match err { | ||||
|         ::Error::Incomplete => (), | ||||
|         other => panic!("expected Incomplete, found {:?}", other) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user