use futures::{Async, Future, Poll}; use h2; use http; use hyper; use tokio_connect::Connect; use tokio_core::reactor::Handle; use tower::{Service, NewService}; use tower_h2; use bind; use super::glue::{BodyStream, HttpBody, HyperConnect}; /// A `NewService` that can speak either HTTP/1 or HTTP/2. pub struct Client where B: tower_h2::Body, { inner: ClientInner, } enum ClientInner where B: tower_h2::Body, { Http1(hyper::Client, BodyStream>), Http2(tower_h2::client::Client), } /// A `Future` returned from `Client::new_service()`. pub struct ClientNewServiceFuture where B: tower_h2::Body + 'static, C: Connect + 'static, { inner: ClientNewServiceFutureInner, } enum ClientNewServiceFutureInner where B: tower_h2::Body + 'static, C: Connect + 'static, { Http1(Option, BodyStream>>), Http2(tower_h2::client::ConnectFuture), } /// The `Service` yielded by `Client::new_service()`. pub struct ClientService where B: tower_h2::Body, { inner: ClientServiceInner, } enum ClientServiceInner where B: tower_h2::Body, { Http1(hyper::Client, BodyStream>), Http2(tower_h2::client::Service), } impl Client where C: Connect + Clone + 'static, C::Future: 'static, B: tower_h2::Body + 'static, { /// Create a new `Client`, bound to a specific protocol (HTTP/1 or HTTP/2). pub fn new(protocol: bind::Protocol, connect: C, executor: Handle) -> Self { match protocol { bind::Protocol::Http1 => { let h1 = hyper::Client::configure() .connector(HyperConnect::new(connect)) .body() .build(&executor); Client { inner: ClientInner::Http1(h1), } }, bind::Protocol::Http2 => { let mut h2_builder = h2::client::Builder::default(); // h2 currently doesn't handle PUSH_PROMISE that well, so we just // disable it for now. h2_builder.enable_push(false); let h2 = tower_h2::client::Client::new(connect, h2_builder, executor); Client { inner: ClientInner::Http2(h2), } } } } } impl NewService for Client where C: Connect + Clone + 'static, C::Future: 'static, B: tower_h2::Body + 'static, { type Request = http::Request; type Response = http::Response; type Error = tower_h2::client::Error; type InitError = tower_h2::client::ConnectError; type Service = ClientService; type Future = ClientNewServiceFuture; fn new_service(&self) -> Self::Future { let inner = match self.inner { ClientInner::Http1(ref h1) => { ClientNewServiceFutureInner::Http1(Some(h1.clone())) }, ClientInner::Http2(ref h2) => { ClientNewServiceFutureInner::Http2(h2.new_service()) }, }; ClientNewServiceFuture { inner, } } } impl Future for ClientNewServiceFuture where C: Connect + 'static, B: tower_h2::Body + 'static, { type Item = ClientService; type Error = tower_h2::client::ConnectError; fn poll(&mut self) -> Poll { let inner = match self.inner { ClientNewServiceFutureInner::Http1(ref mut h1) => { ClientServiceInner::Http1(h1.take().expect("poll more than once")) }, ClientNewServiceFutureInner::Http2(ref mut h2) => { let s = try_ready!(h2.poll()); ClientServiceInner::Http2(s) }, }; Ok(Async::Ready(ClientService { inner, })) } } impl Service for ClientService where C: Connect + 'static, C::Future: 'static, B: tower_h2::Body + 'static, { type Request = http::Request; type Response = http::Response; type Error = tower_h2::client::Error; type Future = ClientServiceFuture; fn poll_ready(&mut self) -> Poll<(), Self::Error> { match self.inner { ClientServiceInner::Http1(_) => Ok(Async::Ready(())), ClientServiceInner::Http2(ref mut h2) => h2.poll_ready(), } } fn call(&mut self, req: Self::Request) -> Self::Future { match self.inner { ClientServiceInner::Http1(ref h1) => { let is_body_empty = req.body().is_end_stream(); let mut req = hyper::Request::from(req.map(BodyStream)); if is_body_empty { req.headers_mut().set(hyper::header::ContentLength(0)); } ClientServiceFuture::Http1(h1.request(req)) }, ClientServiceInner::Http2(ref mut h2) => { ClientServiceFuture::Http2(h2.call(req)) }, } } } pub enum ClientServiceFuture { Http1(hyper::client::FutureResponse), Http2(tower_h2::client::ResponseFuture), } impl Future for ClientServiceFuture { type Item = http::Response; type Error = tower_h2::client::Error; fn poll(&mut self) -> Poll { match *self { ClientServiceFuture::Http1(ref mut f) => { match f.poll() { Ok(Async::Ready(res)) => { let res = http::Response::from(res); let res = res.map(HttpBody::Http1); Ok(Async::Ready(res)) }, Ok(Async::NotReady) => Ok(Async::NotReady), Err(e) => { debug!("http/1 client error: {}", e); Err(h2::Reason::INTERNAL_ERROR.into()) } } }, ClientServiceFuture::Http2(ref mut f) => { let res = try_ready!(f.poll()); let res = res.map(HttpBody::Http2); Ok(Async::Ready(res)) } } } }