Rewrite tests with a hyper server instead of raw TCP

This makes the tests much less brittle, by not depending on the exact
order of the HTTP headers, nor always requiring to check for every
single header.
This commit is contained in:
Sean McArthur
2019-09-23 11:33:04 -07:00
parent 3cf8ede960
commit f4100e4148
10 changed files with 881 additions and 1806 deletions

View File

@@ -1,47 +1,32 @@
#[macro_use]
mod support;
use support::*;
#[tokio::test]
async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
let client = reqwest::Client::new();
let codes = [301, 302, 303];
let codes = [301u16, 302, 303];
for code in codes.iter() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
\r\n\
", code)
;
for &code in codes.iter() {
let redirect = server::http(move |req| {
async move {
if req.method() == "POST" {
assert_eq!(req.uri(), &*format!("/{}", code));
http::Response::builder()
.status(code)
.header("location", "/dst")
.header("server", "test-redirect")
.body(Default::default())
.unwrap()
} else {
assert_eq!(req.method(), "GET");
request: format!("\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
http::Response::builder()
.header("server", "test-dst")
.body(Default::default())
.unwrap()
}
}
});
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
@@ -58,43 +43,28 @@ async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
#[tokio::test]
async fn test_redirect_307_and_308_tries_to_get_again() {
let client = reqwest::Client::new();
let codes = [307, 308];
for code in codes.iter() {
let redirect = server! {
request: format!("\
GET /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
\r\n\
", code)
;
let codes = [307u16, 308];
for &code in codes.iter() {
let redirect = server::http(move |req| {
async move {
assert_eq!(req.method(), "GET");
if req.uri() == &*format!("/{}", code) {
http::Response::builder()
.status(code)
.header("location", "/dst")
.header("server", "test-redirect")
.body(Default::default())
.unwrap()
} else {
assert_eq!(req.uri(), "/dst");
request: format!("\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
http::Response::builder()
.header("server", "test-dst")
.body(Default::default())
.unwrap()
}
}
});
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
@@ -111,47 +81,32 @@ async fn test_redirect_307_and_308_tries_to_get_again() {
#[tokio::test]
async fn test_redirect_307_and_308_tries_to_post_again() {
let client = reqwest::Client::new();
let codes = [307, 308];
for code in codes.iter() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
content-length: 5\r\n\
\r\n\
Hello\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
\r\n\
", code)
;
let codes = [307u16, 308];
for &code in codes.iter() {
let redirect = server::http(move |mut req| {
async move {
assert_eq!(req.method(), "POST");
request: format!("\
POST /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
content-length: 5\r\n\
\r\n\
Hello\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let data = req.body_mut().next().await.unwrap().unwrap();
assert_eq!(&*data, b"Hello");
if req.uri() == &*format!("/{}", code) {
http::Response::builder()
.status(code)
.header("location", "/dst")
.header("server", "test-redirect")
.body(Default::default())
.unwrap()
} else {
assert_eq!(req.uri(), "/dst");
http::Response::builder()
.header("server", "test-dst")
.body(Default::default())
.unwrap()
}
}
});
let url = format!("http://{}/{}", redirect.addr(), code);
let dst = format!("http://{}/{}", redirect.addr(), "dst");
@@ -169,30 +124,25 @@ async fn test_redirect_307_and_308_tries_to_post_again() {
#[test]
fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
let client = reqwest::blocking::Client::new();
let codes = [307, 308];
let codes = [307u16, 308];
for &code in codes.iter() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
transfer-encoding: chunked\r\n\
\r\n\
5\r\n\
Hello\r\n\
0\r\n\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
\r\n\
", code)
};
let redirect = server::http(move |mut req| {
async move {
assert_eq!(req.method(), "POST");
assert_eq!(req.uri(), &*format!("/{}", code));
assert_eq!(req.headers()["transfer-encoding"], "chunked");
let data = req.body_mut().next().await.unwrap().unwrap();
assert_eq!(&*data, b"Hello");
http::Response::builder()
.status(code)
.header("location", "/dst")
.header("server", "test-redirect")
.body(Default::default())
.unwrap()
}
});
let url = format!("http://{}/{}", redirect.addr(), code);
let res = client
@@ -201,50 +151,46 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
.send()
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::from_u16(code).unwrap());
assert_eq!(res.status(), code);
}
}
#[tokio::test]
async fn test_redirect_removes_sensitive_headers() {
let end_server = server! {
request: b"\
GET /otherhost HTTP/1.1\r\n\
accept-encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
use tokio::sync::watch;
let mid_server = server! {
request: b"\
GET /sensitive HTTP/1.1\r\n\
cookie: foo=bar\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: format!("\
HTTP/1.1 302 Found\r\n\
Server: test\r\n\
Location: http://{}/otherhost\r\n\
Content-Length: 0\r\n\
\r\n\
", end_server.addr())
};
let (tx, rx) = watch::channel(None);
let end_server = server::http(move |req| {
let mut rx = rx.clone();
async move {
assert_eq!(req.headers().get("cookie"), None);
let mid_addr = rx.recv().await.unwrap().unwrap();
assert_eq!(
req.headers()["referer"],
format!("http://{}/sensitive", mid_addr)
);
http::Response::default()
}
});
let end_addr = end_server.addr();
let mid_server = server::http(move |req| {
async move {
assert_eq!(req.headers()["cookie"], "foo=bar");
http::Response::builder()
.status(302)
.header("location", format!("http://{}/end", end_addr))
.body(Default::default())
.unwrap()
}
});
tx.broadcast(Some(mid_server.addr())).unwrap();
reqwest::Client::builder()
.referer(false)
.build()
.unwrap()
.get(&format!("http://{}/sensitive", mid_server.addr()))
@@ -259,23 +205,17 @@ async fn test_redirect_removes_sensitive_headers() {
#[tokio::test]
async fn test_redirect_policy_can_return_errors() {
let server = server! {
request: b"\
GET /loop HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 302 Found\r\n\
Server: test\r\n\
Location: /loop\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let server = server::http(move |req| {
async move {
assert_eq!(req.uri(), "/loop");
http::Response::builder()
.status(302)
.header("location", "/loop")
.body(Default::default())
.unwrap()
}
});
let url = format!("http://{}/loop", server.addr());
let err = reqwest::get(&url).await.unwrap_err();
assert!(err.is_redirect());
@@ -283,23 +223,16 @@ async fn test_redirect_policy_can_return_errors() {
#[tokio::test]
async fn test_redirect_policy_can_stop_redirects_without_an_error() {
let server = server! {
request: b"\
GET /no-redirect HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 302 Found\r\n\
Server: test-dont\r\n\
Location: /dont\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let server = server::http(move |req| {
async move {
assert_eq!(req.uri(), "/no-redirect");
http::Response::builder()
.status(302)
.header("location", "/dont")
.body(Default::default())
.unwrap()
}
});
let url = format!("http://{}/no-redirect", server.addr());
@@ -314,48 +247,27 @@ async fn test_redirect_policy_can_stop_redirects_without_an_error() {
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
assert_eq!(
res.headers().get(reqwest::header::SERVER).unwrap(),
&"test-dont"
);
}
#[tokio::test]
async fn test_referer_is_not_set_if_disabled() {
let server = server! {
request: b"\
GET /no-refer HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 302 Found\r\n\
Server: test-no-referer\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
\r\n\
"
;
let server = server::http(move |req| {
async move {
if req.uri() == "/no-refer" {
http::Response::builder()
.status(302)
.header("location", "/dst")
.body(Default::default())
.unwrap()
} else {
assert_eq!(req.uri(), "/dst");
assert_eq!(req.headers().get("referer"), None);
http::Response::default()
}
}
});
request: b"\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
reqwest::Client::builder()
.referer(false)
.build()
@@ -368,23 +280,15 @@ async fn test_referer_is_not_set_if_disabled() {
#[tokio::test]
async fn test_invalid_location_stops_redirect_gh484() {
let server = server! {
request: b"\
GET /yikes HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
HTTP/1.1 302 Found\r\n\
Server: test-yikes\r\n\
Location: http://www.yikes{KABOOM}\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let server = server::http(move |_req| {
async move {
http::Response::builder()
.status(302)
.header("location", "http://www.yikes{KABOOM}")
.body(Default::default())
.unwrap()
}
});
let url = format!("http://{}/yikes", server.addr());
@@ -392,66 +296,38 @@ async fn test_invalid_location_stops_redirect_gh484() {
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
assert_eq!(
res.headers().get(reqwest::header::SERVER).unwrap(),
&"test-yikes"
);
}
#[cfg(feature = "cookies")]
#[tokio::test]
async fn test_redirect_302_with_set_cookies() {
let code = 302;
let server = server::http(move |req| {
async move {
if req.uri() == "/302" {
http::Response::builder()
.status(302)
.header("location", "/dst")
.header("set-cookie", "key=value")
.body(Default::default())
.unwrap()
} else {
assert_eq!(req.uri(), "/dst");
assert_eq!(req.headers()["cookie"], "key=value");
http::Response::default()
}
}
});
let url = format!("http://{}/{}", server.addr(), code);
let dst = format!("http://{}/{}", server.addr(), "dst");
let client = reqwest::ClientBuilder::new()
.cookie_store(true)
.build()
.unwrap();
let server = server! {
request: format!("\
GET /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
Set-Cookie: key=value\r\n\
\r\n\
", code)
;
request: format!("\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
cookie: key=value\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/{}", server.addr(), code);
let dst = format!("http://{}/{}", server.addr(), "dst");
let res = client.get(&url).send().await.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(
res.headers().get(reqwest::header::SERVER).unwrap(),
&"test-dst"
);
}