hyper_trust_dns_connector/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! # hyper_trust_dns_connector
3//!
4//! A crate to make [hickory-resolver](https://docs.rs/hickory-resolver/)'s (previously trust_dns_resolver)
5//! asynchronous resolver compatible with [hyper](https://docs.rs/hyper) client,
6//! to use instead of the default dns threadpool.
7//!
8//! ## Features
9//!
10//! * `hyper-tls-connector` This feature includes
11//! [`hyper-tls`](https://docs.rs/hyper-tls/0.5/hyper_tls/) and
12//! [`native-tls`](https://docs.rs/native-tls/0.2/native_tls/) to
13//! provide a helper function to create a tls connector.
14//!
15//! ## Usage
16//!
17//! [hickory-resolver](https://docs.rs/hickory-resolver/) creates an async resolver
18//! for dns queries, which is then used by hyper
19//!
20//! ## Example
21//!
22//! ```
23//! use hyper_trust_dns_connector::new_async_http_connector;
24//! use hyper::{Client, Body};
25//!
26//! #[tokio::main]
27//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
28//! let http = new_async_http_connector()?;
29//! let client = Client::builder().build::<_, Body>(http);
30//! let res = client.get(hyper::Uri::from_static("http://httpbin.org/ip"))
31//! .await?;
32//! assert_eq!(res.status(), 200);
33//! Ok(())
34//! }
35//! ```
36
37use hickory_resolver::config::{ResolverConfig, ResolverOpts};
38use hickory_resolver::TokioAsyncResolver;
39use hyper::client::connect::dns::Name;
40use hyper::client::HttpConnector;
41use hyper::service::Service;
42use std::io;
43use std::pin::Pin;
44use std::task::{Context, Poll};
45use std::{future::Future, net::SocketAddr, net::ToSocketAddrs};
46
47/// Wrapper around hickory-resolver's
48/// [`TokioAsyncResolver`](https://docs.rs/hickory-resolver/0.24.2/hickory_resolver/type.TokioAsyncResolver.html)
49///
50/// The resolver runs a background Task which manages dns requests. When a new resolver is created,
51/// the background task is also created, it needs to be spawned on top of an executor before using the client,
52/// or dns requests will block.
53#[derive(Debug, Clone)]
54pub struct AsyncHyperResolver(TokioAsyncResolver);
55
56impl AsyncHyperResolver {
57 /// constructs a new resolver, arguments are passed to the corresponding method of
58 /// [`TokioAsyncResolver`](https://docs.rs/hickory-resolver/0.24.2/hickory_resolver/struct.AsyncResolver.html#method.new)
59 pub fn new(config: ResolverConfig, options: ResolverOpts) -> Result<Self, io::Error> {
60 let resolver = TokioAsyncResolver::tokio(config, options);
61 Ok(Self(resolver))
62 }
63
64 /// constructs a new resolver from default configuration, uses the corresponding method of
65 /// [`TokioAsyncResolver`](https://docs.rs/hickory-resolver/0.24.2/hickory_resolver/struct.AsyncResolver.html#method.tokio_from_system_conf)
66 pub fn new_from_system_conf() -> Result<Self, io::Error> {
67 let resolver = TokioAsyncResolver::tokio_from_system_conf()?;
68 Ok(Self(resolver))
69 }
70}
71
72impl Service<Name> for AsyncHyperResolver {
73 type Response = std::vec::IntoIter<SocketAddr>;
74 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
75 type Error = io::Error;
76
77 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
78 Poll::Ready(Ok(()))
79 }
80
81 fn call(&mut self, name: Name) -> Self::Future {
82 let resolver = self.0.clone();
83 Box::pin((|| async move {
84 Ok(resolver
85 .lookup_ip(name.as_str())
86 .await?
87 .iter()
88 .map(|addr| (addr, 0_u16).to_socket_addrs())
89 .try_fold(Vec::new(), |mut acc, s_addr| {
90 acc.extend(s_addr?);
91 Ok::<_, io::Error>(acc)
92 })?
93 .into_iter())
94 })())
95 }
96}
97
98/// A helper function to create an http connector and a dns task with the default configuration
99///
100/// ```
101/// use hyper_trust_dns_connector::new_async_http_connector;
102/// use hyper::{Client, Body};
103///
104/// # #[tokio::main]
105/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
106/// let async_http = new_async_http_connector()?;
107/// let client = Client::builder().build::<_, Body>(async_http);
108/// # Ok(())
109/// # }
110/// ```
111pub fn new_async_http_connector() -> Result<HttpConnector<AsyncHyperResolver>, io::Error> {
112 let resolver = AsyncHyperResolver::new_from_system_conf()?;
113 Ok(HttpConnector::new_with_resolver(resolver))
114}
115
116/// Provides a helper method to create an https connector using
117/// [`hyper-tls`](https://docs.rs/hyper-tls/0.5/hyper_tls/)
118///
119/// ## Example
120///
121/// ```
122/// use hyper::Client;
123/// use hyper_trust_dns_connector::https::new_async_https_connector;
124///
125/// #[tokio::main]
126/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
127/// let https_connector = new_async_https_connector()?;
128/// let client: Client<_> = Client::builder().build(https_connector);
129/// let res = client
130/// .get(hyper::Uri::from_static("https://httpbin.org/ip"))
131/// .await?;
132/// assert_eq!(res.status(), 200);
133/// Ok(())
134/// }
135/// ```
136#[cfg(feature = "hyper-tls-connector")]
137#[cfg_attr(docsrs, doc(cfg(feature = "hyper-tls-connector")))]
138pub mod https {
139
140 use hyper_tls::HttpsConnector;
141 use native_tls::TlsConnector;
142
143 use crate::io;
144 use crate::HttpConnector;
145 use crate::{new_async_http_connector, AsyncHyperResolver};
146
147 #[derive(Debug)]
148 pub enum Error {
149 NativeTls(native_tls::Error),
150 Io(io::Error),
151 }
152
153 impl std::fmt::Display for Error {
154 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155 match self {
156 Error::NativeTls(err) => write!(f, "native_tls error : {}", err),
157 Error::Io(err) => write!(f, "io error : {}", err),
158 }
159 }
160 }
161
162 impl std::error::Error for Error {}
163 impl From<io::Error> for Error {
164 fn from(error: io::Error) -> Self {
165 Error::Io(error)
166 }
167 }
168
169 impl From<native_tls::Error> for Error {
170 fn from(error: native_tls::Error) -> Self {
171 Error::NativeTls(error)
172 }
173 }
174
175 /// A helper function to create an https connector from [`hyper-tls`](https://docs.rs/hyper-tls/0.5/hyper_tls/)
176 /// and a dns task with the default configuration.
177 pub fn new_async_https_connector(
178 ) -> Result<HttpsConnector<HttpConnector<AsyncHyperResolver>>, Error> {
179 let mut http = new_async_http_connector()?;
180 http.enforce_http(false);
181 let tls_connector = TlsConnector::new()?;
182 Ok(HttpsConnector::from((http, tls_connector.into())))
183 }
184}