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.
130 lines
4.2 KiB
Rust
130 lines
4.2 KiB
Rust
use std::{
|
|
io::{self, Write},
|
|
net::TcpStream,
|
|
};
|
|
use testserver::{self, TestServer};
|
|
|
|
use crate::{error::Error, test};
|
|
|
|
use super::super::*;
|
|
|
|
#[test]
|
|
fn redirect_on() {
|
|
test::set_handler("/redirect_on1", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_on2"], vec![])
|
|
});
|
|
test::set_handler("/redirect_on2", |_| {
|
|
test::make_response(200, "OK", vec!["x-foo: bar"], vec![])
|
|
});
|
|
let resp = get("test://host/redirect_on1").call().unwrap();
|
|
assert!(resp.has("x-foo"));
|
|
assert_eq!(resp.header("x-foo").unwrap(), "bar");
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_many() {
|
|
test::set_handler("/redirect_many1", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_many2"], vec![])
|
|
});
|
|
test::set_handler("/redirect_many2", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_many3"], vec![])
|
|
});
|
|
let result = builder()
|
|
.redirects(1)
|
|
.build()
|
|
.get("test://host/redirect_many1")
|
|
.call();
|
|
assert!(matches!(result, Err(e) if e.kind() == ErrorKind::TooManyRedirects));
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_off() -> Result<(), Error> {
|
|
test::set_handler("/redirect_off", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: somewhere.else"], vec![])
|
|
});
|
|
let resp = builder()
|
|
.redirects(0)
|
|
.build()
|
|
.get("test://host/redirect_off")
|
|
.call()?;
|
|
assert_eq!(resp.status(), 302);
|
|
assert!(resp.has("Location"));
|
|
assert_eq!(resp.header("Location").unwrap(), "somewhere.else");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_head() {
|
|
test::set_handler("/redirect_head1", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_head2"], vec![])
|
|
});
|
|
test::set_handler("/redirect_head2", |unit| {
|
|
assert_eq!(unit.method, "HEAD");
|
|
test::make_response(200, "OK", vec!["x-foo: bar"], vec![])
|
|
});
|
|
let resp = head("test://host/redirect_head1").call().unwrap();
|
|
assert_eq!(resp.status(), 200);
|
|
assert_eq!(resp.get_url(), "test://host/redirect_head2");
|
|
assert!(resp.has("x-foo"));
|
|
assert_eq!(resp.header("x-foo").unwrap(), "bar");
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_get() {
|
|
test::set_handler("/redirect_get1", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_get2"], vec![])
|
|
});
|
|
test::set_handler("/redirect_get2", |unit| {
|
|
assert_eq!(unit.method, "GET");
|
|
assert!(unit.has("Range"));
|
|
assert_eq!(unit.header("Range").unwrap(), "bytes=10-50");
|
|
test::make_response(200, "OK", vec!["x-foo: bar"], vec![])
|
|
});
|
|
let resp = get("test://host/redirect_get1")
|
|
.set("Range", "bytes=10-50")
|
|
.call()
|
|
.unwrap();
|
|
assert_eq!(resp.status(), 200);
|
|
assert_eq!(resp.get_url(), "test://host/redirect_get2");
|
|
assert!(resp.has("x-foo"));
|
|
assert_eq!(resp.header("x-foo").unwrap(), "bar");
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_host() {
|
|
// Set up a redirect to a host that doesn't exist; it should fail.
|
|
// TODO: This actually relies on the network for the DNS lookup
|
|
// of example.invalid. We can probably do better by, e.g.
|
|
// overriding the resolver.
|
|
let srv = TestServer::new(|mut stream: TcpStream| -> io::Result<()> {
|
|
testserver::read_request(&stream);
|
|
write!(stream, "HTTP/1.1 302 Found\r\n")?;
|
|
write!(stream, "Location: http://example.invalid/\r\n")?;
|
|
write!(stream, "\r\n")?;
|
|
Ok(())
|
|
});
|
|
let url = format!("http://localhost:{}/", srv.port);
|
|
let result = crate::Agent::new().get(&url).call();
|
|
assert!(
|
|
matches!(result, Err(ref e) if e.kind() == ErrorKind::DnsFailed),
|
|
"expected Err(DnsFailed), got: {:?}",
|
|
result
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn redirect_post() {
|
|
test::set_handler("/redirect_post1", |_| {
|
|
test::make_response(302, "Go here", vec!["Location: /redirect_post2"], vec![])
|
|
});
|
|
test::set_handler("/redirect_post2", |unit| {
|
|
assert_eq!(unit.method, "GET");
|
|
test::make_response(200, "OK", vec!["x-foo: bar"], vec![])
|
|
});
|
|
let resp = post("test://host/redirect_post1").call().unwrap();
|
|
assert_eq!(resp.status(), 200);
|
|
assert_eq!(resp.get_url(), "test://host/redirect_post2");
|
|
assert!(resp.has("x-foo"));
|
|
assert_eq!(resp.header("x-foo").unwrap(), "bar");
|
|
}
|