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 <s.reiter@proxmox.com>
This commit is contained in:
parent
12bcbf0734
commit
5eb9dd0c8a
|
@ -1,8 +1,6 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::task::{Context, Poll};
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use futures::*;
|
use futures::*;
|
||||||
|
@ -19,22 +17,16 @@ use xdg::BaseDirectories;
|
||||||
use proxmox::{
|
use proxmox::{
|
||||||
api::error::HttpError,
|
api::error::HttpError,
|
||||||
sys::linux::tty,
|
sys::linux::tty,
|
||||||
tools::{
|
tools::fs::{file_get_json, replace_file, CreateOptions},
|
||||||
fs::{file_get_json, replace_file, CreateOptions},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::pipe_to_stream::PipeToSendStream;
|
use super::pipe_to_stream::PipeToSendStream;
|
||||||
use crate::api2::types::Userid;
|
use crate::api2::types::Userid;
|
||||||
use crate::tools::async_io::EitherStream;
|
|
||||||
use crate::tools::{
|
use crate::tools::{
|
||||||
self,
|
self,
|
||||||
BroadcastFuture,
|
BroadcastFuture,
|
||||||
DEFAULT_ENCODE_SET,
|
DEFAULT_ENCODE_SET,
|
||||||
socket::{
|
http::HttpsConnector,
|
||||||
set_tcp_keepalive,
|
|
||||||
PROXMOX_BACKUP_TCP_KEEPALIVE_TIME,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -301,7 +293,7 @@ impl HttpClient {
|
||||||
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
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.set_nodelay(true); // important for h2 download performance!
|
||||||
httpc.enforce_http(false); // we want https...
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub mod loopdev;
|
||||||
pub mod fuse_loop;
|
pub mod fuse_loop;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
pub mod zip;
|
pub mod zip;
|
||||||
|
pub mod http;
|
||||||
|
|
||||||
mod parallel_handler;
|
mod parallel_handler;
|
||||||
pub use parallel_handler::*;
|
pub use parallel_handler::*;
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue