saluki_io/net/dns/
hyper.rs1use std::{
2 fmt,
3 future::Future,
4 io,
5 pin::Pin,
6 task::{Context, Poll},
7};
8
9use hyper_util::client::legacy::connect::{
10 dns::{GaiAddrs, GaiResolver, Name},
11 HttpConnector,
12};
13use metrics::Counter;
14use tower::Service;
15
16pub type SystemHttpConnector = HttpConnector<SystemResolver>;
18
19#[derive(Clone)]
23pub struct SystemResolver {
24 inner: GaiResolver,
25 lookup_errors: Option<Counter>,
26}
27
28impl SystemResolver {
29 pub fn new() -> Self {
31 Self {
32 inner: GaiResolver::new(),
33 lookup_errors: None,
34 }
35 }
36
37 pub fn with_lookup_errors_counter(mut self, counter: Counter) -> Self {
39 self.lookup_errors = Some(counter);
40 self
41 }
42
43 pub fn into_http_connector(self) -> SystemHttpConnector {
45 SystemHttpConnector::new_with_resolver(self)
46 }
47}
48
49impl Default for SystemResolver {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55#[derive(Debug)]
57pub struct DnsError(io::Error);
58
59impl fmt::Display for DnsError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "failed to resolve host: {}", self.0)
62 }
63}
64
65impl std::error::Error for DnsError {
66 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67 Some(&self.0)
68 }
69}
70
71impl Service<Name> for SystemResolver {
72 type Response = GaiAddrs;
73 type Error = DnsError;
74
75 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
76
77 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
78 Poll::Ready(Ok(()))
80 }
81
82 fn call(&mut self, name: Name) -> Self::Future {
83 let fut = self.inner.call(name);
84 let lookup_errors = self.lookup_errors.clone();
85
86 Box::pin(async move {
87 match fut.await {
88 Ok(addrs) => Ok(addrs),
89 Err(error) => {
90 if let Some(lookup_errors) = lookup_errors {
91 lookup_errors.increment(1);
92 }
93 Err(DnsError(error))
94 }
95 }
96 })
97 }
98}