Skip to content

Commit

Permalink
feat(unstable): ALPN config in listenTls (denoland#10065)
Browse files Browse the repository at this point in the history
This commit adds the ability for users to configure ALPN protocols when
calling `Deno.listenTls`.
  • Loading branch information
lucacasonato authored Apr 10, 2021
1 parent 1c6602b commit 8d55d8b
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
10 changes: 10 additions & 0 deletions cli/dts/lib.deno.unstable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
85 changes: 85 additions & 0 deletions cli/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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());
}
12 changes: 12 additions & 0 deletions cli/tests/listen_tls_alpn.ts
Original file line number Diff line number Diff line change
@@ -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();
}
2 changes: 2 additions & 0 deletions runtime/js/40_tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@
keyFile,
hostname = "0.0.0.0",
transport = "tcp",
alpnProtocols,
}) {
const res = opListenTls({
port,
certFile,
keyFile,
hostname,
transport,
alpnProtocols,
});
return new TLSListener(res.rid, res.localAddr);
}
Expand Down
6 changes: 6 additions & 0 deletions runtime/ops/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ pub struct ListenTlsArgs {
port: u16,
cert_file: String,
key_file: String,
alpn_protocols: Option<Vec<String>>,
}

fn op_listen_tls(
Expand All @@ -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");
Expand Down

0 comments on commit 8d55d8b

Please sign in to comment.