Rewrite the Error type. (#234)

This adds a source field to keep track of upstream errors and allow
backtraces, plus a URL field to indicate what URL an error was
associated with.

The enum variants we used to use for Error are now part of a new
ErrorKind type. For convenience within ureq, ErrorKinds can be turned
into an Error with `.new()` or `.msg("some additional information")`.

Error acts as a builder, so additional information can be added after
initial construction. For instance, we return a DnsFailed error when
name resolution fails. When that error bubbles up to Request's
`do_call`, Request adds the URL.

Fixes #232.
This commit is contained in:
Jacob Hoffman-Andrews
2020-11-21 16:14:44 -08:00
committed by GitHub
parent dac517e30e
commit fade03b54e
13 changed files with 266 additions and 122 deletions

View File

@@ -7,6 +7,7 @@ use url::Url;
#[cfg(feature = "cookies")]
use cookie::Cookie;
use crate::error::{Error, ErrorKind};
use crate::header;
use crate::resolve::ArcResolver;
use crate::stream::{self, connect_test, Stream};
@@ -15,7 +16,7 @@ use crate::{
body::{self, BodySize, Payload, SizedReader},
header::get_header,
};
use crate::{Error, Header, Response};
use crate::{Header, Response};
/// A Unit is fully-built Request, ready to execute.
///
@@ -173,7 +174,7 @@ pub(crate) fn connect(
let host = unit
.url
.host_str()
.ok_or(Error::BadUrl("no host".to_string()))?;
.ok_or(ErrorKind::BadUrl.msg("no host in URL"))?;
let url = &unit.url;
let method = &unit.method;
// open socket
@@ -234,16 +235,18 @@ pub(crate) fn connect(
// handle redirects
if resp.redirect() && unit.agent.config.redirects > 0 {
if redirect_count == unit.agent.config.redirects {
return Err(Error::TooManyRedirects);
return Err(ErrorKind::TooManyRedirects.new());
}
// the location header
let location = resp.header("location");
if let Some(location) = location {
// join location header to current url in case it it relative
let new_url = url
.join(location)
.map_err(|_| Error::BadUrl(format!("Bad redirection: {}", location)))?;
let new_url = url.join(location).map_err(|e| {
ErrorKind::BadUrl
.msg(&format!("Bad redirection: {}", location))
.src(e)
})?;
// perform the redirect differently depending on 3xx code.
match resp.status() {
@@ -302,7 +305,7 @@ fn extract_cookies(agent: &Agent, url: &Url) -> Option<Header> {
fn connect_socket(unit: &Unit, hostname: &str, use_pooled: bool) -> Result<(Stream, bool), Error> {
match unit.url.scheme() {
"http" | "https" | "test" => (),
_ => return Err(Error::UnknownScheme(unit.url.scheme().to_string())),
scheme => return Err(ErrorKind::UnknownScheme.msg(&format!("unknown scheme '{}'", scheme))),
};
if use_pooled {
let agent = &unit.agent;
@@ -324,7 +327,7 @@ fn connect_socket(unit: &Unit, hostname: &str, use_pooled: bool) -> Result<(Stre
"http" => stream::connect_http(&unit, hostname),
"https" => stream::connect_https(&unit, hostname),
"test" => connect_test(&unit),
_ => Err(Error::UnknownScheme(unit.url.scheme().to_string())),
scheme => Err(ErrorKind::UnknownScheme.msg(&format!("unknown scheme {}", scheme))),
};
Ok((stream?, false))
}