qiniu_http_client/client/resolver/
chained.rs1use 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#[derive(Debug, Clone)]
14pub struct ChainedResolver {
15 resolvers: Arc<[Box<dyn Resolver>]>,
16}
17
18impl ChainedResolver {
19 #[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#[derive(Debug, Default)]
81pub struct ChainedResolverBuilder {
82 resolvers: VecDeque<Box<dyn Resolver>>,
83}
84
85impl ChainedResolverBuilder {
86 #[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 #[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 #[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 #[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}