qiniu_http_client/client/resolver/
chained.rs

1use super::{
2    super::{ResponseError, ResponseErrorKind},
3    ResolveOptions, ResolveResult, Resolver,
4};
5use std::{collections::VecDeque, mem::take, sync::Arc};
6
7#[cfg(feature = "async")]
8use futures::future::BoxFuture;
9
10/// 域名解析串
11///
12/// 将多个域名解析器串联起来,遍历并找寻第一个可用的解析结果
13#[derive(Debug, Clone)]
14pub struct ChainedResolver {
15    resolvers: Arc<[Box<dyn Resolver>]>,
16}
17
18impl ChainedResolver {
19    /// 创建域名解析串构建器
20    #[inline]
21    pub fn builder(first_resolver: impl Resolver + 'static) -> ChainedResolverBuilder {
22        ChainedResolverBuilder::new(first_resolver)
23    }
24}
25
26impl Resolver for ChainedResolver {
27    fn resolve(&self, domain: &str, opts: ResolveOptions) -> ResolveResult {
28        let mut last_result: Option<ResolveResult> = None;
29        for resolver in self.resolvers.iter() {
30            match resolver.resolve(domain, opts) {
31                Ok(answers) if !answers.ip_addrs().is_empty() => return Ok(answers),
32                result => last_result = Some(result),
33            }
34        }
35        last_result.unwrap_or_else(|| Err(no_try_error(opts)))
36    }
37
38    #[cfg(feature = "async")]
39    #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
40    fn async_resolve<'a>(&'a self, domain: &'a str, opts: ResolveOptions<'a>) -> BoxFuture<'a, ResolveResult> {
41        Box::pin(async move {
42            let mut last_result: Option<ResolveResult> = None;
43            for resolver in self.resolvers.iter() {
44                match resolver.async_resolve(domain, opts).await {
45                    Ok(answers) if !answers.ip_addrs().is_empty() => return Ok(answers),
46                    result => last_result = Some(result),
47                }
48            }
49            last_result.unwrap_or_else(|| Err(no_try_error(opts)))
50        })
51    }
52}
53
54fn no_try_error(opts: ResolveOptions) -> ResponseError {
55    let mut err = ResponseError::new_with_msg(ResponseErrorKind::NoTry, "None resolver is tried");
56    if let Some(retried) = opts.retried() {
57        err = err.retried(retried);
58    }
59    err
60}
61
62impl FromIterator<Box<dyn Resolver>> for ChainedResolver {
63    #[inline]
64    fn from_iter<T: IntoIterator<Item = Box<dyn Resolver>>>(iter: T) -> Self {
65        ChainedResolverBuilder::from_iter(iter).build()
66    }
67}
68
69impl<'a> IntoIterator for &'a ChainedResolver {
70    type Item = &'a Box<dyn Resolver>;
71    type IntoIter = std::slice::Iter<'a, Box<dyn Resolver>>;
72
73    #[inline]
74    fn into_iter(self) -> Self::IntoIter {
75        self.resolvers.iter()
76    }
77}
78
79/// 域名解析串构建器
80#[derive(Debug, Default)]
81pub struct ChainedResolverBuilder {
82    resolvers: VecDeque<Box<dyn Resolver>>,
83}
84
85impl ChainedResolverBuilder {
86    /// 创建域名解析串构建器
87    #[inline]
88    pub fn new(first_resolver: impl Resolver + 'static) -> Self {
89        let mut builder = Self::default();
90        builder.append_resolver(first_resolver);
91        builder
92    }
93
94    /// 追加域名解析器
95    #[inline]
96    pub fn append_resolver(&mut self, resolver: impl Resolver + 'static) -> &mut Self {
97        self.resolvers.push_back(Box::new(resolver));
98        self
99    }
100
101    /// 前置域名解析器
102    #[inline]
103    pub fn prepend_resolver(&mut self, resolver: impl Resolver + 'static) -> &mut Self {
104        self.resolvers.push_front(Box::new(resolver));
105        self
106    }
107
108    /// 构建域名解析串
109    #[inline]
110    pub fn build(&mut self) -> ChainedResolver {
111        assert!(
112            !self.resolvers.is_empty(),
113            "ChainedResolverBuilder must owns at least one Resolver"
114        );
115        ChainedResolver {
116            resolvers: Vec::from(take(&mut self.resolvers)).into_boxed_slice().into(),
117        }
118    }
119}
120
121impl FromIterator<Box<dyn Resolver>> for ChainedResolverBuilder {
122    #[inline]
123    fn from_iter<T: IntoIterator<Item = Box<dyn Resolver>>>(iter: T) -> Self {
124        ChainedResolverBuilder {
125            resolvers: VecDeque::from_iter(iter),
126        }
127    }
128}
129
130impl Extend<Box<dyn Resolver>> for ChainedResolverBuilder {
131    #[inline]
132    fn extend<T: IntoIterator<Item = Box<dyn Resolver>>>(&mut self, iter: T) {
133        self.resolvers.extend(iter)
134    }
135}
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use crate::test_utils::{make_dumb_resolver, make_error_resolver, make_static_resolver};
140    use qiniu_http::ResponseErrorKind;
141    use std::{
142        error::Error,
143        net::{IpAddr, Ipv4Addr},
144        result::Result,
145    };
146
147    const IPS: &[IpAddr] = &[
148        IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
149        IpAddr::V4(Ipv4Addr::new(2, 2, 2, 2)),
150    ];
151
152    #[test]
153    fn test_chained_resolver() -> Result<(), Box<dyn Error>> {
154        let resolver = ChainedResolver::builder(make_static_resolver(IPS.to_vec().into()))
155            .prepend_resolver(make_dumb_resolver())
156            .prepend_resolver(make_error_resolver(
157                ResponseErrorKind::LocalIoError.into(),
158                "Test Local IO Error",
159            ))
160            .build();
161
162        let ips = resolver.resolve("testdomain.com", Default::default())?;
163        assert_eq!(ips.ip_addrs(), IPS);
164
165        let resolver = ChainedResolver::builder(make_dumb_resolver())
166            .prepend_resolver(make_static_resolver(IPS.to_vec().into()))
167            .prepend_resolver(make_error_resolver(
168                ResponseErrorKind::LocalIoError.into(),
169                "Test Local IO Error",
170            ))
171            .build();
172
173        let ips = resolver.resolve("testdomain.com", Default::default())?;
174        assert_eq!(ips.ip_addrs(), IPS,);
175
176        let resolver = ChainedResolver::builder(make_error_resolver(
177            ResponseErrorKind::LocalIoError.into(),
178            "Test Local IO Error",
179        ))
180        .prepend_resolver(make_dumb_resolver())
181        .prepend_resolver(make_static_resolver(IPS.to_vec().into()))
182        .build();
183
184        let ips = resolver.resolve("testdomain.com", Default::default())?;
185        assert_eq!(ips.ip_addrs(), IPS,);
186
187        Ok(())
188    }
189}