qiniu_http_client/client/resolver/
simple.rs1use super::{super::ResponseError, ResolveOptions, ResolveResult, Resolver};
2use dns_lookup::lookup_host;
3use qiniu_http::ResponseErrorKind as HttpResponseErrorKind;
4use std::io::Error as IOError;
5
6#[cfg(feature = "async")]
7use futures::future::BoxFuture;
8
9#[derive(Default, Debug, Clone, Copy)]
13pub struct SimpleResolver;
14
15impl Resolver for SimpleResolver {
16 #[inline]
17 fn resolve(&self, domain: &str, opts: ResolveOptions) -> ResolveResult {
18 lookup_host(domain)
19 .map(|ips| ips.into_boxed_slice().into())
20 .map_err(|err| convert_io_error_to_response_error(err, opts))
21 }
22
23 #[inline]
24 #[cfg(feature = "async")]
25 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
26 fn async_resolve<'a>(&'a self, domain: &'a str, opts: ResolveOptions<'a>) -> BoxFuture<'a, ResolveResult> {
27 Box::pin(async move { self.resolve(domain, opts) })
28 }
29}
30
31fn convert_io_error_to_response_error(err: IOError, opts: ResolveOptions) -> ResponseError {
32 let mut err = ResponseError::new(HttpResponseErrorKind::DnsServerError.into(), err);
33 if let Some(retried) = opts.retried() {
34 err = err.retried(retried);
35 }
36 err
37}
38
39#[cfg(all(test, feature = "async"))]
40mod tests {
41 use super::*;
42 use anyhow::Result as AnyResult;
43 use std::{
44 collections::HashSet,
45 net::{IpAddr, Ipv4Addr},
46 };
47
48 const DOMAIN: &str = "dns.alidns.com";
49 const IPS: &[IpAddr] = &[
50 IpAddr::V4(Ipv4Addr::new(223, 5, 5, 5)),
51 IpAddr::V4(Ipv4Addr::new(223, 6, 6, 6)),
52 ];
53
54 #[test]
55 fn test_simple_resolver() -> AnyResult<()> {
56 let resolver = SimpleResolver;
57 let ips = resolver.resolve(DOMAIN, Default::default())?;
58 assert!(is_subset_of(IPS, ips.ip_addrs()));
59 Ok(())
60 }
61
62 fn make_set(ips: impl AsRef<[IpAddr]>) -> HashSet<IpAddr> {
63 let mut h = HashSet::new();
64 h.extend(ips.as_ref());
65 h
66 }
67
68 fn is_subset_of(ips1: impl AsRef<[IpAddr]>, ips2: impl AsRef<[IpAddr]>) -> bool {
69 make_set(ips1).is_subset(&make_set(ips2))
70 }
71}