|  |  |  | @@ -6,26 +6,12 @@ | 
		
	
		
			
				|  |  |  |  | //!   establishes connections over TCP. | 
		
	
		
			
				|  |  |  |  | //! - The [`Connect`](Connect) trait and related types to build custom connectors. | 
		
	
		
			
				|  |  |  |  | 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; | 
		
	
		
			
				|  |  |  |  | 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 super::dns; | 
		
	
		
			
				|  |  |  |  | use self::http_connector::HttpConnectorBlockingTask; | 
		
	
		
			
				|  |  |  |  | #[cfg(feature = "runtime")] pub use self::http::HttpConnector; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// Connect to a destination, returning an IO transport. | 
		
	
		
			
				|  |  |  |  | /// | 
		
	
	
		
			
				
					
					|  |  |  | @@ -135,367 +121,393 @@ impl Connected { | 
		
	
		
			
				|  |  |  |  |     */ | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | fn connect(addr: &SocketAddr, handle: &Option<Handle>) -> io::Result<ConnectFuture> { | 
		
	
		
			
				|  |  |  |  |     if let Some(ref handle) = *handle { | 
		
	
		
			
				|  |  |  |  |         let builder = match addr { | 
		
	
		
			
				|  |  |  |  |             &SocketAddr::V4(_) => TcpBuilder::new_v4()?, | 
		
	
		
			
				|  |  |  |  |             &SocketAddr::V6(_) => TcpBuilder::new_v6()?, | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  | #[cfg(feature = "runtime")] | 
		
	
		
			
				|  |  |  |  | mod http { | 
		
	
		
			
				|  |  |  |  |     use super::*; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         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() | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |     use std::fmt; | 
		
	
		
			
				|  |  |  |  |     use std::io; | 
		
	
		
			
				|  |  |  |  |     use std::mem; | 
		
	
		
			
				|  |  |  |  |     use std::net::SocketAddr; | 
		
	
		
			
				|  |  |  |  |     use std::sync::Arc; | 
		
	
		
			
				|  |  |  |  |     use std::time::Duration; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     use futures::{Async, Poll}; | 
		
	
		
			
				|  |  |  |  |     use futures::future::{Executor, ExecuteError}; | 
		
	
		
			
				|  |  |  |  |     use futures::sync::oneshot; | 
		
	
		
			
				|  |  |  |  |     use futures_cpupool::{Builder as CpuPoolBuilder}; | 
		
	
		
			
				|  |  |  |  |     use http::uri::Scheme; | 
		
	
		
			
				|  |  |  |  |     use net2::TcpBuilder; | 
		
	
		
			
				|  |  |  |  |     use tokio_reactor::Handle; | 
		
	
		
			
				|  |  |  |  |     use tokio_tcp::{TcpStream, ConnectFuture}; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     use super::super::dns; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     use self::http_connector::HttpConnectorBlockingTask; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     fn connect(addr: &SocketAddr, handle: &Option<Handle>) -> io::Result<ConnectFuture> { | 
		
	
		
			
				|  |  |  |  |         if let Some(ref handle) = *handle { | 
		
	
		
			
				|  |  |  |  |             let builder = match addr { | 
		
	
		
			
				|  |  |  |  |                 &SocketAddr::V4(_) => TcpBuilder::new_v4()?, | 
		
	
		
			
				|  |  |  |  |                 &SocketAddr::V6(_) => TcpBuilder::new_v6()?, | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |             builder.bind(any)?; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             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)) | 
		
	
		
			
				|  |  |  |  |         } else { | 
		
	
		
			
				|  |  |  |  |             Ok(TcpStream::connect(addr)) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// A connector for the `http` scheme. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Performs DNS resolution in a thread pool, and then connects over TCP. | 
		
	
		
			
				|  |  |  |  |     #[derive(Clone)] | 
		
	
		
			
				|  |  |  |  |     pub struct HttpConnector { | 
		
	
		
			
				|  |  |  |  |         executor: HttpConnectExecutor, | 
		
	
		
			
				|  |  |  |  |         enforce_http: bool, | 
		
	
		
			
				|  |  |  |  |         handle: Option<Handle>, | 
		
	
		
			
				|  |  |  |  |         keep_alive_timeout: Option<Duration>, | 
		
	
		
			
				|  |  |  |  |         nodelay: bool, | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     impl HttpConnector { | 
		
	
		
			
				|  |  |  |  |         /// Construct a new HttpConnector. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// Takes number of DNS worker threads. | 
		
	
		
			
				|  |  |  |  |         #[inline] | 
		
	
		
			
				|  |  |  |  |         pub fn new(threads: usize) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |             HttpConnector::new_with_handle_opt(threads, None) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, handle)) | 
		
	
		
			
				|  |  |  |  |     } else { | 
		
	
		
			
				|  |  |  |  |         Ok(TcpStream::connect(addr)) | 
		
	
		
			
				|  |  |  |  |         /// Construct a new HttpConnector with a specific Tokio handle. | 
		
	
		
			
				|  |  |  |  |         pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |             HttpConnector::new_with_handle_opt(threads, Some(handle)) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |             let pool = CpuPoolBuilder::new() | 
		
	
		
			
				|  |  |  |  |                 .name_prefix("hyper-dns") | 
		
	
		
			
				|  |  |  |  |                 .pool_size(threads) | 
		
	
		
			
				|  |  |  |  |                 .create(); | 
		
	
		
			
				|  |  |  |  |             HttpConnector::new_with_executor(pool, handle) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         /// Construct a new HttpConnector. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// Takes an executor to run blocking tasks on. | 
		
	
		
			
				|  |  |  |  |         pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector | 
		
	
		
			
				|  |  |  |  |             where E: Executor<HttpConnectorBlockingTask> + Send + Sync | 
		
	
		
			
				|  |  |  |  |         { | 
		
	
		
			
				|  |  |  |  |             HttpConnector { | 
		
	
		
			
				|  |  |  |  |                 executor: HttpConnectExecutor(Arc::new(executor)), | 
		
	
		
			
				|  |  |  |  |                 enforce_http: true, | 
		
	
		
			
				|  |  |  |  |                 handle, | 
		
	
		
			
				|  |  |  |  |                 keep_alive_timeout: None, | 
		
	
		
			
				|  |  |  |  |                 nodelay: false, | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         /// Option to enforce all `Uri`s have the `http` scheme. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// Enabled by default. | 
		
	
		
			
				|  |  |  |  |         #[inline] | 
		
	
		
			
				|  |  |  |  |         pub fn enforce_http(&mut self, is_enforced: bool) { | 
		
	
		
			
				|  |  |  |  |             self.enforce_http = is_enforced; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// If `None`, the option will not be set. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// Default is `None`. | 
		
	
		
			
				|  |  |  |  |         #[inline] | 
		
	
		
			
				|  |  |  |  |         pub fn set_keepalive(&mut self, dur: Option<Duration>) { | 
		
	
		
			
				|  |  |  |  |             self.keep_alive_timeout = dur; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`. | 
		
	
		
			
				|  |  |  |  |         /// | 
		
	
		
			
				|  |  |  |  |         /// Default is `false`. | 
		
	
		
			
				|  |  |  |  |         #[inline] | 
		
	
		
			
				|  |  |  |  |         pub fn set_nodelay(&mut self, nodelay: bool) { | 
		
	
		
			
				|  |  |  |  |             self.nodelay = nodelay; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// A connector for the `http` scheme. | 
		
	
		
			
				|  |  |  |  | /// | 
		
	
		
			
				|  |  |  |  | /// Performs DNS resolution in a thread pool, and then connects over TCP. | 
		
	
		
			
				|  |  |  |  | #[derive(Clone)] | 
		
	
		
			
				|  |  |  |  | pub struct HttpConnector { | 
		
	
		
			
				|  |  |  |  |     executor: HttpConnectExecutor, | 
		
	
		
			
				|  |  |  |  |     enforce_http: bool, | 
		
	
		
			
				|  |  |  |  |     handle: Option<Handle>, | 
		
	
		
			
				|  |  |  |  |     keep_alive_timeout: Option<Duration>, | 
		
	
		
			
				|  |  |  |  |     nodelay: bool, | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |     impl fmt::Debug for HttpConnector { | 
		
	
		
			
				|  |  |  |  |         #[inline] | 
		
	
		
			
				|  |  |  |  |         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |             f.debug_struct("HttpConnector") | 
		
	
		
			
				|  |  |  |  |                 .finish() | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     impl Connect for HttpConnector { | 
		
	
		
			
				|  |  |  |  |         type Transport = TcpStream; | 
		
	
		
			
				|  |  |  |  |         type Error = io::Error; | 
		
	
		
			
				|  |  |  |  |         type Future = HttpConnecting; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         fn connect(&self, dst: Destination) -> Self::Future { | 
		
	
		
			
				|  |  |  |  |             trace!( | 
		
	
		
			
				|  |  |  |  |                 "Http::connect; scheme={}, host={}, port={:?}", | 
		
	
		
			
				|  |  |  |  |                 dst.scheme(), | 
		
	
		
			
				|  |  |  |  |                 dst.host(), | 
		
	
		
			
				|  |  |  |  |                 dst.port(), | 
		
	
		
			
				|  |  |  |  |             ); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             if self.enforce_http { | 
		
	
		
			
				|  |  |  |  |                 if dst.uri.scheme_part() != Some(&Scheme::HTTP) { | 
		
	
		
			
				|  |  |  |  |                     return invalid_url(InvalidUrl::NotHttp, &self.handle); | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } else if dst.uri.scheme_part().is_none() { | 
		
	
		
			
				|  |  |  |  |                 return invalid_url(InvalidUrl::MissingScheme, &self.handle); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             let host = match dst.uri.host() { | 
		
	
		
			
				|  |  |  |  |                 Some(s) => s, | 
		
	
		
			
				|  |  |  |  |                 None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle), | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |             let port = match dst.uri.port() { | 
		
	
		
			
				|  |  |  |  |                 Some(port) => port, | 
		
	
		
			
				|  |  |  |  |                 None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 }, | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             HttpConnecting { | 
		
	
		
			
				|  |  |  |  |                 state: State::Lazy(self.executor.clone(), host.into(), port), | 
		
	
		
			
				|  |  |  |  |                 handle: self.handle.clone(), | 
		
	
		
			
				|  |  |  |  |                 keep_alive_timeout: self.keep_alive_timeout, | 
		
	
		
			
				|  |  |  |  |                 nodelay: self.nodelay, | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl HttpConnector { | 
		
	
		
			
				|  |  |  |  |     /// Construct a new HttpConnector. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Takes number of DNS worker threads. | 
		
	
		
			
				|  |  |  |  |     #[inline] | 
		
	
		
			
				|  |  |  |  |     pub fn new(threads: usize) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |         HttpConnector::new_with_handle_opt(threads, None) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// Construct a new HttpConnector with a specific Tokio handle. | 
		
	
		
			
				|  |  |  |  |     pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |         HttpConnector::new_with_handle_opt(threads, Some(handle)) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector { | 
		
	
		
			
				|  |  |  |  |         let pool = CpuPoolBuilder::new() | 
		
	
		
			
				|  |  |  |  |             .name_prefix("hyper-dns") | 
		
	
		
			
				|  |  |  |  |             .pool_size(threads) | 
		
	
		
			
				|  |  |  |  |             .create(); | 
		
	
		
			
				|  |  |  |  |         HttpConnector::new_with_executor(pool, handle) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// Construct a new HttpConnector. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Takes an executor to run blocking tasks on. | 
		
	
		
			
				|  |  |  |  |     pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector | 
		
	
		
			
				|  |  |  |  |         where E: Executor<HttpConnectorBlockingTask> + Send + Sync | 
		
	
		
			
				|  |  |  |  |     { | 
		
	
		
			
				|  |  |  |  |         HttpConnector { | 
		
	
		
			
				|  |  |  |  |             executor: HttpConnectExecutor(Arc::new(executor)), | 
		
	
		
			
				|  |  |  |  |             enforce_http: true, | 
		
	
		
			
				|  |  |  |  |             handle, | 
		
	
		
			
				|  |  |  |  |     fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting { | 
		
	
		
			
				|  |  |  |  |         HttpConnecting { | 
		
	
		
			
				|  |  |  |  |             state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))), | 
		
	
		
			
				|  |  |  |  |             handle: handle.clone(), | 
		
	
		
			
				|  |  |  |  |             keep_alive_timeout: None, | 
		
	
		
			
				|  |  |  |  |             nodelay: false, | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// Option to enforce all `Uri`s have the `http` scheme. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Enabled by default. | 
		
	
		
			
				|  |  |  |  |     #[inline] | 
		
	
		
			
				|  |  |  |  |     pub fn enforce_http(&mut self, is_enforced: bool) { | 
		
	
		
			
				|  |  |  |  |         self.enforce_http = is_enforced; | 
		
	
		
			
				|  |  |  |  |     #[derive(Debug, Clone, Copy)] | 
		
	
		
			
				|  |  |  |  |     enum InvalidUrl { | 
		
	
		
			
				|  |  |  |  |         MissingScheme, | 
		
	
		
			
				|  |  |  |  |         NotHttp, | 
		
	
		
			
				|  |  |  |  |         MissingAuthority, | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// If `None`, the option will not be set. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Default is `None`. | 
		
	
		
			
				|  |  |  |  |     #[inline] | 
		
	
		
			
				|  |  |  |  |     pub fn set_keepalive(&mut self, dur: Option<Duration>) { | 
		
	
		
			
				|  |  |  |  |         self.keep_alive_timeout = dur; | 
		
	
		
			
				|  |  |  |  |     impl fmt::Display for InvalidUrl { | 
		
	
		
			
				|  |  |  |  |         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |             f.write_str(self.description()) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`. | 
		
	
		
			
				|  |  |  |  |     /// | 
		
	
		
			
				|  |  |  |  |     /// Default is `false`. | 
		
	
		
			
				|  |  |  |  |     #[inline] | 
		
	
		
			
				|  |  |  |  |     pub fn set_nodelay(&mut self, nodelay: bool) { | 
		
	
		
			
				|  |  |  |  |         self.nodelay = nodelay; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl fmt::Debug for HttpConnector { | 
		
	
		
			
				|  |  |  |  |     #[inline] | 
		
	
		
			
				|  |  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |         f.debug_struct("HttpConnector") | 
		
	
		
			
				|  |  |  |  |             .finish() | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl Connect for HttpConnector { | 
		
	
		
			
				|  |  |  |  |     type Transport = TcpStream; | 
		
	
		
			
				|  |  |  |  |     type Error = io::Error; | 
		
	
		
			
				|  |  |  |  |     type Future = HttpConnecting; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     fn connect(&self, dst: Destination) -> Self::Future { | 
		
	
		
			
				|  |  |  |  |         trace!( | 
		
	
		
			
				|  |  |  |  |             "Http::connect; scheme={}, host={}, port={:?}", | 
		
	
		
			
				|  |  |  |  |             dst.scheme(), | 
		
	
		
			
				|  |  |  |  |             dst.host(), | 
		
	
		
			
				|  |  |  |  |             dst.port(), | 
		
	
		
			
				|  |  |  |  |         ); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         if self.enforce_http { | 
		
	
		
			
				|  |  |  |  |             if dst.uri.scheme_part() != Some(&Scheme::HTTP) { | 
		
	
		
			
				|  |  |  |  |                 return invalid_url(InvalidUrl::NotHttp, &self.handle); | 
		
	
		
			
				|  |  |  |  |     impl StdError for InvalidUrl { | 
		
	
		
			
				|  |  |  |  |         fn description(&self) -> &str { | 
		
	
		
			
				|  |  |  |  |             match *self { | 
		
	
		
			
				|  |  |  |  |                 InvalidUrl::MissingScheme => "invalid URL, missing scheme", | 
		
	
		
			
				|  |  |  |  |                 InvalidUrl::NotHttp => "invalid URL, scheme must be http", | 
		
	
		
			
				|  |  |  |  |                 InvalidUrl::MissingAuthority => "invalid URL, missing domain", | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } else if dst.uri.scheme_part().is_none() { | 
		
	
		
			
				|  |  |  |  |             return invalid_url(InvalidUrl::MissingScheme, &self.handle); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         let host = match dst.uri.host() { | 
		
	
		
			
				|  |  |  |  |             Some(s) => s, | 
		
	
		
			
				|  |  |  |  |             None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle), | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |         let port = match dst.uri.port() { | 
		
	
		
			
				|  |  |  |  |             Some(port) => port, | 
		
	
		
			
				|  |  |  |  |             None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 }, | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         HttpConnecting { | 
		
	
		
			
				|  |  |  |  |             state: State::Lazy(self.executor.clone(), host.into(), port), | 
		
	
		
			
				|  |  |  |  |             handle: self.handle.clone(), | 
		
	
		
			
				|  |  |  |  |             keep_alive_timeout: self.keep_alive_timeout, | 
		
	
		
			
				|  |  |  |  |             nodelay: self.nodelay, | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | #[inline] | 
		
	
		
			
				|  |  |  |  | fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting { | 
		
	
		
			
				|  |  |  |  |     HttpConnecting { | 
		
	
		
			
				|  |  |  |  |         state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))), | 
		
	
		
			
				|  |  |  |  |         handle: handle.clone(), | 
		
	
		
			
				|  |  |  |  |         keep_alive_timeout: None, | 
		
	
		
			
				|  |  |  |  |         nodelay: false, | 
		
	
		
			
				|  |  |  |  |     /// A Future representing work to connect to a URL. | 
		
	
		
			
				|  |  |  |  |     #[must_use = "futures do nothing unless polled"] | 
		
	
		
			
				|  |  |  |  |     pub struct HttpConnecting { | 
		
	
		
			
				|  |  |  |  |         state: State, | 
		
	
		
			
				|  |  |  |  |         handle: Option<Handle>, | 
		
	
		
			
				|  |  |  |  |         keep_alive_timeout: Option<Duration>, | 
		
	
		
			
				|  |  |  |  |         nodelay: bool, | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | #[derive(Debug, Clone, Copy)] | 
		
	
		
			
				|  |  |  |  | enum InvalidUrl { | 
		
	
		
			
				|  |  |  |  |     MissingScheme, | 
		
	
		
			
				|  |  |  |  |     NotHttp, | 
		
	
		
			
				|  |  |  |  |     MissingAuthority, | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl fmt::Display for InvalidUrl { | 
		
	
		
			
				|  |  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |         f.write_str(self.description()) | 
		
	
		
			
				|  |  |  |  |     enum State { | 
		
	
		
			
				|  |  |  |  |         Lazy(HttpConnectExecutor, String, u16), | 
		
	
		
			
				|  |  |  |  |         Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>), | 
		
	
		
			
				|  |  |  |  |         Connecting(ConnectingTcp), | 
		
	
		
			
				|  |  |  |  |         Error(Option<io::Error>), | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl StdError for InvalidUrl { | 
		
	
		
			
				|  |  |  |  |     fn description(&self) -> &str { | 
		
	
		
			
				|  |  |  |  |         match *self { | 
		
	
		
			
				|  |  |  |  |             InvalidUrl::MissingScheme => "invalid URL, missing scheme", | 
		
	
		
			
				|  |  |  |  |             InvalidUrl::NotHttp => "invalid URL, scheme must be http", | 
		
	
		
			
				|  |  |  |  |             InvalidUrl::MissingAuthority => "invalid URL, missing domain", | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |     impl Future for HttpConnecting { | 
		
	
		
			
				|  |  |  |  |         type Item = (TcpStream, Connected); | 
		
	
		
			
				|  |  |  |  |         type Error = io::Error; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | /// A Future representing work to connect to a URL. | 
		
	
		
			
				|  |  |  |  | #[must_use = "futures do nothing unless polled"] | 
		
	
		
			
				|  |  |  |  | pub struct HttpConnecting { | 
		
	
		
			
				|  |  |  |  |     state: State, | 
		
	
		
			
				|  |  |  |  |     handle: Option<Handle>, | 
		
	
		
			
				|  |  |  |  |     keep_alive_timeout: Option<Duration>, | 
		
	
		
			
				|  |  |  |  |     nodelay: bool, | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | enum State { | 
		
	
		
			
				|  |  |  |  |     Lazy(HttpConnectExecutor, String, u16), | 
		
	
		
			
				|  |  |  |  |     Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>), | 
		
	
		
			
				|  |  |  |  |     Connecting(ConnectingTcp), | 
		
	
		
			
				|  |  |  |  |     Error(Option<io::Error>), | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl Future for HttpConnecting { | 
		
	
		
			
				|  |  |  |  |     type Item = (TcpStream, Connected); | 
		
	
		
			
				|  |  |  |  |     type Error = io::Error; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | 
		
	
		
			
				|  |  |  |  |         loop { | 
		
	
		
			
				|  |  |  |  |             let state; | 
		
	
		
			
				|  |  |  |  |             match self.state { | 
		
	
		
			
				|  |  |  |  |                 State::Lazy(ref 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) { | 
		
	
		
			
				|  |  |  |  |                         state = State::Connecting(ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |                             addrs: addrs, | 
		
	
		
			
				|  |  |  |  |                             current: None | 
		
	
		
			
				|  |  |  |  |                         }) | 
		
	
		
			
				|  |  |  |  |                     } else { | 
		
	
		
			
				|  |  |  |  |                         let host = mem::replace(host, String::new()); | 
		
	
		
			
				|  |  |  |  |                         let work = dns::Work::new(host, port); | 
		
	
		
			
				|  |  |  |  |                         state = State::Resolving(oneshot::spawn(work, executor)); | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 }, | 
		
	
		
			
				|  |  |  |  |                 State::Resolving(ref mut future) => { | 
		
	
		
			
				|  |  |  |  |                     match try!(future.poll()) { | 
		
	
		
			
				|  |  |  |  |                         Async::NotReady => return Ok(Async::NotReady), | 
		
	
		
			
				|  |  |  |  |                         Async::Ready(addrs) => { | 
		
	
		
			
				|  |  |  |  |         fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | 
		
	
		
			
				|  |  |  |  |             loop { | 
		
	
		
			
				|  |  |  |  |                 let state; | 
		
	
		
			
				|  |  |  |  |                 match self.state { | 
		
	
		
			
				|  |  |  |  |                     State::Lazy(ref 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) { | 
		
	
		
			
				|  |  |  |  |                             state = State::Connecting(ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |                                 addrs: addrs, | 
		
	
		
			
				|  |  |  |  |                                 current: None, | 
		
	
		
			
				|  |  |  |  |                                 current: None | 
		
	
		
			
				|  |  |  |  |                             }) | 
		
	
		
			
				|  |  |  |  |                         } else { | 
		
	
		
			
				|  |  |  |  |                             let host = mem::replace(host, String::new()); | 
		
	
		
			
				|  |  |  |  |                             let work = dns::Work::new(host, port); | 
		
	
		
			
				|  |  |  |  |                             state = State::Resolving(oneshot::spawn(work, executor)); | 
		
	
		
			
				|  |  |  |  |                         } | 
		
	
		
			
				|  |  |  |  |                     }; | 
		
	
		
			
				|  |  |  |  |                 }, | 
		
	
		
			
				|  |  |  |  |                 State::Connecting(ref mut c) => { | 
		
	
		
			
				|  |  |  |  |                     let sock = try_ready!(c.poll(&self.handle)); | 
		
	
		
			
				|  |  |  |  |                     }, | 
		
	
		
			
				|  |  |  |  |                     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, | 
		
	
		
			
				|  |  |  |  |                                 }) | 
		
	
		
			
				|  |  |  |  |                             } | 
		
	
		
			
				|  |  |  |  |                         }; | 
		
	
		
			
				|  |  |  |  |                     }, | 
		
	
		
			
				|  |  |  |  |                     State::Connecting(ref mut c) => { | 
		
	
		
			
				|  |  |  |  |                         let sock = try_ready!(c.poll(&self.handle)); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                     if let Some(dur) = self.keep_alive_timeout { | 
		
	
		
			
				|  |  |  |  |                         sock.set_keepalive(Some(dur))?; | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                     sock.set_nodelay(self.nodelay)?; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                     return Ok(Async::Ready((sock, Connected::new()))); | 
		
	
		
			
				|  |  |  |  |                 }, | 
		
	
		
			
				|  |  |  |  |                 State::Error(ref mut e) => return Err(e.take().expect("polled more than once")), | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |             self.state = state; | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl fmt::Debug for HttpConnecting { | 
		
	
		
			
				|  |  |  |  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |         f.pad("HttpConnecting") | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | struct ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |     addrs: dns::IpAddrs, | 
		
	
		
			
				|  |  |  |  |     current: Option<ConnectFuture>, | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | impl ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |     // not a Future, since passing a &Handle to poll | 
		
	
		
			
				|  |  |  |  |     fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> { | 
		
	
		
			
				|  |  |  |  |         let mut err = None; | 
		
	
		
			
				|  |  |  |  |         loop { | 
		
	
		
			
				|  |  |  |  |             if let Some(ref mut current) = self.current { | 
		
	
		
			
				|  |  |  |  |                 match current.poll() { | 
		
	
		
			
				|  |  |  |  |                     Ok(ok) => return Ok(ok), | 
		
	
		
			
				|  |  |  |  |                     Err(e) => { | 
		
	
		
			
				|  |  |  |  |                         trace!("connect error {:?}", e); | 
		
	
		
			
				|  |  |  |  |                         err = Some(e); | 
		
	
		
			
				|  |  |  |  |                         if let Some(addr) = self.addrs.next() { | 
		
	
		
			
				|  |  |  |  |                             debug!("connecting to {}", addr); | 
		
	
		
			
				|  |  |  |  |                             *current = connect(&addr, handle)?; | 
		
	
		
			
				|  |  |  |  |                             continue; | 
		
	
		
			
				|  |  |  |  |                         if let Some(dur) = self.keep_alive_timeout { | 
		
	
		
			
				|  |  |  |  |                             sock.set_keepalive(Some(dur))?; | 
		
	
		
			
				|  |  |  |  |                         } | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                         sock.set_nodelay(self.nodelay)?; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                         return Ok(Async::Ready((sock, Connected::new()))); | 
		
	
		
			
				|  |  |  |  |                     }, | 
		
	
		
			
				|  |  |  |  |                     State::Error(ref mut e) => return Err(e.take().expect("polled more than once")), | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |             } else if let Some(addr) = self.addrs.next() { | 
		
	
		
			
				|  |  |  |  |                 debug!("connecting to {}", addr); | 
		
	
		
			
				|  |  |  |  |                 self.current = Some(connect(&addr, handle)?); | 
		
	
		
			
				|  |  |  |  |                 continue; | 
		
	
		
			
				|  |  |  |  |                 self.state = state; | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             return Err(err.take().expect("missing connect error")); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 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<dns::Work> | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     impl fmt::Debug for HttpConnectorBlockingTask { | 
		
	
		
			
				|  |  |  |  |     impl fmt::Debug for HttpConnecting { | 
		
	
		
			
				|  |  |  |  |         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
		
	
		
			
				|  |  |  |  |             f.pad("HttpConnectorBlockingTask") | 
		
	
		
			
				|  |  |  |  |             f.pad("HttpConnecting") | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     impl Future for HttpConnectorBlockingTask { | 
		
	
		
			
				|  |  |  |  |         type Item = (); | 
		
	
		
			
				|  |  |  |  |         type Error = (); | 
		
	
		
			
				|  |  |  |  |     struct ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |         addrs: dns::IpAddrs, | 
		
	
		
			
				|  |  |  |  |         current: Option<ConnectFuture>, | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         fn poll(&mut self) -> Poll<(), ()> { | 
		
	
		
			
				|  |  |  |  |             self.work.poll() | 
		
	
		
			
				|  |  |  |  |     impl ConnectingTcp { | 
		
	
		
			
				|  |  |  |  |         // not a Future, since passing a &Handle to poll | 
		
	
		
			
				|  |  |  |  |         fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> { | 
		
	
		
			
				|  |  |  |  |             let mut err = None; | 
		
	
		
			
				|  |  |  |  |             loop { | 
		
	
		
			
				|  |  |  |  |                 if let Some(ref mut current) = self.current { | 
		
	
		
			
				|  |  |  |  |                     match current.poll() { | 
		
	
		
			
				|  |  |  |  |                         Ok(ok) => return Ok(ok), | 
		
	
		
			
				|  |  |  |  |                         Err(e) => { | 
		
	
		
			
				|  |  |  |  |                             trace!("connect error {:?}", e); | 
		
	
		
			
				|  |  |  |  |                             err = Some(e); | 
		
	
		
			
				|  |  |  |  |                             if let Some(addr) = self.addrs.next() { | 
		
	
		
			
				|  |  |  |  |                                 debug!("connecting to {}", addr); | 
		
	
		
			
				|  |  |  |  |                                 *current = connect(&addr, handle)?; | 
		
	
		
			
				|  |  |  |  |                                 continue; | 
		
	
		
			
				|  |  |  |  |                             } | 
		
	
		
			
				|  |  |  |  |                         } | 
		
	
		
			
				|  |  |  |  |                     } | 
		
	
		
			
				|  |  |  |  |                 } else if let Some(addr) = self.addrs.next() { | 
		
	
		
			
				|  |  |  |  |                     debug!("connecting to {}", addr); | 
		
	
		
			
				|  |  |  |  |                     self.current = Some(connect(&addr, handle)?); | 
		
	
		
			
				|  |  |  |  |                     continue; | 
		
	
		
			
				|  |  |  |  |                 } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |                 return Err(err.take().expect("missing connect error")); | 
		
	
		
			
				|  |  |  |  |             } | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     // 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<dns::Work> | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         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<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>>> { | 
		
	
		
			
				|  |  |  |  |             self.0.execute(HttpConnectorBlockingTask { work: future }) | 
		
	
		
			
				|  |  |  |  |                 .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work)) | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     #[cfg(test)] | 
		
	
		
			
				|  |  |  |  |     mod tests { | 
		
	
		
			
				|  |  |  |  |         #![allow(deprecated)] | 
		
	
		
			
				|  |  |  |  |         use std::io; | 
		
	
		
			
				|  |  |  |  |         use futures::Future; | 
		
	
		
			
				|  |  |  |  |         use super::{Connect, Destination, HttpConnector}; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         #[test] | 
		
	
		
			
				|  |  |  |  |         fn test_errors_missing_authority() { | 
		
	
		
			
				|  |  |  |  |             let uri = "/foo/bar?baz".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |             let dst = Destination { | 
		
	
		
			
				|  |  |  |  |                 uri, | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |             let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         #[test] | 
		
	
		
			
				|  |  |  |  |         fn test_errors_enforce_http() { | 
		
	
		
			
				|  |  |  |  |             let uri = "https://example.domain/foo/bar?baz".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |             let dst = Destination { | 
		
	
		
			
				|  |  |  |  |                 uri, | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |             let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         #[test] | 
		
	
		
			
				|  |  |  |  |         fn test_errors_missing_scheme() { | 
		
	
		
			
				|  |  |  |  |             let uri = "example.domain".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |             let dst = Destination { | 
		
	
		
			
				|  |  |  |  |                 uri, | 
		
	
		
			
				|  |  |  |  |             }; | 
		
	
		
			
				|  |  |  |  |             let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |             assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | #[derive(Clone)] | 
		
	
		
			
				|  |  |  |  | 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>>> { | 
		
	
		
			
				|  |  |  |  |         self.0.execute(HttpConnectorBlockingTask { work: future }) | 
		
	
		
			
				|  |  |  |  |             .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work)) | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | #[cfg(test)] | 
		
	
		
			
				|  |  |  |  | mod tests { | 
		
	
		
			
				|  |  |  |  |     #![allow(deprecated)] | 
		
	
		
			
				|  |  |  |  |     use std::io; | 
		
	
		
			
				|  |  |  |  |     use futures::Future; | 
		
	
		
			
				|  |  |  |  |     use super::{Connect, Destination, HttpConnector}; | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     #[test] | 
		
	
		
			
				|  |  |  |  |     fn test_errors_missing_authority() { | 
		
	
		
			
				|  |  |  |  |         let uri = "/foo/bar?baz".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |         let dst = Destination { | 
		
	
		
			
				|  |  |  |  |             uri, | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |         let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     #[test] | 
		
	
		
			
				|  |  |  |  |     fn test_errors_enforce_http() { | 
		
	
		
			
				|  |  |  |  |         let uri = "https://example.domain/foo/bar?baz".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |         let dst = Destination { | 
		
	
		
			
				|  |  |  |  |             uri, | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |         let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |     #[test] | 
		
	
		
			
				|  |  |  |  |     fn test_errors_missing_scheme() { | 
		
	
		
			
				|  |  |  |  |         let uri = "example.domain".parse().unwrap(); | 
		
	
		
			
				|  |  |  |  |         let dst = Destination { | 
		
	
		
			
				|  |  |  |  |             uri, | 
		
	
		
			
				|  |  |  |  |         }; | 
		
	
		
			
				|  |  |  |  |         let connector = HttpConnector::new(1); | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |         assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					
					| 
							
							
							
						 |  |  |   |