saluki_io/net/dns/
hyper.rs1use std::{
2 future::Future,
3 net::{IpAddr, SocketAddr},
4 pin::Pin,
5 sync::Arc,
6 task::{Context, Poll},
7};
8
9use hickory_resolver::{net::NetError, TokioResolver};
10use hyper_util::client::legacy::connect::{dns::Name, HttpConnector};
11use metrics::Counter;
12use saluki_error::{ErrorContext as _, GenericError};
13use tower::Service;
14
15pub type HickoryHttpConnector = HttpConnector<HickoryResolver>;
17
18#[derive(Clone)]
20pub struct HickoryResolver {
21 resolver: Arc<TokioResolver>,
22 lookup_errors: Option<Counter>,
23}
24
25impl HickoryResolver {
26 pub fn from_system_conf() -> Result<Self, GenericError> {
36 let resolver = TokioResolver::builder_tokio()
37 .error_context("Failed to load the system resolver configuration.")?
38 .build()
39 .error_context("Failed to build the resolver.")?;
40
41 Ok(Self {
42 resolver: Arc::new(resolver),
43 lookup_errors: None,
44 })
45 }
46
47 pub fn noop() -> Self {
54 use hickory_resolver::{config::ResolverConfig, net::runtime::TokioRuntimeProvider};
55 let config = ResolverConfig::from_parts(None, vec![], vec![]);
56 let resolver = TokioResolver::builder_with_config(config, TokioRuntimeProvider::default())
57 .build()
58 .expect("noop resolver with empty config should always succeed");
59 Self {
60 resolver: Arc::new(resolver),
61 lookup_errors: None,
62 }
63 }
64
65 pub fn with_lookup_errors_counter(mut self, counter: Counter) -> Self {
67 self.lookup_errors = Some(counter);
68 self
69 }
70
71 pub fn into_http_connector(self) -> HickoryHttpConnector {
73 HickoryHttpConnector::new_with_resolver(self)
74 }
75}
76
77impl Service<Name> for HickoryResolver {
78 type Response = SocketAddrs;
79 type Error = NetError;
80
81 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
82
83 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84 Poll::Ready(Ok(()))
85 }
86
87 fn call(&mut self, name: Name) -> Self::Future {
88 let resolver = self.resolver.clone();
89 let lookup_errors = self.lookup_errors.clone();
90
91 Box::pin(async move {
92 let response = match resolver.lookup_ip(name.as_str()).await {
93 Ok(response) => response,
94 Err(error) => {
95 if let Some(lookup_errors) = lookup_errors {
96 lookup_errors.increment(1);
97 }
98 return Err(error);
99 }
100 };
101 Ok(response.iter().collect())
102 })
103 }
104}
105
106pub struct SocketAddrs(Vec<IpAddr>);
107
108impl Iterator for SocketAddrs {
109 type Item = SocketAddr;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 self.0.pop().map(|ip| SocketAddr::new(ip, 0))
113 }
114}
115
116impl FromIterator<IpAddr> for SocketAddrs {
117 fn from_iter<I: IntoIterator<Item = IpAddr>>(iter: I) -> Self {
118 Self(iter.into_iter().collect())
119 }
120}