[pbs-devel] [RFC proxmox-backup 3/5] new http client implementation SimpleHttp (avoid static HTTP_CLIENT)

Dietmar Maurer dietmar at proxmox.com
Wed Apr 21 13:17:00 CEST 2021


This one will have proxy support.
---
 src/api2/node/apt.rs      |   8 ++-
 src/tools/http.rs         | 122 +++++++++++++++++++++-----------------
 src/tools/subscription.rs |  10 ++--
 3 files changed, 80 insertions(+), 60 deletions(-)

diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs
index e77b89fa..dbdb2019 100644
--- a/src/api2/node/apt.rs
+++ b/src/api2/node/apt.rs
@@ -7,7 +7,7 @@ use proxmox::api::{api, RpcEnvironment, RpcEnvironmentType, Permission};
 use proxmox::api::router::{Router, SubdirMap};
 
 use crate::server::WorkerTask;
-use crate::tools::{apt, http, subscription};
+use crate::tools::{apt, http::SimpleHttp, subscription};
 
 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
 use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA};
@@ -194,10 +194,12 @@ fn apt_get_changelog(
         bail!("Package '{}' not found", name);
     }
 
+    let mut client = SimpleHttp::new();
+
     let changelog_url = &pkg_info[0].change_log_url;
     // FIXME: use 'apt-get changelog' for proxmox packages as well, once repo supports it
     if changelog_url.starts_with("http://download.proxmox.com/") {
-        let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url, None))
+        let changelog = crate::tools::runtime::block_on(client.get_string(changelog_url, None))
             .map_err(|err| format_err!("Error downloading changelog from '{}': {}", changelog_url, err))?;
         Ok(json!(changelog))
 
@@ -221,7 +223,7 @@ fn apt_get_changelog(
         auth_header.insert("Authorization".to_owned(),
             format!("Basic {}", base64::encode(format!("{}:{}", key, id))));
 
-        let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url, Some(&auth_header)))
+        let changelog = crate::tools::runtime::block_on(client.get_string(changelog_url, Some(&auth_header)))
             .map_err(|err| format_err!("Error downloading changelog from '{}': {}", changelog_url, err))?;
         Ok(json!(changelog))
 
diff --git a/src/tools/http.rs b/src/tools/http.rs
index 3cd3af4e..f19d6527 100644
--- a/src/tools/http.rs
+++ b/src/tools/http.rs
@@ -1,5 +1,4 @@
 use anyhow::{Error, format_err, bail};
-use lazy_static::lazy_static;
 use std::task::{Context, Poll};
 use std::os::unix::io::AsRawFd;
 use std::collections::HashMap;
@@ -21,68 +20,85 @@ use crate::tools::{
     },
 };
 
-lazy_static! {
-    static ref HTTP_CLIENT: Client<HttpsConnector, Body> = {
-        let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
-        let httpc = HttpConnector::new();
-        let https = HttpsConnector::with_connector(httpc, connector);
-        Client::builder().build(https)
-    };
+/// Asyncrounous HTTP client implementation
+pub struct SimpleHttp {
+    client: Client<HttpsConnector, Body>,
 }
 
-pub async fn get_string(uri: &str, extra_headers: Option<&HashMap<String, String>>) -> Result<String, Error> {
-    let mut request = Request::builder()
-        .method("GET")
-        .uri(uri)
-        .header("User-Agent", "proxmox-backup-client/1.0");
+impl SimpleHttp {
 
-    if let Some(hs) = extra_headers {
-        for (h, v) in hs.iter() {
-            request = request.header(h, v);
-        }
+    pub fn new() -> Self {
+        let ssl_connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
+        Self::with_ssl_connector(ssl_connector)
     }
 
-    let request = request.body(Body::empty())?;
-
-    let res = HTTP_CLIENT.request(request).await?;
+    pub fn with_ssl_connector(ssl_connector: SslConnector) -> Self {
+        let connector = HttpConnector::new();
+        let https = HttpsConnector::with_connector(connector, ssl_connector);
+        let client = Client::builder().build(https);
+        Self { client }
+    }
 
-    let status = res.status();
-    if !status.is_success() {
-        bail!("Got bad status '{}' from server", status)
+    pub async fn post(
+        &mut self,
+        uri: &str,
+        body: Option<String>,
+        content_type: Option<&str>,
+    ) -> Result<Response<Body>, Error> {
+
+        let body = if let Some(body) = body {
+            Body::from(body)
+        } else {
+            Body::empty()
+        };
+        let content_type = content_type.unwrap_or("application/json");
+
+        let request = Request::builder()
+            .method("POST")
+            .uri(uri)
+            .header("User-Agent", "proxmox-backup-client/1.0")
+            .header(hyper::header::CONTENT_TYPE, content_type)
+            .body(body)?;
+
+        self.client.request(request)
+            .map_err(Error::from)
+            .await
     }
 
-    response_body_string(res).await
-}
+    pub async fn get_string(
+        &mut self,
+        uri: &str,
+        extra_headers: Option<&HashMap<String, String>>,
+    ) -> Result<String, Error> {
 
-pub async fn response_body_string(res: Response<Body>) -> Result<String, Error> {
-    let buf = hyper::body::to_bytes(res).await?;
-    String::from_utf8(buf.to_vec())
-        .map_err(|err| format_err!("Error converting HTTP result data: {}", err))
-}
+        let mut request = Request::builder()
+            .method("GET")
+            .uri(uri)
+            .header("User-Agent", "proxmox-backup-client/1.0");
+
+        if let Some(hs) = extra_headers {
+            for (h, v) in hs.iter() {
+                request = request.header(h, v);
+            }
+        }
 
-pub async fn post(
-    uri: &str,
-    body: Option<String>,
-    content_type: Option<&str>,
-) -> Result<Response<Body>, Error> {
-    let body = if let Some(body) = body {
-        Body::from(body)
-    } else {
-        Body::empty()
-    };
-    let content_type = content_type.unwrap_or("application/json");
-
-    let request = Request::builder()
-        .method("POST")
-        .uri(uri)
-        .header("User-Agent", "proxmox-backup-client/1.0")
-        .header(hyper::header::CONTENT_TYPE, content_type)
-        .body(body)?;
-
-
-    HTTP_CLIENT.request(request)
-        .map_err(Error::from)
-        .await
+        let request = request.body(Body::empty())?;
+
+        let res = self.client.request(request).await?;
+
+        let status = res.status();
+        if !status.is_success() {
+            bail!("Got bad status '{}' from server", status)
+        }
+
+        Self::response_body_string(res).await
+    }
+
+    pub async fn response_body_string(res: Response<Body>) -> Result<String, Error> {
+        let buf = hyper::body::to_bytes(res).await?;
+        String::from_utf8(buf.to_vec())
+            .map_err(|err| format_err!("Error converting HTTP result data: {}", err))
+    }
 }
 
 #[derive(Clone)]
diff --git a/src/tools/subscription.rs b/src/tools/subscription.rs
index 9b9534ac..9a920aee 100644
--- a/src/tools/subscription.rs
+++ b/src/tools/subscription.rs
@@ -6,8 +6,7 @@ use regex::Regex;
 
 use proxmox::api::api;
 
-use crate::tools;
-use crate::tools::http;
+use crate::tools::{self, http::SimpleHttp};
 use proxmox::tools::fs::{replace_file, CreateOptions};
 
 /// How long the local key is valid for in between remote checks
@@ -102,10 +101,13 @@ async fn register_subscription(
         "ip": "localhost",
         "check_token": challenge,
     });
+
+    let mut client = SimpleHttp::new();
+
     let uri = "https://shop.maurer-it.com/modules/servers/licensing/verify.php";
     let query = tools::json_object_to_query(params)?;
-    let response = http::post(uri, Some(query), Some("application/x-www-form-urlencoded")).await?;
-    let body = http::response_body_string(response).await?;
+    let response = client.post(uri, Some(query), Some("application/x-www-form-urlencoded")).await?;
+    let body = SimpleHttp::response_body_string(response).await?;
 
     Ok((body, challenge))
 }
-- 
2.20.1





More information about the pbs-devel mailing list