[pbs-devel] [PATCH proxmox-backup 2/7] add tools::http for generic HTTP GET and move HttpsConnector there
Stefan Reiter
s.reiter at proxmox.com
Wed Oct 21 11:41:11 CEST 2020
...to avoid having the tools:: module depend on api2.
The get_string function is based directly on hyper and thus relatively
simple, not supporting redirects for example.
Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---
src/client/http_client.rs | 75 ++--------------------------
src/tools.rs | 1 +
src/tools/http.rs | 100 ++++++++++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+), 72 deletions(-)
create mode 100644 src/tools/http.rs
diff --git a/src/client/http_client.rs b/src/client/http_client.rs
index e92d4b18..b57630f8 100644
--- a/src/client/http_client.rs
+++ b/src/client/http_client.rs
@@ -1,8 +1,6 @@
use std::io::Write;
-use std::task::{Context, Poll};
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;
-use std::os::unix::io::AsRawFd;
use anyhow::{bail, format_err, Error};
use futures::*;
@@ -19,22 +17,16 @@ use xdg::BaseDirectories;
use proxmox::{
api::error::HttpError,
sys::linux::tty,
- tools::{
- fs::{file_get_json, replace_file, CreateOptions},
- }
+ tools::fs::{file_get_json, replace_file, CreateOptions},
};
use super::pipe_to_stream::PipeToSendStream;
use crate::api2::types::Userid;
-use crate::tools::async_io::EitherStream;
use crate::tools::{
self,
BroadcastFuture,
DEFAULT_ENCODE_SET,
- socket::{
- set_tcp_keepalive,
- PROXMOX_BACKUP_TCP_KEEPALIVE_TIME,
- },
+ http::HttpsConnector,
};
#[derive(Clone)]
@@ -301,7 +293,7 @@ impl HttpClient {
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
}
- let mut httpc = hyper::client::HttpConnector::new();
+ let mut httpc = HttpConnector::new();
httpc.set_nodelay(true); // important for h2 download performance!
httpc.enforce_http(false); // we want https...
@@ -929,64 +921,3 @@ impl H2Client {
}
}
}
-
-#[derive(Clone)]
-pub struct HttpsConnector {
- http: HttpConnector,
- ssl_connector: std::sync::Arc<SslConnector>,
-}
-
-impl HttpsConnector {
- pub fn with_connector(mut http: HttpConnector, ssl_connector: SslConnector) -> Self {
- http.enforce_http(false);
-
- Self {
- http,
- ssl_connector: std::sync::Arc::new(ssl_connector),
- }
- }
-}
-
-type MaybeTlsStream = EitherStream<
- tokio::net::TcpStream,
- tokio_openssl::SslStream<tokio::net::TcpStream>,
->;
-
-impl hyper::service::Service<Uri> for HttpsConnector {
- type Response = MaybeTlsStream;
- type Error = Error;
- type Future = std::pin::Pin<Box<
- dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static
- >>;
-
- fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
- // This connector is always ready, but others might not be.
- Poll::Ready(Ok(()))
- }
-
- fn call(&mut self, dst: Uri) -> Self::Future {
- let mut this = self.clone();
- async move {
- let is_https = dst
- .scheme()
- .ok_or_else(|| format_err!("missing URL scheme"))?
- == "https";
- let host = dst
- .host()
- .ok_or_else(|| format_err!("missing hostname in destination url?"))?
- .to_string();
-
- let config = this.ssl_connector.configure();
- let conn = this.http.call(dst).await?;
-
- let _ = set_tcp_keepalive(conn.as_raw_fd(), PROXMOX_BACKUP_TCP_KEEPALIVE_TIME);
-
- if is_https {
- let conn = tokio_openssl::connect(config?, &host, conn).await?;
- Ok(MaybeTlsStream::Right(conn))
- } else {
- Ok(MaybeTlsStream::Left(conn))
- }
- }.boxed()
- }
-}
diff --git a/src/tools.rs b/src/tools.rs
index 22d6c344..0e4a65c7 100644
--- a/src/tools.rs
+++ b/src/tools.rs
@@ -37,6 +37,7 @@ pub mod loopdev;
pub mod fuse_loop;
pub mod socket;
pub mod zip;
+pub mod http;
mod parallel_handler;
pub use parallel_handler::*;
diff --git a/src/tools/http.rs b/src/tools/http.rs
new file mode 100644
index 00000000..fe432806
--- /dev/null
+++ b/src/tools/http.rs
@@ -0,0 +1,100 @@
+use anyhow::{Error, format_err, bail};
+use lazy_static::lazy_static;
+use std::task::{Context, Poll};
+use std::os::unix::io::AsRawFd;
+
+use hyper::{Uri, Body};
+use hyper::client::{Client, HttpConnector};
+use openssl::ssl::{SslConnector, SslMethod};
+use futures::*;
+
+use crate::tools::{
+ async_io::EitherStream,
+ socket::{
+ set_tcp_keepalive,
+ PROXMOX_BACKUP_TCP_KEEPALIVE_TIME,
+ },
+};
+
+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)
+ };
+}
+
+pub async fn get_string<U: AsRef<str>>(uri: U) -> Result<String, Error> {
+ let res = HTTP_CLIENT.get(uri.as_ref().parse()?).await?;
+
+ let status = res.status();
+ if !status.is_success() {
+ bail!("Got bad status '{}' from server", status)
+ }
+
+ 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)]
+pub struct HttpsConnector {
+ http: HttpConnector,
+ ssl_connector: std::sync::Arc<SslConnector>,
+}
+
+impl HttpsConnector {
+ pub fn with_connector(mut http: HttpConnector, ssl_connector: SslConnector) -> Self {
+ http.enforce_http(false);
+
+ Self {
+ http,
+ ssl_connector: std::sync::Arc::new(ssl_connector),
+ }
+ }
+}
+
+type MaybeTlsStream = EitherStream<
+ tokio::net::TcpStream,
+ tokio_openssl::SslStream<tokio::net::TcpStream>,
+>;
+
+impl hyper::service::Service<Uri> for HttpsConnector {
+ type Response = MaybeTlsStream;
+ type Error = Error;
+ type Future = std::pin::Pin<Box<
+ dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static
+ >>;
+
+ fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ // This connector is always ready, but others might not be.
+ Poll::Ready(Ok(()))
+ }
+
+ fn call(&mut self, dst: Uri) -> Self::Future {
+ let mut this = self.clone();
+ async move {
+ let is_https = dst
+ .scheme()
+ .ok_or_else(|| format_err!("missing URL scheme"))?
+ == "https";
+ let host = dst
+ .host()
+ .ok_or_else(|| format_err!("missing hostname in destination url?"))?
+ .to_string();
+
+ let config = this.ssl_connector.configure();
+ let conn = this.http.call(dst).await?;
+
+ let _ = set_tcp_keepalive(conn.as_raw_fd(), PROXMOX_BACKUP_TCP_KEEPALIVE_TIME);
+
+ if is_https {
+ let conn = tokio_openssl::connect(config?, &host, conn).await?;
+ Ok(MaybeTlsStream::Right(conn))
+ } else {
+ Ok(MaybeTlsStream::Left(conn))
+ }
+ }.boxed()
+ }
+}
--
2.20.1
More information about the pbs-devel
mailing list