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}