oxihttp_client/
resolver.rs1use std::future::Future;
3use std::net::SocketAddr;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use hyper_util::client::legacy::connect::dns::Name;
8use oxihttp_core::OxiHttpError;
9use tower_service::Service;
10
11pub trait DnsResolver: Send + Sync + 'static {
15 fn resolve(
17 &self,
18 name: &str,
19 ) -> Pin<Box<dyn Future<Output = Result<Vec<SocketAddr>, OxiHttpError>> + Send>>;
20}
21
22#[derive(Debug, Clone, Default)]
24pub struct GaiDnsResolver;
25
26impl DnsResolver for GaiDnsResolver {
27 fn resolve(
28 &self,
29 name: &str,
30 ) -> Pin<Box<dyn Future<Output = Result<Vec<SocketAddr>, OxiHttpError>> + Send>> {
31 let host = format!("{name}:0");
32 Box::pin(async move {
33 let addrs: Vec<SocketAddr> = tokio::net::lookup_host(host)
34 .await
35 .map_err(OxiHttpError::from)?
36 .collect();
37 Ok(addrs)
38 })
39 }
40}
41
42#[derive(Clone)]
48pub struct BoxResolver(pub(crate) std::sync::Arc<dyn DnsResolver>);
49
50pub struct BoxResolverAddrs(std::vec::IntoIter<SocketAddr>);
54
55impl Iterator for BoxResolverAddrs {
56 type Item = SocketAddr;
57 fn next(&mut self) -> Option<SocketAddr> {
58 self.0.next()
59 }
60}
61
62type BoxError = Box<dyn std::error::Error + Send + Sync>;
63
64impl Service<Name> for BoxResolver {
65 type Response = BoxResolverAddrs;
66 type Error = BoxError;
67 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
68
69 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
70 Poll::Ready(Ok(()))
71 }
72
73 fn call(&mut self, name: Name) -> Self::Future {
74 let resolver = self.0.clone();
75 let host = name.as_str().to_owned();
76 Box::pin(async move {
77 let addrs = resolver
78 .resolve(&host)
79 .await
80 .map_err(|e| Box::new(e) as BoxError)?;
81 Ok(BoxResolverAddrs(addrs.into_iter()))
82 })
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use std::net::SocketAddr;
90
91 struct FixedResolver(Vec<SocketAddr>);
92 impl DnsResolver for FixedResolver {
93 fn resolve(
94 &self,
95 _name: &str,
96 ) -> Pin<Box<dyn Future<Output = Result<Vec<SocketAddr>, OxiHttpError>> + Send>> {
97 let addrs = self.0.clone();
98 Box::pin(async move { Ok(addrs) })
99 }
100 }
101
102 struct EmptyResolver;
103 impl DnsResolver for EmptyResolver {
104 fn resolve(
105 &self,
106 name: &str,
107 ) -> Pin<Box<dyn Future<Output = Result<Vec<SocketAddr>, OxiHttpError>> + Send>> {
108 let name = name.to_owned();
109 Box::pin(async move { Err(OxiHttpError::Dns(format!("no address for {name}"))) })
110 }
111 }
112
113 #[tokio::test]
114 async fn test_gai_resolver() {
115 let r = GaiDnsResolver;
116 let addrs = r.resolve("localhost").await.expect("resolve");
117 assert!(!addrs.is_empty());
118 }
119
120 #[tokio::test]
121 async fn test_fixed_resolver_builds_client() {
122 let resolver = FixedResolver(vec!["127.0.0.1:0".parse().expect("addr")]);
123 let _client = crate::ClientBuilder::new()
124 .with_resolver(resolver)
125 .build_with_resolver()
126 .expect("build");
127 }
128
129 #[tokio::test]
130 async fn test_empty_resolver_error() {
131 let r = EmptyResolver;
132 let result = r.resolve("nonexistent.example").await;
133 assert!(result.is_err());
134 let err = result.expect_err("should be error");
135 assert!(matches!(err, OxiHttpError::Dns(_)));
136 }
137}