//! HTTP Client use std::cell::RefCell; use std::fmt; use std::io; use std::marker::PhantomData; use std::rc::Rc; use std::time::Duration; use futures::{Future, Poll, Stream}; use futures::future::{self, Executor}; #[cfg(feature = "compat")] use http; use tokio::reactor::Handle; pub use tokio_service::Service; use header::{Headers, Host}; use proto; use proto::request; use method::Method; use self::pool::Pool; use uri::{self, Uri}; use version::HttpVersion; pub use proto::response::Response; pub use proto::request::Request; pub use self::connect::{HttpConnector, Connect}; use self::background::{bg, Background}; mod connect; mod dns; mod pool; #[cfg(feature = "compat")] mod compat_impl; #[cfg(feature = "compat")] pub mod compat; /// A Client to make outgoing HTTP requests. // If the Connector is clone, then the Client can be clone easily. pub struct Client { connector: C, executor: Exec, pool: Pool>, } impl Client { /// Create a new Client with the default config. #[inline] pub fn new(handle: &Handle) -> Client { Config::default().build(handle) } } impl Client { /// Configure a Client. /// /// # Example /// /// ```no_run /// # extern crate hyper; /// # extern crate tokio_core; /// /// # fn main() { /// # let core = tokio_core::reactor::Core::new().unwrap(); /// # let handle = core.handle(); /// let client = hyper::Client::configure() /// .keep_alive(true) /// .build(&handle); /// # drop(client); /// # } /// ``` #[inline] pub fn configure() -> Config { Config::default() } } impl Client { // 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"), } } /// Create a new client with a specific connector. #[inline] fn configured(config: Config, exec: Exec) -> Client { Client { connector: config.connector, executor: exec, pool: Pool::new(config.keep_alive, config.keep_alive_timeout) } } } impl Client where C: Connect, B: Stream + 'static, B::Item: AsRef<[u8]>, { /// Send a GET Request using this Client. #[inline] pub fn get(&self, url: Uri) -> FutureResponse { self.request(Request::new(Method::Get, url)) } /// Send a constructed Request using this Client. #[inline] pub fn request(&self, req: Request) -> FutureResponse { self.call(req) } /// Send an `http::Request` using this Client. #[inline] #[cfg(feature = "compat")] pub fn request_compat(&self, req: http::Request) -> compat::CompatFutureResponse { self::compat_impl::future(self.call(req.into())) } /// Convert into a client accepting `http::Request`. #[cfg(feature = "compat")] pub fn into_compat(self) -> compat::CompatClient { self::compat_impl::client(self) } } /// A `Future` that will resolve to an HTTP Response. #[must_use = "futures do nothing unless polled"] pub struct FutureResponse(Box + 'static>); impl fmt::Debug for FutureResponse { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad("Future") } } impl Future for FutureResponse { type Item = Response; type Error = ::Error; fn poll(&mut self) -> Poll { self.0.poll() } } impl Service for Client where C: Connect, B: Stream + 'static, B::Item: AsRef<[u8]>, { type Request = Request; type Response = Response; type Error = ::Error; type Future = FutureResponse; fn call(&self, req: Self::Request) -> Self::Future { match req.version() { HttpVersion::Http10 | HttpVersion::Http11 => (), other => { error!("Request has unsupported version \"{}\"", other); return FutureResponse(Box::new(future::err(::Error::Version))); } } let url = req.uri().clone(); let domain = match uri::scheme_and_authority(&url) { Some(uri) => uri, None => { return FutureResponse(Box::new(future::err(::Error::Io( io::Error::new( io::ErrorKind::InvalidInput, "invalid URI for Client Request" ) )))); } }; let host = Host::new(domain.host().expect("authority implies host").to_owned(), domain.port()); let (mut head, body) = request::split(req); let mut headers = Headers::new(); headers.set(host); headers.extend(head.headers.iter()); head.headers = headers; use futures::Sink; use futures::sync::{mpsc, oneshot}; let checkout = self.pool.checkout(domain.as_ref()); let connect = { let executor = self.executor.clone(); let pool = self.pool.clone(); let pool_key = Rc::new(domain.to_string()); self.connector.connect(url) .and_then(move |io| { let (tx, rx) = mpsc::channel(0); let tx = HyperClient { tx: RefCell::new(tx), should_close: true, }; let pooled = pool.pooled(pool_key, tx); let conn = proto::Conn::<_, _, proto::ClientTransaction, _>::new(io, pooled.clone()); let dispatch = proto::dispatch::Dispatcher::new(proto::dispatch::Client::new(rx), conn); executor.execute(dispatch.map_err(|e| debug!("client connection error: {}", e)))?; Ok(pooled) }) }; let race = checkout.select(connect) .map(|(client, _work)| client) .map_err(|(e, _work)| { // the Pool Checkout cannot error, so the only error // is from the Connector // XXX: should wait on the Checkout? Problem is // that if the connector is failing, it may be that we // never had a pooled stream at all e.into() }); let resp = race.and_then(move |mut client| { let (callback, rx) = oneshot::channel(); client.tx.borrow_mut().start_send(proto::dispatch::ClientMsg::Request(head, body, callback)).unwrap(); client.should_close = false; rx.then(|res| { match res { Ok(Ok(res)) => Ok(res), Ok(Err(err)) => Err(err), Err(_) => panic!("dispatch dropped without returning error"), } }) }); FutureResponse(Box::new(resp)) } } impl Clone for Client { fn clone(&self) -> Client { Client { connector: self.connector.clone(), executor: self.executor.clone(), pool: self.pool.clone(), } } } impl fmt::Debug for Client { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad("Client") } } struct HyperClient { tx: RefCell<::futures::sync::mpsc::Sender>>, should_close: bool, } impl Clone for HyperClient { fn clone(&self) -> HyperClient { HyperClient { tx: self.tx.clone(), should_close: self.should_close, } } } impl Drop for HyperClient { fn drop(&mut self) { if self.should_close { self.should_close = false; let _ = self.tx.borrow_mut().try_send(proto::dispatch::ClientMsg::Close); } } } /// Configuration for a Client pub struct Config { _body_type: PhantomData, //connect_timeout: Duration, connector: C, keep_alive: bool, keep_alive_timeout: Option, //TODO: make use of max_idle config max_idle: usize, no_proto: bool, } /// Phantom type used to signal that `Config` should create a `HttpConnector`. #[derive(Debug, Clone, Copy)] pub struct UseDefaultConnector(()); impl Default for Config { fn default() -> Config { Config { _body_type: PhantomData::, //connect_timeout: Duration::from_secs(10), connector: UseDefaultConnector(()), keep_alive: true, keep_alive_timeout: Some(Duration::from_secs(90)), max_idle: 5, no_proto: false, } } } impl Config { /// Set the body stream to be used by the `Client`. /// /// # Example /// /// ```rust /// # use hyper::client::Config; /// let cfg = Config::default() /// .body::(); /// # drop(cfg); #[inline] pub fn body(self) -> Config { Config { _body_type: PhantomData::, //connect_timeout: self.connect_timeout, connector: self.connector, keep_alive: self.keep_alive, keep_alive_timeout: self.keep_alive_timeout, max_idle: self.max_idle, no_proto: self.no_proto, } } /// Set the `Connect` type to be used. #[inline] pub fn connector(self, val: CC) -> Config { Config { _body_type: self._body_type, //connect_timeout: self.connect_timeout, connector: val, keep_alive: self.keep_alive, keep_alive_timeout: self.keep_alive_timeout, max_idle: self.max_idle, no_proto: self.no_proto, } } /// Enable or disable keep-alive mechanics. /// /// Default is enabled. #[inline] pub fn keep_alive(mut self, val: bool) -> Config { self.keep_alive = val; self } /// Set an optional timeout for idle sockets being kept-alive. /// /// Pass `None` to disable timeout. /// /// Default is 90 seconds. #[inline] pub fn keep_alive_timeout(mut self, val: Option) -> Config { self.keep_alive_timeout = val; self } /* /// Set the timeout for connecting to a URL. /// /// Default is 10 seconds. #[inline] pub fn connect_timeout(mut self, val: Duration) -> Config { self.connect_timeout = val; self } */ #[doc(hidden)] #[deprecated(since="0.11.11", note="no_proto is always enabled")] pub fn no_proto(self) -> Config { self } } impl Config where C: Connect, B: Stream, B::Item: AsRef<[u8]>, { /// Construct the Client with this configuration. #[inline] pub fn build(self, handle: &Handle) -> Client { Client::configured(self, Exec::Handle(handle.clone())) } /// 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 + 'static, { Client::configured(self, Exec::Executor(Rc::new(executor))) } } impl Config where B: Stream, B::Item: AsRef<[u8]>, { /// Construct the Client with this configuration. #[inline] pub fn build(self, handle: &Handle) -> Client { self.connector(HttpConnector::new(4, handle)).build(handle) } } impl fmt::Debug for Config { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Config") .field("keep_alive", &self.keep_alive) .field("keep_alive_timeout", &self.keep_alive_timeout) .field("max_idle", &self.max_idle) .finish() } } impl Clone for Config { fn clone(&self) -> Config { Config { connector: self.connector.clone(), .. *self } } } // ===== impl Exec ===== #[derive(Clone)] enum Exec { Handle(Handle), Executor(Rc>), } impl Exec { fn execute(&self, fut: F) -> io::Result<()> where F: Future + 'static, { match *self { Exec::Handle(ref h) => h.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(()) } } // ===== 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>, } pub fn bg(fut: Box>) -> Background { Background { inner: fut, } } impl Future for Background { type Item = (); type Error = (); fn poll(&mut self) -> Poll { self.inner.poll() } } }