diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index ddf597a0ab7123..c607b6f41f945e 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -935,6 +935,22 @@ declare namespace Deno { certChain?: string; /** PEM formatted (RSA or PKCS8) private key of client certificate. */ privateKey?: string; + /** **UNSTABLE**: new API, yet to be vetted. + * + * Application-Layer Protocol Negotiation (ALPN) protocols supported by + * the client. If not specified, no ALPN extension will be included in the + * TLS handshake. + */ + alpnProtocols?: string[]; + } + + export interface TlsConn extends Conn { + /** **UNSTABLE**: new API, yet to be vetted. + * + * Returns the ALPN protocol selected during negotiation with the server. + * If no ALPN protocol selected, returns `null`. + */ + getAgreedAlpnProtocol(): Promise; } /** **UNSTABLE** New API, yet to be vetted. diff --git a/ext/net/02_tls.js b/ext/net/02_tls.js index 9ae6cb055c5951..a314121acbbaf6 100644 --- a/ext/net/02_tls.js +++ b/ext/net/02_tls.js @@ -27,7 +27,15 @@ return core.opAsync("op_tls_handshake", rid); } + function opTlsGetAlpnProtocol(rid) { + return core.opAsync("op_tls_get_alpn_protocol", rid); + } + class TlsConn extends Conn { + getAgreedAlpnProtocol() { + return opTlsGetAlpnProtocol(this.rid); + } + handshake() { return opTlsHandshake(this.rid); } @@ -41,6 +49,7 @@ caCerts = [], certChain = undefined, privateKey = undefined, + alpnProtocols, }) { const res = await opConnectTls({ port, @@ -50,6 +59,7 @@ caCerts, certChain, privateKey, + alpnProtocols, }); return new TlsConn(res.rid, res.remoteAddr, res.localAddr); } diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 87744ed63ab5b4..a28f525fd6a95b 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -190,6 +190,13 @@ impl TlsStream { fn poll_handshake(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner_mut().poll_handshake(cx) } + + fn get_alpn_protocol(&mut self) -> Option { + match self.inner_mut().tls.get_alpn_protocol() { + None => None, + Some(s) => Some(std::str::from_utf8(s).unwrap().to_string()) + } + } } impl AsyncRead for TlsStream { @@ -517,6 +524,10 @@ impl ReadHalf { .tls_stream .into_inner() } + + fn get_alpn_protocol(&mut self) -> Option { + self.shared.get_alpn_protocol() + } } impl AsyncRead for ReadHalf { @@ -658,6 +669,11 @@ impl Shared { fn drop_shared_waker(self_ptr: *const ()) { let _ = unsafe { Weak::from_raw(self_ptr as *const Self) }; } + + fn get_alpn_protocol(self: &Arc) -> Option { + let mut tls_stream = self.tls_stream.lock(); + return tls_stream.get_alpn_protocol(); + } } struct ImplementReadTrait<'a, T>(&'a mut T); @@ -691,6 +707,7 @@ pub fn init() -> Vec { ("op_tls_listen", op_sync(op_tls_listen::

)), ("op_tls_accept", op_async(op_tls_accept)), ("op_tls_handshake", op_async(op_tls_handshake)), + ("op_tls_get_alpn_protocol", op_async(op_tls_get_alpn_protocol)), ] } @@ -753,6 +770,13 @@ impl TlsStreamResource { } Ok(()) } + + pub async fn get_alpn_protocol(self: &Rc) -> + Result, AnyError> + { + let mut rd = RcRef::map(self, |r| &r.rd).borrow_mut().await; + Ok(rd.get_alpn_protocol()) + } } impl Resource for TlsStreamResource { @@ -787,6 +811,7 @@ pub struct ConnectTlsArgs { ca_certs: Vec, cert_chain: Option, private_key: Option, + alpn_protocols: Option>, } #[derive(Deserialize)] @@ -948,6 +973,12 @@ where unsafely_ignore_certificate_errors, )?; + if let Some(alpn_protocols) = args.alpn_protocols { + super::check_unstable2(&state, "Deno.connectTls#alpnProtocols"); + tls_config.alpn_protocols = + alpn_protocols.into_iter().map(|s| s.into_bytes()).collect(); + } + if args.cert_chain.is_some() || args.private_key.is_some() { let cert_chain = args .cert_chain @@ -1151,3 +1182,15 @@ pub async fn op_tls_handshake( .get::(rid)?; resource.handshake().await } + +pub async fn op_tls_get_alpn_protocol( + state: Rc>, + rid: ResourceId, + _: (), +) -> Result, AnyError> { + let resource = state + .borrow() + .resource_table + .get::(rid)?; + resource.get_alpn_protocol().await +}