1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

feat(ext/net): add TlsConn.handshake() (#12467)

A `handshake()` method was added that returns when the TLS handshake is
complete. The `TlsListener` and `TlsConn` interfaces were added to
accomodate this new method.

Closes: #11759.
This commit is contained in:
Bert Belder 2021-10-26 22:27:47 +02:00 committed by GitHub
parent c27ef0ac7b
commit cf9c4f0031
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 30 deletions

View file

@ -1091,7 +1091,7 @@ declare namespace Deno {
* *
* Requires `allow-net` permission. * Requires `allow-net` permission.
*/ */
export function connectTls(options: ConnectTlsOptions): Promise<Conn>; export function connectTls(options: ConnectTlsOptions): Promise<TlsConn>;
export interface StartTlsOptions { export interface StartTlsOptions {
/** A literal IP address or host name that can be resolved to an IP address. /** A literal IP address or host name that can be resolved to an IP address.
@ -1130,7 +1130,7 @@ declare namespace Deno {
export function startTls( export function startTls(
conn: Conn, conn: Conn,
options?: StartTlsOptions, options?: StartTlsOptions,
): Promise<Conn>; ): Promise<TlsConn>;
export interface ListenTlsOptions { export interface ListenTlsOptions {
/** **UNSTABLE**: new API, yet to be vetted. /** **UNSTABLE**: new API, yet to be vetted.

View file

@ -1122,14 +1122,6 @@ unitTest(
}, },
); );
// TODO(piscisaureus): use `TlsConn.handhake()` instead, once this is added to
// the public API in Deno 1.16.
function tlsHandshake(conn: Deno.Conn): Promise<void> {
// deno-lint-ignore no-explicit-any
const opAsync = (Deno as any).core.opAsync;
return opAsync("op_tls_handshake", conn.rid);
}
unitTest( unitTest(
{ permissions: { read: true, net: true } }, { permissions: { read: true, net: true } },
async function tlsHandshakeSuccess() { async function tlsHandshakeSuccess() {
@ -1151,7 +1143,7 @@ unitTest(
const [conn1, conn2] = await Promise.all([acceptPromise, connectPromise]); const [conn1, conn2] = await Promise.all([acceptPromise, connectPromise]);
listener.close(); listener.close();
await Promise.all([tlsHandshake(conn1), tlsHandshake(conn2)]); await Promise.all([conn1.handshake(), conn2.handshake()]);
// Begin sending a 10mb blob over the TLS connection. // Begin sending a 10mb blob over the TLS connection.
const whole = new Uint8Array(10 << 20); // 10mb. const whole = new Uint8Array(10 << 20); // 10mb.
@ -1162,28 +1154,28 @@ unitTest(
const half = new Uint8Array(whole.byteLength / 2); const half = new Uint8Array(whole.byteLength / 2);
const receivePromise = readFull(conn2, half); const receivePromise = readFull(conn2, half);
await tlsHandshake(conn1); await conn1.handshake();
await tlsHandshake(conn2); await conn2.handshake();
// Finish receiving the first 5mb. // Finish receiving the first 5mb.
assertEquals(await receivePromise, half.length); assertEquals(await receivePromise, half.length);
// See that we can call `handshake()` in the middle of large reads and writes. // See that we can call `handshake()` in the middle of large reads and writes.
await tlsHandshake(conn1); await conn1.handshake();
await tlsHandshake(conn2); await conn2.handshake();
// Receive second half of large blob. Wait for the send promise and check it. // Receive second half of large blob. Wait for the send promise and check it.
assertEquals(await readFull(conn2, half), half.length); assertEquals(await readFull(conn2, half), half.length);
assertEquals(await sendPromise, whole.length); assertEquals(await sendPromise, whole.length);
await tlsHandshake(conn1); await conn1.handshake();
await tlsHandshake(conn2); await conn2.handshake();
await conn1.closeWrite(); await conn1.closeWrite();
await conn2.closeWrite(); await conn2.closeWrite();
await tlsHandshake(conn1); await conn1.handshake();
await tlsHandshake(conn2); await conn2.handshake();
conn1.close(); conn1.close();
conn2.close(); conn2.close();
@ -1216,7 +1208,7 @@ unitTest(
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
// Handshake fails because the client rejects the server certificate. // Handshake fails because the client rejects the server certificate.
await assertRejects( await assertRejects(
() => tlsHandshake(conn), () => conn.handshake(),
Deno.errors.InvalidData, Deno.errors.InvalidData,
"BadCertificate", "BadCertificate",
); );
@ -1230,7 +1222,7 @@ unitTest(
const conn = await Deno.connectTls({ hostname, port }); const conn = await Deno.connectTls({ hostname, port });
// Handshake fails because the server presents a self-signed certificate. // Handshake fails because the server presents a self-signed certificate.
await assertRejects( await assertRejects(
() => tlsHandshake(conn), () => conn.handshake(),
Deno.errors.InvalidData, Deno.errors.InvalidData,
"UnknownIssuer", "UnknownIssuer",
); );
@ -1247,7 +1239,7 @@ unitTest(
}); });
// Handshake fails because hostname doesn't match the certificate. // Handshake fails because hostname doesn't match the certificate.
await assertRejects( await assertRejects(
() => tlsHandshake(tlsConn), () => tlsConn.handshake(),
Deno.errors.InvalidData, Deno.errors.InvalidData,
"CertNotValidForName", "CertNotValidForName",
); );

View file

@ -23,6 +23,16 @@
return core.opAsync("op_start_tls", args); return core.opAsync("op_start_tls", args);
} }
function opTlsHandshake(rid) {
return core.opAsync("op_tls_handshake", rid);
}
class TlsConn extends Conn {
handshake() {
return opTlsHandshake(this.rid);
}
}
async function connectTls({ async function connectTls({
port, port,
hostname = "127.0.0.1", hostname = "127.0.0.1",
@ -41,13 +51,13 @@
certChain, certChain,
privateKey, privateKey,
}); });
return new Conn(res.rid, res.remoteAddr, res.localAddr); return new TlsConn(res.rid, res.remoteAddr, res.localAddr);
} }
class TLSListener extends Listener { class TlsListener extends Listener {
async accept() { async accept() {
const res = await opAcceptTLS(this.rid); const res = await opAcceptTLS(this.rid);
return new Conn(res.rid, res.remoteAddr, res.localAddr); return new TlsConn(res.rid, res.remoteAddr, res.localAddr);
} }
} }
@ -67,7 +77,7 @@
transport, transport,
alpnProtocols, alpnProtocols,
}); });
return new TLSListener(res.rid, res.localAddr); return new TlsListener(res.rid, res.localAddr);
} }
async function startTls( async function startTls(
@ -80,13 +90,14 @@
certFile, certFile,
caCerts, caCerts,
}); });
return new Conn(res.rid, res.remoteAddr, res.localAddr); return new TlsConn(res.rid, res.remoteAddr, res.localAddr);
} }
window.__bootstrap.tls = { window.__bootstrap.tls = {
startTls, startTls,
listenTls, listenTls,
connectTls, connectTls,
TLSListener, TlsConn,
TlsListener,
}; };
})(this); })(this);

View file

@ -33,6 +33,13 @@ declare namespace Deno {
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>; [Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
} }
/** Specialized listener that accepts TLS connections. */
export interface TlsListener extends Listener, AsyncIterable<TlsConn> {
/** Waits for a TLS client to connect and accepts the connection. */
accept(): Promise<TlsConn>;
[Symbol.asyncIterator](): AsyncIterableIterator<TlsConn>;
}
export interface Conn extends Reader, Writer, Closer { export interface Conn extends Reader, Writer, Closer {
/** The local address of the connection. */ /** The local address of the connection. */
readonly localAddr: Addr; readonly localAddr: Addr;
@ -45,6 +52,13 @@ declare namespace Deno {
closeWrite(): Promise<void>; closeWrite(): Promise<void>;
} }
export interface TlsConn extends Conn {
/** Runs the client or server handshake protocol to completion if that has
* not happened yet. Calling this method is optional; the TLS handshake
* will be completed automatically as soon as data is sent or received. */
handshake(): Promise<void>;
}
export interface ListenOptions { export interface ListenOptions {
/** The port to listen on. */ /** The port to listen on. */
port: number; port: number;
@ -90,7 +104,7 @@ declare namespace Deno {
* ``` * ```
* *
* Requires `allow-net` permission. */ * Requires `allow-net` permission. */
export function listenTls(options: ListenTlsOptions): Listener; export function listenTls(options: ListenTlsOptions): TlsListener;
export interface ConnectOptions { export interface ConnectOptions {
/** The port to connect to. */ /** The port to connect to. */
@ -150,7 +164,7 @@ declare namespace Deno {
* *
* Requires `allow-net` permission. * Requires `allow-net` permission.
*/ */
export function connectTls(options: ConnectTlsOptions): Promise<Conn>; export function connectTls(options: ConnectTlsOptions): Promise<TlsConn>;
/** Shutdown socket send operations. /** Shutdown socket send operations.
* *