src/client/http_client.rs: use async for more functions

This commit is contained in:
Dietmar Maurer 2019-09-04 12:47:01 +02:00
parent 96f5e80abb
commit 1a7a0e74c0

View File

@ -143,7 +143,7 @@ impl HttpClient {
}) })
} }
/// Login future /// Login
/// ///
/// Login is done on demand, so this is onyl required if you need /// Login is done on demand, so this is onyl required if you need
/// access to authentication data in 'AuthInfo'. /// access to authentication data in 'AuthInfo'.
@ -188,97 +188,85 @@ impl HttpClient {
.build::<_, Body>(https) .build::<_, Body>(https)
} }
pub fn request(&self, mut req: Request<Body>) -> impl Future<Output = Result<Value, Error>> { pub async fn request(&self, mut req: Request<Body>) -> Result<Value, Error> {
let login = self.auth.listen();
let client = self.client.clone(); let client = self.client.clone();
login.and_then(move |auth| { let auth = self.login().await?;
let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET)); let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap()); req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
req.headers_mut().insert("CSRFPreventionToken", HeaderValue::from_str(&auth.token).unwrap()); req.headers_mut().insert("CSRFPreventionToken", HeaderValue::from_str(&auth.token).unwrap());
let request = Self::api_request(client, req); Self::api_request(client, req).await
request
})
} }
pub fn get( pub async fn get(
&self, &self,
path: &str, path: &str,
data: Option<Value>, data: Option<Value>,
) -> impl Future<Output = Result<Value, Error>> { ) -> Result<Value, Error> {
let req = Self::request_builder(&self.server, "GET", path, data).unwrap(); let req = Self::request_builder(&self.server, "GET", path, data).unwrap();
self.request(req) self.request(req).await
} }
pub fn delete( pub async fn delete(
&mut self, &mut self,
path: &str, path: &str,
data: Option<Value>, data: Option<Value>,
) -> impl Future<Output = Result<Value, Error>> { ) -> Result<Value, Error> {
let req = Self::request_builder(&self.server, "DELETE", path, data).unwrap(); let req = Self::request_builder(&self.server, "DELETE", path, data).unwrap();
self.request(req) self.request(req).await
} }
pub fn post( pub async fn post(
&mut self, &mut self,
path: &str, path: &str,
data: Option<Value>, data: Option<Value>,
) -> impl Future<Output = Result<Value, Error>> { ) -> Result<Value, Error> {
let req = Self::request_builder(&self.server, "POST", path, data).unwrap(); let req = Self::request_builder(&self.server, "POST", path, data).unwrap();
self.request(req) self.request(req).await
} }
pub fn download<W: Write + Send + 'static>( pub async fn download(
&mut self, &mut self,
path: &str, path: &str,
output: W, output: &mut (dyn Write + Send),
) -> impl Future<Output = Result<W, Error>> { ) -> Result<(), Error> {
let mut req = Self::request_builder(&self.server, "GET", path, None).unwrap(); let mut req = Self::request_builder(&self.server, "GET", path, None).unwrap();
let login = self.auth.listen();
let client = self.client.clone(); let client = self.client.clone();
login.and_then(move |auth| { let auth = self.login().await?;
let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET)); let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap()); req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
client.request(req) let resp = client.request(req).await?;
.map_err(Error::from)
.and_then(|resp| {
let status = resp.status(); let status = resp.status();
if !status.is_success() { if !status.is_success() {
future::Either::Left(
HttpClient::api_response(resp) HttpClient::api_response(resp)
.map(|_| Err(format_err!("unknown error"))) .map(|_| Err(format_err!("unknown error")))
) .await?
} else { } else {
future::Either::Right(
resp.into_body() resp.into_body()
.map_err(Error::from) .map_err(Error::from)
.try_fold(output, move |mut acc, chunk| async move { .try_fold(output, move |acc, chunk| async move {
acc.write_all(&chunk)?; acc.write_all(&chunk)?;
Ok::<_, Error>(acc) Ok::<_, Error>(acc)
}) })
) .await?;
} }
}) Ok(())
})
} }
pub fn upload( pub async fn upload(
&mut self, &mut self,
content_type: &str, content_type: &str,
body: Body, body: Body,
path: &str, path: &str,
data: Option<Value>, data: Option<Value>,
) -> impl Future<Output = Result<Value, Error>> { ) -> Result<Value, Error> {
let path = path.trim_matches('/'); let path = path.trim_matches('/');
let mut url = format!("https://{}:8007/{}", &self.server, path); let mut url = format!("https://{}:8007/{}", &self.server, path);
@ -298,17 +286,17 @@ impl HttpClient {
.header("Content-Type", content_type) .header("Content-Type", content_type)
.body(body).unwrap(); .body(body).unwrap();
self.request(req) self.request(req).await
} }
pub fn start_backup( pub async fn start_backup(
&self, &self,
datastore: &str, datastore: &str,
backup_type: &str, backup_type: &str,
backup_id: &str, backup_id: &str,
backup_time: DateTime<Utc>, backup_time: DateTime<Utc>,
debug: bool, debug: bool,
) -> impl Future<Output = Result<Arc<BackupClient>, Error>> { ) -> Result<Arc<BackupClient>, Error> {
let param = json!({ let param = json!({
"backup-type": backup_type, "backup-type": backup_type,
@ -320,18 +308,19 @@ impl HttpClient {
let req = Self::request_builder(&self.server, "GET", "/api2/json/backup", Some(param)).unwrap(); let req = Self::request_builder(&self.server, "GET", "/api2/json/backup", Some(param)).unwrap();
self.start_h2_connection(req, String::from(PROXMOX_BACKUP_PROTOCOL_ID_V1!())) let (h2, canceller) = self.start_h2_connection(req, String::from(PROXMOX_BACKUP_PROTOCOL_ID_V1!())).await?;
.map_ok(|(h2, canceller)| BackupClient::new(h2, canceller))
Ok(BackupClient::new(h2, canceller))
} }
pub fn start_backup_reader( pub async fn start_backup_reader(
&self, &self,
datastore: &str, datastore: &str,
backup_type: &str, backup_type: &str,
backup_id: &str, backup_id: &str,
backup_time: DateTime<Utc>, backup_time: DateTime<Utc>,
debug: bool, debug: bool,
) -> impl Future<Output = Result<Arc<BackupReader>, Error>> { ) -> Result<Arc<BackupReader>, Error> {
let param = json!({ let param = json!({
"backup-type": backup_type, "backup-type": backup_type,
@ -342,55 +331,48 @@ impl HttpClient {
}); });
let req = Self::request_builder(&self.server, "GET", "/api2/json/reader", Some(param)).unwrap(); let req = Self::request_builder(&self.server, "GET", "/api2/json/reader", Some(param)).unwrap();
self.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())) let (h2, canceller) = self.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())).await?;
.map_ok(|(h2, canceller)| BackupReader::new(h2, canceller))
Ok(BackupReader::new(h2, canceller))
} }
pub fn start_h2_connection( pub async fn start_h2_connection(
&self, &self,
mut req: Request<Body>, mut req: Request<Body>,
protocol_name: String, protocol_name: String,
) -> impl Future<Output = Result<(H2Client, Canceller), Error>> { ) -> Result<(H2Client, Canceller), Error> {
let login = self.auth.listen(); let auth = self.login().await?;
let client = self.client.clone(); let client = self.client.clone();
login.and_then(move |auth| {
let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET)); let enc_ticket = format!("PBSAuthCookie={}", percent_encode(auth.ticket.as_bytes(), DEFAULT_ENCODE_SET));
req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap()); req.headers_mut().insert("Cookie", HeaderValue::from_str(&enc_ticket).unwrap());
req.headers_mut().insert("UPGRADE", HeaderValue::from_str(&protocol_name).unwrap()); req.headers_mut().insert("UPGRADE", HeaderValue::from_str(&protocol_name).unwrap());
client.request(req) let resp = client.request(req).await?;
.map_err(Error::from)
.and_then(|resp| {
let status = resp.status(); let status = resp.status();
if status != http::StatusCode::SWITCHING_PROTOCOLS { if status != http::StatusCode::SWITCHING_PROTOCOLS {
future::Either::Left(
Self::api_response(resp) Self::api_response(resp)
.map(|_| Err(format_err!("unknown error"))) .map(|_| Err(format_err!("unknown error")))
) .await?;
} else { unreachable!();
future::Either::Right( }
resp
let upgraded = resp
.into_body() .into_body()
.on_upgrade() .on_upgrade()
.map_err(Error::from) .await?;
)
}
})
.and_then(|upgraded| {
let max_window_size = (1 << 31) - 2; let max_window_size = (1 << 31) - 2;
h2::client::Builder::new() let (h2, connection) = h2::client::Builder::new()
.initial_connection_window_size(max_window_size) .initial_connection_window_size(max_window_size)
.initial_window_size(max_window_size) .initial_window_size(max_window_size)
.max_frame_size(4*1024*1024) .max_frame_size(4*1024*1024)
.handshake(upgraded) .handshake(upgraded)
.map_err(Error::from) .await?;
})
.and_then(|(h2, connection)| async move {
let connection = connection let connection = connection
.map_err(|_| panic!("HTTP/2.0 connection failed")); .map_err(|_| panic!("HTTP/2.0 connection failed"));
@ -406,8 +388,6 @@ impl HttpClient {
// Wait until the `SendRequest` handle has available capacity. // Wait until the `SendRequest` handle has available capacity.
let c = h2.ready().await?; let c = h2.ready().await?;
Ok((H2Client::new(c), canceller)) Ok((H2Client::new(c), canceller))
}.boxed())
})
} }
async fn credentials( async fn credentials(
@ -450,14 +430,15 @@ impl HttpClient {
} }
} }
fn api_request( async fn api_request(
client: Client<HttpsConnector>, client: Client<HttpsConnector>,
req: Request<Body> req: Request<Body>
) -> impl Future<Output = Result<Value, Error>> { ) -> Result<Value, Error> {
client.request(req) client.request(req)
.map_err(Error::from) .map_err(Error::from)
.and_then(Self::api_response) .and_then(Self::api_response)
.await
} }
pub fn request_builder(server: &str, method: &str, path: &str, data: Option<Value>) -> Result<Request<Body>, Error> { pub fn request_builder(server: &str, method: &str, path: &str, data: Option<Value>) -> Result<Request<Body>, Error> {