ureq/unversioned/
resolver.rs1use std::fmt::{self, Debug};
14use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs};
15use std::sync::mpsc::{self, RecvTimeoutError};
16use std::thread::{self};
17use std::vec::IntoIter;
18
19use http::Uri;
20use http::uri::{Authority, Scheme};
21
22use crate::Error;
23use crate::config::Config;
24use crate::http;
25use crate::transport::NextTimeout;
26use crate::util::{SchemeExt, UriExt};
27
28pub trait Resolver: Debug + Send + Sync + 'static {
30 fn resolve(
37 &self,
38 uri: &Uri,
39 config: &Config,
40 timeout: NextTimeout,
41 ) -> Result<ResolvedSocketAddrs, Error>;
42
43 fn empty(&self) -> ResolvedSocketAddrs {
45 fn uninited_socketaddr() -> SocketAddr {
46 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0)
47 }
48
49 ArrayVec::from_fn(|_| uninited_socketaddr())
50 }
51}
52
53const MAX_ADDRS: usize = 16;
55
56pub use ureq_proto::ArrayVec;
57
58pub type ResolvedSocketAddrs = ArrayVec<SocketAddr, MAX_ADDRS>;
60
61#[derive(Default)]
66pub struct DefaultResolver {
67 _private: (),
68}
69
70impl DefaultResolver {
71 pub fn host_and_port(scheme: &Scheme, authority: &Authority) -> Option<String> {
76 let port = authority.port_u16().or_else(|| scheme.default_port())?;
77
78 Some(format!("{}:{}", authority.host(), port))
79 }
80}
81
82impl Resolver for DefaultResolver {
83 fn resolve(
84 &self,
85 uri: &Uri,
86 config: &Config,
87 timeout: NextTimeout,
88 ) -> Result<ResolvedSocketAddrs, Error> {
89 uri.ensure_valid_url()?;
90
91 let scheme = uri.scheme().unwrap();
93 let authority = uri.authority().unwrap();
94
95 if cfg!(feature = "_test") {
96 let mut v = ArrayVec::from_fn(|_| "0.0.0.0:1".parse().unwrap());
97 v.push(SocketAddr::V4(SocketAddrV4::new(
98 Ipv4Addr::new(10, 0, 0, 1),
99 authority
100 .port_u16()
101 .or_else(|| scheme.default_port())
102 .unwrap(),
104 )));
105 return Ok(v);
106 }
107
108 let addr = DefaultResolver::host_and_port(scheme, authority).unwrap();
111
112 let use_sync = timeout.after.is_not_happening();
114
115 let iter = if use_sync {
116 trace!("Resolve: {}", addr);
117 addr.to_socket_addrs()?
119 } else {
120 trace!("Resolve with timeout ({:?}): {} ", timeout, addr);
121 resolve_async(addr, timeout)?
122 };
123
124 let ip_family = config.ip_family();
125 let wanted = ip_family.keep_wanted(iter);
126
127 let mut result = self.empty();
128 for addr in wanted.take(MAX_ADDRS) {
129 result.push(addr);
130 }
131
132 debug!("Resolved: {:?}", result);
133
134 if result.is_empty() {
135 Err(Error::HostNotFound)
136 } else {
137 Ok(result)
138 }
139 }
140}
141
142fn resolve_async(addr: String, timeout: NextTimeout) -> Result<IntoIter<SocketAddr>, Error> {
143 let (tx, rx) = mpsc::sync_channel(1);
147 thread::spawn(move || tx.send(addr.to_socket_addrs()).ok());
148
149 match rx.recv_timeout(*timeout.after) {
150 Ok(v) => Ok(v?),
151 Err(c) => match c {
152 RecvTimeoutError::Timeout => Err(Error::Timeout(timeout.reason)),
154 RecvTimeoutError::Disconnected => unreachable!("mpsc sender gone"),
156 },
157 }
158}
159
160impl fmt::Debug for DefaultResolver {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 f.debug_struct("DefaultResolver").finish()
163 }
164}
165
166#[cfg(test)]
167mod test {
168 use crate::transport::time::Duration;
169
170 use super::*;
171
172 #[test]
173 fn unknown_scheme() {
174 let uri: Uri = "foo://some:42/123".parse().unwrap();
175 let config = Config::default();
176 let err = DefaultResolver::default()
177 .resolve(
178 &uri,
179 &config,
180 NextTimeout {
181 after: Duration::NotHappening,
182 reason: crate::Timeout::Global,
183 },
184 )
185 .unwrap_err();
186 assert!(matches!(err, Error::BadUri(_)));
187 assert_eq!(err.to_string(), "bad uri: unknown scheme: foo");
188 }
189}