Skip to content

Commit

Permalink
chore: refactor test_server and move to rustls-tokio-stream (denoland…
Browse files Browse the repository at this point in the history
…#21117)

Remove tokio-rustls as a direct dependency of Deno and refactor
test_server to reduce code duplication.

All tcp and tls listener paths go through the same streams now, with the
exception of the simpler Hyper http-only handlers (those can be done in
a later follow-up).

Minor bugs fixed:
 - gRPC server should only serve h2
 - WebSocket over http/2 had a port overlap
- Restored missing eye-catchers for some servers (still missing on Hyper
ones)
  • Loading branch information
mmastrac committed Nov 8, 2023
1 parent 5e82fce commit 02c5f49
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 501 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ ring = "^0.17.0"
rusqlite = { version = "=0.29.0", features = ["unlock_notify", "bundled"] }
rustls = "0.21.8"
rustls-pemfile = "1.0.0"
rustls-tokio-stream = "=0.2.7"
rustls-tokio-stream = "=0.2.9"
rustls-webpki = "0.101.4"
webpki-roots = "0.25.2"
scopeguard = "1.2.0"
Expand All @@ -148,7 +148,6 @@ termcolor = "1.1.3"
thiserror = "1.0.40"
tokio = { version = "1.28.1", features = ["full"] }
tokio-metrics = { version = "0.3.0", features = ["rt"] }
tokio-rustls = "0.24.0"
tokio-util = "0.7.4"
tower-lsp = { version = "=0.20.0", features = ["proposed"] }
url = { version = "2.3.1", features = ["serde", "expose_internals"] }
Expand Down
8 changes: 4 additions & 4 deletions cli/tests/unit/websocket_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ Deno.test(async function websocketConstructorTakeURLObjectAsParameter() {

Deno.test(async function websocketH2SendSmallPacket() {
const promise = deferred();
const ws = new WebSocket(new URL("wss:https://localhost:4248/"));
assertEquals(ws.url, "wss:https://localhost:4248/");
const ws = new WebSocket(new URL("wss:https://localhost:4249/"));
assertEquals(ws.url, "wss:https://localhost:4249/");
let messageCount = 0;
ws.onerror = (e) => promise.reject(e);
ws.onopen = () => {
Expand All @@ -53,8 +53,8 @@ Deno.test(async function websocketH2SendSmallPacket() {

Deno.test(async function websocketH2SendLargePacket() {
const promise = deferred();
const ws = new WebSocket(new URL("wss:https://localhost:4248/"));
assertEquals(ws.url, "wss:https://localhost:4248/");
const ws = new WebSocket(new URL("wss:https://localhost:4249/"));
assertEquals(ws.url, "wss:https://localhost:4249/");
let messageCount = 0;
ws.onerror = (e) => promise.reject(e);
ws.onopen = () => {
Expand Down
2 changes: 1 addition & 1 deletion test_util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ regex.workspace = true
reqwest.workspace = true
ring.workspace = true
rustls-pemfile.workspace = true
rustls-tokio-stream.workspace = true
semver = "=1.0.14"
serde.workspace = true
serde_json.workspace = true
tar.workspace = true
tempfile.workspace = true
termcolor.workspace = true
tokio.workspace = true
tokio-rustls.workspace = true
url.workspace = true

[target.'cfg(windows)'.dependencies]
Expand Down
133 changes: 133 additions & 0 deletions test_util/src/https.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use anyhow::anyhow;
use futures::Stream;
use futures::StreamExt;
use rustls::Certificate;
use rustls::PrivateKey;
use rustls_tokio_stream::rustls;
use rustls_tokio_stream::TlsStream;
use std::io;
use std::num::NonZeroUsize;
use std::result::Result;
use std::sync::Arc;
use tokio::net::TcpStream;

use crate::get_tcp_listener_stream;
use crate::testdata_path;

pub const TLS_BUFFER_SIZE: Option<NonZeroUsize> = NonZeroUsize::new(65536);

#[derive(Default)]
pub enum SupportedHttpVersions {
#[default]
All,
Http1Only,
Http2Only,
}

pub fn get_tls_listener_stream_from_tcp(
tls_config: Arc<rustls::ServerConfig>,
mut tcp: impl Stream<Item = Result<TcpStream, std::io::Error>> + Unpin + 'static,
) -> impl Stream<Item = Result<TlsStream, std::io::Error>> + Unpin {
async_stream::stream! {
while let Some(result) = tcp.next().await {
match result {
Ok(tcp) => yield Ok(TlsStream::new_server_side(tcp, tls_config.clone(), TLS_BUFFER_SIZE)),
Err(e) => yield Err(e),
};
}
}.boxed_local()
}

pub async fn get_tls_listener_stream(
name: &'static str,
port: u16,
http: SupportedHttpVersions,
) -> impl Stream<Item = Result<TlsStream, std::io::Error>> + Unpin {
let cert_file = "tls/localhost.crt";
let key_file = "tls/localhost.key";
let ca_cert_file = "tls/RootCA.pem";
let tls_config = get_tls_config(cert_file, key_file, ca_cert_file, http)
.await
.unwrap();

let tcp = get_tcp_listener_stream(name, port).await;
get_tls_listener_stream_from_tcp(tls_config, tcp)
}

pub async fn get_tls_config(
cert: &str,
key: &str,
ca: &str,
http_versions: SupportedHttpVersions,
) -> io::Result<Arc<rustls::ServerConfig>> {
let cert_path = testdata_path().join(cert);
let key_path = testdata_path().join(key);
let ca_path = testdata_path().join(ca);

let cert_file = std::fs::File::open(cert_path)?;
let key_file = std::fs::File::open(key_path)?;
let ca_file = std::fs::File::open(ca_path)?;

let certs: Vec<Certificate> = {
let mut cert_reader = io::BufReader::new(cert_file);
rustls_pemfile::certs(&mut cert_reader)
.unwrap()
.into_iter()
.map(Certificate)
.collect()
};

let mut ca_cert_reader = io::BufReader::new(ca_file);
let ca_cert = rustls_pemfile::certs(&mut ca_cert_reader)
.expect("Cannot load CA certificate")
.remove(0);

let mut key_reader = io::BufReader::new(key_file);
let key = {
let pkcs8_key = rustls_pemfile::pkcs8_private_keys(&mut key_reader)
.expect("Cannot load key file");
let rsa_key = rustls_pemfile::rsa_private_keys(&mut key_reader)
.expect("Cannot load key file");
if !pkcs8_key.is_empty() {
Some(pkcs8_key[0].clone())
} else if !rsa_key.is_empty() {
Some(rsa_key[0].clone())
} else {
None
}
};

match key {
Some(key) => {
let mut root_cert_store = rustls::RootCertStore::empty();
root_cert_store.add(&rustls::Certificate(ca_cert)).unwrap();

// Allow (but do not require) client authentication.

let mut config = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(Arc::new(
rustls::server::AllowAnyAnonymousOrAuthenticatedClient::new(
root_cert_store,
),
))
.with_single_cert(certs, PrivateKey(key))
.map_err(|e| anyhow!("Error setting cert: {:?}", e))
.unwrap();

match http_versions {
SupportedHttpVersions::All => {
config.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
}
SupportedHttpVersions::Http1Only => {}
SupportedHttpVersions::Http2Only => {
config.alpn_protocols = vec!["h2".into()];
}
}

Ok(Arc::new(config))
}
None => Err(io::Error::new(io::ErrorKind::Other, "Cannot find key")),
}
}
Loading

0 comments on commit 02c5f49

Please sign in to comment.