Add connect_timeout to async and sync clients
				
					
				
			This commit is contained in:
		| @@ -66,6 +66,9 @@ struct Config { | |||||||
|     hostname_verification: bool, |     hostname_verification: bool, | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     certs_verification: bool, |     certs_verification: bool, | ||||||
|  |     connect_timeout: Option<Duration>, | ||||||
|  |     #[cfg(feature = "tls")] | ||||||
|  |     identity: Option<Identity>, | ||||||
|     proxies: Vec<Proxy>, |     proxies: Vec<Proxy>, | ||||||
|     redirect_policy: RedirectPolicy, |     redirect_policy: RedirectPolicy, | ||||||
|     referer: bool, |     referer: bool, | ||||||
| @@ -73,8 +76,6 @@ struct Config { | |||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     root_certs: Vec<Certificate>, |     root_certs: Vec<Certificate>, | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     identity: Option<Identity>, |  | ||||||
|     #[cfg(feature = "tls")] |  | ||||||
|     tls: TlsBackend, |     tls: TlsBackend, | ||||||
|     http2_only: bool, |     http2_only: bool, | ||||||
|     local_address: Option<IpAddr>, |     local_address: Option<IpAddr>, | ||||||
| @@ -97,6 +98,7 @@ impl ClientBuilder { | |||||||
|                 hostname_verification: true, |                 hostname_verification: true, | ||||||
|                 #[cfg(feature = "tls")] |                 #[cfg(feature = "tls")] | ||||||
|                 certs_verification: true, |                 certs_verification: true, | ||||||
|  |                 connect_timeout: None, | ||||||
|                 proxies: Vec::new(), |                 proxies: Vec::new(), | ||||||
|                 redirect_policy: RedirectPolicy::default(), |                 redirect_policy: RedirectPolicy::default(), | ||||||
|                 referer: true, |                 referer: true, | ||||||
| @@ -123,7 +125,7 @@ impl ClientBuilder { | |||||||
|         let config = self.config; |         let config = self.config; | ||||||
|         let proxies = Arc::new(config.proxies); |         let proxies = Arc::new(config.proxies); | ||||||
|  |  | ||||||
|         let connector = { |         let mut connector = { | ||||||
|             #[cfg(feature = "tls")] |             #[cfg(feature = "tls")] | ||||||
|             match config.tls { |             match config.tls { | ||||||
|                 #[cfg(feature = "default-tls")] |                 #[cfg(feature = "default-tls")] | ||||||
| @@ -177,6 +179,8 @@ impl ClientBuilder { | |||||||
|             Connector::new(proxies.clone(), config.local_address)? |             Connector::new(proxies.clone(), config.local_address)? | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         connector.set_timeout(config.connect_timeout); | ||||||
|  |  | ||||||
|         let mut builder = ::hyper::Client::builder(); |         let mut builder = ::hyper::Client::builder(); | ||||||
|         if config.http2_only { |         if config.http2_only { | ||||||
|             builder.http2_only(true); |             builder.http2_only(true); | ||||||
| @@ -312,7 +316,8 @@ impl ClientBuilder { | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Set a timeout for both the read and write operations of a client. |     // Currently not used, so hide from docs. | ||||||
|  |     #[doc(hidden)] | ||||||
|     pub fn timeout(mut self, timeout: Duration) -> ClientBuilder { |     pub fn timeout(mut self, timeout: Duration) -> ClientBuilder { | ||||||
|         self.config.timeout = Some(timeout); |         self.config.timeout = Some(timeout); | ||||||
|         self |         self | ||||||
| @@ -324,6 +329,19 @@ impl ClientBuilder { | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Set a timeout for only the connect phase of a `Client`. | ||||||
|  |     /// | ||||||
|  |     /// Default is `None`. | ||||||
|  |     /// | ||||||
|  |     /// # Note | ||||||
|  |     /// | ||||||
|  |     /// This **requires** the futures be executed in a tokio runtime with | ||||||
|  |     /// a tokio timer enabled. | ||||||
|  |     pub fn connect_timeout(mut self, timeout: Duration) -> ClientBuilder { | ||||||
|  |         self.config.connect_timeout = Some(timeout); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[doc(hidden)] |     #[doc(hidden)] | ||||||
|     #[deprecated(note = "DNS no longer uses blocking threads")] |     #[deprecated(note = "DNS no longer uses blocking threads")] | ||||||
|     pub fn dns_threads(self, _threads: usize) -> ClientBuilder { |     pub fn dns_threads(self, _threads: usize) -> ClientBuilder { | ||||||
|   | |||||||
| @@ -281,12 +281,28 @@ impl ClientBuilder { | |||||||
|     /// |     /// | ||||||
|     /// Pass `None` to disable timeout. |     /// Pass `None` to disable timeout. | ||||||
|     pub fn timeout<T>(mut self, timeout: T) -> ClientBuilder |     pub fn timeout<T>(mut self, timeout: T) -> ClientBuilder | ||||||
|     where T: Into<Option<Duration>>, |     where | ||||||
|  |         T: Into<Option<Duration>>, | ||||||
|     { |     { | ||||||
|         self.timeout = Timeout(timeout.into()); |         self.timeout = Timeout(timeout.into()); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Set a timeout for only the connect phase of a `Client`. | ||||||
|  |     /// | ||||||
|  |     /// Default is `None`. | ||||||
|  |     pub fn connect_timeout<T>(self, timeout: T) -> ClientBuilder | ||||||
|  |     where | ||||||
|  |         T: Into<Option<Duration>>, | ||||||
|  |     { | ||||||
|  |         let timeout = timeout.into(); | ||||||
|  |         if let Some(dur) = timeout { | ||||||
|  |             self.with_inner(|inner| inner.connect_timeout(dur)) | ||||||
|  |         } else { | ||||||
|  |             self | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fn with_inner<F>(mut self, func: F) -> ClientBuilder |     fn with_inner<F>(mut self, func: F) -> ClientBuilder | ||||||
|     where |     where | ||||||
|         F: FnOnce(async_impl::ClientBuilder) -> async_impl::ClientBuilder, |         F: FnOnce(async_impl::ClientBuilder) -> async_impl::ClientBuilder, | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ use futures::Future; | |||||||
| use http::uri::Scheme; | use http::uri::Scheme; | ||||||
| use hyper::client::connect::{Connect, Connected, Destination}; | use hyper::client::connect::{Connect, Connected, Destination}; | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
|  | use tokio_timer::Timeout; | ||||||
|  |  | ||||||
|  |  | ||||||
| #[cfg(feature = "default-tls")] | #[cfg(feature = "default-tls")] | ||||||
| @@ -14,6 +15,7 @@ use bytes::BufMut; | |||||||
| use std::io; | use std::io; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use std::net::IpAddr; | use std::net::IpAddr; | ||||||
|  | use std::time::Duration; | ||||||
|  |  | ||||||
| #[cfg(feature = "trust-dns")] | #[cfg(feature = "trust-dns")] | ||||||
| use dns::TrustDnsResolver; | use dns::TrustDnsResolver; | ||||||
| @@ -26,8 +28,9 @@ type HttpConnector = ::hyper::client::HttpConnector; | |||||||
|  |  | ||||||
|  |  | ||||||
| pub(crate) struct Connector { | pub(crate) struct Connector { | ||||||
|  |     inner: Inner, | ||||||
|     proxies: Arc<Vec<Proxy>>, |     proxies: Arc<Vec<Proxy>>, | ||||||
|     inner: Inner |     timeout: Option<Duration>, | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Inner { | enum Inner { | ||||||
| @@ -49,8 +52,9 @@ impl Connector { | |||||||
|         let mut http = http_connector()?; |         let mut http = http_connector()?; | ||||||
|         http.set_local_address(local_addr.into()); |         http.set_local_address(local_addr.into()); | ||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|  |             inner: Inner::Http(http), | ||||||
|             proxies, |             proxies, | ||||||
|             inner: Inner::Http(http) |             timeout: None, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -70,8 +74,9 @@ impl Connector { | |||||||
|         let http = ::hyper_tls::HttpsConnector::from((http, tls.clone())); |         let http = ::hyper_tls::HttpsConnector::from((http, tls.clone())); | ||||||
|  |  | ||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|  |             inner: Inner::DefaultTls(http, tls), | ||||||
|             proxies, |             proxies, | ||||||
|             inner: Inner::DefaultTls(http, tls) |             timeout: None, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -89,10 +94,15 @@ impl Connector { | |||||||
|         let http = ::hyper_rustls::HttpsConnector::from((http, tls.clone())); |         let http = ::hyper_rustls::HttpsConnector::from((http, tls.clone())); | ||||||
|  |  | ||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|  |             inner: Inner::RustlsTls(http, Arc::new(tls)), | ||||||
|             proxies, |             proxies, | ||||||
|             inner: Inner::RustlsTls(http, Arc::new(tls)) |             timeout: None, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub(crate) fn set_timeout(&mut self, timeout: Option<Duration>) { | ||||||
|  |         self.timeout = timeout; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(feature = "trust-dns")] | #[cfg(feature = "trust-dns")] | ||||||
| @@ -113,9 +123,27 @@ impl Connect for Connector { | |||||||
|     type Future = Connecting; |     type Future = Connecting; | ||||||
|  |  | ||||||
|     fn connect(&self, dst: Destination) -> Self::Future { |     fn connect(&self, dst: Destination) -> Self::Future { | ||||||
|  |         macro_rules! timeout { | ||||||
|  |             ($future:expr) => { | ||||||
|  |                 if let Some(dur) = self.timeout { | ||||||
|  |                     Box::new(Timeout::new($future, dur).map_err(|err| { | ||||||
|  |                         if err.is_inner() { | ||||||
|  |                             err.into_inner().expect("is_inner") | ||||||
|  |                         } else if err.is_elapsed() { | ||||||
|  |                             io::Error::new(io::ErrorKind::TimedOut, "connect timed out") | ||||||
|  |                         } else { | ||||||
|  |                             io::Error::new(io::ErrorKind::Other, err) | ||||||
|  |                         } | ||||||
|  |                     })) | ||||||
|  |                 } else { | ||||||
|  |                     Box::new($future) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         macro_rules! connect { |         macro_rules! connect { | ||||||
|             ( $http:expr, $dst:expr, $proxy:expr ) => { |             ( $http:expr, $dst:expr, $proxy:expr ) => { | ||||||
|                 Box::new($http.connect($dst) |                 timeout!($http.connect($dst) | ||||||
|                     .map(|(io, connected)| (Box::new(io) as Conn, connected.proxy($proxy)))) |                     .map(|(io, connected)| (Box::new(io) as Conn, connected.proxy($proxy)))) | ||||||
|             }; |             }; | ||||||
|             ( $dst:expr, $proxy:expr ) => { |             ( $dst:expr, $proxy:expr ) => { | ||||||
| @@ -158,7 +186,7 @@ impl Connect for Connector { | |||||||
|                         let host = dst.host().to_owned(); |                         let host = dst.host().to_owned(); | ||||||
|                         let port = dst.port().unwrap_or(443); |                         let port = dst.port().unwrap_or(443); | ||||||
|                         let tls = tls.clone(); |                         let tls = tls.clone(); | ||||||
|                         return Box::new(http.connect(ndst).and_then(move |(conn, connected)| { |                         return timeout!(http.connect(ndst).and_then(move |(conn, connected)| { | ||||||
|                             trace!("tunneling HTTPS over proxy"); |                             trace!("tunneling HTTPS over proxy"); | ||||||
|                             tunnel(conn, host.clone(), port, auth) |                             tunnel(conn, host.clone(), port, auth) | ||||||
|                                 .and_then(move |tunneled| { |                                 .and_then(move |tunneled| { | ||||||
| @@ -178,7 +206,7 @@ impl Connect for Connector { | |||||||
|                         let host = dst.host().to_owned(); |                         let host = dst.host().to_owned(); | ||||||
|                         let port = dst.port().unwrap_or(443); |                         let port = dst.port().unwrap_or(443); | ||||||
|                         let tls = tls.clone(); |                         let tls = tls.clone(); | ||||||
|                         return Box::new(http.connect(ndst).and_then(move |(conn, connected)| { |                         return timeout!(http.connect(ndst).and_then(move |(conn, connected)| { | ||||||
|                             trace!("tunneling HTTPS over proxy"); |                             trace!("tunneling HTTPS over proxy"); | ||||||
|                             let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) |                             let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) | ||||||
|                                 .map(|dnsname| dnsname.to_owned()) |                                 .map(|dnsname| dnsname.to_owned()) | ||||||
|   | |||||||
| @@ -198,6 +198,7 @@ extern crate serde_urlencoded; | |||||||
| extern crate tokio; | extern crate tokio; | ||||||
| #[cfg_attr(feature = "default-tls", macro_use)] | #[cfg_attr(feature = "default-tls", macro_use)] | ||||||
| extern crate tokio_io; | extern crate tokio_io; | ||||||
|  | extern crate tokio_timer; | ||||||
| #[cfg(feature = "trust-dns")] | #[cfg(feature = "trust-dns")] | ||||||
| extern crate trust_dns_resolver; | extern crate trust_dns_resolver; | ||||||
| extern crate url; | extern crate url; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user