347 lines
11 KiB
Rust
347 lines
11 KiB
Rust
#![cfg(not(target_arch = "wasm32"))]
|
|
mod support;
|
|
use futures_util::stream::StreamExt;
|
|
use support::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_301_and_302_and_303_changes_post_to_get() {
|
|
let client = reqwest::Client::new();
|
|
let codes = [301u16, 302, 303];
|
|
|
|
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");
|
|
|
|
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");
|
|
let res = client.post(&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"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_307_and_308_tries_to_get_again() {
|
|
let client = reqwest::Client::new();
|
|
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");
|
|
|
|
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");
|
|
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"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_307_and_308_tries_to_post_again() {
|
|
let _ = env_logger::try_init();
|
|
let client = reqwest::Client::new();
|
|
let codes = [307u16, 308];
|
|
for &code in codes.iter() {
|
|
let redirect = server::http(move |mut req| async move {
|
|
assert_eq!(req.method(), "POST");
|
|
assert_eq!(req.headers()["content-length"], "5");
|
|
|
|
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");
|
|
let res = client.post(&url).body("Hello").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"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "blocking")]
|
|
#[test]
|
|
fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
|
|
let client = reqwest::blocking::Client::new();
|
|
let codes = [307u16, 308];
|
|
for &code in codes.iter() {
|
|
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
|
|
.post(&url)
|
|
.body(reqwest::blocking::Body::new(&b"Hello"[..]))
|
|
.send()
|
|
.unwrap();
|
|
assert_eq!(res.url().as_str(), url);
|
|
assert_eq!(res.status(), code);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_removes_sensitive_headers() {
|
|
use tokio::sync::watch;
|
|
|
|
let (tx, rx) = watch::channel::<Option<std::net::SocketAddr>>(None);
|
|
|
|
let end_server = server::http(move |req| {
|
|
let mut rx = rx.clone();
|
|
async move {
|
|
assert_eq!(req.headers().get("cookie"), None);
|
|
|
|
rx.changed().await.unwrap();
|
|
let mid_addr = rx.borrow().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.send(Some(mid_server.addr())).unwrap();
|
|
|
|
reqwest::Client::builder()
|
|
.build()
|
|
.unwrap()
|
|
.get(&format!("http://{}/sensitive", mid_server.addr()))
|
|
.header(
|
|
reqwest::header::COOKIE,
|
|
reqwest::header::HeaderValue::from_static("foo=bar"),
|
|
)
|
|
.send()
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_policy_can_return_errors() {
|
|
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());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_redirect_policy_can_stop_redirects_without_an_error() {
|
|
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());
|
|
|
|
let res = reqwest::Client::builder()
|
|
.redirect(reqwest::redirect::Policy::none())
|
|
.build()
|
|
.unwrap()
|
|
.get(&url)
|
|
.send()
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(res.url().as_str(), url);
|
|
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_referer_is_not_set_if_disabled() {
|
|
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()
|
|
}
|
|
});
|
|
|
|
reqwest::Client::builder()
|
|
.referer(false)
|
|
.build()
|
|
.unwrap()
|
|
.get(&format!("http://{}/no-refer", server.addr()))
|
|
.send()
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_invalid_location_stops_redirect_gh484() {
|
|
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());
|
|
|
|
let res = reqwest::get(&url).await.unwrap();
|
|
|
|
assert_eq!(res.url().as_str(), url);
|
|
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
|
|
}
|
|
|
|
#[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 res = client.get(&url).send().await.unwrap();
|
|
|
|
assert_eq!(res.url().as_str(), dst);
|
|
assert_eq!(res.status(), reqwest::StatusCode::OK);
|
|
}
|
|
|
|
#[cfg(feature = "__rustls")]
|
|
#[tokio::test]
|
|
#[ignore = "Needs TLS support in the test server"]
|
|
async fn test_redirect_https_only_enforced_gh1312() {
|
|
let server = server::http(move |_req| async move {
|
|
http::Response::builder()
|
|
.status(302)
|
|
.header("location", "http://insecure")
|
|
.body(Default::default())
|
|
.unwrap()
|
|
});
|
|
|
|
let url = format!("https://{}/yikes", server.addr());
|
|
|
|
let res = reqwest::Client::builder()
|
|
.danger_accept_invalid_certs(true)
|
|
.use_rustls_tls()
|
|
.https_only(true)
|
|
.build()
|
|
.expect("client builder")
|
|
.get(&url)
|
|
.send()
|
|
.await;
|
|
|
|
let err = res.unwrap_err();
|
|
assert!(err.is_redirect());
|
|
}
|