diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/reqwest/src/error.rs | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/reqwest/src/error.rs')
-rw-r--r-- | vendor/reqwest/src/error.rs | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/vendor/reqwest/src/error.rs b/vendor/reqwest/src/error.rs new file mode 100644 index 000000000..0e6bd247d --- /dev/null +++ b/vendor/reqwest/src/error.rs @@ -0,0 +1,383 @@ +#![cfg_attr(target_arch = "wasm32", allow(unused))] +use std::error::Error as StdError; +use std::fmt; +use std::io; + +use crate::{StatusCode, Url}; + +/// A `Result` alias where the `Err` case is `reqwest::Error`. +pub type Result<T> = std::result::Result<T, Error>; + +/// The Errors that may occur when processing a `Request`. +/// +/// Note: Errors may include the full URL used to make the `Request`. If the URL +/// contains sensitive information (e.g. an API key as a query parameter), be +/// sure to remove it ([`without_url`](Error::without_url)) +pub struct Error { + inner: Box<Inner>, +} + +pub(crate) type BoxError = Box<dyn StdError + Send + Sync>; + +struct Inner { + kind: Kind, + source: Option<BoxError>, + url: Option<Url>, +} + +impl Error { + pub(crate) fn new<E>(kind: Kind, source: Option<E>) -> Error + where + E: Into<BoxError>, + { + Error { + inner: Box::new(Inner { + kind, + source: source.map(Into::into), + url: None, + }), + } + } + + /// Returns a possible URL related to this error. + /// + /// # Examples + /// + /// ``` + /// # async fn run() { + /// // displays last stop of a redirect loop + /// let response = reqwest::get("http://site.with.redirect.loop").await; + /// if let Err(e) = response { + /// if e.is_redirect() { + /// if let Some(final_stop) = e.url() { + /// println!("redirect loop at {}", final_stop); + /// } + /// } + /// } + /// # } + /// ``` + pub fn url(&self) -> Option<&Url> { + self.inner.url.as_ref() + } + + /// Returns a mutable reference to the URL related to this error + /// + /// This is useful if you need to remove sensitive information from the URL + /// (e.g. an API key in the query), but do not want to remove the URL + /// entirely. + pub fn url_mut(&mut self) -> Option<&mut Url> { + self.inner.url.as_mut() + } + + /// Add a url related to this error (overwriting any existing) + pub fn with_url(mut self, url: Url) -> Self { + self.inner.url = Some(url); + self + } + + /// Strip the related url from this error (if, for example, it contains + /// sensitive information) + pub fn without_url(mut self) -> Self { + self.inner.url = None; + self + } + + /// Returns true if the error is from a type Builder. + pub fn is_builder(&self) -> bool { + matches!(self.inner.kind, Kind::Builder) + } + + /// Returns true if the error is from a `RedirectPolicy`. + pub fn is_redirect(&self) -> bool { + matches!(self.inner.kind, Kind::Redirect) + } + + /// Returns true if the error is from `Response::error_for_status`. + pub fn is_status(&self) -> bool { + matches!(self.inner.kind, Kind::Status(_)) + } + + /// Returns true if the error is related to a timeout. + pub fn is_timeout(&self) -> bool { + let mut source = self.source(); + + while let Some(err) = source { + if err.is::<TimedOut>() { + return true; + } + source = err.source(); + } + + false + } + + /// Returns true if the error is related to the request + pub fn is_request(&self) -> bool { + matches!(self.inner.kind, Kind::Request) + } + + #[cfg(not(target_arch = "wasm32"))] + /// Returns true if the error is related to connect + pub fn is_connect(&self) -> bool { + let mut source = self.source(); + + while let Some(err) = source { + if let Some(hyper_err) = err.downcast_ref::<hyper::Error>() { + if hyper_err.is_connect() { + return true; + } + } + + source = err.source(); + } + + false + } + + /// Returns true if the error is related to the request or response body + pub fn is_body(&self) -> bool { + matches!(self.inner.kind, Kind::Body) + } + + /// Returns true if the error is related to decoding the response's body + pub fn is_decode(&self) -> bool { + matches!(self.inner.kind, Kind::Decode) + } + + /// Returns the status code, if the error was generated from a response. + pub fn status(&self) -> Option<StatusCode> { + match self.inner.kind { + Kind::Status(code) => Some(code), + _ => None, + } + } + + // private + + #[allow(unused)] + pub(crate) fn into_io(self) -> io::Error { + io::Error::new(io::ErrorKind::Other, self) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut builder = f.debug_struct("reqwest::Error"); + + builder.field("kind", &self.inner.kind); + + if let Some(ref url) = self.inner.url { + builder.field("url", url); + } + if let Some(ref source) = self.inner.source { + builder.field("source", source); + } + + builder.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.inner.kind { + Kind::Builder => f.write_str("builder error")?, + Kind::Request => f.write_str("error sending request")?, + Kind::Body => f.write_str("request or response body error")?, + Kind::Decode => f.write_str("error decoding response body")?, + Kind::Redirect => f.write_str("error following redirect")?, + Kind::Upgrade => f.write_str("error upgrading connection")?, + Kind::Status(ref code) => { + let prefix = if code.is_client_error() { + "HTTP status client error" + } else { + debug_assert!(code.is_server_error()); + "HTTP status server error" + }; + write!(f, "{} ({})", prefix, code)?; + } + }; + + if let Some(url) = &self.inner.url { + write!(f, " for url ({})", url.as_str())?; + } + + if let Some(e) = &self.inner.source { + write!(f, ": {}", e)?; + } + + Ok(()) + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + self.inner.source.as_ref().map(|e| &**e as _) + } +} + +#[cfg(target_arch = "wasm32")] +impl From<crate::error::Error> for wasm_bindgen::JsValue { + fn from(err: Error) -> wasm_bindgen::JsValue { + js_sys::Error::from(err).into() + } +} + +#[cfg(target_arch = "wasm32")] +impl From<crate::error::Error> for js_sys::Error { + fn from(err: Error) -> js_sys::Error { + js_sys::Error::new(&format!("{}", err)) + } +} + +#[derive(Debug)] +pub(crate) enum Kind { + Builder, + Request, + Redirect, + Status(StatusCode), + Body, + Decode, + Upgrade, +} + +// constructors + +pub(crate) fn builder<E: Into<BoxError>>(e: E) -> Error { + Error::new(Kind::Builder, Some(e)) +} + +pub(crate) fn body<E: Into<BoxError>>(e: E) -> Error { + Error::new(Kind::Body, Some(e)) +} + +pub(crate) fn decode<E: Into<BoxError>>(e: E) -> Error { + Error::new(Kind::Decode, Some(e)) +} + +pub(crate) fn request<E: Into<BoxError>>(e: E) -> Error { + Error::new(Kind::Request, Some(e)) +} + +pub(crate) fn redirect<E: Into<BoxError>>(e: E, url: Url) -> Error { + Error::new(Kind::Redirect, Some(e)).with_url(url) +} + +pub(crate) fn status_code(url: Url, status: StatusCode) -> Error { + Error::new(Kind::Status(status), None::<Error>).with_url(url) +} + +pub(crate) fn url_bad_scheme(url: Url) -> Error { + Error::new(Kind::Builder, Some(BadScheme)).with_url(url) +} + +if_wasm! { + pub(crate) fn wasm(js_val: wasm_bindgen::JsValue) -> BoxError { + format!("{:?}", js_val).into() + } +} + +pub(crate) fn upgrade<E: Into<BoxError>>(e: E) -> Error { + Error::new(Kind::Upgrade, Some(e)) +} + +// io::Error helpers + +#[allow(unused)] +pub(crate) fn into_io(e: Error) -> io::Error { + e.into_io() +} + +#[allow(unused)] +pub(crate) fn decode_io(e: io::Error) -> Error { + if e.get_ref().map(|r| r.is::<Error>()).unwrap_or(false) { + *e.into_inner() + .expect("io::Error::get_ref was Some(_)") + .downcast::<Error>() + .expect("StdError::is() was true") + } else { + decode(e) + } +} + +// internal Error "sources" + +#[derive(Debug)] +pub(crate) struct TimedOut; + +impl fmt::Display for TimedOut { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("operation timed out") + } +} + +impl StdError for TimedOut {} + +#[derive(Debug)] +pub(crate) struct BadScheme; + +impl fmt::Display for BadScheme { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("URL scheme is not allowed") + } +} + +impl StdError for BadScheme {} + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_send<T: Send>() {} + fn assert_sync<T: Sync>() {} + + #[test] + fn test_source_chain() { + let root = Error::new(Kind::Request, None::<Error>); + assert!(root.source().is_none()); + + let link = super::body(root); + assert!(link.source().is_some()); + assert_send::<Error>(); + assert_sync::<Error>(); + } + + #[test] + fn mem_size_of() { + use std::mem::size_of; + assert_eq!(size_of::<Error>(), size_of::<usize>()); + } + + #[test] + fn roundtrip_io_error() { + let orig = super::request("orig"); + // Convert reqwest::Error into an io::Error... + let io = orig.into_io(); + // Convert that io::Error back into a reqwest::Error... + let err = super::decode_io(io); + // It should have pulled out the original, not nested it... + match err.inner.kind { + Kind::Request => (), + _ => panic!("{:?}", err), + } + } + + #[test] + fn from_unknown_io_error() { + let orig = io::Error::new(io::ErrorKind::Other, "orly"); + let err = super::decode_io(orig); + match err.inner.kind { + Kind::Decode => (), + _ => panic!("{:?}", err), + } + } + + #[test] + fn is_timeout() { + let err = super::request(super::TimedOut); + assert!(err.is_timeout()); + + let io = io::Error::new(io::ErrorKind::Other, err); + let nested = super::request(io); + assert!(nested.is_timeout()); + } +} |