]> git.proxmox.com Git - proxmox.git/blob - proxmox-http/src/client/simple.rs
http: fix typo
[proxmox.git] / proxmox-http / src / client / simple.rs
1 use anyhow::{bail, format_err, Error};
2 use std::collections::HashMap;
3
4 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
5 use std::str::FromStr;
6
7 use futures::*;
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;
13 use hyper::Body;
14 use openssl::ssl::{SslConnector, SslMethod};
15
16 use crate::client::HttpsConnector;
17 use crate::HttpOptions;
18
19 /// Asynchronous HTTP client implementation
20 pub struct Client {
21 client: HyperClient<HttpsConnector, Body>,
22 options: HttpOptions,
23 }
24
25 impl Client {
26 pub const DEFAULT_USER_AGENT_STRING: &'static str = "proxmox-simple-http-client/0.1";
27
28 pub fn new() -> Self {
29 Self::with_options(HttpOptions::default())
30 }
31
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)
35 }
36
37 pub fn with_ssl_connector(ssl_connector: SslConnector, options: HttpOptions) -> Self {
38 let connector = HttpConnector::new();
39 let mut https = HttpsConnector::with_connector(
40 connector,
41 ssl_connector,
42 options.tcp_keepalive.unwrap_or(7200),
43 );
44 if let Some(ref proxy_config) = options.proxy_config {
45 https.set_proxy(proxy_config.clone());
46 }
47 let client = HyperClient::builder().build(https);
48 Self { client, options }
49 }
50
51 pub fn set_user_agent(&mut self, user_agent: &str) -> Result<(), Error> {
52 self.options.user_agent = Some(user_agent.to_owned());
53 Ok(())
54 }
55
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)?,
62 );
63 }
64 }
65 Ok(())
66 }
67
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)?
71 } else {
72 HeaderValue::from_str(Self::DEFAULT_USER_AGENT_STRING)?
73 };
74
75 request
76 .headers_mut()
77 .insert(hyper::header::USER_AGENT, user_agent);
78
79 self.add_proxy_headers(&mut request)?;
80
81 self.client.request(request).map_err(Error::from).await
82 }
83
84 pub async fn post(
85 &self,
86 uri: &str,
87 body: Option<String>,
88 content_type: Option<&str>,
89 ) -> Result<Response<Body>, Error> {
90 let body = if let Some(body) = body {
91 Body::from(body)
92 } else {
93 Body::empty()
94 };
95 let content_type = content_type.unwrap_or("application/json");
96
97 let request = Request::builder()
98 .method("POST")
99 .uri(uri)
100 .header(hyper::header::CONTENT_TYPE, content_type)
101 .body(body)?;
102
103 self.request(request).await
104 }
105
106 pub async fn get_string(
107 &self,
108 uri: &str,
109 extra_headers: Option<&HashMap<String, String>>,
110 ) -> Result<String, Error> {
111 let mut request = Request::builder().method("GET").uri(uri);
112
113 if let Some(hs) = extra_headers {
114 for (h, v) in hs.iter() {
115 request = request.header(h, v);
116 }
117 }
118
119 let request = request.body(Body::empty())?;
120
121 let res = self.request(request).await?;
122
123 let status = res.status();
124 if !status.is_success() {
125 bail!("Got bad status '{}' from server", status)
126 }
127
128 Self::response_body_string(res).await
129 }
130
131 pub async fn response_body_string(res: Response<Body>) -> Result<String, Error> {
132 Self::convert_body_to_string(Ok(res))
133 .await
134 .map(|res| res.into_body())
135 }
136
137 async fn convert_body_to_string(
138 response: Result<Response<Body>, Error>,
139 ) -> Result<Response<String>, Error> {
140 match response {
141 Ok(res) => {
142 let (parts, body) = res.into_parts();
143
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))?;
147
148 Ok(Response::from_parts(parts, new_body))
149 }
150 Err(err) => Err(err),
151 }
152 }
153 }
154
155 impl Default for Client {
156 fn default() -> Self {
157 Self::new()
158 }
159 }
160
161 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
162 impl crate::HttpClient<Body> for Client {
163 fn get(
164 &self,
165 uri: &str,
166 extra_headers: Option<&HashMap<String, String>>,
167 ) -> Result<Response<Body>, Error> {
168 let mut req = Request::builder()
169 .method("GET")
170 .uri(uri)
171 .body(Body::empty())?;
172
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)?);
177 }
178 }
179
180 proxmox_async::runtime::block_on(self.request(req))
181 }
182
183 fn post(
184 &self,
185 uri: &str,
186 body: Option<&str>,
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))
190 }
191
192 fn request(&self, request: Request<Body>) -> Result<Response<Body>, Error> {
193 proxmox_async::runtime::block_on(async move { self.request(request).await })
194 }
195 }
196
197 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
198 impl crate::HttpClient<String> for Client {
199 fn get(
200 &self,
201 uri: &str,
202 extra_headers: Option<&HashMap<String, String>>,
203 ) -> Result<Response<String>, Error> {
204 let mut req = Request::builder()
205 .method("GET")
206 .uri(uri)
207 .body(Body::empty())?;
208
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)?);
213 }
214 }
215
216 proxmox_async::runtime::block_on(async move {
217 Self::convert_body_to_string(self.request(req).await).await
218 })
219 }
220
221 fn post(
222 &self,
223 uri: &str,
224 body: Option<&str>,
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)
230 .await,
231 )
232 .await
233 })
234 }
235
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
242 })
243 }
244 }