Mercurial > lbo > hg > async-google-apis
view async-google-apis-common/src/http.rs @ 85:8affd19aab34
yapf
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sat, 24 Oct 2020 17:27:54 +0200 |
parents | 171be899018e |
children | c260d92db27f |
line wrap: on
line source
use crate::*; use anyhow::Context; fn body_to_str(b: hyper::body::Bytes) -> String { String::from_utf8(b.to_vec()).unwrap_or("[UTF-8 decode failed]".into()) } /// This type is used as type parameter to the following functions, when `rq` is `None`. #[derive(Debug, Serialize)] pub struct EmptyRequest {} /// The Content-Type header is set automatically to application/json. pub async fn do_request<Req: Serialize + std::fmt::Debug, Resp: DeserializeOwned + Clone>( cl: &TlsClient, path: &str, headers: &[(String, String)], http_method: &str, rq: Option<Req>, ) -> Result<Resp> { let mut reqb = hyper::Request::builder().uri(path).method(http_method); for (k, v) in headers { reqb = reqb.header(k, v); } reqb = reqb.header("Content-Type", "application/json"); let body_str; if let Some(rq) = rq { body_str = serde_json::to_string(&rq).context(format!("{:?}", rq))?; } else { body_str = "".to_string(); } let body; if body_str == "null" { body = hyper::Body::from(""); } else { body = hyper::Body::from(body_str); } let http_request = reqb.body(body)?; debug!("do_request: Launching HTTP request: {:?}", http_request); let http_response = cl.request(http_request).await?; let status = http_response.status(); debug!( "do_request: HTTP response with status {} received: {:?}", status, http_response ); let response_body = hyper::body::to_bytes(http_response.into_body()).await?; if !status.is_success() { Err(ApiError::HTTPResponseError(status, body_to_str(response_body)).into()) } else { // Evaluate body_to_str lazily serde_json::from_reader(response_body.as_ref()) .map_err(|e| anyhow::Error::from(e).context(body_to_str(response_body))) } } /// The Content-Length header is set automatically. pub async fn do_upload_multipart<Req: Serialize + std::fmt::Debug, Resp: DeserializeOwned + Clone>( cl: &TlsClient, path: &str, headers: &[(String, String)], http_method: &str, req: Option<Req>, data: hyper::body::Bytes, ) -> Result<Resp> { let mut reqb = hyper::Request::builder().uri(path).method(http_method); for (k, v) in headers { reqb = reqb.header(k, v); } let data = multipart::format_multipart(&req, data)?; reqb = reqb.header("Content-Length", data.as_ref().len()); reqb = reqb.header( "Content-Type", format!("multipart/related; boundary={}", multipart::MIME_BOUNDARY), ); let body = hyper::Body::from(data.as_ref().to_vec()); let http_request = reqb.body(body)?; debug!( "do_upload_multipart: Launching HTTP request: {:?}", http_request ); let http_response = cl.request(http_request).await?; let status = http_response.status(); debug!( "do_upload_multipart: HTTP response with status {} received: {:?}", status, http_response ); let response_body = hyper::body::to_bytes(http_response.into_body()).await?; if !status.is_success() { Err(ApiError::HTTPResponseError(status, body_to_str(response_body)).into()) } else { serde_json::from_reader(response_body.as_ref()) .map_err(|e| anyhow::Error::from(e).context(body_to_str(response_body))) } } pub async fn do_download<Req: Serialize + std::fmt::Debug>( cl: &TlsClient, path: &str, headers: &[(String, String)], http_method: &str, rq: Option<Req>, dst: &mut dyn std::io::Write, ) -> Result<()> { let mut path = path.to_string(); let mut http_response; let mut i = 0; // Follow redirects. loop { let mut reqb = hyper::Request::builder().uri(&path).method(http_method); for (k, v) in headers { reqb = reqb.header(k, v); } let body_str = serde_json::to_string(&rq).context(format!("{:?}", rq))?; let body; if body_str == "null" { body = hyper::Body::from(""); } else { body = hyper::Body::from(body_str); } let http_request = reqb.body(body)?; debug!( "do_download: Redirect {}, Launching HTTP request: {:?}", i, http_request ); http_response = Some(cl.request(http_request).await?); let status = http_response.as_ref().unwrap().status(); debug!( "do_download: Redirect {}, HTTP response with status {} received: {:?}", i, status, http_response ); if status.is_success() { break; } else if status.is_redirection() { i += 1; let new_location = http_response .as_ref() .unwrap() .headers() .get(hyper::header::LOCATION); if new_location.is_none() { return Err(ApiError::RedirectError(format!( "Redirect doesn't contain a Location: header" )) .into()); } path = new_location.unwrap().to_str()?.to_string(); continue; } else if !status.is_success() { return Err(ApiError::HTTPResponseError( status, body_to_str(hyper::body::to_bytes(http_response.unwrap().into_body()).await?), ) .into()); } } let response_body = http_response.unwrap().into_body(); let write_results = response_body .map(move |chunk| { dst.write(chunk?.as_ref()) .map(|_| ()) .map_err(anyhow::Error::from) }) .collect::<Vec<Result<()>>>() .await; if let Some(e) = write_results.into_iter().find(|r| r.is_err()) { return e; } Ok(()) }