diff --git a/Cargo.toml b/Cargo.toml index c03a8d12..eae05e80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ arc-swap = "1.7" # Cloud storage support base64 = { version = "0.22", default-features = false, features = ["std"], optional = true } form_urlencoded = { version = "1.2", optional = true } +hickory-resolver = { version = "0.25", optional = true } http-body-util = { version = "0.1.2", optional = true } httparse = { version = "1.8.0", default-features = false, features = ["std"], optional = true } hyper = { version = "1.2", default-features = false, optional = true } @@ -76,6 +77,7 @@ azure = ["cloud", "httparse"] fs = ["walkdir"] gcp = ["cloud", "rustls-pemfile"] aws = ["cloud", "md-5"] +hickory-dns = ["dep:hickory-resolver", "reqwest?/hickory-dns"] http = ["cloud"] tls-webpki-roots = ["reqwest?/rustls-tls-webpki-roots"] integration = ["rand"] diff --git a/src/azure/client.rs b/src/azure/client.rs index 0748ae84..78b0087b 100644 --- a/src/azure/client.rs +++ b/src/azure/client.rs @@ -1567,7 +1567,10 @@ Time:2018-06-14T16:46:54.6040685Z\r server.push_fn(move |req| { assert_eq!(req.method(), Method::PUT); assert_eq!(req.headers().get(IF_NONE_MATCH).unwrap(), "*"); - Response::builder().status(status).body(String::new()).unwrap() + Response::builder() + .status(status) + .body(String::new()) + .unwrap() }); let credential_provider = Arc::new(StaticCredentialProvider::new( diff --git a/src/client/dns.rs b/src/client/dns.rs index 32e9291b..e765e4fe 100644 --- a/src/client/dns.rs +++ b/src/client/dns.rs @@ -16,35 +16,79 @@ // under the License. use std::net::ToSocketAddrs; +use std::sync::Arc; +#[cfg(feature = "hickory-dns")] +use hickory_resolver::{config::LookupIpStrategy, TokioResolver}; use rand::prelude::SliceRandom; use reqwest::dns::{Addrs, Name, Resolve, Resolving}; +#[cfg(feature = "hickory-dns")] +use tokio::sync::OnceCell; use tokio::task::JoinSet; type DynErr = Box; -#[derive(Debug)] +#[derive(Debug, Default)] pub(crate) struct ShuffleResolver; impl Resolve for ShuffleResolver { fn resolve(&self, name: Name) -> Resolving { + resolve_socket_addrs(name) + } +} + +#[cfg(feature = "hickory-dns")] +#[derive(Debug, Default)] +pub(crate) struct HickoryShuffleResolver { + hickory: Arc>, +} + +#[cfg(feature = "hickory-dns")] +impl Resolve for HickoryShuffleResolver { + fn resolve(&self, name: Name) -> Resolving { + let resolver = Arc::clone(&self.hickory); + Box::pin(async move { - // use `JoinSet` to propagate cancelation - let mut tasks = JoinSet::new(); - tasks.spawn_blocking(move || { - let it = (name.as_str(), 0).to_socket_addrs()?; - let mut addrs = it.collect::>(); - - addrs.shuffle(&mut rand::rng()); - - Ok(Box::new(addrs.into_iter()) as Addrs) - }); - - tasks - .join_next() - .await - .expect("spawned on task") - .map_err(|err| Box::new(err) as DynErr)? + let resolver = resolver + .get_or_try_init(|| async { + let mut builder = TokioResolver::builder_tokio() + .map_err(|err| -> DynErr { Box::new(err) })?; + builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4AndIpv6; + Ok::(builder.build()) + }) + .await?; + + let mut addrs = resolver + .lookup_ip(name.as_str()) + .await? + .into_iter() + .map(|ip_addr| std::net::SocketAddr::new(ip_addr, 0)) + .collect::>(); + + addrs.shuffle(&mut rand::rng()); + + Ok(Box::new(addrs.into_iter()) as Addrs) }) } } + +fn resolve_socket_addrs(name: Name) -> Resolving { + Box::pin(async move { + // use `JoinSet` to propagate cancelation + let mut tasks = JoinSet::new(); + tasks.spawn_blocking(move || { + let it = (name.as_str(), 0).to_socket_addrs()?; + let mut addrs = it.collect::>(); + + addrs.shuffle(&mut rand::rng()); + + Ok(Box::new(addrs.into_iter()) as Addrs) + }); + + tasks + .join_next() + .await + .expect("spawned on task") + .map_err(|err| Box::new(err) as DynErr)? + }) +} diff --git a/src/client/mod.rs b/src/client/mod.rs index a71814b9..0da754f9 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -706,7 +706,15 @@ impl ClientOptions { builder = builder.no_gzip().no_brotli().no_zstd().no_deflate(); if self.randomize_addresses.get()? { - builder = builder.dns_resolver(Arc::new(dns::ShuffleResolver)); + #[cfg(feature = "hickory-dns")] + { + builder = builder.dns_resolver(Arc::new(dns::HickoryShuffleResolver::default())); + } + + #[cfg(not(feature = "hickory-dns"))] + { + builder = builder.dns_resolver(Arc::new(dns::ShuffleResolver::default())); + } } builder