]>
Commit | Line | Data |
---|---|---|
e5ef69ec DM |
1 | use anyhow::{Error, format_err, bail}; |
2 | use std::collections::HashMap; | |
3 | ||
4 | use hyper::Body; | |
5 | use hyper::client::{Client, HttpConnector}; | |
6 | use http::{Request, Response, HeaderValue}; | |
7 | use openssl::ssl::{SslConnector, SslMethod}; | |
8 | use futures::*; | |
9 | ||
3ed7e875 | 10 | use crate::tools::PROXMOX_BACKUP_TCP_KEEPALIVE_TIME; |
e5ef69ec DM |
11 | use crate::tools::http::{HttpsConnector, ProxyConfig}; |
12 | ||
13 | /// Asyncrounous HTTP client implementation | |
14 | pub struct SimpleHttp { | |
15 | client: Client<HttpsConnector, Body>, | |
16 | proxy_authorization: Option<String>, // Proxy-Authorization header value | |
17 | user_agent: Option<String>, | |
18 | } | |
19 | ||
20 | impl SimpleHttp { | |
21 | ||
22 | pub const DEFAULT_USER_AGENT_STRING: &'static str = "proxmox-backup-client/1.0"; | |
23 | ||
24 | pub fn new(proxy_config: Option<ProxyConfig>) -> Self { | |
25 | let ssl_connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); | |
26 | Self::with_ssl_connector(ssl_connector, proxy_config) | |
27 | } | |
28 | ||
29 | pub fn with_ssl_connector(ssl_connector: SslConnector, proxy_config: Option<ProxyConfig>) -> Self { | |
30 | ||
31 | let mut proxy_authorization = None; | |
32 | if let Some(ref proxy_config) = proxy_config { | |
33 | if !proxy_config.force_connect { | |
34 | proxy_authorization = proxy_config.authorization.clone(); | |
35 | } | |
36 | } | |
37 | ||
38 | let connector = HttpConnector::new(); | |
3ed7e875 | 39 | let mut https = HttpsConnector::with_connector(connector, ssl_connector, PROXMOX_BACKUP_TCP_KEEPALIVE_TIME); |
e5ef69ec DM |
40 | if let Some(proxy_config) = proxy_config { |
41 | https.set_proxy(proxy_config); | |
42 | } | |
43 | let client = Client::builder().build(https); | |
44 | Self { client, proxy_authorization, user_agent: None } | |
45 | } | |
46 | ||
47 | pub fn set_user_agent(&mut self, user_agent: &str) -> Result<(), Error> { | |
48 | self.user_agent = Some(user_agent.to_owned()); | |
49 | Ok(()) | |
50 | } | |
51 | ||
52 | fn add_proxy_headers(&self, request: &mut Request<Body>) -> Result<(), Error> { | |
53 | if request.uri().scheme() != Some(&http::uri::Scheme::HTTPS) { | |
54 | if let Some(ref authorization) = self.proxy_authorization { | |
55 | request | |
56 | .headers_mut() | |
57 | .insert( | |
58 | http::header::PROXY_AUTHORIZATION, | |
59 | HeaderValue::from_str(authorization)?, | |
60 | ); | |
61 | } | |
62 | } | |
63 | Ok(()) | |
64 | } | |
65 | ||
66 | pub async fn request(&self, mut request: Request<Body>) -> Result<Response<Body>, Error> { | |
67 | let user_agent = if let Some(ref user_agent) = self.user_agent { | |
68 | HeaderValue::from_str(&user_agent)? | |
69 | } else { | |
70 | HeaderValue::from_str(Self::DEFAULT_USER_AGENT_STRING)? | |
71 | }; | |
72 | ||
73 | request.headers_mut().insert(hyper::header::USER_AGENT, user_agent); | |
74 | ||
75 | self.add_proxy_headers(&mut request)?; | |
76 | ||
77 | self.client.request(request) | |
78 | .map_err(Error::from) | |
79 | .await | |
80 | } | |
81 | ||
82 | pub async fn post( | |
83 | &mut self, | |
84 | uri: &str, | |
85 | body: Option<String>, | |
86 | content_type: Option<&str>, | |
87 | ) -> Result<Response<Body>, Error> { | |
88 | ||
89 | let body = if let Some(body) = body { | |
90 | Body::from(body) | |
91 | } else { | |
92 | Body::empty() | |
93 | }; | |
94 | let content_type = content_type.unwrap_or("application/json"); | |
95 | ||
96 | let request = Request::builder() | |
97 | .method("POST") | |
98 | .uri(uri) | |
99 | .header(hyper::header::CONTENT_TYPE, content_type) | |
100 | .body(body)?; | |
101 | ||
102 | self.request(request).await | |
103 | } | |
104 | ||
105 | pub async fn get_string( | |
106 | &mut self, | |
107 | uri: &str, | |
108 | extra_headers: Option<&HashMap<String, String>>, | |
109 | ) -> Result<String, Error> { | |
110 | ||
111 | let mut request = Request::builder() | |
112 | .method("GET") | |
113 | .uri(uri); | |
114 | ||
115 | if let Some(hs) = extra_headers { | |
116 | for (h, v) in hs.iter() { | |
117 | request = request.header(h, v); | |
118 | } | |
119 | } | |
120 | ||
121 | let request = request.body(Body::empty())?; | |
122 | ||
123 | let res = self.request(request).await?; | |
124 | ||
125 | let status = res.status(); | |
126 | if !status.is_success() { | |
127 | bail!("Got bad status '{}' from server", status) | |
128 | } | |
129 | ||
130 | Self::response_body_string(res).await | |
131 | } | |
132 | ||
133 | pub async fn response_body_string(res: Response<Body>) -> Result<String, Error> { | |
134 | let buf = hyper::body::to_bytes(res).await?; | |
135 | String::from_utf8(buf.to_vec()) | |
136 | .map_err(|err| format_err!("Error converting HTTP result data: {}", err)) | |
137 | } | |
138 | } |