Coverage Report

Created: 2026-01-30 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/reqwest-0.12.24/src/dns/resolve.rs
Line
Count
Source
1
use hyper_util::client::legacy::connect::dns::Name as HyperName;
2
use tower_service::Service;
3
4
use std::collections::HashMap;
5
use std::future::Future;
6
use std::net::SocketAddr;
7
use std::pin::Pin;
8
use std::str::FromStr;
9
use std::sync::Arc;
10
use std::task::{Context, Poll};
11
12
use crate::error::BoxError;
13
14
/// Alias for an `Iterator` trait object over `SocketAddr`.
15
pub type Addrs = Box<dyn Iterator<Item = SocketAddr> + Send>;
16
17
/// Alias for the `Future` type returned by a DNS resolver.
18
pub type Resolving = Pin<Box<dyn Future<Output = Result<Addrs, BoxError>> + Send>>;
19
20
/// Trait for customizing DNS resolution in reqwest.
21
pub trait Resolve: Send + Sync {
22
    /// Performs DNS resolution on a `Name`.
23
    /// The return type is a future containing an iterator of `SocketAddr`.
24
    ///
25
    /// It differs from `tower_service::Service<Name>` in several ways:
26
    ///  * It is assumed that `resolve` will always be ready to poll.
27
    ///  * It does not need a mutable reference to `self`.
28
    ///  * Since trait objects cannot make use of associated types, it requires
29
    ///    wrapping the returned `Future` and its contained `Iterator` with `Box`.
30
    ///
31
    /// Explicitly specified port in the URL will override any port in the resolved `SocketAddr`s.
32
    /// Otherwise, port `0` will be replaced by the conventional port for the given scheme (e.g. 80 for http).
33
    fn resolve(&self, name: Name) -> Resolving;
34
}
35
36
/// A name that must be resolved to addresses.
37
#[derive(Debug)]
38
pub struct Name(pub(super) HyperName);
39
40
/// A more general trait implemented for types implementing `Resolve`.
41
///
42
/// Unnameable, only exported to aid seeing what implements this.
43
pub trait IntoResolve {
44
    #[doc(hidden)]
45
    fn into_resolve(self) -> Arc<dyn Resolve>;
46
}
47
48
impl Name {
49
    /// View the name as a string.
50
0
    pub fn as_str(&self) -> &str {
51
0
        self.0.as_str()
52
0
    }
53
}
54
55
impl FromStr for Name {
56
    type Err = sealed::InvalidNameError;
57
58
0
    fn from_str(host: &str) -> Result<Self, Self::Err> {
59
0
        HyperName::from_str(host)
60
0
            .map(Name)
61
0
            .map_err(|_| sealed::InvalidNameError { _ext: () })
62
0
    }
63
}
64
65
#[derive(Clone)]
66
pub(crate) struct DynResolver {
67
    resolver: Arc<dyn Resolve>,
68
}
69
70
impl DynResolver {
71
0
    pub(crate) fn new(resolver: Arc<dyn Resolve>) -> Self {
72
0
        Self { resolver }
73
0
    }
74
75
    #[cfg(feature = "socks")]
76
    pub(crate) fn gai() -> Self {
77
        Self::new(Arc::new(super::gai::GaiResolver::new()))
78
    }
79
80
    /// Resolve an HTTP host and port, not just a domain name.
81
    ///
82
    /// This does the same thing that hyper-util's HttpConnector does, before
83
    /// calling out to its underlying DNS resolver.
84
    #[cfg(feature = "socks")]
85
    pub(crate) async fn http_resolve(
86
        &self,
87
        target: &http::Uri,
88
    ) -> Result<impl Iterator<Item = std::net::SocketAddr>, BoxError> {
89
        let host = target.host().ok_or("missing host")?;
90
        let port = target
91
            .port_u16()
92
            .unwrap_or_else(|| match target.scheme_str() {
93
                Some("https") => 443,
94
                Some("socks4") | Some("socks4a") | Some("socks5") | Some("socks5h") => 1080,
95
                _ => 80,
96
            });
97
98
        let explicit_port = target.port().is_some();
99
100
        let addrs = self.resolver.resolve(host.parse()?).await?;
101
102
        Ok(addrs.map(move |mut addr| {
103
            if explicit_port || addr.port() == 0 {
104
                addr.set_port(port);
105
            }
106
            addr
107
        }))
108
    }
109
}
110
111
impl Service<HyperName> for DynResolver {
112
    type Response = Addrs;
113
    type Error = BoxError;
114
    type Future = Resolving;
115
116
0
    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
117
0
        Poll::Ready(Ok(()))
118
0
    }
119
120
0
    fn call(&mut self, name: HyperName) -> Self::Future {
121
0
        self.resolver.resolve(Name(name))
122
0
    }
123
}
124
125
pub(crate) struct DnsResolverWithOverrides {
126
    dns_resolver: Arc<dyn Resolve>,
127
    overrides: Arc<HashMap<String, Vec<SocketAddr>>>,
128
}
129
130
impl DnsResolverWithOverrides {
131
0
    pub(crate) fn new(
132
0
        dns_resolver: Arc<dyn Resolve>,
133
0
        overrides: HashMap<String, Vec<SocketAddr>>,
134
0
    ) -> Self {
135
0
        DnsResolverWithOverrides {
136
0
            dns_resolver,
137
0
            overrides: Arc::new(overrides),
138
0
        }
139
0
    }
140
}
141
142
impl Resolve for DnsResolverWithOverrides {
143
0
    fn resolve(&self, name: Name) -> Resolving {
144
0
        match self.overrides.get(name.as_str()) {
145
0
            Some(dest) => {
146
0
                let addrs: Addrs = Box::new(dest.clone().into_iter());
147
0
                Box::pin(std::future::ready(Ok(addrs)))
148
            }
149
0
            None => self.dns_resolver.resolve(name),
150
        }
151
0
    }
152
}
153
154
impl IntoResolve for Arc<dyn Resolve> {
155
0
    fn into_resolve(self) -> Arc<dyn Resolve> {
156
0
        self
157
0
    }
158
}
159
160
impl<R> IntoResolve for Arc<R>
161
where
162
    R: Resolve + 'static,
163
{
164
0
    fn into_resolve(self) -> Arc<dyn Resolve> {
165
0
        self
166
0
    }
167
}
168
169
impl<R> IntoResolve for R
170
where
171
    R: Resolve + 'static,
172
{
173
0
    fn into_resolve(self) -> Arc<dyn Resolve> {
174
0
        Arc::new(self)
175
0
    }
176
}
177
178
mod sealed {
179
    use std::fmt;
180
181
    #[derive(Debug)]
182
    pub struct InvalidNameError {
183
        pub(super) _ext: (),
184
    }
185
186
    impl fmt::Display for InvalidNameError {
187
0
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188
0
            f.write_str("invalid DNS name")
189
0
        }
190
    }
191
192
    impl std::error::Error for InvalidNameError {}
193
}