#![cfg(not(target_arch = "wasm32"))]
#![cfg(not(feature = "rustls-tls-manual-roots-no-provider"))]
mod support;
use support::server;
use std::env;
use std::sync::LazyLock;
use tokio::sync::Mutex;
static HTTP_PROXY_ENV_MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
#[tokio::test]
async fn http_proxy() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
async { http::Response::default() }
});
let proxy = format!("http://{}", server.addr());
let res = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[tokio::test]
async fn http_proxy_basic_auth() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
async { http::Response::default() }
});
let proxy = format!("http://{}", server.addr());
let res = reqwest::Client::builder()
.proxy(
reqwest::Proxy::http(&proxy)
.unwrap()
.basic_auth("Aladdin", "open sesame"),
)
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[tokio::test]
async fn http_proxy_basic_auth_parsed() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
async { http::Response::default() }
});
let proxy = format!("http://Aladdin:open sesame@{}", server.addr());
let res = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[tokio::test]
async fn system_http_proxy_basic_auth_parsed() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuc2VzYW1l"
);
async { http::Response::default() }
});
let _env_lock = HTTP_PROXY_ENV_MUTEX.lock().await;
let system_proxy = env::var("http_proxy");
env::set_var(
"http_proxy",
format!("http://Aladdin:opensesame@{}", server.addr()),
);
let res = reqwest::Client::builder()
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
match system_proxy {
Err(_) => env::remove_var("http_proxy"),
Ok(proxy) => env::set_var("http_proxy", proxy),
}
}
#[tokio::test]
async fn test_no_proxy() {
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), "/4");
async { http::Response::default() }
});
let proxy = format!("http://{}", server.addr());
let url = format!("http://{}/4", server.addr());
let res = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.no_proxy()
.build()
.unwrap()
.get(&url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[tokio::test]
async fn test_custom_headers() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
async { http::Response::default() }
});
let proxy = format!("http://{}", server.addr());
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::PROXY_AUTHORIZATION,
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".parse().unwrap(),
);
let res = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy).unwrap().headers(headers))
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[tokio::test]
async fn test_using_system_proxy() {
let url = "http://not.a.real.sub.hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "not.a.real.sub.hyper.rs.local");
async { http::Response::default() }
});
let _env_lock = HTTP_PROXY_ENV_MUTEX.lock().await;
let system_proxy = env::var("http_proxy");
env::set_var("http_proxy", format!("http://{}", server.addr()));
let res = reqwest::get(url).await.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
match system_proxy {
Err(_) => env::remove_var("http_proxy"),
Ok(proxy) => env::set_var("http_proxy", proxy),
}
}
#[tokio::test]
async fn http_over_http() {
let url = "http://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
async { http::Response::default() }
});
let proxy = format!("http://{}", server.addr());
let res = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[cfg(feature = "__tls")]
#[tokio::test]
async fn tunnel_detects_auth_required() {
let url = "https://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "CONNECT");
assert_eq!(req.uri(), "hyper.rs.local:443");
assert!(!req
.headers()
.contains_key(http::header::PROXY_AUTHORIZATION));
async {
let mut res = http::Response::default();
*res.status_mut() = http::StatusCode::PROXY_AUTHENTICATION_REQUIRED;
res
}
});
let proxy = format!("http://{}", server.addr());
let err = reqwest::Client::builder()
.proxy(reqwest::Proxy::https(&proxy).unwrap())
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap_err();
let err = support::error::inspect(err).pop().unwrap();
assert!(
err.contains("auth"),
"proxy auth err expected, got: {:?}",
err
);
}
#[cfg(feature = "__tls")]
#[tokio::test]
async fn tunnel_includes_proxy_auth() {
let url = "https://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "CONNECT");
assert_eq!(req.uri(), "hyper.rs.local:443");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
async {
let mut res = http::Response::default();
*res.status_mut() = http::StatusCode::BAD_REQUEST;
res
}
});
let proxy = format!("http://Aladdin:open%20sesame@{}", server.addr());
let err = reqwest::Client::builder()
.proxy(reqwest::Proxy::https(&proxy).unwrap())
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap_err();
let err = support::error::inspect(err).pop().unwrap();
assert!(
err.contains("unsuccessful"),
"tunnel unsuccessful expected, got: {:?}",
err
);
}
#[cfg(feature = "__tls")]
#[tokio::test]
async fn tunnel_includes_user_agent() {
let url = "https://hyper.rs.local/prox";
let server = server::http(move |req| {
assert_eq!(req.method(), "CONNECT");
assert_eq!(req.uri(), "hyper.rs.local:443");
assert_eq!(req.headers()["user-agent"], "reqwest-test");
async {
let mut res = http::Response::default();
*res.status_mut() = http::StatusCode::BAD_REQUEST;
res
}
});
let proxy = format!("http://{}", server.addr());
let err = reqwest::Client::builder()
.proxy(reqwest::Proxy::https(&proxy).unwrap())
.user_agent("reqwest-test")
.build()
.unwrap()
.get(url)
.send()
.await
.unwrap_err();
let err = support::error::inspect(err).pop().unwrap();
assert!(
err.contains("unsuccessful"),
"tunnel unsuccessful expected, got: {:?}",
err
);
}
#[tokio::test]
async fn tunnel_includes_proxy_auth_with_multiple_proxies() {
let url = "http://hyper.rs.local/prox";
let server1 = server::http(move |req| {
assert_eq!(req.method(), "GET");
assert_eq!(req.uri(), url);
assert_eq!(req.headers()["host"], "hyper.rs.local");
assert_eq!(
req.headers()["proxy-authorization"],
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
assert_eq!(req.headers()["proxy-header"], "proxy2");
async { http::Response::default() }
});
let proxy_url = format!("http://Aladdin:open%20sesame@{}", server1.addr());
let mut headers1 = reqwest::header::HeaderMap::new();
headers1.insert("proxy-header", "proxy1".parse().unwrap());
let mut headers2 = reqwest::header::HeaderMap::new();
headers2.insert("proxy-header", "proxy2".parse().unwrap());
let client = reqwest::Client::builder()
.proxy(
reqwest::Proxy::https(&proxy_url)
.unwrap()
.headers(headers1.clone()),
)
.proxy(
reqwest::Proxy::http(&proxy_url)
.unwrap()
.headers(headers2.clone()),
)
.build()
.unwrap();
let res = client.get(url).send().await.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
let client = reqwest::Client::builder()
.proxy(reqwest::Proxy::http(&proxy_url).unwrap().headers(headers2))
.proxy(reqwest::Proxy::https(&proxy_url).unwrap().headers(headers1))
.build()
.unwrap();
let res = client.get(url).send().await.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}