]> git.proxmox.com Git - proxmox-backup.git/blob - src/client/http_client.rs
cleanup
[proxmox-backup.git] / src / client / http_client.rs
1 use failure::*;
2
3 use http::Uri;
4 use hyper::Body;
5 use hyper::client::Client;
6 use hyper::rt::{self, Future};
7
8 use http::Request;
9 use futures::stream::Stream;
10
11 use serde_json::{Value};
12 use url::percent_encoding::{percent_encode, DEFAULT_ENCODE_SET};
13
14 /// HTTP(S) API client
15 pub struct HttpClient {
16 username: String,
17 server: String,
18 }
19
20 impl HttpClient {
21
22 pub fn new(server: &str, username: &str) -> Self {
23 Self {
24 server: String::from(server),
25 username: String::from(username),
26 }
27 }
28
29 fn run_request(
30 request: Request<Body>,
31 ) -> Result<Value, Error> {
32 let mut builder = native_tls::TlsConnector::builder();
33 // FIXME: We need a CLI option for this!
34 builder.danger_accept_invalid_certs(true);
35 let tlsconnector = builder.build()?;
36 let mut httpc = hyper::client::HttpConnector::new(1);
37 httpc.enforce_http(false); // we want https...
38 let mut https = hyper_tls::HttpsConnector::from((httpc, tlsconnector));
39 https.https_only(true); // force it!
40 let client = Client::builder().build::<_, Body>(https);
41
42 let (tx, rx) = std::sync::mpsc::channel();
43
44 let future = client
45 .request(request)
46 .map_err(Error::from)
47 .and_then(|resp| {
48
49 let status = resp.status();
50
51 resp.into_body().concat2().map_err(Error::from)
52 .and_then(move |data| {
53
54 let text = String::from_utf8(data.to_vec()).unwrap();
55 if status.is_success() {
56 if text.len() > 0 {
57 let value: Value = serde_json::from_str(&text)?;
58 Ok(value)
59 } else {
60 Ok(Value::Null)
61 }
62 } else {
63 bail!("HTTP Error {}: {}", status, text);
64 }
65 })
66 })
67 .then(move |res| {
68 tx.send(res).unwrap();
69 Ok(())
70 });
71
72 // drop client, else client keeps connectioon open (keep-alive feature)
73 drop(client);
74
75 rt::run(future);
76
77 rx.recv().unwrap()
78 }
79
80 pub fn get(&self, path: &str) -> Result<Value, Error> {
81
82 let path = path.trim_matches('/');
83 let url: Uri = format!("https://{}:8007/{}", self.server, path).parse()?;
84
85 let ticket = self.login()?;
86
87 let enc_ticket = percent_encode(ticket.as_bytes(), DEFAULT_ENCODE_SET).to_string();
88
89 let request = Request::builder()
90 .method("GET")
91 .uri(url)
92 .header("User-Agent", "proxmox-backup-client/1.0")
93 .header("Cookie", format!("PBSAuthCookie={}", enc_ticket))
94 .body(Body::empty())?;
95
96 Self::run_request(request)
97 }
98
99 fn login(&self) -> Result<String, Error> {
100
101 let url: Uri = format!("https://{}:8007/{}", self.server, "/api2/json/access/ticket").parse()?;
102
103 let password = match std::env::var("PBS_PASSWORD") {
104 Ok(p) => p,
105 Err(err) => bail!("missing passphrase - {}", err),
106 };
107
108 let query = url::form_urlencoded::Serializer::new(String::new())
109 .append_pair("username", &self.username)
110 .append_pair("password", &password)
111 .finish();
112
113 let request = Request::builder()
114 .method("POST")
115 .uri(url)
116 .header("User-Agent", "proxmox-backup-client/1.0")
117 .header("Content-Type", "application/x-www-form-urlencoded")
118 .body(Body::from(query))?;
119
120 let auth_res = Self::run_request(request)?;
121
122 let ticket = match auth_res["data"]["ticket"].as_str() {
123 Some(t) => t,
124 None => bail!("got unexpected respose for login request."),
125 };
126
127 Ok(ticket.to_owned())
128 }
129
130 pub fn upload(&self, content_type: &str, body: Body, path: &str) -> Result<Value, Error> {
131
132 let path = path.trim_matches('/');
133 let url: Uri = format!("https://{}:8007/{}", self.server, path).parse()?;
134
135 let ticket = self.login()?;
136
137 let enc_ticket = percent_encode(ticket.as_bytes(), DEFAULT_ENCODE_SET).to_string();
138
139 let request = Request::builder()
140 .method("POST")
141 .uri(url)
142 .header("User-Agent", "proxmox-backup-client/1.0")
143 .header("Cookie", format!("PBSAuthCookie={}", enc_ticket))
144 .header("Content-Type", content_type)
145 .body(body)?;
146
147 Self::run_request(request)
148 }
149 }