diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 152046f..b9b672f 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -31,7 +31,7 @@ use log::debug; use super::request::{Request, RequestBuilder}; use super::response::Response; use super::Body; -use crate::connect::Connector; +use crate::connect::{Connector, HttpConnector}; #[cfg(feature = "cookies")] use crate::cookie; use crate::into_url::{expect_uri, try_uri}; @@ -91,6 +91,7 @@ struct Config { nodelay: bool, #[cfg(feature = "cookies")] cookie_store: Option, + trust_dns: bool, error: Option, } @@ -138,6 +139,7 @@ impl ClientBuilder { http2_initial_connection_window_size: None, local_address: None, nodelay: false, + trust_dns: cfg!(feature = "trust-dns"), #[cfg(feature = "cookies")] cookie_store: None, }, @@ -169,6 +171,14 @@ impl ClientBuilder { headers.get(USER_AGENT).cloned() } + let http = match config.trust_dns { + false => HttpConnector::new_gai(), + #[cfg(feature = "trust-dns")] + true => HttpConnector::new_trust_dns()?, + #[cfg(not(feature = "trust-dns"))] + true => unreachable!("trust-dns shouldn't be enabled unless the feature is"), + }; + #[cfg(feature = "__tls")] match config.tls { #[cfg(feature = "default-tls")] @@ -195,6 +205,7 @@ impl ClientBuilder { Connector::new_default_tls( + http, tls, proxies.clone(), user_agent(&config.headers), @@ -205,6 +216,7 @@ impl ClientBuilder { #[cfg(feature = "native-tls")] TlsBackend::BuiltNativeTls(conn) => { Connector::from_built_default_tls( + http, conn, proxies.clone(), user_agent(&config.headers), @@ -214,6 +226,7 @@ impl ClientBuilder { #[cfg(feature = "rustls-tls")] TlsBackend::BuiltRustls(conn) => { Connector::new_rustls_tls( + http, conn, proxies.clone(), user_agent(&config.headers), @@ -247,6 +260,7 @@ impl ClientBuilder { } Connector::new_rustls_tls( + http, tls, proxies.clone(), user_agent(&config.headers), @@ -266,7 +280,7 @@ impl ClientBuilder { } #[cfg(not(feature = "__tls"))] - Connector::new(proxies.clone(), config.local_address, config.nodelay)? + Connector::new(http, proxies.clone(), config.local_address, config.nodelay)? }; connector.set_timeout(config.connect_timeout); @@ -793,6 +807,36 @@ impl ClientBuilder { self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured; self } + + /// Enables the [trust-dns](trust_dns_resolver) async resolver instead of a default threadpool using `getaddrinfo`. + /// + /// If the `trust-dns` feature is turned on, the default option is enabled. + /// + /// # Optional + /// + /// This requires the optional `trust-dns` feature to be enabled + #[cfg(feature = "trust-dns")] + pub fn trust_dns(mut self, enable: bool) -> ClientBuilder { + self.config.trust_dns = enable; + self + } + + /// Disables the trust-dns async resolver. + /// + /// This method exists even if the optional `trust-dns` feature is not enabled. + /// This can be used to ensure a `Client` doesn't use the trust-dns async resolver + /// even if another dependency were to enable the optional `trust-dns` feature. + pub fn no_trust_dns(self) -> ClientBuilder { + #[cfg(feature = "trust-dns")] + { + self.trust_dns(false) + } + + #[cfg(not(feature = "trust-dns"))] + { + self + } + } } type HyperClient = hyper::Client; diff --git a/src/blocking/client.rs b/src/blocking/client.rs index 6a82f36..447c2cf 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -481,6 +481,27 @@ impl ClientBuilder { self.with_inner(move |inner| inner.use_preconfigured_tls(tls)) } + /// Enables the [trust-dns](trust_dns_resolver) async resolver instead of a default threadpool using `getaddrinfo`. + /// + /// If the `trust-dns` feature is turned on, the default option is enabled. + /// + /// # Optional + /// + /// This requires the optional `trust-dns` feature to be enabled + #[cfg(feature = "trust-dns")] + pub fn trust_dns(self, enable: bool) -> ClientBuilder { + self.with_inner(|inner| inner.trust_dns(enable)) + } + + /// Disables the trust-dns async resolver. + /// + /// This method exists even if the optional `trust-dns` feature is not enabled. + /// This can be used to ensure a `Client` doesn't use the trust-dns async resolver + /// even if another dependency were to enable the optional `trust-dns` feature. + pub fn no_trust_dns(self) -> ClientBuilder { + self.with_inner(|inner| inner.no_trust_dns()) + } + // private fn with_inner(mut self, func: F) -> ClientBuilder diff --git a/src/connect.rs b/src/connect.rs index f2cb647..c88a846 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -7,6 +7,7 @@ use tokio::io::{AsyncRead, AsyncWrite}; use native_tls_crate::{TlsConnector, TlsConnectorBuilder}; #[cfg(feature = "__tls")] use http::header::HeaderValue; +use futures_util::future::Either; use bytes::{Buf, BufMut}; use std::future::Future; @@ -28,10 +29,80 @@ use self::native_tls_conn::NativeTlsConn; #[cfg(feature = "rustls-tls")] use self::rustls_tls_conn::RustlsTlsConn; -#[cfg(feature = "trust-dns")] -type HttpConnector = hyper::client::HttpConnector; -#[cfg(not(feature = "trust-dns"))] -type HttpConnector = hyper::client::HttpConnector; +#[derive(Clone)] +pub(crate) enum HttpConnector { + Gai(hyper::client::HttpConnector), + #[cfg(feature = "trust-dns")] + TrustDns(hyper::client::HttpConnector), +} + +impl HttpConnector { + pub(crate) fn new_gai() -> Self { + Self::Gai(hyper::client::HttpConnector::new()) + } + + #[cfg(feature = "trust-dns")] + pub(crate) fn new_trust_dns() -> crate::Result { + TrustDnsResolver::new() + .map(hyper::client::HttpConnector::new_with_resolver) + .map(Self::TrustDns) + .map_err(crate::error::builder) + } +} + +macro_rules! impl_http_connector { + ($(fn $name:ident(&mut self, $($par_name:ident: $par_type:ty),*)$( -> $return:ty)?;)+) => { + #[allow(dead_code)] + impl HttpConnector { + $( + fn $name(&mut self, $($par_name: $par_type),*)$( -> $return)? { + match self { + Self::Gai(resolver) => resolver.$name($($par_name),*), + #[cfg(feature = "trust-dns")] + Self::TrustDns(resolver) => resolver.$name($($par_name),*), + } + } + )+ + } + }; +} + +impl_http_connector! { + fn set_local_address(&mut self, addr: Option); + fn enforce_http(&mut self, is_enforced: bool); + fn set_nodelay(&mut self, nodelay: bool); +} + +impl Service for HttpConnector { + type Response = >::Response; + type Error = >::Error; + #[cfg(feature = "trust-dns")] + type Future = Either< + >::Future, + as Service>::Future, + >; + #[cfg(not(feature = "trust-dns"))] + type Future = Either< + >::Future, + >::Future, + >; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + match self { + Self::Gai(resolver) => resolver.poll_ready(cx), + #[cfg(feature = "trust-dns")] + Self::TrustDns(resolver) => resolver.poll_ready(cx), + } + } + + fn call(&mut self, dst: Uri) -> Self::Future { + match self { + Self::Gai(resolver) => Either::Left(resolver.call(dst)), + #[cfg(feature = "trust-dns")] + Self::TrustDns(resolver) => Either::Right(resolver.call(dst)), + } + } +} #[derive(Clone)] pub(crate) struct Connector { @@ -62,6 +133,7 @@ enum Inner { impl Connector { #[cfg(not(feature = "__tls"))] pub(crate) fn new( + mut http: HttpConnector, proxies: Arc>, local_addr: T, nodelay: bool, @@ -69,7 +141,6 @@ impl Connector { where T: Into>, { - let mut http = http_connector()?; http.set_local_address(local_addr.into()); http.set_nodelay(nodelay); Ok(Connector { @@ -82,6 +153,7 @@ impl Connector { #[cfg(feature = "default-tls")] pub(crate) fn new_default_tls( + http: HttpConnector, tls: TlsConnectorBuilder, proxies: Arc>, user_agent: Option, @@ -93,6 +165,7 @@ impl Connector { { let tls = tls.build().map_err(crate::error::builder)?; Self::from_built_default_tls( + http, tls, proxies, user_agent, @@ -103,6 +176,7 @@ impl Connector { #[cfg(feature = "default-tls")] pub(crate) fn from_built_default_tls ( + mut http: HttpConnector, tls: TlsConnector, proxies: Arc>, user_agent: Option, @@ -111,7 +185,6 @@ impl Connector { where T: Into>, { - let mut http = http_connector()?; http.set_local_address(local_addr.into()); http.enforce_http(false); @@ -127,6 +200,7 @@ impl Connector { #[cfg(feature = "rustls-tls")] pub(crate) fn new_rustls_tls( + mut http: HttpConnector, tls: rustls::ClientConfig, proxies: Arc>, user_agent: Option, @@ -136,7 +210,6 @@ impl Connector { where T: Into>, { - let mut http = http_connector()?; http.set_local_address(local_addr.into()); http.enforce_http(false); @@ -413,19 +486,6 @@ fn into_uri(scheme: Scheme, host: Authority) -> Uri { .expect("scheme and authority is valid Uri") } -#[cfg(feature = "trust-dns")] -fn http_connector() -> crate::Result { - TrustDnsResolver::new() - .map(HttpConnector::new_with_resolver) - .map_err(crate::error::builder) -} - -#[cfg(not(feature = "trust-dns"))] -fn http_connector() -> crate::Result { - Ok(HttpConnector::new()) -} - - async fn with_timeout(f: F, timeout: Option) -> Result where F: Future>,