summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Casonato <lucacasonato@yahoo.com>2021-04-10 22:04:44 +0200
committerGitHub <noreply@github.com>2021-04-10 22:04:44 +0200
commit8d55d8b6be8731d37ccf6a29127b3a91a8319d0b (patch)
tree4495109b4d5a7a71d4d12fbe814292b1e5a97c6a
parent1c6602b85b50bc45cbf8cd1422091888e1561cd8 (diff)
feat(unstable): ALPN config in listenTls (#10065)
This commit adds the ability for users to configure ALPN protocols when calling `Deno.listenTls`.
-rw-r--r--cli/dts/lib.deno.unstable.d.ts10
-rw-r--r--cli/tests/integration_tests.rs85
-rw-r--r--cli/tests/listen_tls_alpn.ts12
-rw-r--r--runtime/js/40_tls.js2
-rw-r--r--runtime/ops/tls.rs6
5 files changed, 115 insertions, 0 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index ffe80eab7..9113d38c7 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -1001,6 +1001,16 @@ declare namespace Deno {
options?: StartTlsOptions,
): Promise<Conn>;
+ export interface ListenTlsOptions {
+ /** **UNSTABLE**: new API, yet to be vetted.
+ *
+ * Application-Layer Protocol Negotiation (ALPN) protocols to announce to
+ * the client. If not specified, no ALPN extension will be included in the
+ * TLS handshake.
+ */
+ alpnProtocols?: string[];
+ }
+
/** **UNSTABLE**: The `signo` argument may change to require the Deno.Signal
* enum.
*
diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs
index 2d4d8995e..827fc871b 100644
--- a/cli/tests/integration_tests.rs
+++ b/cli/tests/integration_tests.rs
@@ -5,11 +5,17 @@ use deno_core::serde_json;
use deno_core::url;
use deno_runtime::deno_fetch::reqwest;
use deno_runtime::deno_websocket::tokio_tungstenite;
+use rustls::Session;
use std::fs;
+use std::io::BufReader;
+use std::io::Cursor;
use std::io::{BufRead, Read, Write};
use std::process::Command;
+use std::sync::Arc;
use tempfile::TempDir;
use test_util as util;
+use tokio_rustls::rustls;
+use tokio_rustls::webpki;
#[test]
fn js_unit_tests_lint() {
@@ -5879,3 +5885,82 @@ console.log("finish");
handle.abort();
}
}
+
+#[tokio::test]
+async fn listen_tls_alpn() {
+ let child = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--quiet")
+ .arg("--allow-net")
+ .arg("--allow-read")
+ .arg("./cli/tests/listen_tls_alpn.ts")
+ .arg("4504")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let mut stdout = child.stdout.unwrap();
+ let mut buffer = [0; 5];
+ let read = stdout.read(&mut buffer).unwrap();
+ assert_eq!(read, 5);
+ let msg = std::str::from_utf8(&buffer).unwrap();
+ assert_eq!(msg, "READY");
+
+ let mut cfg = rustls::ClientConfig::new();
+ let reader =
+ &mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
+ cfg.root_store.add_pem_file(reader).unwrap();
+ cfg.alpn_protocols.push("foobar".as_bytes().to_vec());
+
+ let tls_connector = tokio_rustls::TlsConnector::from(Arc::new(cfg));
+ let hostname = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
+ let stream = tokio::net::TcpStream::connect("localhost:4504")
+ .await
+ .unwrap();
+
+ let tls_stream = tls_connector.connect(hostname, stream).await.unwrap();
+ let (_, session) = tls_stream.get_ref();
+
+ let alpn = session.get_alpn_protocol().unwrap();
+ assert_eq!(std::str::from_utf8(alpn).unwrap(), "foobar");
+}
+
+#[tokio::test]
+async fn listen_tls_alpn_fail() {
+ let child = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("--unstable")
+ .arg("--quiet")
+ .arg("--allow-net")
+ .arg("--allow-read")
+ .arg("./cli/tests/listen_tls_alpn.ts")
+ .arg("4505")
+ .stdout(std::process::Stdio::piped())
+ .spawn()
+ .unwrap();
+ let mut stdout = child.stdout.unwrap();
+ let mut buffer = [0; 5];
+ let read = stdout.read(&mut buffer).unwrap();
+ assert_eq!(read, 5);
+ let msg = std::str::from_utf8(&buffer).unwrap();
+ assert_eq!(msg, "READY");
+
+ let mut cfg = rustls::ClientConfig::new();
+ let reader =
+ &mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
+ cfg.root_store.add_pem_file(reader).unwrap();
+ cfg.alpn_protocols.push("boofar".as_bytes().to_vec());
+
+ let tls_connector = tokio_rustls::TlsConnector::from(Arc::new(cfg));
+ let hostname = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
+ let stream = tokio::net::TcpStream::connect("localhost:4505")
+ .await
+ .unwrap();
+
+ let tls_stream = tls_connector.connect(hostname, stream).await.unwrap();
+ let (_, session) = tls_stream.get_ref();
+
+ assert!(session.get_alpn_protocol().is_none());
+}
diff --git a/cli/tests/listen_tls_alpn.ts b/cli/tests/listen_tls_alpn.ts
new file mode 100644
index 000000000..6aedf0e4f
--- /dev/null
+++ b/cli/tests/listen_tls_alpn.ts
@@ -0,0 +1,12 @@
+const listener = Deno.listenTls({
+ port: Number(Deno.args[0]),
+ certFile: "./cli/tests/tls/localhost.crt",
+ keyFile: "./cli/tests/tls/localhost.key",
+ alpnProtocols: ["h2", "http/1.1", "foobar"],
+});
+
+console.log("READY");
+
+for await (const conn of listener) {
+ conn.close();
+}
diff --git a/runtime/js/40_tls.js b/runtime/js/40_tls.js
index da43afaac..e11754b0d 100644
--- a/runtime/js/40_tls.js
+++ b/runtime/js/40_tls.js
@@ -51,6 +51,7 @@
keyFile,
hostname = "0.0.0.0",
transport = "tcp",
+ alpnProtocols,
}) {
const res = opListenTls({
port,
@@ -58,6 +59,7 @@
keyFile,
hostname,
transport,
+ alpnProtocols,
});
return new TLSListener(res.rid, res.localAddr);
}
diff --git a/runtime/ops/tls.rs b/runtime/ops/tls.rs
index d9c5f1854..83dbbfcd1 100644
--- a/runtime/ops/tls.rs
+++ b/runtime/ops/tls.rs
@@ -300,6 +300,7 @@ pub struct ListenTlsArgs {
port: u16,
cert_file: String,
key_file: String,
+ alpn_protocols: Option<Vec<String>>,
}
fn op_listen_tls(
@@ -318,6 +319,11 @@ fn op_listen_tls(
permissions.read.check(Path::new(&key_file))?;
}
let mut config = ServerConfig::new(NoClientAuth::new());
+ if let Some(alpn_protocols) = args.alpn_protocols {
+ super::check_unstable(state, "Deno.listenTls#alpn_protocols");
+ config.alpn_protocols =
+ alpn_protocols.into_iter().map(|s| s.into_bytes()).collect();
+ }
config
.set_single_cert(load_certs(&cert_file)?, load_keys(&key_file)?.remove(0))
.expect("invalid key or certificate");