From 519d7f2b8ab323b34071c8eedce03f89e4fde6d0 Mon Sep 17 00:00:00 2001
From: fakeshadow <24548779@qq.com>
Date: Tue, 9 Feb 2021 02:41:20 -0800
Subject: [PATCH] add trust-dns optional feature for actix-http and awc (#1969)

---
 Cargo.toml                         |  1 -
 actix-http/CHANGES.md              |  4 ++
 actix-http/Cargo.toml              |  7 +--
 actix-http/src/client/connector.rs | 84 ++++++++++++++++++++++++++++--
 actix-http/src/error.rs            |  6 ---
 awc/CHANGES.md                     |  2 +
 awc/Cargo.toml                     |  3 ++
 src/test.rs                        |  6 ++-
 8 files changed, 98 insertions(+), 15 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 479a89f8..80187c8a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -109,7 +109,6 @@ smallvec = "1.6"
 
 [dev-dependencies]
 actix = { version = "0.11.0-beta.2", default-features = false }
-actix-http = { version = "3.0.0-beta.1", features = ["actors"] }
 rand = "0.8"
 env_logger = "0.8"
 serde_derive = "1.0"
diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md
index 52980fb5..ba8cc6e6 100644
--- a/actix-http/CHANGES.md
+++ b/actix-http/CHANGES.md
@@ -7,6 +7,7 @@
 * `ResponseBuilder::append_header` method which allows using typed headers. [#1869]
 * `TestRequest::insert_header` method which allows using typed headers. [#1869]
 * `ContentEncoding` implements all necessary header traits. [#1912]
+* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
 
 ### Changed
 * `ResponseBuilder::content_type` now takes an `impl IntoHeaderValue` to support using typed
@@ -26,6 +27,8 @@
 * `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869]
 * `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869]
 * `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869]
+* `actors` optional feature. [#1969]
+* `ResponseError` impl for `actix::MailboxError`. [#1969]
 
 [#1869]: https://github.com/actix/actix-web/pull/1869
 [#1894]: https://github.com/actix/actix-web/pull/1894
@@ -34,6 +37,7 @@
 [#1905]: https://github.com/actix/actix-web/pull/1905
 [#1912]: https://github.com/actix/actix-web/pull/1912
 [#1957]: https://github.com/actix/actix-web/pull/1957
+[#1969]: https://github.com/actix/actix-web/pull/1969
 
 
 ## 3.0.0-beta.1 - 2021-01-07
diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml
index 22a54f56..9f62639f 100644
--- a/actix-http/Cargo.toml
+++ b/actix-http/Cargo.toml
@@ -36,8 +36,8 @@ compress = ["flate2", "brotli2"]
 # support for secure cookies
 secure-cookies = ["cookie/secure"]
 
-# support for actix Actor messages
-actors = ["actix"]
+# trust-dns as client dns resolver
+trust-dns = ["trust-dns-resolver"]
 
 [dependencies]
 actix-service = "2.0.0-beta.4"
@@ -45,7 +45,6 @@ actix-codec = "0.4.0-beta.1"
 actix-utils = "3.0.0-beta.2"
 actix-rt = "2"
 actix-tls = "3.0.0-beta.2"
-actix = { version = "0.11.0-beta.2", default-features = false, optional = true }
 
 base64 = "0.13"
 bitflags = "1.2"
@@ -84,6 +83,8 @@ time = { version = "0.2.23", default-features = false, features = ["std"] }
 brotli2 = { version="0.3.2", optional = true }
 flate2 = { version = "1.0.13", optional = true }
 
+trust-dns-resolver = { version = "0.20.0", optional = true }
+
 [dev-dependencies]
 actix-server = "2.0.0-beta.3"
 actix-http-test = { version = "3.0.0-beta.1", features = ["openssl"] }
diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs
index 425ee0f7..7c8e2b2a 100644
--- a/actix-http/src/client/connector.rs
+++ b/actix-http/src/client/connector.rs
@@ -6,7 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite};
 use actix_rt::net::TcpStream;
 use actix_service::{apply_fn, Service, ServiceExt};
 use actix_tls::connect::{
-    default_connector, Connect as TcpConnect, Connection as TcpConnection,
+    new_connector, Connect as TcpConnect, Connection as TcpConnection, Resolver,
 };
 use actix_utils::timeout::{TimeoutError, TimeoutService};
 use http::Uri;
@@ -19,7 +19,6 @@ use super::Connect;
 
 #[cfg(feature = "openssl")]
 use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector;
-
 #[cfg(feature = "rustls")]
 use actix_tls::connect::ssl::rustls::ClientConfig;
 #[cfg(feature = "rustls")]
@@ -70,7 +69,7 @@ impl Connector<(), ()> {
     > {
         Connector {
             ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
-            connector: default_connector(),
+            connector: new_connector(resolver::resolver()),
             config: ConnectorConfig::default(),
             _phantom: PhantomData,
         }
@@ -532,3 +531,82 @@ mod connect_impl {
         }
     }
 }
+
+#[cfg(not(feature = "trust-dns"))]
+mod resolver {
+    use super::*;
+
+    pub(super) fn resolver() -> Resolver {
+        Resolver::Default
+    }
+}
+
+#[cfg(feature = "trust-dns")]
+mod resolver {
+    use std::{cell::RefCell, net::SocketAddr};
+
+    use actix_tls::connect::Resolve;
+    use futures_core::future::LocalBoxFuture;
+    use trust_dns_resolver::{
+        config::{ResolverConfig, ResolverOpts},
+        system_conf::read_system_conf,
+        TokioAsyncResolver,
+    };
+
+    use super::*;
+
+    pub(super) fn resolver() -> Resolver {
+        // new type for impl Resolve trait for TokioAsyncResolver.
+        struct TrustDnsResolver(TokioAsyncResolver);
+
+        impl Resolve for TrustDnsResolver {
+            fn lookup<'a>(
+                &'a self,
+                host: &'a str,
+                port: u16,
+            ) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>>
+            {
+                Box::pin(async move {
+                    let res = self
+                        .0
+                        .lookup_ip(host)
+                        .await?
+                        .iter()
+                        .map(|ip| SocketAddr::new(ip, port))
+                        .collect();
+                    Ok(res)
+                })
+            }
+        }
+
+        // dns struct is cached in thread local.
+        // so new client constructor can reuse the existing dns resolver.
+        thread_local! {
+            static TRUST_DNS_RESOLVER: RefCell<Option<Resolver>> = RefCell::new(None);
+        }
+
+        // get from thread local or construct a new trust-dns resolver.
+        TRUST_DNS_RESOLVER.with(|local| {
+            let resolver = local.borrow().as_ref().map(Clone::clone);
+            match resolver {
+                Some(resolver) => resolver,
+                None => {
+                    let (cfg, opts) = match read_system_conf() {
+                        Ok((cfg, opts)) => (cfg, opts),
+                        Err(e) => {
+                            log::error!("TRust-DNS can not load system config: {}", e);
+                            (ResolverConfig::default(), ResolverOpts::default())
+                        }
+                    };
+
+                    let resolver = TokioAsyncResolver::tokio(cfg, opts).unwrap();
+
+                    // box trust dns resolver and put it in thread local.
+                    let resolver = Resolver::new_custom(TrustDnsResolver(resolver));
+                    *local.borrow_mut() = Some(resolver.clone());
+                    resolver
+                }
+            }
+        })
+    }
+}
diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs
index 7beedc3b..cea1f5d4 100644
--- a/actix-http/src/error.rs
+++ b/actix-http/src/error.rs
@@ -968,12 +968,6 @@ where
     InternalError::new(err, StatusCode::NETWORK_AUTHENTICATION_REQUIRED).into()
 }
 
-#[cfg(feature = "actors")]
-/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix::MailboxError`].
-///
-/// This is only supported when the feature `actors` is enabled.
-impl ResponseError for actix::MailboxError {}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/awc/CHANGES.md b/awc/CHANGES.md
index 16ec7ad1..80895f46 100644
--- a/awc/CHANGES.md
+++ b/awc/CHANGES.md
@@ -4,6 +4,7 @@
 ### Added
 * `ClientRequest::insert_header` method which allows using typed headers. [#1869]
 * `ClientRequest::append_header` method which allows using typed headers. [#1869]
+* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969]
 
 ### Changed
 * Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905]
@@ -16,6 +17,7 @@
 
 [#1869]: https://github.com/actix/actix-web/pull/1869
 [#1905]: https://github.com/actix/actix-web/pull/1905
+[#1969]: https://github.com/actix/actix-web/pull/1969
 
 
 ## 3.0.0-beta.1 - 2021-01-07
diff --git a/awc/Cargo.toml b/awc/Cargo.toml
index 7a913078..483e0496 100644
--- a/awc/Cargo.toml
+++ b/awc/Cargo.toml
@@ -36,6 +36,9 @@ rustls = ["tls-rustls", "actix-http/rustls"]
 # content-encoding support
 compress = ["actix-http/compress"]
 
+# trust-dns as dns resolver
+trust-dns = ["actix-http/trust-dns"]
+
 [dependencies]
 actix-codec = "0.4.0-beta.1"
 actix-service = "2.0.0-beta.4"
diff --git a/src/test.rs b/src/test.rs
index 62c329c9..5da100b8 100644
--- a/src/test.rs
+++ b/src/test.rs
@@ -1277,8 +1277,10 @@ mod tests {
         async fn actor_handler(
             addr: Data<Addr<MyActor>>,
         ) -> Result<impl Responder, Error> {
-            // `?` operator tests "actors" feature flag on actix-http
-            let res = addr.send(Num(1)).await?;
+            let res = addr
+                .send(Num(1))
+                .await
+                .map_err(crate::error::ErrorInternalServerError)?;
 
             if res == 1 {
                 Ok(HttpResponse::Ok())