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