From 5eb9dd0c8a5d7c8f76b584ab81e3926bbfd62c38 Mon Sep 17 00:00:00 2001 From: Stefan Reiter Date: Wed, 21 Oct 2020 11:41:11 +0200 Subject: [PATCH] add tools::http for generic HTTP GET and move HttpsConnector there ...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 --- 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, -} - -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, ->; - -impl hyper::service::Service for HttpsConnector { - type Response = MaybeTlsStream; - type Error = Error; - type Future = std::pin::Pin> + Send + 'static - >>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - // 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 = { + 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>(uri: U) -> Result { + 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, +} + +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, +>; + +impl hyper::service::Service for HttpsConnector { + type Response = MaybeTlsStream; + type Error = Error; + type Future = std::pin::Pin> + Send + 'static + >>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + // 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.39.5