qiniu_http_client/client/resolver/
simple.rs

1use 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/// 简单域名解析器
10///
11/// 基于 [`libc`](https://man7.org/linux/man-pages/man7/libc.7.html) 库的域名解析接口实现
12#[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}