1 use anyhow
::{bail, format_err, Error}
;
2 use std
::collections
::HashMap
;
4 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
8 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
9 use http
::header
::HeaderName
;
10 use http
::{HeaderValue, Request, Response}
;
11 use hyper
::client
::Client
as HyperClient
;
12 use hyper
::client
::HttpConnector
;
14 use openssl
::ssl
::{SslConnector, SslMethod}
;
16 use crate::client
::HttpsConnector
;
17 use crate::HttpOptions
;
19 /// Asynchronous HTTP client implementation
21 client
: HyperClient
<HttpsConnector
, Body
>,
26 pub const DEFAULT_USER_AGENT_STRING
: &'
static str = "proxmox-simple-http-client/0.1";
28 pub fn new() -> Self {
29 Self::with_options(HttpOptions
::default())
32 pub fn with_options(options
: HttpOptions
) -> Self {
33 let ssl_connector
= SslConnector
::builder(SslMethod
::tls()).unwrap().build();
34 Self::with_ssl_connector(ssl_connector
, options
)
37 pub fn with_ssl_connector(ssl_connector
: SslConnector
, options
: HttpOptions
) -> Self {
38 let connector
= HttpConnector
::new();
39 let mut https
= HttpsConnector
::with_connector(
42 options
.tcp_keepalive
.unwrap_or(7200),
44 if let Some(ref proxy_config
) = options
.proxy_config
{
45 https
.set_proxy(proxy_config
.clone());
47 let client
= HyperClient
::builder().build(https
);
48 Self { client, options }
51 pub fn set_user_agent(&mut self, user_agent
: &str) -> Result
<(), Error
> {
52 self.options
.user_agent
= Some(user_agent
.to_owned());
56 fn add_proxy_headers(&self, request
: &mut Request
<Body
>) -> Result
<(), Error
> {
57 if request
.uri().scheme() != Some(&http
::uri
::Scheme
::HTTPS
) {
58 if let Some(ref authorization
) = self.options
.get_proxy_authorization() {
59 request
.headers_mut().insert(
60 http
::header
::PROXY_AUTHORIZATION
,
61 HeaderValue
::from_str(authorization
)?
,
68 pub async
fn request(&self, mut request
: Request
<Body
>) -> Result
<Response
<Body
>, Error
> {
69 let user_agent
= if let Some(user_agent
) = &self.options
.user_agent
{
70 HeaderValue
::from_str(user_agent
)?
72 HeaderValue
::from_str(Self::DEFAULT_USER_AGENT_STRING
)?
77 .insert(hyper
::header
::USER_AGENT
, user_agent
);
79 self.add_proxy_headers(&mut request
)?
;
81 self.client
.request(request
).map_err(Error
::from
).await
88 content_type
: Option
<&str>,
89 ) -> Result
<Response
<Body
>, Error
> {
90 let body
= if let Some(body
) = body
{
95 let content_type
= content_type
.unwrap_or("application/json");
97 let request
= Request
::builder()
100 .header(hyper
::header
::CONTENT_TYPE
, content_type
)
103 self.request(request
).await
106 pub async
fn get_string(
109 extra_headers
: Option
<&HashMap
<String
, String
>>,
110 ) -> Result
<String
, Error
> {
111 let mut request
= Request
::builder().method("GET").uri(uri
);
113 if let Some(hs
) = extra_headers
{
114 for (h
, v
) in hs
.iter() {
115 request
= request
.header(h
, v
);
119 let request
= request
.body(Body
::empty())?
;
121 let res
= self.request(request
).await?
;
123 let status
= res
.status();
124 if !status
.is_success() {
125 bail
!("Got bad status '{}' from server", status
)
128 Self::response_body_string(res
).await
131 pub async
fn response_body_string(res
: Response
<Body
>) -> Result
<String
, Error
> {
132 Self::convert_body_to_string(Ok(res
))
134 .map(|res
| res
.into_body())
137 async
fn convert_body_to_string(
138 response
: Result
<Response
<Body
>, Error
>,
139 ) -> Result
<Response
<String
>, Error
> {
142 let (parts
, body
) = res
.into_parts();
144 let buf
= hyper
::body
::to_bytes(body
).await?
;
145 let new_body
= String
::from_utf8(buf
.to_vec())
146 .map_err(|err
| format_err
!("Error converting HTTP result data: {}", err
))?
;
148 Ok(Response
::from_parts(parts
, new_body
))
150 Err(err
) => Err(err
),
155 impl Default
for Client
{
156 fn default() -> Self {
161 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
162 impl crate::HttpClient
<Body
> for Client
{
166 extra_headers
: Option
<&HashMap
<String
, String
>>,
167 ) -> Result
<Response
<Body
>, Error
> {
168 let mut req
= Request
::builder()
171 .body(Body
::empty())?
;
173 if let Some(extra_headers
) = extra_headers
{
174 let headers
= req
.headers_mut();
175 for (header
, value
) in extra_headers
{
176 headers
.insert(HeaderName
::from_str(header
)?
, HeaderValue
::from_str(value
)?
);
180 proxmox_async
::runtime
::block_on(self.request(req
))
187 content_type
: Option
<&str>,
188 ) -> Result
<Response
<Body
>, Error
> {
189 proxmox_async
::runtime
::block_on(self.post(uri
, body
.map(|s
| s
.to_owned()), content_type
))
192 fn request(&self, request
: Request
<Body
>) -> Result
<Response
<Body
>, Error
> {
193 proxmox_async
::runtime
::block_on(async
move { self.request(request).await }
)
197 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
198 impl crate::HttpClient
<String
> for Client
{
202 extra_headers
: Option
<&HashMap
<String
, String
>>,
203 ) -> Result
<Response
<String
>, Error
> {
204 let mut req
= Request
::builder()
207 .body(Body
::empty())?
;
209 if let Some(extra_headers
) = extra_headers
{
210 let headers
= req
.headers_mut();
211 for (header
, value
) in extra_headers
{
212 headers
.insert(HeaderName
::from_str(header
)?
, HeaderValue
::from_str(value
)?
);
216 proxmox_async
::runtime
::block_on(async
move {
217 Self::convert_body_to_string(self.request(req
).await
).await
225 content_type
: Option
<&str>,
226 ) -> Result
<Response
<String
>, Error
> {
227 proxmox_async
::runtime
::block_on(async
move {
228 Self::convert_body_to_string(
229 self.post(uri
, body
.map(|s
| s
.to_owned()), content_type
)
236 fn request(&self, request
: Request
<String
>) -> Result
<Response
<String
>, Error
> {
237 proxmox_async
::runtime
::block_on(async
move {
238 let (parts
, body
) = request
.into_parts();
239 let body
= Body
::from(body
);
240 let request
= Request
::from_parts(parts
, body
);
241 Self::convert_body_to_string(self.request(request
).await
).await