public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Fabian Grünbichler" <f.gruenbichler@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox 10/13] http: takeover simple HTTP client from proxmox_backup
Date: Fri, 14 May 2021 15:44:46 +0200	[thread overview]
Message-ID: <20210514134457.1447930-11-f.gruenbichler@proxmox.com> (raw)
In-Reply-To: <20210514134457.1447930-1-f.gruenbichler@proxmox.com>

adapted to use already moved helpers/code.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
 proxmox-http/src/http/client.rs        |   4 +
 proxmox-http/src/http/client/simple.rs | 157 +++++++++++++++++++++++++
 2 files changed, 161 insertions(+)
 create mode 100644 proxmox-http/src/http/client/simple.rs

diff --git a/proxmox-http/src/http/client.rs b/proxmox-http/src/http/client.rs
index 21a65e3..c55cbd4 100644
--- a/proxmox-http/src/http/client.rs
+++ b/proxmox-http/src/http/client.rs
@@ -1,3 +1,7 @@
 mod connector;
 
 pub use connector::HttpsConnector;
+
+mod simple;
+pub use simple::SimpleHttp;
+pub use simple::SimpleHttpOptions;
diff --git a/proxmox-http/src/http/client/simple.rs b/proxmox-http/src/http/client/simple.rs
new file mode 100644
index 0000000..110fa55
--- /dev/null
+++ b/proxmox-http/src/http/client/simple.rs
@@ -0,0 +1,157 @@
+use anyhow::{Error, format_err, bail};
+use std::collections::HashMap;
+
+use hyper::Body;
+use hyper::client::{Client, HttpConnector};
+use http::{Request, Response, HeaderValue};
+use openssl::ssl::{SslConnector, SslMethod};
+use futures::*;
+
+use crate::http::{
+    ProxyConfig,
+    client::HttpsConnector,
+};
+
+/// Options for a SimpleHttp client.
+#[derive(Default)]
+pub struct SimpleHttpOptions {
+    /// Proxy configuration
+    pub proxy_config: Option<ProxyConfig>,
+    /// `User-Agent` header value, defaults to `proxmox-simple-http-client/0.1`
+    pub user_agent: Option<String>,
+    /// TCP keepalive time, defaults to 7200
+    pub tcp_keepalive: Option<u32>,
+}
+
+impl SimpleHttpOptions {
+    fn get_proxy_authorization(&self) -> Option<String> {
+        if let Some(ref proxy_config) = self.proxy_config {
+            if !proxy_config.force_connect {
+               return proxy_config.authorization.clone();
+            }
+        }
+
+        None
+    }
+}
+
+/// Asyncrounous HTTP client implementation
+pub struct SimpleHttp {
+    client: Client<HttpsConnector, Body>,
+    options: SimpleHttpOptions,
+}
+
+impl SimpleHttp {
+    pub const DEFAULT_USER_AGENT_STRING: &'static str = "proxmox-simple-http-client/0.1";
+
+    pub fn new() -> Self {
+        Self::with_options(SimpleHttpOptions::default())
+    }
+
+    pub fn with_options(options: SimpleHttpOptions) -> Self {
+        let ssl_connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
+        Self::with_ssl_connector(ssl_connector, options)
+    }
+
+    pub fn with_ssl_connector(ssl_connector: SslConnector, options: SimpleHttpOptions) -> Self {
+        let connector = HttpConnector::new();
+        let mut https = HttpsConnector::with_connector(connector, ssl_connector, options.tcp_keepalive.unwrap_or(7200));
+        if let Some(ref proxy_config) = options.proxy_config {
+            https.set_proxy(proxy_config.clone());
+        }
+        let client = Client::builder().build(https);
+        Self { client, options }
+    }
+
+    pub fn set_user_agent(&mut self, user_agent: &str) -> Result<(), Error> {
+        self.options.user_agent = Some(user_agent.to_owned());
+        Ok(())
+    }
+
+    fn add_proxy_headers(&self, request: &mut Request<Body>) -> Result<(), Error> {
+        if request.uri().scheme() != Some(&http::uri::Scheme::HTTPS) {
+            if let Some(ref authorization) = self.options.get_proxy_authorization() {
+                request
+                    .headers_mut()
+                    .insert(
+                        http::header::PROXY_AUTHORIZATION,
+                        HeaderValue::from_str(authorization)?,
+                    );
+            }
+        }
+        Ok(())
+    }
+
+    pub async fn request(&self, mut request: Request<Body>) -> Result<Response<Body>, Error> {
+        let user_agent = if let Some(ref user_agent) = self.options.user_agent {
+            HeaderValue::from_str(&user_agent)?
+        } else {
+            HeaderValue::from_str(Self::DEFAULT_USER_AGENT_STRING)?
+        };
+
+        request.headers_mut().insert(hyper::header::USER_AGENT, user_agent);
+
+        self.add_proxy_headers(&mut request)?;
+
+        self.client.request(request)
+            .map_err(Error::from)
+            .await
+    }
+
+    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(hyper::header::CONTENT_TYPE, content_type)
+            .body(body)?;
+
+        self.request(request).await
+    }
+
+    pub async fn get_string(
+        &mut self,
+        uri: &str,
+        extra_headers: Option<&HashMap<String, String>>,
+    ) -> Result<String, Error> {
+
+        let mut request = Request::builder()
+            .method("GET")
+            .uri(uri);
+
+        if let Some(hs) = extra_headers {
+            for (h, v) in hs.iter() {
+                request = request.header(h, v);
+            }
+        }
+
+        let request = request.body(Body::empty())?;
+
+        let res = self.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))
+    }
+}
-- 
2.20.1





  parent reply	other threads:[~2021-05-14 13:45 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-14 13:44 [pbs-devel] [PATCH proxmox(-backup) 00/21] pull HTTP code into proxmox/proxmox-http Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 01/13] proxmox: add missing +router -> futures dep Fabian Grünbichler
2021-05-17  8:22   ` [pbs-devel] applied: " Dietmar Maurer
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 02/13] meta: add empty proxmox-http sub-crate Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 03/13] http: takeover websocket feature from proxmox Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 04/13] http: make clippy happy Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 05/13] proxmox: takeover socket helper from proxmox_backup Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 06/13] http: takeover MaybeTlsStream " Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 07/13] http: takeover build_authority helper " Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 08/13] http: takeover ProxyConfig " Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 09/13] http: takeover tools::http " Fabian Grünbichler
2021-05-14 13:44 ` Fabian Grünbichler [this message]
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 11/13] http: make clippy happy Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 12/13] http: rustfmt Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox 13/13] http: update d/control Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 1/8] move websocket to new 'proxmox_http' crate Fabian Grünbichler
2021-05-17  8:35   ` [pbs-devel] applied: " Dietmar Maurer
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 2/8] refactor: move socket helper to proxmox crate Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 3/8] move MaybeTlsStream wrapper to proxmox_http Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 4/8] HttpsConnector: make keepalive configurable Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 5/8] move ProxyConfig to proxmox_http Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 6/8] move tools::http " Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 7/8] SimpleHttp: factor out product-specific bits Fabian Grünbichler
2021-05-14 13:44 ` [pbs-devel] [PATCH proxmox-backup 8/8] move SimpleHttp to proxmox_http Fabian Grünbichler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210514134457.1447930-11-f.gruenbichler@proxmox.com \
    --to=f.gruenbichler@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal