diff --git a/ext/node/polyfills/_tls_wrap.ts b/ext/node/polyfills/_tls_wrap.ts index a614b45df0a8d2..e36fc637e762e4 100644 --- a/ext/node/polyfills/_tls_wrap.ts +++ b/ext/node/polyfills/_tls_wrap.ts @@ -68,6 +68,7 @@ export class TLSSocket extends net.Socket { secureConnecting: boolean; _SNICallback: any; servername: string | null; + alpnProtocol: string | boolean | null; alpnProtocols: string[] | null; authorized: boolean; authorizationError: any; @@ -114,6 +115,7 @@ export class TLSSocket extends net.Socket { this.secureConnecting = true; this._SNICallback = null; this.servername = null; + this.alpnProtocol = null; this.alpnProtocols = tlsOptions.ALPNProtocols; this.authorized = false; this.authorizationError = null; @@ -151,10 +153,21 @@ export class TLSSocket extends net.Socket { handle.afterConnect = async (req: any, status: number) => { try { const conn = await Deno.startTls(handle[kStreamBaseField], options); + try { + const hs = await conn.handshake(); + if (hs.alpnProtocol) { + tlssock.alpnProtocol = hs.alpnProtocol; + } else { + tlssock.alpnProtocol = false; + } + } catch { + // Don't interrupt "secure" event to let the first read/write + // operation emit the error. + } handle[kStreamBaseField] = conn; tlssock.emit("secure"); tlssock.removeListener("end", onConnectEnd); - } catch { + } catch (_) { // TODO(kt3k): Handle this } return afterConnect.call(handle, req, status); @@ -269,6 +282,7 @@ export class ServerImpl extends EventEmitter { // Creates TCP handle and socket directly from Deno.TlsConn. // This works as TLS socket. We don't use TLSSocket class for doing // this because Deno.startTls only supports client side tcp connection. + // TODO(@satyarohith): set TLSSocket.alpnProtocol when we use TLSSocket class. const handle = new TCP(TCPConstants.SOCKET, await listener.accept()); const socket = new net.Socket({ handle }); this.emit("secureConnection", socket); diff --git a/tests/unit_node/tls_test.ts b/tests/unit_node/tls_test.ts index 847ec2dde31278..236dab20867989 100644 --- a/tests/unit_node/tls_test.ts +++ b/tests/unit_node/tls_test.ts @@ -235,3 +235,31 @@ Deno.test("tls.rootCertificates is not empty", () => { (tls.rootCertificates as string[]).push("new cert"); }, TypeError); }); + +Deno.test("TLSSocket.alpnProtocol is set for client", async () => { + const listener = Deno.listenTls({ + hostname: "localhost", + port: 0, + key, + cert, + alpnProtocols: ["a"], + }); + const outgoing = tls.connect({ + host: "::1", + servername: "localhost", + port: listener.addr.port, + ALPNProtocols: ["a"], + secureContext: { + ca: rootCaCert, + // deno-lint-ignore no-explicit-any + } as any, + }); + + const conn = await listener.accept(); + const handshake = await conn.handshake(); + assertEquals(handshake.alpnProtocol, "a"); + conn.close(); + outgoing.destroy(); + listener.close(); + await new Promise((resolve) => outgoing.on("close", resolve)); +});