From f8d1c1a8af122d8955b4b08fa564723badbb3f77 Mon Sep 17 00:00:00 2001
From: "Aode (Lion)" <asonix@asonix.dog>
Date: Mon, 24 Jan 2022 18:42:15 -0600
Subject: [PATCH] Re-use a basic request in all possible cases

---
 src/appservice_server.rs |  6 +-----
 src/database/globals.rs  | 40 ++++++++++++++++++++++++++++++----------
 src/database/pusher.rs   |  6 +-----
 src/server_server.rs     | 23 ++++++++++++-----------
 4 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/src/appservice_server.rs b/src/appservice_server.rs
index ed886d6c..a5d795f6 100644
--- a/src/appservice_server.rs
+++ b/src/appservice_server.rs
@@ -46,11 +46,7 @@ where
     *reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
 
     let url = reqwest_request.url().clone();
-    let mut response = globals
-        .reqwest_client()?
-        .build()?
-        .execute(reqwest_request)
-        .await?;
+    let mut response = globals.reqwest_client().execute(reqwest_request).await?;
 
     // reqwest::Response -> http::Response conversion
     let status = response.status();
diff --git a/src/database/globals.rs b/src/database/globals.rs
index 098d8197..da91c1fb 100644
--- a/src/database/globals.rs
+++ b/src/database/globals.rs
@@ -39,6 +39,7 @@ pub struct Globals {
     keypair: Arc<ruma::signatures::Ed25519KeyPair>,
     dns_resolver: TokioAsyncResolver,
     jwt_decoding_key: Option<jsonwebtoken::DecodingKey<'static>>,
+    basic_client: reqwest::Client,
     pub(super) server_signingkeys: Arc<dyn Tree>,
     pub bad_event_ratelimiter: Arc<RwLock<HashMap<Box<EventId>, RateLimitState>>>,
     pub bad_signature_ratelimiter: Arc<RwLock<HashMap<Vec<String>, RateLimitState>>>,
@@ -132,6 +133,8 @@ impl Globals {
             .as_ref()
             .map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static());
 
+        let basic_client = reqwest_client_builder(&config, None)?.build()?;
+
         let s = Self {
             globals,
             config,
@@ -141,6 +144,7 @@ impl Globals {
             })?,
             actual_destination_cache: Arc::new(RwLock::new(WellKnownMap::new())),
             tls_name_override,
+            basic_client,
             server_signingkeys,
             jwt_decoding_key,
             bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
@@ -163,17 +167,15 @@ impl Globals {
         &self.keypair
     }
 
-    /// Returns a reqwest client which can be used to send requests.
-    pub fn reqwest_client(&self) -> Result<reqwest::ClientBuilder> {
-        let mut reqwest_client_builder = reqwest::Client::builder()
-            .connect_timeout(Duration::from_secs(30))
-            .timeout(Duration::from_secs(60 * 3))
-            .pool_max_idle_per_host(1);
-        if let Some(proxy) = self.config.proxy.to_proxy()? {
-            reqwest_client_builder = reqwest_client_builder.proxy(proxy);
-        }
+    /// Returns a reqwest client which can be used to send requests
+    pub fn reqwest_client(&self) -> reqwest::Client {
+        // can't return &Client or else we'll hold a lock around the DB across an await
+        self.basic_client.clone()
+    }
 
-        Ok(reqwest_client_builder)
+    /// Returns a reqwest client builder which can be customized and used to send requests.
+    pub fn reqwest_client_builder(&self) -> Result<reqwest::ClientBuilder> {
+        reqwest_client_builder(&self.config, Some(1))
     }
 
     #[tracing::instrument(skip(self))]
@@ -340,3 +342,21 @@ impl Globals {
         r
     }
 }
+
+fn reqwest_client_builder(
+    config: &Config,
+    max_idle: Option<usize>,
+) -> Result<reqwest::ClientBuilder> {
+    let mut reqwest_client_builder = reqwest::Client::builder()
+        .connect_timeout(Duration::from_secs(30))
+        .timeout(Duration::from_secs(60 * 3));
+
+    if let Some(max_idle) = max_idle {
+        reqwest_client_builder = reqwest_client_builder.pool_max_idle_per_host(max_idle);
+    }
+    if let Some(proxy) = config.proxy.to_proxy()? {
+        reqwest_client_builder = reqwest_client_builder.proxy(proxy);
+    }
+
+    Ok(reqwest_client_builder)
+}
diff --git a/src/database/pusher.rs b/src/database/pusher.rs
index 97ca85d8..d63db1d7 100644
--- a/src/database/pusher.rs
+++ b/src/database/pusher.rs
@@ -115,11 +115,7 @@ where
     //*reqwest_request.timeout_mut() = Some(Duration::from_secs(5));
 
     let url = reqwest_request.url().clone();
-    let response = globals
-        .reqwest_client()?
-        .build()?
-        .execute(reqwest_request)
-        .await;
+    let response = globals.reqwest_client().execute(reqwest_request).await;
 
     match response {
         Ok(mut response) => {
diff --git a/src/server_server.rs b/src/server_server.rs
index 9129951b..205355f9 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -237,21 +237,25 @@ where
 
     let url = reqwest_request.url().clone();
 
-    let mut client = globals.reqwest_client()?;
-    if let Some((override_name, port)) = globals
+    let client = if let Some((override_name, port)) = globals
         .tls_name_override
         .read()
         .unwrap()
         .get(&actual_destination.hostname())
     {
-        client = client.resolve(
-            &actual_destination.hostname(),
-            SocketAddr::new(override_name[0], *port),
-        );
+        globals
+            .reqwest_client_builder()?
+            .resolve(
+                &actual_destination.hostname(),
+                SocketAddr::new(override_name[0], *port),
+            )
+            .build()?
         // port will be ignored
-    }
+    } else {
+        globals.reqwest_client()
+    };
 
-    let response = client.build()?.execute(reqwest_request).await;
+    let response = client.execute(reqwest_request).await;
 
     match response {
         Ok(mut response) => {
@@ -492,9 +496,6 @@ async fn request_well_known(
     let body: serde_json::Value = serde_json::from_str(
         &globals
             .reqwest_client()
-            .ok()?
-            .build()
-            .ok()?
             .get(&format!(
                 "https://{}/.well-known/matrix/server",
                 destination