refactor(client): use async/await in HttpConnector (#2019)
Closes #1984
This commit is contained in:
committed by
Sean McArthur
parent
19a7aab51f
commit
30ac01c180
@@ -21,18 +21,16 @@
|
||||
//! Ok::<_, Infallible>(iter::once(IpAddr::from([127, 0, 0, 1])))
|
||||
//! });
|
||||
//! ```
|
||||
use std::{fmt, io, vec};
|
||||
use std::error::Error;
|
||||
use std::net::{
|
||||
IpAddr, Ipv4Addr, Ipv6Addr,
|
||||
SocketAddr, ToSocketAddrs,
|
||||
SocketAddrV4, SocketAddrV6,
|
||||
};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
use std::str::FromStr;
|
||||
use std::task::{self, Poll};
|
||||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
use std::{fmt, io, vec};
|
||||
|
||||
use tokio::task::JoinHandle;
|
||||
use tower_service::Service;
|
||||
use crate::common::{Future, Pin, Poll, task};
|
||||
|
||||
pub(super) use self::sealed::Resolve;
|
||||
|
||||
@@ -60,9 +58,7 @@ pub struct GaiFuture {
|
||||
|
||||
impl Name {
|
||||
pub(super) fn new(host: String) -> Name {
|
||||
Name {
|
||||
host,
|
||||
}
|
||||
Name { host }
|
||||
}
|
||||
|
||||
/// View the hostname as a string slice.
|
||||
@@ -104,13 +100,10 @@ impl fmt::Display for InvalidNameError {
|
||||
|
||||
impl Error for InvalidNameError {}
|
||||
|
||||
|
||||
impl GaiResolver {
|
||||
/// Construct a new `GaiResolver`.
|
||||
pub fn new() -> Self {
|
||||
GaiResolver {
|
||||
_priv: (),
|
||||
}
|
||||
GaiResolver { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,13 +119,12 @@ impl Service<Name> for GaiResolver {
|
||||
fn call(&mut self, name: Name) -> Self::Future {
|
||||
let blocking = tokio::task::spawn_blocking(move || {
|
||||
debug!("resolving host={:?}", name.host);
|
||||
(&*name.host, 0).to_socket_addrs()
|
||||
(&*name.host, 0)
|
||||
.to_socket_addrs()
|
||||
.map(|i| IpAddrs { iter: i })
|
||||
});
|
||||
|
||||
GaiFuture {
|
||||
inner: blocking,
|
||||
}
|
||||
GaiFuture { inner: blocking }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,37 +172,46 @@ pub(super) struct IpAddrs {
|
||||
|
||||
impl IpAddrs {
|
||||
pub(super) fn new(addrs: Vec<SocketAddr>) -> Self {
|
||||
IpAddrs { iter: addrs.into_iter() }
|
||||
IpAddrs {
|
||||
iter: addrs.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_parse(host: &str, port: u16) -> Option<IpAddrs> {
|
||||
if let Ok(addr) = host.parse::<Ipv4Addr>() {
|
||||
let addr = SocketAddrV4::new(addr, port);
|
||||
return Some(IpAddrs { iter: vec![SocketAddr::V4(addr)].into_iter() })
|
||||
return Some(IpAddrs {
|
||||
iter: vec![SocketAddr::V4(addr)].into_iter(),
|
||||
});
|
||||
}
|
||||
let host = host.trim_start_matches('[').trim_end_matches(']');
|
||||
if let Ok(addr) = host.parse::<Ipv6Addr>() {
|
||||
let addr = SocketAddrV6::new(addr, port, 0, 0);
|
||||
return Some(IpAddrs { iter: vec![SocketAddr::V6(addr)].into_iter() })
|
||||
return Some(IpAddrs {
|
||||
iter: vec![SocketAddr::V6(addr)].into_iter(),
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn split_by_preference(self, local_addr: Option<IpAddr>) -> (IpAddrs, IpAddrs) {
|
||||
if let Some(local_addr) = local_addr {
|
||||
let preferred = self.iter
|
||||
let preferred = self
|
||||
.iter
|
||||
.filter(|addr| addr.is_ipv6() == local_addr.is_ipv6())
|
||||
.collect();
|
||||
|
||||
(IpAddrs::new(preferred), IpAddrs::new(vec![]))
|
||||
} else {
|
||||
let preferring_v6 = self.iter
|
||||
let preferring_v6 = self
|
||||
.iter
|
||||
.as_slice()
|
||||
.first()
|
||||
.map(SocketAddr::is_ipv6)
|
||||
.unwrap_or(false);
|
||||
|
||||
let (preferred, fallback) = self.iter
|
||||
let (preferred, fallback) = self
|
||||
.iter
|
||||
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);
|
||||
|
||||
(IpAddrs::new(preferred), IpAddrs::new(fallback))
|
||||
@@ -281,8 +282,15 @@ impl Future for TokioThreadpoolGaiFuture {
|
||||
type Output = Result<GaiAddrs, io::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
match ready!(tokio_executor::threadpool::blocking(|| (self.name.as_str(), 0).to_socket_addrs())) {
|
||||
Ok(Ok(iter)) => Poll::Ready(Ok(GaiAddrs { inner: IpAddrs { iter } })),
|
||||
match ready!(tokio_executor::threadpool::blocking(|| (
|
||||
self.name.as_str(),
|
||||
0
|
||||
)
|
||||
.to_socket_addrs()))
|
||||
{
|
||||
Ok(Ok(iter)) => Poll::Ready(Ok(GaiAddrs {
|
||||
inner: IpAddrs { iter },
|
||||
})),
|
||||
Ok(Err(e)) => Poll::Ready(Err(e)),
|
||||
// a BlockingError, meaning not on a tokio_executor::threadpool :(
|
||||
Err(e) => Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))),
|
||||
@@ -292,15 +300,15 @@ impl Future for TokioThreadpoolGaiFuture {
|
||||
*/
|
||||
|
||||
mod sealed {
|
||||
use tower_service::Service;
|
||||
use crate::common::{Future, Poll, task};
|
||||
use super::{IpAddr, Name};
|
||||
use crate::common::{task, Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
// "Trait alias" for `Service<Name, Response = Addrs>`
|
||||
pub trait Resolve {
|
||||
type Addrs: Iterator<Item=IpAddr>;
|
||||
type Addrs: Iterator<Item = IpAddr>;
|
||||
type Error: Into<Box<dyn std::error::Error + Send + Sync>>;
|
||||
type Future: Future<Output=Result<Self::Addrs, Self::Error>>;
|
||||
type Future: Future<Output = Result<Self::Addrs, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
fn resolve(&mut self, name: Name) -> Self::Future;
|
||||
@@ -309,7 +317,7 @@ mod sealed {
|
||||
impl<S> Resolve for S
|
||||
where
|
||||
S: Service<Name>,
|
||||
S::Response: Iterator<Item=IpAddr>,
|
||||
S::Response: Iterator<Item = IpAddr>,
|
||||
S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
type Addrs = S::Response;
|
||||
@@ -326,33 +334,49 @@ mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn resolve<R>(resolver: &mut R, name: Name) -> Result<R::Addrs, R::Error>
|
||||
where
|
||||
R: Resolve,
|
||||
{
|
||||
futures_util::future::poll_fn(|cx| resolver.poll_ready(cx)).await?;
|
||||
resolver.resolve(name).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use super::*;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[test]
|
||||
fn test_ip_addrs_split_by_preference() {
|
||||
let v4_addr = (Ipv4Addr::new(127, 0, 0, 1), 80).into();
|
||||
let v6_addr = (Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 80).into();
|
||||
|
||||
let (mut preferred, mut fallback) =
|
||||
IpAddrs { iter: vec![v4_addr, v6_addr].into_iter() }.split_by_preference(None);
|
||||
let (mut preferred, mut fallback) = IpAddrs {
|
||||
iter: vec![v4_addr, v6_addr].into_iter(),
|
||||
}
|
||||
.split_by_preference(None);
|
||||
assert!(preferred.next().unwrap().is_ipv4());
|
||||
assert!(fallback.next().unwrap().is_ipv6());
|
||||
|
||||
let (mut preferred, mut fallback) =
|
||||
IpAddrs { iter: vec![v6_addr, v4_addr].into_iter() }.split_by_preference(None);
|
||||
let (mut preferred, mut fallback) = IpAddrs {
|
||||
iter: vec![v6_addr, v4_addr].into_iter(),
|
||||
}
|
||||
.split_by_preference(None);
|
||||
assert!(preferred.next().unwrap().is_ipv6());
|
||||
assert!(fallback.next().unwrap().is_ipv4());
|
||||
|
||||
let (mut preferred, fallback) =
|
||||
IpAddrs { iter: vec![v4_addr, v6_addr].into_iter() }.split_by_preference(Some(v4_addr.ip()));
|
||||
let (mut preferred, fallback) = IpAddrs {
|
||||
iter: vec![v4_addr, v6_addr].into_iter(),
|
||||
}
|
||||
.split_by_preference(Some(v4_addr.ip()));
|
||||
assert!(preferred.next().unwrap().is_ipv4());
|
||||
assert!(fallback.is_empty());
|
||||
|
||||
let (mut preferred, fallback) =
|
||||
IpAddrs { iter: vec![v4_addr, v6_addr].into_iter() }.split_by_preference(Some(v6_addr.ip()));
|
||||
let (mut preferred, fallback) = IpAddrs {
|
||||
iter: vec![v4_addr, v6_addr].into_iter(),
|
||||
}
|
||||
.split_by_preference(Some(v6_addr.ip()));
|
||||
assert!(preferred.next().unwrap().is_ipv6());
|
||||
assert!(fallback.is_empty());
|
||||
}
|
||||
@@ -370,10 +394,8 @@ mod tests {
|
||||
let uri = ::http::Uri::from_static("http://[::1]:8080/");
|
||||
let dst = super::super::Destination { uri };
|
||||
|
||||
let mut addrs = IpAddrs::try_parse(
|
||||
dst.host(),
|
||||
dst.port().expect("port")
|
||||
).expect("try_parse");
|
||||
let mut addrs =
|
||||
IpAddrs::try_parse(dst.host(), dst.port().expect("port")).expect("try_parse");
|
||||
|
||||
let expected = "[::1]:8080".parse::<SocketAddr>().expect("expected");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user