summaryrefslogtreecommitdiff
path: root/test_util/src/https.rs
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-11-08 13:00:29 -0700
committerGitHub <noreply@github.com>2023-11-08 13:00:29 -0700
commit02c5f49a7aab7b8cfe5ad3b282e6668a1aecddbb (patch)
tree64a9d9c25002bb9769457aa809ee9e2db57bf9aa /test_util/src/https.rs
parent5e82fce0a0051d694ab14467c120a1578c86bb42 (diff)
chore: refactor test_server and move to rustls-tokio-stream (#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)
Diffstat (limited to 'test_util/src/https.rs')
-rw-r--r--test_util/src/https.rs133
1 files changed, 133 insertions, 0 deletions
diff --git a/test_util/src/https.rs b/test_util/src/https.rs
new file mode 100644
index 000000000..8793e3c37
--- /dev/null
+++ b/test_util/src/https.rs
@@ -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")),
+ }
+}