wreq-rb 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +2688 -0
- data/Cargo.toml +6 -0
- data/README.md +179 -0
- data/ext/wreq_rb/Cargo.toml +39 -0
- data/ext/wreq_rb/extconf.rb +22 -0
- data/ext/wreq_rb/src/client.rs +565 -0
- data/ext/wreq_rb/src/error.rs +25 -0
- data/ext/wreq_rb/src/lib.rs +20 -0
- data/ext/wreq_rb/src/response.rs +132 -0
- data/lib/wreq-rb/version.rb +5 -0
- data/lib/wreq-rb.rb +17 -0
- data/patches/0001-add-transfer-size-tracking.patch +292 -0
- data/vendor/wreq/Cargo.toml +306 -0
- data/vendor/wreq/LICENSE +202 -0
- data/vendor/wreq/README.md +122 -0
- data/vendor/wreq/examples/cert_store.rs +77 -0
- data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
- data/vendor/wreq/examples/emulation.rs +118 -0
- data/vendor/wreq/examples/form.rs +14 -0
- data/vendor/wreq/examples/http1_websocket.rs +37 -0
- data/vendor/wreq/examples/http2_websocket.rs +45 -0
- data/vendor/wreq/examples/json_dynamic.rs +41 -0
- data/vendor/wreq/examples/json_typed.rs +47 -0
- data/vendor/wreq/examples/keylog.rs +16 -0
- data/vendor/wreq/examples/request_with_emulation.rs +115 -0
- data/vendor/wreq/examples/request_with_interface.rs +37 -0
- data/vendor/wreq/examples/request_with_local_address.rs +16 -0
- data/vendor/wreq/examples/request_with_proxy.rs +13 -0
- data/vendor/wreq/examples/request_with_redirect.rs +22 -0
- data/vendor/wreq/examples/request_with_version.rs +15 -0
- data/vendor/wreq/examples/tor_socks.rs +24 -0
- data/vendor/wreq/examples/unix_socket.rs +33 -0
- data/vendor/wreq/src/client/body.rs +304 -0
- data/vendor/wreq/src/client/conn/conn.rs +231 -0
- data/vendor/wreq/src/client/conn/connector.rs +549 -0
- data/vendor/wreq/src/client/conn/http.rs +1023 -0
- data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
- data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
- data/vendor/wreq/src/client/conn/proxy.rs +39 -0
- data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
- data/vendor/wreq/src/client/conn/uds.rs +44 -0
- data/vendor/wreq/src/client/conn/verbose.rs +149 -0
- data/vendor/wreq/src/client/conn.rs +323 -0
- data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
- data/vendor/wreq/src/client/core/body/length.rs +118 -0
- data/vendor/wreq/src/client/core/body.rs +34 -0
- data/vendor/wreq/src/client/core/common/buf.rs +149 -0
- data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
- data/vendor/wreq/src/client/core/common/watch.rs +76 -0
- data/vendor/wreq/src/client/core/common.rs +3 -0
- data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
- data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
- data/vendor/wreq/src/client/core/conn.rs +11 -0
- data/vendor/wreq/src/client/core/dispatch.rs +299 -0
- data/vendor/wreq/src/client/core/error.rs +435 -0
- data/vendor/wreq/src/client/core/ext.rs +201 -0
- data/vendor/wreq/src/client/core/http1.rs +178 -0
- data/vendor/wreq/src/client/core/http2.rs +483 -0
- data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
- data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
- data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
- data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
- data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
- data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
- data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
- data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
- data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
- data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
- data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
- data/vendor/wreq/src/client/core/proto.rs +58 -0
- data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
- data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
- data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
- data/vendor/wreq/src/client/core/rt.rs +25 -0
- data/vendor/wreq/src/client/core/upgrade.rs +267 -0
- data/vendor/wreq/src/client/core.rs +16 -0
- data/vendor/wreq/src/client/emulation.rs +161 -0
- data/vendor/wreq/src/client/http/client/error.rs +142 -0
- data/vendor/wreq/src/client/http/client/exec.rs +29 -0
- data/vendor/wreq/src/client/http/client/extra.rs +77 -0
- data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
- data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
- data/vendor/wreq/src/client/http/client/util.rs +104 -0
- data/vendor/wreq/src/client/http/client.rs +1003 -0
- data/vendor/wreq/src/client/http/future.rs +99 -0
- data/vendor/wreq/src/client/http.rs +1629 -0
- data/vendor/wreq/src/client/layer/config/options.rs +156 -0
- data/vendor/wreq/src/client/layer/config.rs +116 -0
- data/vendor/wreq/src/client/layer/cookie.rs +161 -0
- data/vendor/wreq/src/client/layer/decoder.rs +139 -0
- data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
- data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
- data/vendor/wreq/src/client/layer/redirect.rs +145 -0
- data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
- data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
- data/vendor/wreq/src/client/layer/retry.rs +151 -0
- data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
- data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
- data/vendor/wreq/src/client/layer/timeout.rs +177 -0
- data/vendor/wreq/src/client/layer.rs +15 -0
- data/vendor/wreq/src/client/multipart.rs +717 -0
- data/vendor/wreq/src/client/request.rs +818 -0
- data/vendor/wreq/src/client/response.rs +534 -0
- data/vendor/wreq/src/client/ws/json.rs +99 -0
- data/vendor/wreq/src/client/ws/message.rs +453 -0
- data/vendor/wreq/src/client/ws.rs +714 -0
- data/vendor/wreq/src/client.rs +27 -0
- data/vendor/wreq/src/config.rs +140 -0
- data/vendor/wreq/src/cookie.rs +579 -0
- data/vendor/wreq/src/dns/gai.rs +249 -0
- data/vendor/wreq/src/dns/hickory.rs +78 -0
- data/vendor/wreq/src/dns/resolve.rs +180 -0
- data/vendor/wreq/src/dns.rs +69 -0
- data/vendor/wreq/src/error.rs +502 -0
- data/vendor/wreq/src/ext.rs +398 -0
- data/vendor/wreq/src/hash.rs +143 -0
- data/vendor/wreq/src/header.rs +506 -0
- data/vendor/wreq/src/into_uri.rs +187 -0
- data/vendor/wreq/src/lib.rs +586 -0
- data/vendor/wreq/src/proxy/mac.rs +82 -0
- data/vendor/wreq/src/proxy/matcher.rs +806 -0
- data/vendor/wreq/src/proxy/uds.rs +66 -0
- data/vendor/wreq/src/proxy/win.rs +31 -0
- data/vendor/wreq/src/proxy.rs +569 -0
- data/vendor/wreq/src/redirect.rs +575 -0
- data/vendor/wreq/src/retry.rs +198 -0
- data/vendor/wreq/src/sync.rs +129 -0
- data/vendor/wreq/src/tls/conn/cache.rs +123 -0
- data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
- data/vendor/wreq/src/tls/conn/ext.rs +82 -0
- data/vendor/wreq/src/tls/conn/macros.rs +34 -0
- data/vendor/wreq/src/tls/conn/service.rs +138 -0
- data/vendor/wreq/src/tls/conn.rs +681 -0
- data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
- data/vendor/wreq/src/tls/keylog.rs +99 -0
- data/vendor/wreq/src/tls/options.rs +464 -0
- data/vendor/wreq/src/tls/x509/identity.rs +122 -0
- data/vendor/wreq/src/tls/x509/parser.rs +71 -0
- data/vendor/wreq/src/tls/x509/store.rs +228 -0
- data/vendor/wreq/src/tls/x509.rs +68 -0
- data/vendor/wreq/src/tls.rs +154 -0
- data/vendor/wreq/src/trace.rs +55 -0
- data/vendor/wreq/src/util.rs +122 -0
- data/vendor/wreq/tests/badssl.rs +228 -0
- data/vendor/wreq/tests/brotli.rs +350 -0
- data/vendor/wreq/tests/client.rs +1098 -0
- data/vendor/wreq/tests/connector_layers.rs +227 -0
- data/vendor/wreq/tests/cookie.rs +306 -0
- data/vendor/wreq/tests/deflate.rs +347 -0
- data/vendor/wreq/tests/emulation.rs +260 -0
- data/vendor/wreq/tests/gzip.rs +347 -0
- data/vendor/wreq/tests/layers.rs +261 -0
- data/vendor/wreq/tests/multipart.rs +165 -0
- data/vendor/wreq/tests/proxy.rs +438 -0
- data/vendor/wreq/tests/redirect.rs +629 -0
- data/vendor/wreq/tests/retry.rs +135 -0
- data/vendor/wreq/tests/support/delay_server.rs +117 -0
- data/vendor/wreq/tests/support/error.rs +16 -0
- data/vendor/wreq/tests/support/layer.rs +183 -0
- data/vendor/wreq/tests/support/mod.rs +9 -0
- data/vendor/wreq/tests/support/server.rs +232 -0
- data/vendor/wreq/tests/timeouts.rs +281 -0
- data/vendor/wreq/tests/unix_socket.rs +135 -0
- data/vendor/wreq/tests/upgrade.rs +98 -0
- data/vendor/wreq/tests/zstd.rs +559 -0
- metadata +225 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
future::Future,
|
|
3
|
+
io,
|
|
4
|
+
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
|
|
5
|
+
pin::Pin,
|
|
6
|
+
task::{self, Poll},
|
|
7
|
+
vec,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
use tokio::task::JoinHandle;
|
|
11
|
+
use tower::Service;
|
|
12
|
+
|
|
13
|
+
use super::{Addrs, Name, Resolve, Resolving};
|
|
14
|
+
|
|
15
|
+
/// A resolver using blocking `getaddrinfo` calls in a threadpool.
|
|
16
|
+
#[derive(Clone, Default)]
|
|
17
|
+
pub struct GaiResolver {
|
|
18
|
+
_priv: (),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// An iterator of IP addresses returned from `getaddrinfo`.
|
|
22
|
+
pub struct GaiAddrs {
|
|
23
|
+
inner: SocketAddrs,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// A future to resolve a name returned by `GaiResolver`.
|
|
27
|
+
pub struct GaiFuture {
|
|
28
|
+
inner: JoinHandle<Result<SocketAddrs, io::Error>>,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// A wrapper around `SocketAddrs` to implement the `Iterator` trait.
|
|
32
|
+
pub(crate) struct SocketAddrs {
|
|
33
|
+
iter: vec::IntoIter<SocketAddr>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ==== impl GaiResolver ====
|
|
37
|
+
|
|
38
|
+
impl GaiResolver {
|
|
39
|
+
/// Creates a new [`GaiResolver`].
|
|
40
|
+
pub fn new() -> Self {
|
|
41
|
+
GaiResolver { _priv: () }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl Service<Name> for GaiResolver {
|
|
46
|
+
type Response = GaiAddrs;
|
|
47
|
+
type Error = io::Error;
|
|
48
|
+
type Future = GaiFuture;
|
|
49
|
+
|
|
50
|
+
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), io::Error>> {
|
|
51
|
+
Poll::Ready(Ok(()))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fn call(&mut self, name: Name) -> Self::Future {
|
|
55
|
+
let blocking = tokio::task::spawn_blocking(move || {
|
|
56
|
+
debug!("resolving {}", name);
|
|
57
|
+
(name.as_str(), 0)
|
|
58
|
+
.to_socket_addrs()
|
|
59
|
+
.map(|i| SocketAddrs { iter: i })
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
GaiFuture { inner: blocking }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
impl Resolve for GaiResolver {
|
|
67
|
+
fn resolve(&self, name: Name) -> Resolving {
|
|
68
|
+
let mut this = self.clone();
|
|
69
|
+
Box::pin(async move {
|
|
70
|
+
this.call(name)
|
|
71
|
+
.await
|
|
72
|
+
.map(|addrs| Box::new(addrs) as Addrs)
|
|
73
|
+
.map_err(Into::into)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ==== impl GaiFuture ====
|
|
79
|
+
|
|
80
|
+
impl Future for GaiFuture {
|
|
81
|
+
type Output = Result<GaiAddrs, io::Error>;
|
|
82
|
+
|
|
83
|
+
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
|
84
|
+
Pin::new(&mut self.inner).poll(cx).map(|res| match res {
|
|
85
|
+
Ok(Ok(addrs)) => Ok(GaiAddrs { inner: addrs }),
|
|
86
|
+
Ok(Err(err)) => Err(err),
|
|
87
|
+
Err(join_err) => {
|
|
88
|
+
if join_err.is_cancelled() {
|
|
89
|
+
Err(io::Error::new(io::ErrorKind::Interrupted, join_err))
|
|
90
|
+
} else {
|
|
91
|
+
panic!("gai background task failed: {join_err:?}")
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
impl Drop for GaiFuture {
|
|
99
|
+
fn drop(&mut self) {
|
|
100
|
+
self.inner.abort();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ==== impl GaiAddrs ====
|
|
105
|
+
|
|
106
|
+
impl Iterator for GaiAddrs {
|
|
107
|
+
type Item = SocketAddr;
|
|
108
|
+
|
|
109
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
110
|
+
self.inner.next()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ==== impl SocketAddrs ====
|
|
115
|
+
|
|
116
|
+
impl SocketAddrs {
|
|
117
|
+
pub(crate) fn new(addrs: Vec<SocketAddr>) -> Self {
|
|
118
|
+
SocketAddrs {
|
|
119
|
+
iter: addrs.into_iter(),
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub(crate) fn try_parse(host: &str, port: u16) -> Option<SocketAddrs> {
|
|
124
|
+
if let Ok(addr) = host.parse::<Ipv4Addr>() {
|
|
125
|
+
let addr = SocketAddrV4::new(addr, port);
|
|
126
|
+
return Some(SocketAddrs {
|
|
127
|
+
iter: vec![SocketAddr::V4(addr)].into_iter(),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if let Ok(addr) = host.parse::<Ipv6Addr>() {
|
|
131
|
+
let addr = SocketAddrV6::new(addr, port, 0, 0);
|
|
132
|
+
return Some(SocketAddrs {
|
|
133
|
+
iter: vec![SocketAddr::V6(addr)].into_iter(),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
None
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#[inline]
|
|
140
|
+
fn filter(self, predicate: impl FnMut(&SocketAddr) -> bool) -> SocketAddrs {
|
|
141
|
+
SocketAddrs::new(self.iter.filter(predicate).collect())
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
pub(crate) fn split_by_preference(
|
|
145
|
+
self,
|
|
146
|
+
local_addr_ipv4: Option<Ipv4Addr>,
|
|
147
|
+
local_addr_ipv6: Option<Ipv6Addr>,
|
|
148
|
+
) -> (SocketAddrs, SocketAddrs) {
|
|
149
|
+
match (local_addr_ipv4, local_addr_ipv6) {
|
|
150
|
+
(Some(_), None) => (self.filter(SocketAddr::is_ipv4), SocketAddrs::new(vec![])),
|
|
151
|
+
(None, Some(_)) => (self.filter(SocketAddr::is_ipv6), SocketAddrs::new(vec![])),
|
|
152
|
+
_ => {
|
|
153
|
+
let preferring_v6 = self
|
|
154
|
+
.iter
|
|
155
|
+
.as_slice()
|
|
156
|
+
.first()
|
|
157
|
+
.map(SocketAddr::is_ipv6)
|
|
158
|
+
.unwrap_or(false);
|
|
159
|
+
|
|
160
|
+
let (preferred, fallback) = self
|
|
161
|
+
.iter
|
|
162
|
+
.partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);
|
|
163
|
+
|
|
164
|
+
(SocketAddrs::new(preferred), SocketAddrs::new(fallback))
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
pub(crate) fn is_empty(&self) -> bool {
|
|
170
|
+
self.iter.as_slice().is_empty()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
pub(crate) fn len(&self) -> usize {
|
|
174
|
+
self.iter.as_slice().len()
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
impl Iterator for SocketAddrs {
|
|
179
|
+
type Item = SocketAddr;
|
|
180
|
+
#[inline]
|
|
181
|
+
fn next(&mut self) -> Option<SocketAddr> {
|
|
182
|
+
self.iter.next()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#[cfg(test)]
|
|
187
|
+
mod tests {
|
|
188
|
+
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
189
|
+
|
|
190
|
+
use super::*;
|
|
191
|
+
|
|
192
|
+
#[test]
|
|
193
|
+
fn test_ip_addrs_split_by_preference() {
|
|
194
|
+
let ip_v4 = Ipv4Addr::new(127, 0, 0, 1);
|
|
195
|
+
let ip_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
|
|
196
|
+
let v4_addr = (ip_v4, 80).into();
|
|
197
|
+
let v6_addr = (ip_v6, 80).into();
|
|
198
|
+
|
|
199
|
+
let (mut preferred, mut fallback) = SocketAddrs {
|
|
200
|
+
iter: vec![v4_addr, v6_addr].into_iter(),
|
|
201
|
+
}
|
|
202
|
+
.split_by_preference(None, None);
|
|
203
|
+
assert!(preferred.next().unwrap().is_ipv4());
|
|
204
|
+
assert!(fallback.next().unwrap().is_ipv6());
|
|
205
|
+
|
|
206
|
+
let (mut preferred, mut fallback) = SocketAddrs {
|
|
207
|
+
iter: vec![v6_addr, v4_addr].into_iter(),
|
|
208
|
+
}
|
|
209
|
+
.split_by_preference(None, None);
|
|
210
|
+
assert!(preferred.next().unwrap().is_ipv6());
|
|
211
|
+
assert!(fallback.next().unwrap().is_ipv4());
|
|
212
|
+
|
|
213
|
+
let (mut preferred, mut fallback) = SocketAddrs {
|
|
214
|
+
iter: vec![v4_addr, v6_addr].into_iter(),
|
|
215
|
+
}
|
|
216
|
+
.split_by_preference(Some(ip_v4), Some(ip_v6));
|
|
217
|
+
assert!(preferred.next().unwrap().is_ipv4());
|
|
218
|
+
assert!(fallback.next().unwrap().is_ipv6());
|
|
219
|
+
|
|
220
|
+
let (mut preferred, mut fallback) = SocketAddrs {
|
|
221
|
+
iter: vec![v6_addr, v4_addr].into_iter(),
|
|
222
|
+
}
|
|
223
|
+
.split_by_preference(Some(ip_v4), Some(ip_v6));
|
|
224
|
+
assert!(preferred.next().unwrap().is_ipv6());
|
|
225
|
+
assert!(fallback.next().unwrap().is_ipv4());
|
|
226
|
+
|
|
227
|
+
let (mut preferred, fallback) = SocketAddrs {
|
|
228
|
+
iter: vec![v4_addr, v6_addr].into_iter(),
|
|
229
|
+
}
|
|
230
|
+
.split_by_preference(Some(ip_v4), None);
|
|
231
|
+
assert!(preferred.next().unwrap().is_ipv4());
|
|
232
|
+
assert!(fallback.is_empty());
|
|
233
|
+
|
|
234
|
+
let (mut preferred, fallback) = SocketAddrs {
|
|
235
|
+
iter: vec![v4_addr, v6_addr].into_iter(),
|
|
236
|
+
}
|
|
237
|
+
.split_by_preference(None, Some(ip_v6));
|
|
238
|
+
assert!(preferred.next().unwrap().is_ipv6());
|
|
239
|
+
assert!(fallback.is_empty());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#[test]
|
|
243
|
+
fn test_name_from_str() {
|
|
244
|
+
const DOMAIN: &str = "test.example.com";
|
|
245
|
+
let name = Name::from(DOMAIN);
|
|
246
|
+
assert_eq!(name.as_str(), DOMAIN);
|
|
247
|
+
assert_eq!(name.to_string(), DOMAIN);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//! DNS resolution via the [hickory-resolver](https://github.com/hickory-dns/hickory-dns) crate
|
|
2
|
+
|
|
3
|
+
use std::{net::SocketAddr, sync::LazyLock};
|
|
4
|
+
|
|
5
|
+
use hickory_resolver::{
|
|
6
|
+
TokioResolver,
|
|
7
|
+
config::{LookupIpStrategy, ResolverConfig},
|
|
8
|
+
lookup_ip::LookupIpIntoIter,
|
|
9
|
+
name_server::TokioConnectionProvider,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
use super::{Addrs, Name, Resolve, Resolving};
|
|
13
|
+
|
|
14
|
+
/// Wrapper around an [`TokioResolver`], which implements the `Resolve` trait.
|
|
15
|
+
#[derive(Debug, Clone)]
|
|
16
|
+
pub struct HickoryDnsResolver {
|
|
17
|
+
/// Shared, lazily-initialized Tokio-based DNS resolver.
|
|
18
|
+
///
|
|
19
|
+
/// Backed by [`LazyLock`] to guarantee thread-safe, one-time creation.
|
|
20
|
+
/// On initialization, it attempts to load the system's DNS configuration;
|
|
21
|
+
/// if unavailable, it falls back to sensible default settings.
|
|
22
|
+
resolver: &'static LazyLock<TokioResolver>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
impl HickoryDnsResolver {
|
|
26
|
+
/// Create a new resolver with the default configuration,
|
|
27
|
+
/// which reads from `/etc/resolve.conf`. The options are
|
|
28
|
+
/// overriden to look up for both IPv4 and IPv6 addresses
|
|
29
|
+
/// to work with "happy eyeballs" algorithm.
|
|
30
|
+
pub fn new() -> HickoryDnsResolver {
|
|
31
|
+
static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
|
|
32
|
+
let mut builder = match TokioResolver::builder_tokio() {
|
|
33
|
+
Ok(resolver) => {
|
|
34
|
+
debug!("using system DNS configuration");
|
|
35
|
+
resolver
|
|
36
|
+
}
|
|
37
|
+
Err(_err) => {
|
|
38
|
+
debug!("error reading DNS system conf: {}, using defaults", _err);
|
|
39
|
+
TokioResolver::builder_with_config(
|
|
40
|
+
ResolverConfig::default(),
|
|
41
|
+
TokioConnectionProvider::default(),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
|
46
|
+
builder.build()
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
HickoryDnsResolver {
|
|
50
|
+
resolver: &RESOLVER,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
struct SocketAddrs {
|
|
56
|
+
iter: LookupIpIntoIter,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl Resolve for HickoryDnsResolver {
|
|
60
|
+
fn resolve(&self, name: Name) -> Resolving {
|
|
61
|
+
let resolver = self.clone();
|
|
62
|
+
Box::pin(async move {
|
|
63
|
+
let lookup = resolver.resolver.lookup_ip(name.as_str()).await?;
|
|
64
|
+
let addrs: Addrs = Box::new(SocketAddrs {
|
|
65
|
+
iter: lookup.into_iter(),
|
|
66
|
+
});
|
|
67
|
+
Ok(addrs)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl Iterator for SocketAddrs {
|
|
73
|
+
type Item = SocketAddr;
|
|
74
|
+
|
|
75
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
76
|
+
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
borrow::Cow,
|
|
3
|
+
collections::HashMap,
|
|
4
|
+
fmt,
|
|
5
|
+
future::Future,
|
|
6
|
+
net::SocketAddr,
|
|
7
|
+
pin::Pin,
|
|
8
|
+
sync::Arc,
|
|
9
|
+
task::{Context, Poll},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
use tower::Service;
|
|
13
|
+
|
|
14
|
+
use crate::error::BoxError;
|
|
15
|
+
|
|
16
|
+
/// A domain name to resolve into IP addresses.
|
|
17
|
+
#[derive(Clone, Hash, Eq, PartialEq)]
|
|
18
|
+
pub struct Name {
|
|
19
|
+
host: Box<str>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl Name {
|
|
23
|
+
/// Creates a new [`Name`] from a string slice.
|
|
24
|
+
#[inline]
|
|
25
|
+
pub fn new(host: Box<str>) -> Name {
|
|
26
|
+
Name { host }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// View the hostname as a string slice.
|
|
30
|
+
#[inline]
|
|
31
|
+
pub fn as_str(&self) -> &str {
|
|
32
|
+
&self.host
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl From<&str> for Name {
|
|
37
|
+
fn from(value: &str) -> Self {
|
|
38
|
+
Name::new(value.into())
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl fmt::Debug for Name {
|
|
43
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
44
|
+
fmt::Debug::fmt(&self.host, f)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
impl fmt::Display for Name {
|
|
49
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
50
|
+
fmt::Display::fmt(&self.host, f)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Alias for an `Iterator` trait object over `SocketAddr`.
|
|
55
|
+
pub type Addrs = Box<dyn Iterator<Item = SocketAddr> + Send>;
|
|
56
|
+
|
|
57
|
+
/// Alias for the `Future` type returned by a DNS resolver.
|
|
58
|
+
pub type Resolving = Pin<Box<dyn Future<Output = Result<Addrs, BoxError>> + Send>>;
|
|
59
|
+
|
|
60
|
+
/// Trait for customizing DNS resolution in wreq.
|
|
61
|
+
pub trait Resolve: Send + Sync {
|
|
62
|
+
/// Performs DNS resolution on a `Name`.
|
|
63
|
+
/// The return type is a future containing an iterator of `SocketAddr`.
|
|
64
|
+
///
|
|
65
|
+
/// It differs from `tower::Service<Name>` in several ways:
|
|
66
|
+
/// * It is assumed that `resolve` will always be ready to poll.
|
|
67
|
+
/// * It does not need a mutable reference to `self`.
|
|
68
|
+
/// * Since trait objects cannot make use of associated types, it requires wrapping the
|
|
69
|
+
/// returned `Future` and its contained `Iterator` with `Box`.
|
|
70
|
+
///
|
|
71
|
+
/// Explicitly specified port in the URI will override any port in the resolved `SocketAddr`s.
|
|
72
|
+
/// Otherwise, port `0` will be replaced by the conventional port for the given scheme (e.g. 80
|
|
73
|
+
/// for http).
|
|
74
|
+
fn resolve(&self, name: Name) -> Resolving;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Trait for converting types into a shared DNS resolver ([`Arc<dyn Resolve>`]).
|
|
78
|
+
///
|
|
79
|
+
/// Implemented for any [`Resolve`] type, [`Arc<T>`] where `T: Resolve`, and [`Arc<dyn Resolve>`].
|
|
80
|
+
/// Enables ergonomic conversion to a trait object for use in APIs without manual Arc wrapping.
|
|
81
|
+
pub trait IntoResolve {
|
|
82
|
+
/// Converts the implementor into an [`Arc<dyn Resolve>`].
|
|
83
|
+
///
|
|
84
|
+
/// This method enables ergonomic conversion of concrete resolvers, [`Arc<T>`], or
|
|
85
|
+
/// existing [`Arc<dyn Resolve>`] into a trait object suitable for APIs that expect
|
|
86
|
+
/// a shared DNS resolver.
|
|
87
|
+
fn into_resolve(self) -> Arc<dyn Resolve>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl IntoResolve for Arc<dyn Resolve> {
|
|
91
|
+
#[inline]
|
|
92
|
+
fn into_resolve(self) -> Arc<dyn Resolve> {
|
|
93
|
+
self
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
impl<R> IntoResolve for Arc<R>
|
|
98
|
+
where
|
|
99
|
+
R: Resolve + 'static,
|
|
100
|
+
{
|
|
101
|
+
#[inline]
|
|
102
|
+
fn into_resolve(self) -> Arc<dyn Resolve> {
|
|
103
|
+
self
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
impl<R> IntoResolve for R
|
|
108
|
+
where
|
|
109
|
+
R: Resolve + 'static,
|
|
110
|
+
{
|
|
111
|
+
#[inline]
|
|
112
|
+
fn into_resolve(self) -> Arc<dyn Resolve> {
|
|
113
|
+
Arc::new(self)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Adapter that wraps a [`Resolve`] trait object to work with Tower's `Service` trait.
|
|
118
|
+
///
|
|
119
|
+
/// This allows custom DNS resolvers implementing `Resolve` to be used in contexts
|
|
120
|
+
/// that expect a `Service<Name>` implementation.
|
|
121
|
+
#[derive(Clone)]
|
|
122
|
+
pub(crate) struct DynResolver {
|
|
123
|
+
resolver: Arc<dyn Resolve>,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
impl DynResolver {
|
|
127
|
+
/// Creates a new [`DynResolver`] with the provided resolver.
|
|
128
|
+
pub(crate) fn new(resolver: Arc<dyn Resolve>) -> Self {
|
|
129
|
+
Self { resolver }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
impl Service<Name> for DynResolver {
|
|
134
|
+
type Response = Addrs;
|
|
135
|
+
type Error = BoxError;
|
|
136
|
+
type Future = Resolving;
|
|
137
|
+
|
|
138
|
+
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
139
|
+
Poll::Ready(Ok(()))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn call(&mut self, name: Name) -> Self::Future {
|
|
143
|
+
self.resolver.resolve(name)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// DNS resolver that supports hostname overrides.
|
|
148
|
+
///
|
|
149
|
+
/// This resolver first checks for manual hostname-to-IP mappings before
|
|
150
|
+
/// falling back to the underlying DNS resolver. Useful for testing or
|
|
151
|
+
/// bypassing DNS for specific domains.
|
|
152
|
+
pub(crate) struct DnsResolverWithOverrides {
|
|
153
|
+
dns_resolver: Arc<dyn Resolve>,
|
|
154
|
+
overrides: Arc<HashMap<Cow<'static, str>, Vec<SocketAddr>>>,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
impl DnsResolverWithOverrides {
|
|
158
|
+
/// Creates a new [`DnsResolverWithOverrides`] with the provided DNS resolver and overrides.
|
|
159
|
+
pub(crate) fn new(
|
|
160
|
+
dns_resolver: Arc<dyn Resolve>,
|
|
161
|
+
overrides: HashMap<Cow<'static, str>, Vec<SocketAddr>>,
|
|
162
|
+
) -> Self {
|
|
163
|
+
DnsResolverWithOverrides {
|
|
164
|
+
dns_resolver,
|
|
165
|
+
overrides: Arc::new(overrides),
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
impl Resolve for DnsResolverWithOverrides {
|
|
171
|
+
fn resolve(&self, name: Name) -> Resolving {
|
|
172
|
+
match self.overrides.get(name.as_str()) {
|
|
173
|
+
Some(dest) => {
|
|
174
|
+
let addrs: Addrs = Box::new(dest.clone().into_iter());
|
|
175
|
+
Box::pin(std::future::ready(Ok(addrs)))
|
|
176
|
+
}
|
|
177
|
+
None => self.dns_resolver.resolve(name),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//! DNS resolution
|
|
2
|
+
|
|
3
|
+
pub(crate) mod gai;
|
|
4
|
+
#[cfg(feature = "hickory-dns")]
|
|
5
|
+
pub(crate) mod hickory;
|
|
6
|
+
pub(crate) mod resolve;
|
|
7
|
+
|
|
8
|
+
pub use resolve::{Addrs, IntoResolve, Name, Resolve, Resolving};
|
|
9
|
+
|
|
10
|
+
pub(crate) use self::{
|
|
11
|
+
gai::{GaiResolver, SocketAddrs},
|
|
12
|
+
resolve::{DnsResolverWithOverrides, DynResolver},
|
|
13
|
+
sealed::{InternalResolve, resolve},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
mod sealed {
|
|
17
|
+
use std::{
|
|
18
|
+
future::Future,
|
|
19
|
+
net::SocketAddr,
|
|
20
|
+
task::{self, Poll},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
use tower::Service;
|
|
24
|
+
|
|
25
|
+
use super::Name;
|
|
26
|
+
use crate::error::BoxError;
|
|
27
|
+
|
|
28
|
+
/// Internal adapter trait for DNS resolvers.
|
|
29
|
+
///
|
|
30
|
+
/// This trait provides a unified interface for different resolver implementations,
|
|
31
|
+
/// allowing both custom [`super::Resolve`] types and Tower [`Service`] implementations
|
|
32
|
+
/// to be used interchangeably within the connector.
|
|
33
|
+
pub trait InternalResolve {
|
|
34
|
+
type Addrs: Iterator<Item = SocketAddr>;
|
|
35
|
+
type Error: Into<BoxError>;
|
|
36
|
+
type Future: Future<Output = Result<Self::Addrs, Self::Error>>;
|
|
37
|
+
|
|
38
|
+
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
|
|
39
|
+
fn resolve(&mut self, name: Name) -> Self::Future;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Automatic implementation for any Tower [`Service`] that resolves names to socket addresses.
|
|
43
|
+
impl<S> InternalResolve for S
|
|
44
|
+
where
|
|
45
|
+
S: Service<Name>,
|
|
46
|
+
S::Response: Iterator<Item = SocketAddr>,
|
|
47
|
+
S::Error: Into<BoxError>,
|
|
48
|
+
{
|
|
49
|
+
type Addrs = S::Response;
|
|
50
|
+
type Error = S::Error;
|
|
51
|
+
type Future = S::Future;
|
|
52
|
+
|
|
53
|
+
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
54
|
+
Service::poll_ready(self, cx)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn resolve(&mut self, name: Name) -> Self::Future {
|
|
58
|
+
Service::call(self, name)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub async fn resolve<R>(resolver: &mut R, name: Name) -> Result<R::Addrs, R::Error>
|
|
63
|
+
where
|
|
64
|
+
R: InternalResolve,
|
|
65
|
+
{
|
|
66
|
+
std::future::poll_fn(|cx| resolver.poll_ready(cx)).await?;
|
|
67
|
+
resolver.resolve(name).await
|
|
68
|
+
}
|
|
69
|
+
}
|