From b9d8a215b42f3a68796754f453a8a1a71c340b85 Mon Sep 17 00:00:00 2001
From: Nikolay Kim <fafhrd91@gmail.com>
Date: Thu, 11 Apr 2019 09:57:21 -0700
Subject: [PATCH] Start trust-dns default resolver on first use

---
 actix-connect/CHANGES.md      |  6 ++++++
 actix-connect/Cargo.toml      | 10 ++++-----
 actix-connect/src/lib.rs      | 40 +++++++++++++++++++++++++----------
 actix-connect/src/resolver.rs | 32 ++++++++++++++++++++++------
 4 files changed, 65 insertions(+), 23 deletions(-)

diff --git a/actix-connect/CHANGES.md b/actix-connect/CHANGES.md
index 4a3e86bc..d1086e2a 100644
--- a/actix-connect/CHANGES.md
+++ b/actix-connect/CHANGES.md
@@ -1,5 +1,11 @@
 # Changes
 
+## [0.1.3] - 2019-04-11
+
+### Changed
+
+* Start trust-dns default resolver on first use
+
 ## [0.1.2] - 2019-04-04
 
 ### Added
diff --git a/actix-connect/Cargo.toml b/actix-connect/Cargo.toml
index ebbf0ace..4416be30 100644
--- a/actix-connect/Cargo.toml
+++ b/actix-connect/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "actix-connect"
-version = "0.1.2"
+version = "0.1.3"
 authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
 description = "Actix Connector - tcp connector service"
 keywords = ["network", "framework", "async", "futures"]
@@ -30,9 +30,9 @@ ssl = ["openssl", "tokio-openssl"]
 uri = ["http"]
 
 [dependencies]
-actix-service = "0.3.4"
-actix-codec = "0.1.1"
-actix-utils = "0.3.4"
+actix-service = "0.3.6"
+actix-codec = "0.1.2"
+actix-utils = "0.3.5"
 derive_more = "0.14.0"
 either = "1.5.1"
 futures = "0.1.25"
@@ -40,7 +40,7 @@ http = { version = "0.1.16", optional = true }
 log = "0.4"
 tokio-tcp = "0.1.3"
 tokio-current-thread = "0.1.5"
-trust-dns-resolver = { version="0.11.0-alpha.2", default-features = false }
+trust-dns-resolver = { version="0.11.0-alpha.3", default-features = false }
 
 # openssl
 openssl = { version="0.10", optional = true }
diff --git a/actix-connect/src/lib.rs b/actix-connect/src/lib.rs
index 5d4a6c62..ccd537d9 100644
--- a/actix-connect/src/lib.rs
+++ b/actix-connect/src/lib.rs
@@ -8,6 +8,8 @@
 #[macro_use]
 extern crate log;
 
+use std::cell::RefCell;
+
 mod connect;
 mod connector;
 mod error;
@@ -46,18 +48,34 @@ pub fn start_resolver(cfg: ResolverConfig, opts: ResolverOpts) -> AsyncResolver
     resolver
 }
 
-pub fn start_default_resolver() -> AsyncResolver {
-    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())
-        }
-    };
+thread_local! {
+    static DEFAULT_RESOLVER: RefCell<Option<AsyncResolver>> = RefCell::new(None);
+}
 
-    let (resolver, bg) = AsyncResolver::new(cfg, opts);
-    tokio_current_thread::spawn(bg);
-    resolver
+pub(crate) fn get_default_resolver() -> AsyncResolver {
+    DEFAULT_RESOLVER.with(|cell| {
+        if let Some(ref resolver) = *cell.borrow() {
+            return resolver.clone();
+        }
+
+        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, bg) = AsyncResolver::new(cfg, opts);
+        tokio_current_thread::spawn(bg);
+
+        *cell.borrow_mut() = Some(resolver.clone());
+        resolver
+    })
+}
+
+pub fn start_default_resolver() -> AsyncResolver {
+    get_default_resolver()
 }
 
 /// Create tcp connector service
diff --git a/actix-connect/src/resolver.rs b/actix-connect/src/resolver.rs
index 21d0c281..aba97ad9 100644
--- a/actix-connect/src/resolver.rs
+++ b/actix-connect/src/resolver.rs
@@ -10,10 +10,11 @@ use trust_dns_resolver::{AsyncResolver, Background};
 
 use crate::connect::{Address, Connect};
 use crate::error::ConnectError;
+use crate::get_default_resolver;
 
 /// DNS Resolver Service factory
 pub struct ResolverFactory<T> {
-    resolver: AsyncResolver,
+    resolver: Option<AsyncResolver>,
     _t: PhantomData<T>,
 }
 
@@ -21,13 +22,18 @@ impl<T> ResolverFactory<T> {
     /// Create new resolver instance with custom configuration and options.
     pub fn new(resolver: AsyncResolver) -> Self {
         ResolverFactory {
-            resolver,
+            resolver: Some(resolver),
             _t: PhantomData,
         }
     }
+}
 
-    pub fn resolver(&self) -> &AsyncResolver {
-        &self.resolver
+impl<T> Default for ResolverFactory<T> {
+    fn default() -> Self {
+        ResolverFactory {
+            resolver: None,
+            _t: PhantomData,
+        }
     }
 }
 
@@ -58,7 +64,7 @@ impl<T: Address> NewService for ResolverFactory<T> {
 
 /// DNS Resolver Service
 pub struct Resolver<T> {
-    resolver: AsyncResolver,
+    resolver: Option<AsyncResolver>,
     _t: PhantomData<T>,
 }
 
@@ -66,7 +72,16 @@ impl<T> Resolver<T> {
     /// Create new resolver instance with custom configuration and options.
     pub fn new(resolver: AsyncResolver) -> Self {
         Resolver {
-            resolver,
+            resolver: Some(resolver),
+            _t: PhantomData,
+        }
+    }
+}
+
+impl<T> Default for Resolver<T> {
+    fn default() -> Self {
+        Resolver {
+            resolver: None,
             _t: PhantomData,
         }
     }
@@ -100,7 +115,10 @@ impl<T: Address> Service for Resolver<T> {
                 Either::B(ok(req))
             } else {
                 trace!("DNS resolver: resolving host {:?}", req.host());
-                Either::A(ResolverFuture::new(req, &self.resolver))
+                if self.resolver.is_none() {
+                    self.resolver = Some(get_default_resolver());
+                }
+                Either::A(ResolverFuture::new(req, self.resolver.as_ref().unwrap()))
             }
         }
     }