1 use anyhow
::{Error, format_err, bail}
;
2 use lazy_static
::lazy_static
;
3 use std
::task
::{Context, Poll}
;
4 use std
::os
::unix
::io
::AsRawFd
;
5 use std
::collections
::HashMap
;
8 use hyper
::{Uri, Body}
;
9 use hyper
::client
::{Client, HttpConnector}
;
10 use http
::{Request, Response}
;
11 use openssl
::ssl
::{SslConnector, SslMethod}
;
15 async_io
::EitherStream
,
18 PROXMOX_BACKUP_TCP_KEEPALIVE_TIME
,
23 static ref HTTP_CLIENT
: Client
<HttpsConnector
, Body
> = {
24 let connector
= SslConnector
::builder(SslMethod
::tls()).unwrap().build();
25 let httpc
= HttpConnector
::new();
26 let https
= HttpsConnector
::with_connector(httpc
, connector
);
27 Client
::builder().build(https
)
31 pub async
fn get_string(uri
: &str, extra_headers
: Option
<&HashMap
<String
, String
>>) -> Result
<String
, Error
> {
32 let mut request
= Request
::builder()
35 .header("User-Agent", "proxmox-backup-client/1.0");
37 if let Some(hs
) = extra_headers
{
38 for (h
, v
) in hs
.iter() {
39 request
= request
.header(h
, v
);
43 let request
= request
.body(Body
::empty())?
;
45 let res
= HTTP_CLIENT
.request(request
).await?
;
47 let status
= res
.status();
48 if !status
.is_success() {
49 bail
!("Got bad status '{}' from server", status
)
52 response_body_string(res
).await
55 pub async
fn response_body_string(res
: Response
<Body
>) -> Result
<String
, Error
> {
56 let buf
= hyper
::body
::to_bytes(res
).await?
;
57 String
::from_utf8(buf
.to_vec())
58 .map_err(|err
| format_err
!("Error converting HTTP result data: {}", err
))
64 content_type
: Option
<&str>,
65 ) -> Result
<Response
<Body
>, Error
> {
66 let body
= if let Some(body
) = body
{
71 let content_type
= content_type
.unwrap_or("application/json");
73 let request
= Request
::builder()
76 .header("User-Agent", "proxmox-backup-client/1.0")
77 .header(hyper
::header
::CONTENT_TYPE
, content_type
)
81 HTTP_CLIENT
.request(request
)
87 pub struct HttpsConnector
{
89 ssl_connector
: std
::sync
::Arc
<SslConnector
>,
93 pub fn with_connector(mut http
: HttpConnector
, ssl_connector
: SslConnector
) -> Self {
94 http
.enforce_http(false);
98 ssl_connector
: std
::sync
::Arc
::new(ssl_connector
),
103 type MaybeTlsStream
= EitherStream
<
104 tokio
::net
::TcpStream
,
105 Pin
<Box
<tokio_openssl
::SslStream
<tokio
::net
::TcpStream
>>>,
108 impl hyper
::service
::Service
<Uri
> for HttpsConnector
{
109 type Response
= MaybeTlsStream
;
111 type Future
= std
::pin
::Pin
<Box
<
112 dyn Future
<Output
= Result
<Self::Response
, Self::Error
>> + Send
+ '
static
115 fn poll_ready(&mut self, _
: &mut Context
<'_
>) -> Poll
<Result
<(), Self::Error
>> {
116 // This connector is always ready, but others might not be.
120 fn call(&mut self, dst
: Uri
) -> Self::Future
{
121 let mut this
= self.clone();
125 .ok_or_else(|| format_err
!("missing URL scheme"))?
128 let config
= this
.ssl_connector
.configure();
129 let dst_str
= dst
.to_string(); // for error messages
134 .map_err(|err
| format_err
!("error connecting to {} - {}", dst_str
, err
))?
;
136 let _
= set_tcp_keepalive(conn
.as_raw_fd(), PROXMOX_BACKUP_TCP_KEEPALIVE_TIME
);
139 let conn
: tokio_openssl
::SslStream
<tokio
::net
::TcpStream
> = tokio_openssl
::SslStream
::new(config?
.into_ssl(&dst_str
)?
, conn
)?
;
140 let mut conn
= Box
::pin(conn
);
141 conn
.as_mut().connect().await?
;
142 Ok(MaybeTlsStream
::Right(conn
))
144 Ok(MaybeTlsStream
::Left(conn
))