Mercurial > lbo > hg > async-google-apis
changeset 81:171be899018e
Improve error reporting
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sat, 24 Oct 2020 11:16:52 +0200 |
parents | c2efbeae7103 |
children | 9bffd4cf3e50 |
files | async-google-apis-common/Cargo.lock async-google-apis-common/src/error.rs async-google-apis-common/src/http.rs async-google-apis-common/src/lib.rs async-google-apis-common/src/multipart.rs |
diffstat | 5 files changed, 101 insertions(+), 174 deletions(-) [+] |
line wrap: on
line diff
--- a/async-google-apis-common/Cargo.lock Sat Oct 24 11:12:10 2020 +0200 +++ b/async-google-apis-common/Cargo.lock Sat Oct 24 11:16:52 2020 +0200 @@ -14,18 +14,18 @@ [[package]] name = "async-google-apis-common" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "chrono", "hyper", - "hyper-rustls 0.20.0", + "hyper-rustls", "log", "percent-encoding", "radix64", "serde", "serde_json", - "tokio 0.3.0", + "tokio", "yup-oauth2", ] @@ -117,15 +117,6 @@ ] [[package]] -name = "ct-logs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c8e13110a84b6315df212c045be706af261fd364791cad863285439ebba672e" -dependencies = [ - "sct", -] - -[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -256,7 +247,7 @@ "http", "indexmap", "slab", - "tokio 0.2.22", + "tokio", "tokio-util", "tracing", ] @@ -318,7 +309,7 @@ "itoa", "pin-project", "socket2", - "tokio 0.2.22", + "tokio", "tower-service", "tracing", "want", @@ -331,32 +322,14 @@ checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" dependencies = [ "bytes", - "ct-logs 0.6.0", + "ct-logs", "futures-util", "hyper", "log", - "rustls 0.17.0", - "rustls-native-certs 0.3.0", - "tokio 0.2.22", - "tokio-rustls 0.13.1", - "webpki", -] - -[[package]] -name = "hyper-rustls" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" -dependencies = [ - "bytes", - "ct-logs 0.7.0", - "futures-util", - "hyper", - "log", - "rustls 0.18.1", - "rustls-native-certs 0.4.0", - "tokio 0.2.22", - "tokio-rustls 0.14.1", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", "webpki", ] @@ -634,40 +607,15 @@ ] [[package]] -name = "rustls" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" -dependencies = [ - "base64 0.12.3", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] name = "rustls-native-certs" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" dependencies = [ "openssl-probe", - "rustls 0.17.0", + "rustls", "schannel", - "security-framework 0.4.4", -] - -[[package]] -name = "rustls-native-certs" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629d439a7672da82dd955498445e496ee2096fe2117b9f796558a43fdb9e59b8" -dependencies = [ - "openssl-probe", - "rustls 0.18.1", - "schannel", - "security-framework 1.0.0", + "security-framework", ] [[package]] @@ -712,20 +660,7 @@ "core-foundation", "core-foundation-sys", "libc", - "security-framework-sys 0.4.3", -] - -[[package]] -name = "security-framework" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad502866817f0575705bd7be36e2b2535cc33262d493aa733a2ec862baa2bc2b" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys 1.0.0", + "security-framework-sys", ] [[package]] @@ -739,16 +674,6 @@ ] [[package]] -name = "security-framework-sys" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ceb04988b17b6d1dcd555390fa822ca5637b4a14e1f5099f13d351bed4d6c7" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -846,28 +771,14 @@ "mio", "pin-project-lite", "slab", -] - -[[package]] -name = "tokio" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7137dbb0abee577362ccdc7df21605cfcbb949243aeab47dac9ea6ef7d830e21" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "memchr", - "pin-project-lite", - "slab", "tokio-macros", ] [[package]] name = "tokio-macros" -version = "0.3.0" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48caa7b66c7a6ec943edf78d21a594fbeb24e536c781da67d5c32edec54103f" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ "proc-macro2", "quote", @@ -881,20 +792,8 @@ checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" dependencies = [ "futures-core", - "rustls 0.17.0", - "tokio 0.2.22", - "webpki", -] - -[[package]] -name = "tokio-rustls" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" -dependencies = [ - "futures-core", - "rustls 0.18.1", - "tokio 0.2.22", + "rustls", + "tokio", "webpki", ] @@ -909,7 +808,7 @@ "futures-sink", "log", "pin-project-lite", - "tokio 0.2.22", + "tokio", ] [[package]] @@ -1122,22 +1021,22 @@ [[package]] name = "yup-oauth2" -version = "5.0.0" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634867fbb0b01d365fdb5f244b54bdc248d96ac18372f76bc97bfbd2ae250bf2" +checksum = "749192b9464694a95dbaf0586e845c835b315e38d491aa2766a8477aaadb48ec" dependencies = [ "base64 0.12.3", "chrono", "futures", "http", "hyper", - "hyper-rustls 0.21.0", + "hyper-rustls", "log", "percent-encoding", - "rustls 0.17.0", + "rustls", "seahash", "serde", "serde_json", - "tokio 0.3.0", + "tokio", "url", ]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/async-google-apis-common/src/error.rs Sat Oct 24 11:16:52 2020 +0200 @@ -0,0 +1,13 @@ +#[derive(Debug)] +pub enum ApiError { + HTTPResponseError(hyper::StatusCode, String), + RedirectError(String), + InputDataError(String), +} + +impl std::error::Error for ApiError {} +impl std::fmt::Display for ApiError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +}
--- a/async-google-apis-common/src/http.rs Sat Oct 24 11:12:10 2020 +0200 +++ b/async-google-apis-common/src/http.rs Sat Oct 24 11:16:52 2020 +0200 @@ -1,11 +1,17 @@ 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(Serialize)] +#[derive(Debug, Serialize)] pub struct EmptyRequest {} /// The Content-Type header is set automatically to application/json. -pub async fn do_request<Req: Serialize, Resp: DeserializeOwned + Clone>( +pub async fn do_request<Req: Serialize + std::fmt::Debug, Resp: DeserializeOwned + Clone>( cl: &TlsClient, path: &str, headers: &[(String, String)], @@ -19,7 +25,7 @@ reqb = reqb.header("Content-Type", "application/json"); let body_str; if let Some(rq) = rq { - body_str = serde_json::to_string(&rq)?; + body_str = serde_json::to_string(&rq).context(format!("{:?}", rq))?; } else { body_str = "".to_string(); } @@ -38,22 +44,23 @@ let http_response = cl.request(http_request).await?; let status = http_response.status(); - debug!("do_request: HTTP response with status {} received: {:?}", status, http_response); + debug!( + "do_request: HTTP response with status {} received: {:?}", + status, http_response + ); let response_body = hyper::body::to_bytes(http_response.into_body()).await?; - let response_body_str = String::from_utf8(response_body.to_vec()); if !status.is_success() { - Err(Error::new(ApiError::HTTPError( - status, - response_body_str.unwrap_or("".to_string()), - ))) + Err(ApiError::HTTPResponseError(status, body_to_str(response_body)).into()) } else { - serde_json::from_reader(response_body.as_ref()).map_err(Error::from) + // 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, Resp: DeserializeOwned + Clone>( +pub async fn do_upload_multipart<Req: Serialize + std::fmt::Debug, Resp: DeserializeOwned + Clone>( cl: &TlsClient, path: &str, headers: &[(String, String)], @@ -75,24 +82,27 @@ 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); + 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); + debug!( + "do_upload_multipart: HTTP response with status {} received: {:?}", + status, http_response + ); let response_body = hyper::body::to_bytes(http_response.into_body()).await?; - let response_body_str = String::from_utf8(response_body.to_vec()); if !status.is_success() { - Err(Error::new(ApiError::HTTPError( - status, - response_body_str.unwrap_or("".to_string()), - ))) + Err(ApiError::HTTPResponseError(status, body_to_str(response_body)).into()) } else { - serde_json::from_reader(response_body.as_ref()).map_err(Error::from) + 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>( +pub async fn do_download<Req: Serialize + std::fmt::Debug>( cl: &TlsClient, path: &str, headers: &[(String, String)], @@ -110,7 +120,7 @@ for (k, v) in headers { reqb = reqb.header(k, v); } - let body_str = serde_json::to_string(&rq)?; + let body_str = serde_json::to_string(&rq).context(format!("{:?}", rq))?; let body; if body_str == "null" { body = hyper::Body::from(""); @@ -119,11 +129,17 @@ } let http_request = reqb.body(body)?; - debug!("do_download: Redirect {}, Launching HTTP request: {:?}", i, http_request); + 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); + debug!( + "do_download: Redirect {}, HTTP response with status {} received: {:?}", + i, status, http_response + ); if status.is_success() { break; @@ -135,21 +151,29 @@ .headers() .get(hyper::header::LOCATION); if new_location.is_none() { - return Err(Error::new(ApiError::HTTPError( - status, - format!("Redirect doesn't contain a Location: header"), - ))); + 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(Error::new(ApiError::HTTPError(status, String::new()))); + 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(Error::from)) + .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()) {
--- a/async-google-apis-common/src/lib.rs Sat Oct 24 11:12:10 2020 +0200 +++ b/async-google-apis-common/src/lib.rs Sat Oct 24 11:16:52 2020 +0200 @@ -1,11 +1,18 @@ //! Common types, imports, and functions used by generated code, including HTTP requests and error //! types. +mod error; +pub use error::*; +mod http; +pub use http::*; + +mod multipart; + pub use hyper; +pub use log::{debug, error, info, trace, warn}; pub use serde; pub use serde_json; pub use yup_oauth2; -pub use log::{trace, debug, info, warn, error}; pub use anyhow::{Error, Result}; pub use chrono::{DateTime, Utc}; @@ -17,21 +24,3 @@ pub type Authenticator = yup_oauth2::authenticator::Authenticator<TlsConnr>; pub type TlsClient = hyper::Client<TlsConnr, hyper::Body>; pub type TlsConnr = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>; - -#[derive(Debug, Clone)] -pub enum ApiError { - InputDataError(String), - HTTPError(hyper::StatusCode, String), -} - -impl std::error::Error for ApiError {} -impl std::fmt::Display for ApiError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(self, f) - } -} - -mod multipart; -mod http; -pub use http::*; -
--- a/async-google-apis-common/src/multipart.rs Sat Oct 24 11:12:10 2020 +0200 +++ b/async-google-apis-common/src/multipart.rs Sat Oct 24 11:16:52 2020 +0200 @@ -5,10 +5,12 @@ use serde::Serialize; use std::io::Write; +use anyhow::Context; + pub const MIME_BOUNDARY: &'static str = "PB0BHe6XN3O6Q4bpnWQgS1pKfMfglTZdifFvh8YIc2APj4Cz3C"; -pub fn format_multipart<Req: Serialize>(req: &Req, data: Bytes) -> anyhow::Result<Bytes> { - let meta = serde_json::to_string(req)?; +pub fn format_multipart<Req: Serialize + std::fmt::Debug>(req: &Req, data: Bytes) -> anyhow::Result<Bytes> { + let meta = serde_json::to_string(req).context(format!("{:?}", req))?; let mut buf = Vec::with_capacity(meta.len() + (1.5 * (data.len() as f64)) as usize); // Write metadata.