Files
Kevin Guthrie 3a95c50aa1 RUSTSEC-2026-0098 and RUSTSEC-2026-0099 fixes
Bump dev-deps to pull in rustls-webpki 0.103.12.
2026-05-01 15:55:50 -07:00

1123 lines
32 KiB
Rust

// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod utils;
use bytes::Bytes;
use h2::client;
use http::Request;
use http_body_util::BodyExt;
use hyper_util::client::legacy::Client;
#[cfg(unix)]
use hyperlocal::{UnixClientExt, Uri};
use reqwest::{header, StatusCode};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use utils::server_utils::init;
fn is_specified_port(port: u16) -> bool {
(1..65535).contains(&port)
}
#[tokio::test]
async fn test_origin_alive() {
init();
let res = reqwest::get("http://127.0.0.1:8000/").await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
async fn test_simple_proxy() {
init();
let res = reqwest::get("http://127.0.0.1:6147").await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers["x-server-addr"], "127.0.0.1:6147");
let sockaddr = headers["x-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.1");
assert!(is_specified_port(sockaddr.port()));
assert_eq!(headers["x-upstream-server-addr"], "127.0.0.1:8000");
let sockaddr = headers["x-upstream-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.2");
assert!(is_specified_port(sockaddr.port()));
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_h2_to_h1() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let res = client
.get("https://127.0.0.1:6150")
.header("sni", "openrusty.org")
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers["x-server-addr"], "127.0.0.1:6150");
let sockaddr = headers["x-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.1");
assert!(is_specified_port(sockaddr.port()));
assert_eq!(headers["x-upstream-server-addr"], "127.0.0.1:8443");
let sockaddr = headers["x-upstream-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.2");
assert!(is_specified_port(sockaddr.port()));
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_h2_to_h2() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let res = client
.get("https://127.0.0.1:6150")
.header("sni", "openrusty.org")
.header("x-h2", "true")
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers["x-server-addr"], "127.0.0.1:6150");
let sockaddr = headers["x-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.1");
assert!(is_specified_port(sockaddr.port()));
assert_eq!(headers["x-upstream-server-addr"], "127.0.0.1:8443");
let sockaddr = headers["x-upstream-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.2");
assert!(is_specified_port(sockaddr.port()));
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
async fn test_h2c_to_h2c() {
init();
let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.http2_only(true)
.build_http::<http_body_util::Empty<Bytes>>();
let mut req = http::Request::builder()
.uri("http://127.0.0.1:6146")
.body(http_body_util::Empty::<Bytes>::new())
.unwrap();
req.headers_mut()
.insert("x-h2", http::HeaderValue::from_bytes(b"true").unwrap());
let res = client.request(req).await.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let body = res.into_body().collect().await.unwrap().to_bytes();
assert_eq!(body.as_ref(), b"Hello World!\n");
}
#[tokio::test]
async fn test_h1_on_h2c_port() {
init();
let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.http2_only(false)
.build_http::<http_body_util::Empty<Bytes>>();
let mut req = http::Request::builder()
.uri("http://127.0.0.1:6146")
.body(http_body_util::Empty::<Bytes>::new())
.unwrap();
req.headers_mut()
.insert("x-h2", http::HeaderValue::from_bytes(b"true").unwrap());
let res = client.request(req).await.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_11);
let body = res.into_body().collect().await.unwrap().to_bytes();
assert_eq!(body.as_ref(), b"Hello World!\n");
}
#[tokio::test]
#[cfg(feature = "openssl_derived")]
async fn test_h2_to_h2_host_override() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let res = client
.get("https://127.0.0.1:6150")
.header("x-h2", "true")
.header("host-override", "test.com")
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_h2_to_h2_upload() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let payload = "test upload";
let res = client
.get("https://127.0.0.1:6150/echo")
.header("sni", "openrusty.org")
.header("x-h2", "true")
.body(payload)
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let body = res.text().await.unwrap();
assert_eq!(body, payload);
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_h2_to_h1_upload() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let payload = "test upload";
let res = client
.get("https://127.0.0.1:6150/echo")
.header("sni", "openrusty.org")
.body(payload)
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let body = res.text().await.unwrap();
assert_eq!(body, payload);
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_h2_head() {
init();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let res = client
.head("https://127.0.0.1:6150/set_content_length")
.header("sni", "openrusty.org")
.header("x-h2", "true")
.header("x-set-content-length", "11")
.send()
.await
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.version(), reqwest::Version::HTTP_2);
let body = res.text().await.unwrap();
// should not be any body, despite content-length
assert_eq!(body, "");
}
#[cfg(unix)]
#[tokio::test]
async fn test_simple_proxy_uds() {
init();
let url = Uri::new("/tmp/pingora_proxy.sock", "/").into();
let client: Client<hyperlocal::UnixConnector, http_body_util::Empty<Bytes>> = Client::unix();
let res = client.get(url).await.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::OK);
let (resp, body) = res.into_parts();
let headers = &resp.headers;
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers["x-server-addr"], "/tmp/pingora_proxy.sock");
assert_eq!(headers["x-client-addr"], "unset"); // unnamed UDS
assert_eq!(headers["x-upstream-server-addr"], "127.0.0.1:8000");
let sockaddr = headers["x-upstream-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.2");
assert!(is_specified_port(sockaddr.port()));
let body = http_body_util::BodyExt::collect(body)
.await
.unwrap()
.to_bytes();
assert_eq!(body.as_ref(), b"Hello World!\n");
}
#[cfg(unix)]
#[tokio::test]
async fn test_simple_proxy_uds_peer() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6147")
.header("x-uds-peer", "1") // force upstream peer to be UDS
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = &res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers["x-server-addr"], "127.0.0.1:6147");
let sockaddr = headers["x-client-addr"]
.to_str()
.unwrap()
.parse::<std::net::SocketAddr>()
.unwrap();
assert_eq!(sockaddr.ip().to_string(), "127.0.0.1");
assert!(is_specified_port(sockaddr.port()));
assert_eq!(headers["x-upstream-client-addr"], "unset"); // unnamed UDS
assert_eq!(
headers["x-upstream-server-addr"],
"/tmp/pingora_nginx_test.sock"
);
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
async fn test_dropped_conn_get() {
init();
let client = reqwest::Client::new();
let port = "8001"; // special port to avoid unexpected connection reuse from other tests
for _ in 1..3 {
// load conns into pool
let res = client
.get("http://127.0.0.1:6147")
.header("x-port", port)
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
let res = client
.get("http://127.0.0.1:6147/bad_lb")
.header("x-port", port)
.send()
.await
.unwrap();
// retry gives 200
assert_eq!(res.status(), StatusCode::OK);
let body = res.text().await.unwrap();
assert_eq!(body, "dog!\n");
}
async fn test_dropped_conn_post_empty_body() {
init();
let client = reqwest::Client::new();
let port = "8001"; // special port to avoid unexpected connection reuse from other tests
for _ in 1..3 {
// load conn into pool
let res = client
.get("http://127.0.0.1:6147")
.header("x-port", port)
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
let res = client
.post("http://127.0.0.1:6147/bad_lb")
.header("x-port", port)
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body = res.text().await.unwrap();
assert_eq!(body, "dog!\n");
}
async fn test_dropped_conn_post_body() {
init();
let client = reqwest::Client::new();
let port = "8001"; // special port to avoid unexpected connection reuse from other tests
for _ in 1..3 {
// load conn into pool
let res = client
.get("http://127.0.0.1:6147")
.header("x-port", port)
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
let res = client
.post("http://127.0.0.1:6147/bad_lb")
.header("x-port", port)
.body("cat!")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body = res.text().await.unwrap();
assert_eq!(body, "cat!\n");
}
async fn test_dropped_conn_post_body_over() {
init();
let client = reqwest::Client::new();
let port = "8001"; // special port to avoid unexpected connection reuse from other tests
let large_body = String::from_utf8(vec![b'e'; 1024 * 64 + 1]).unwrap();
for _ in 1..3 {
// load conn into pool
let res = client
.get("http://127.0.0.1:6147")
.header("x-port", port)
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
let res = client
.post("http://127.0.0.1:6147/bad_lb")
.header("x-port", port)
.body(large_body)
.send()
.await
.unwrap();
// 502, body larger than buffer limit
assert_eq!(res.status(), StatusCode::from_u16(502).unwrap());
}
#[tokio::test]
async fn test_dropped_conn() {
// These tests can race with each other
// So force run them sequentially
test_dropped_conn_get().await;
test_dropped_conn_post_empty_body().await;
test_dropped_conn_post_body().await;
test_dropped_conn_post_body_over().await;
}
// currently not supported with Rustls implementation
#[cfg(feature = "openssl_derived")]
#[tokio::test]
async fn test_tls_no_verify() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_verify_sni_not_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "openrusty.org")
.header("verify", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
// currently not supported with Rustls implementation
#[cfg(feature = "openssl_derived")]
#[tokio::test]
async fn test_tls_none_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_verify_sni_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_underscore_sub_sni_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "d_g.openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_underscore_non_sub_sni_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "open_rusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
let headers = res.headers();
assert_eq!(headers[header::CONNECTION], "close");
}
#[cfg(feature = "openssl_derived")]
#[tokio::test]
async fn test_tls_alt_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "open_rusty.org")
.header("alt", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "openssl_derived")]
#[tokio::test]
async fn test_tls_underscore_sub_alt_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "open_rusty.org")
.header("alt", "d_g.openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_underscore_non_sub_alt_verify_host() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("sni", "open_rusty.org")
.header("alt", "open_rusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
}
#[tokio::test]
async fn test_upstream_compression() {
init();
// disable reqwest gzip support to check compression headers and body
// otherwise reqwest will decompress and strip the headers
let client = reqwest::ClientBuilder::new().gzip(false).build().unwrap();
let res = client
.get("http://127.0.0.1:6147/no_compression")
.header("accept-encoding", "gzip")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get("Content-Encoding").unwrap(), "gzip");
let body = res.bytes().await.unwrap();
assert!(body.len() < 32);
// Next let reqwest decompress to validate the data
let client = reqwest::ClientBuilder::new().gzip(true).build().unwrap();
let res = client
.get("http://127.0.0.1:6147/no_compression")
.header("accept-encoding", "gzip")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body = res.bytes().await.unwrap();
assert_eq!(body.as_ref(), &[b'B'; 32]);
}
#[tokio::test]
async fn test_downstream_compression() {
init();
// disable reqwest gzip support to check compression headers and body
// otherwise reqwest will decompress and strip the headers
let client = reqwest::ClientBuilder::new().gzip(false).build().unwrap();
let res = client
.get("http://127.0.0.1:6147/no_compression")
// tell the test proxy to use downstream compression module instead of upstream
.header("x-downstream-compression", "1")
.header("accept-encoding", "gzip")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get("Content-Encoding").unwrap(), "gzip");
let body = res.bytes().await.unwrap();
assert!(body.len() < 32);
// Next let reqwest decompress to validate the data
let client = reqwest::ClientBuilder::new().gzip(true).build().unwrap();
let res = client
.get("http://127.0.0.1:6147/no_compression")
.header("accept-encoding", "gzip")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body = res.bytes().await.unwrap();
assert_eq!(body.as_ref(), &[b'B'; 32]);
}
#[tokio::test]
async fn test_connect_close() {
init();
// default keep-alive
let client = reqwest::ClientBuilder::new().build().unwrap();
let res = client.get("http://127.0.0.1:6147").send().await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers[header::CONNECTION], "keep-alive");
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
// close
let client = reqwest::ClientBuilder::new().build().unwrap();
let res = client
.get("http://127.0.0.1:6147")
.header("connection", "close")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "13");
assert_eq!(headers[header::CONNECTION], "close");
let body = res.text().await.unwrap();
assert_eq!(body, "Hello World!\n");
}
#[tokio::test]
async fn test_connect_proxying_disallowed_h1() {
init();
let mut stream = TcpStream::connect("127.0.0.1:6147").await.unwrap();
let request = b"CONNECT pingora.org:443 HTTP/1.1\r\nHost: pingora.org:443\r\n\r\n";
stream.write_all(request).await.unwrap();
let mut buf = [0u8; 1024];
let read = stream.read(&mut buf).await.unwrap();
let resp = std::str::from_utf8(&buf[..read]).unwrap();
let status_line = resp.lines().next().unwrap_or("");
assert!(status_line.contains(" 405 "));
}
#[tokio::test]
async fn test_connect_proxying_disallowed_h2() {
init();
let tcp = TcpStream::connect("127.0.0.1:6146").await.unwrap();
let (mut h2, connection) = client::handshake(tcp).await.unwrap();
tokio::spawn(async move {
connection.await.unwrap();
});
let request = Request::builder()
.method("CONNECT")
.uri("http://pingora.org:443/")
.body(())
.unwrap();
let (response, _body) = h2.send_request(request, true).unwrap();
let (head, mut body) = response.await.unwrap().into_parts();
assert_eq!(head.status.as_u16(), 405);
while let Some(chunk) = body.data().await {
assert!(chunk.unwrap().is_empty());
}
}
#[tokio::test]
async fn test_connect_proxying_allowed_h1() {
init();
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let upstream_addr = listener.local_addr().unwrap();
// Note per RFC CONNECT 2xx responses are not allowed to have response
// bodies, so this is non-standard behavior.
tokio::spawn(async move {
let (mut socket, _) = listener.accept().await.unwrap();
let mut buf = [0u8; 1024];
let _ = socket.read(&mut buf).await.unwrap();
let response = b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok";
socket.write_all(response).await.unwrap();
let _ = socket.shutdown().await;
});
let mut stream = TcpStream::connect("127.0.0.1:6160").await.unwrap();
let request = format!(
"CONNECT pingora.org:443 HTTP/1.1\r\nHost: pingora.org:443\r\nX-Port: {}\r\n\r\n",
upstream_addr.port()
);
stream.write_all(request.as_bytes()).await.unwrap();
let mut buf = vec![0u8; 1024];
let read = stream.read(&mut buf).await.unwrap();
let resp = std::str::from_utf8(&buf[..read]).unwrap();
let status_line = resp.lines().next().unwrap_or("");
assert!(status_line.contains(" 200 "));
assert!(resp.ends_with("ok"));
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_mtls_no_client_cert() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("x-port", "8444")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
// 400: because no cert
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_mtls_no_intermediate_cert() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/tls_verify")
.header("x-port", "8444")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.header("client_cert", "1")
.send()
.await
.unwrap();
// 400: because no intermediate cert
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
}
#[tokio::test]
#[cfg(feature = "any_tls")]
async fn test_mtls() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/")
.header("x-port", "8444")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.header("client_cert", "1")
.header("client_intermediate", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "any_tls")]
async fn assert_reuse(req: reqwest::RequestBuilder) {
req.try_clone().unwrap().send().await.unwrap();
let res = req.send().await.unwrap();
let headers = res.headers();
assert!(headers.get("x-conn-reuse").is_some());
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_mtls_diff_cert_no_reuse() {
init();
let client = reqwest::Client::new();
let req = client
.get("http://127.0.0.1:6149/")
.header("x-port", "8444")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.header("client_cert", "1")
.header("client_intermediate", "1");
// pre check re-use
assert_reuse(req).await;
// different cert no re-use
let res = client
.get("http://127.0.0.1:6149/")
.header("x-port", "8444")
.header("sni", "openrusty.org")
.header("verify", "1")
.header("verify_host", "1")
.header("client_cert", "2")
.header("client_intermediate", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert!(headers.get("x-conn-reuse").is_none());
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_diff_verify_no_reuse() {
init();
let client = reqwest::Client::new();
let req = client
.get("http://127.0.0.1:6149/")
.header("sni", "dog.openrusty.org")
.header("verify", "1");
// pre check re-use
assert_reuse(req).await;
// disable 'verify' no re-use
let res = client
.get("http://127.0.0.1:6149/")
.header("sni", "dog.openrusty.org")
.header("verify", "0")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert!(headers.get("x-conn-reuse").is_none());
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_diff_verify_host_no_reuse() {
init();
let client = reqwest::Client::new();
let req = client
.get("http://127.0.0.1:6149/")
.header("sni", "cat.openrusty.org")
.header("verify", "1")
.header("verify_host", "1");
// pre check re-use
assert_reuse(req).await;
// disable 'verify_host' no re-use
let res = client
.get("http://127.0.0.1:6149/")
.header("sni", "cat.openrusty.org")
.header("verify", "1")
.header("verify_host", "0")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert!(headers.get("x-conn-reuse").is_none());
}
#[cfg(feature = "any_tls")]
#[tokio::test]
async fn test_tls_diff_alt_cnt_no_reuse() {
init();
let client = reqwest::Client::new();
let req = client
.get("http://127.0.0.1:6149/")
.header("sni", "openrusty.org")
.header("alt", "cat.com")
.header("verify", "1")
.header("verify_host", "1");
// pre check re-use
assert_reuse(req).await;
// use alt-cn no reuse
let res = client
.get("http://127.0.0.1:6149/")
.header("sni", "openrusty.org")
.header("alt", "dog.com")
.header("verify", "1")
.header("verify_host", "1")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert!(headers.get("x-conn-reuse").is_none());
}
#[cfg(feature = "s2n")]
#[tokio::test]
async fn test_tls_psk() {
use crate::utils::server_utils::TEST_PSK_IDENTITY;
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/")
.header("sni", "openrusty.org")
.header("psk_identity", TEST_PSK_IDENTITY)
.header("x-port", "6151")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
}
#[cfg(feature = "s2n")]
#[tokio::test]
async fn test_tls_psk_invalid() {
init();
let client = reqwest::Client::new();
let res = client
.get("http://127.0.0.1:6149/")
.header("sni", "openrusty.org")
.header("psk_identity", "BAD_IDENTITY")
.header("x-port", "6151")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
}
#[tokio::test]
async fn test_error_before_headers_sent() {
init();
let url = "http://127.0.0.1:6146/sleep/test_error_before_headers_sent.txt";
let tcp = TcpStream::connect("127.0.0.1:6146").await.unwrap();
let (mut client, h2) = client::handshake(tcp).await.unwrap();
tokio::spawn(async move {
h2.await.unwrap();
});
let request = Request::builder()
.uri(url)
.header("x-set-sleep", "0")
.header("x-abort", "true")
.body(())
.unwrap();
let (response, mut _stream) = client.send_request(request, true).unwrap();
let response = response.await.unwrap();
let mut body = response.into_body();
while let Some(chunk) = body.data().await {
assert_eq!(chunk.unwrap(), Bytes::new());
}
}
#[tokio::test]
async fn test_error_after_headers_sent_rst_received() {
init();
let url = "http://127.0.0.1:6146/connection_die/test_error_after_headers_sent_rst_received.txt";
let tcp = TcpStream::connect("127.0.0.1:6146").await.unwrap();
let (mut client, h2) = client::handshake(tcp).await.unwrap();
tokio::spawn(async move {
h2.await.unwrap();
});
let request = Request::builder().uri(url).body(()).unwrap();
let (response, mut _stream) = client.send_request(request, true).unwrap();
let response = response.await.unwrap();
let mut body = response.into_body();
let chunk = body.data().await.unwrap();
assert_eq!(chunk.unwrap(), Bytes::from_static(b"AAAAA"));
let err = body.data().await.unwrap().err().unwrap();
assert_eq!(err.reason().unwrap(), h2::Reason::CANCEL);
}
#[tokio::test]
async fn test_103() {
init();
let res = reqwest::get("http://127.0.0.1:6147/103").await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let headers = res.headers();
assert_eq!(headers[header::CONTENT_LENGTH], "6");
let body = res.text().await.unwrap();
assert_eq!(body, "123456");
}
#[tokio::test]
async fn test_103_die() {
init();
let res = reqwest::get("http://127.0.0.1:6147/103-die").await.unwrap();
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
}