5 use hyper
::client
::Client
;
6 use hyper
::rt
::{self, Future}
;
9 use futures
::stream
::Stream
;
11 use serde_json
::{Value}
;
12 use url
::percent_encoding
::{percent_encode, DEFAULT_ENCODE_SET}
;
14 /// HTTP(S) API client
15 pub struct HttpClient
{
22 pub fn new(server
: &str, username
: &str) -> Self {
24 server
: String
::from(server
),
25 username
: String
::from(username
),
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
);
42 let (tx
, rx
) = std
::sync
::mpsc
::channel();
49 let status
= resp
.status();
51 resp
.into_body().concat2().map_err(Error
::from
)
52 .and_then(move |data
| {
54 let text
= String
::from_utf8(data
.to_vec()).unwrap();
55 if status
.is_success() {
57 let value
: Value
= serde_json
::from_str(&text
)?
;
63 bail
!("HTTP Error {}: {}", status
, text
);
68 tx
.send(res
).unwrap();
72 // drop client, else client keeps connectioon open (keep-alive feature)
80 pub fn get(&self, path
: &str) -> Result
<Value
, Error
> {
82 let path
= path
.trim_matches('
/'
);
83 let url
: Uri
= format
!("https://{}:8007/{}", self.server
, path
).parse()?
;
85 let ticket
= self.login()?
;
87 let enc_ticket
= percent_encode(ticket
.as_bytes(), DEFAULT_ENCODE_SET
).to_string();
89 let request
= Request
::builder()
92 .header("User-Agent", "proxmox-backup-client/1.0")
93 .header("Cookie", format
!("PBSAuthCookie={}", enc_ticket
))
94 .body(Body
::empty())?
;
96 Self::run_request(request
)
99 fn login(&self) -> Result
<String
, Error
> {
101 let url
: Uri
= format
!("https://{}:8007/{}", self.server
, "/api2/json/access/ticket").parse()?
;
103 let password
= match std
::env
::var("PBS_PASSWORD") {
105 Err(err
) => bail
!("missing passphrase - {}", err
),
108 let query
= url
::form_urlencoded
::Serializer
::new(String
::new())
109 .append_pair("username", &self.username
)
110 .append_pair("password", &password
)
113 let request
= Request
::builder()
116 .header("User-Agent", "proxmox-backup-client/1.0")
117 .header("Content-Type", "application/x-www-form-urlencoded")
118 .body(Body
::from(query
))?
;
120 let auth_res
= Self::run_request(request
)?
;
122 let ticket
= match auth_res
["data"]["ticket"].as_str() {
124 None
=> bail
!("got unexpected respose for login request."),
127 Ok(ticket
.to_owned())
130 pub fn upload(&self, content_type
: &str, body
: Body
, path
: &str) -> Result
<Value
, Error
> {
132 let path
= path
.trim_matches('
/'
);
133 let url
: Uri
= format
!("https://{}:8007/{}", self.server
, path
).parse()?
;
135 let ticket
= self.login()?
;
137 let enc_ticket
= percent_encode(ticket
.as_bytes(), DEFAULT_ENCODE_SET
).to_string();
139 let request
= Request
::builder()
142 .header("User-Agent", "proxmox-backup-client/1.0")
143 .header("Cookie", format
!("PBSAuthCookie={}", enc_ticket
))
144 .header("Content-Type", content_type
)
147 Self::run_request(request
)