summaryrefslogtreecommitdiff
path: root/ext/fetch/tests.rs
diff options
context:
space:
mode:
authorSean McArthur <sean@seanmonstar.com>2024-07-24 13:20:06 -0700
committerGitHub <noreply@github.com>2024-07-24 20:20:06 +0000
commitc7f468d33b5d0814b56036639eb2a8226d4bfbbf (patch)
tree7464522c9574c57d2c3e9c4902278864631d83f1 /ext/fetch/tests.rs
parentb305ba3e1c155a33139ec0d764f547ffde4d4de4 (diff)
fix(ext/fetch): use correct ALPN to proxies (#24696)
Sending ALPN to a proxy, and then when tunneling, requires better juggling of TLS configs. This improves the choice of TLS config in the proxy connector, based on what reqwest does. It also includes some `ext/fetch/tests.rs` that check the different combinations. Fixes #24632 Fixes #24691
Diffstat (limited to 'ext/fetch/tests.rs')
-rw-r--r--ext/fetch/tests.rs188
1 files changed, 188 insertions, 0 deletions
diff --git a/ext/fetch/tests.rs b/ext/fetch/tests.rs
new file mode 100644
index 000000000..c99a08d34
--- /dev/null
+++ b/ext/fetch/tests.rs
@@ -0,0 +1,188 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::net::SocketAddr;
+use std::sync::Arc;
+
+use bytes::Bytes;
+use http_body_util::BodyExt;
+use tokio::io::AsyncReadExt;
+use tokio::io::AsyncWriteExt;
+
+use super::create_http_client;
+use super::CreateHttpClientOptions;
+
+static EXAMPLE_CRT: &[u8] = include_bytes!("../tls/testdata/example1_cert.der");
+static EXAMPLE_KEY: &[u8] =
+ include_bytes!("../tls/testdata/example1_prikey.der");
+
+#[tokio::test]
+async fn test_https_proxy_http11() {
+ let src_addr = create_https_server(false).await;
+ let prx_addr = create_http_proxy(src_addr).await;
+ run_test_client(prx_addr, src_addr, false, http::Version::HTTP_11).await;
+}
+
+#[tokio::test]
+async fn test_https_proxy_h2() {
+ let src_addr = create_https_server(true).await;
+ let prx_addr = create_http_proxy(src_addr).await;
+ run_test_client(prx_addr, src_addr, false, http::Version::HTTP_2).await;
+}
+
+#[tokio::test]
+async fn test_https_proxy_https_h2() {
+ let src_addr = create_https_server(true).await;
+ let prx_addr = create_https_proxy(src_addr).await;
+ run_test_client(prx_addr, src_addr, true, http::Version::HTTP_2).await;
+}
+
+async fn run_test_client(
+ prx_addr: SocketAddr,
+ src_addr: SocketAddr,
+ https: bool,
+ ver: http::Version,
+) {
+ let client = create_http_client(
+ "fetch/test",
+ CreateHttpClientOptions {
+ root_cert_store: None,
+ ca_certs: vec![],
+ proxy: Some(deno_tls::Proxy {
+ url: format!("http{}://{}", if https { "s" } else { "" }, prx_addr),
+ basic_auth: None,
+ }),
+ unsafely_ignore_certificate_errors: Some(vec![]),
+ client_cert_chain_and_key: None,
+ pool_max_idle_per_host: None,
+ pool_idle_timeout: None,
+ http1: true,
+ http2: true,
+ },
+ )
+ .unwrap();
+
+ let req = http::Request::builder()
+ .uri(format!("https://{}/foo", src_addr))
+ .body(
+ http_body_util::Empty::new()
+ .map_err(|err| match err {})
+ .boxed(),
+ )
+ .unwrap();
+ let resp = client.send(req).await.unwrap();
+ assert_eq!(resp.status(), http::StatusCode::OK);
+ assert_eq!(resp.version(), ver);
+ let hello = resp.collect().await.unwrap().to_bytes();
+ assert_eq!(hello, "hello from server");
+}
+
+async fn create_https_server(allow_h2: bool) -> SocketAddr {
+ let mut tls_config = deno_tls::rustls::server::ServerConfig::builder()
+ .with_no_client_auth()
+ .with_single_cert(
+ vec![EXAMPLE_CRT.into()],
+ webpki::types::PrivateKeyDer::try_from(EXAMPLE_KEY).unwrap(),
+ )
+ .unwrap();
+ if allow_h2 {
+ tls_config.alpn_protocols.push("h2".into());
+ }
+ tls_config.alpn_protocols.push("http/1.1".into());
+ let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::from(tls_config));
+ let src_tcp = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
+ let src_addr = src_tcp.local_addr().unwrap();
+
+ tokio::spawn(async move {
+ while let Ok((sock, _)) = src_tcp.accept().await {
+ let conn = tls_acceptor.accept(sock).await.unwrap();
+ if conn.get_ref().1.alpn_protocol() == Some(b"h2") {
+ let fut = hyper::server::conn::http2::Builder::new(
+ hyper_util::rt::TokioExecutor::new(),
+ )
+ .serve_connection(
+ hyper_util::rt::TokioIo::new(conn),
+ hyper::service::service_fn(|_req| async {
+ Ok::<_, std::convert::Infallible>(http::Response::new(
+ http_body_util::Full::<Bytes>::new("hello from server".into()),
+ ))
+ }),
+ );
+ tokio::spawn(fut);
+ } else {
+ let fut = hyper::server::conn::http1::Builder::new().serve_connection(
+ hyper_util::rt::TokioIo::new(conn),
+ hyper::service::service_fn(|_req| async {
+ Ok::<_, std::convert::Infallible>(http::Response::new(
+ http_body_util::Full::<Bytes>::new("hello from server".into()),
+ ))
+ }),
+ );
+ tokio::spawn(fut);
+ }
+ }
+ });
+
+ src_addr
+}
+
+async fn create_http_proxy(src_addr: SocketAddr) -> SocketAddr {
+ let prx_tcp = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
+ let prx_addr = prx_tcp.local_addr().unwrap();
+
+ tokio::spawn(async move {
+ while let Ok((mut sock, _)) = prx_tcp.accept().await {
+ let fut = async move {
+ let mut buf = [0u8; 4096];
+ let _n = sock.read(&mut buf).await.unwrap();
+ assert_eq!(&buf[..7], b"CONNECT");
+ let mut dst_tcp =
+ tokio::net::TcpStream::connect(src_addr).await.unwrap();
+ sock.write_all(b"HTTP/1.1 200 OK\r\n\r\n").await.unwrap();
+ tokio::io::copy_bidirectional(&mut sock, &mut dst_tcp)
+ .await
+ .unwrap();
+ };
+ tokio::spawn(fut);
+ }
+ });
+
+ prx_addr
+}
+
+async fn create_https_proxy(src_addr: SocketAddr) -> SocketAddr {
+ let mut tls_config = deno_tls::rustls::server::ServerConfig::builder()
+ .with_no_client_auth()
+ .with_single_cert(
+ vec![EXAMPLE_CRT.into()],
+ webpki::types::PrivateKeyDer::try_from(EXAMPLE_KEY).unwrap(),
+ )
+ .unwrap();
+ // Set ALPN, to check our proxy connector. But we shouldn't receive anything.
+ tls_config.alpn_protocols.push("h2".into());
+ tls_config.alpn_protocols.push("http/1.1".into());
+ let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::from(tls_config));
+ let prx_tcp = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
+ let prx_addr = prx_tcp.local_addr().unwrap();
+
+ tokio::spawn(async move {
+ while let Ok((sock, _)) = prx_tcp.accept().await {
+ let mut sock = tls_acceptor.accept(sock).await.unwrap();
+ assert_eq!(sock.get_ref().1.alpn_protocol(), None);
+
+ let fut = async move {
+ let mut buf = [0u8; 4096];
+ let _n = sock.read(&mut buf).await.unwrap();
+ assert_eq!(&buf[..7], b"CONNECT");
+ let mut dst_tcp =
+ tokio::net::TcpStream::connect(src_addr).await.unwrap();
+ sock.write_all(b"HTTP/1.1 200 OK\r\n\r\n").await.unwrap();
+ tokio::io::copy_bidirectional(&mut sock, &mut dst_tcp)
+ .await
+ .unwrap();
+ };
+ tokio::spawn(fut);
+ }
+ });
+
+ prx_addr
+}