1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -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.
*/
export function connectTls(options: ConnectTlsOptions): Promise<Conn>;
export function connectTls(options: ConnectTlsOptions): Promise<TlsConn>;
export interface StartTlsOptions {
/** 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(
conn: Conn,
options?: StartTlsOptions,
): Promise<Conn>;
): Promise<TlsConn>;
export interface ListenTlsOptions {
/** **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(
{ permissions: { read: true, net: true } },
async function tlsHandshakeSuccess() {
@ -1151,7 +1143,7 @@ unitTest(
const [conn1, conn2] = await Promise.all([acceptPromise, connectPromise]);
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.
const whole = new Uint8Array(10 << 20); // 10mb.
@ -1162,28 +1154,28 @@ unitTest(
const half = new Uint8Array(whole.byteLength / 2);
const receivePromise = readFull(conn2, half);
await tlsHandshake(conn1);
await tlsHandshake(conn2);
await conn1.handshake();
await conn2.handshake();
// Finish receiving the first 5mb.
assertEquals(await receivePromise, half.length);
// See that we can call `handshake()` in the middle of large reads and writes.
await tlsHandshake(conn1);
await tlsHandshake(conn2);
await conn1.handshake();
await conn2.handshake();
// Receive second half of large blob. Wait for the send promise and check it.
assertEquals(await readFull(conn2, half), half.length);
assertEquals(await sendPromise, whole.length);
await tlsHandshake(conn1);
await tlsHandshake(conn2);
await conn1.handshake();
await conn2.handshake();
await conn1.closeWrite();
await conn2.closeWrite();
await tlsHandshake(conn1);
await tlsHandshake(conn2);
await conn1.handshake();
await conn2.handshake();
conn1.close();
conn2.close();
@ -1216,7 +1208,7 @@ unitTest(
for (let i = 0; i < 10; i++) {
// Handshake fails because the client rejects the server certificate.
await assertRejects(
() => tlsHandshake(conn),
() => conn.handshake(),
Deno.errors.InvalidData,
"BadCertificate",
);
@ -1230,7 +1222,7 @@ unitTest(
const conn = await Deno.connectTls({ hostname, port });
// Handshake fails because the server presents a self-signed certificate.
await assertRejects(
() => tlsHandshake(conn),
() => conn.handshake(),
Deno.errors.InvalidData,
"UnknownIssuer",
);
@ -1247,7 +1239,7 @@ unitTest(
});
// Handshake fails because hostname doesn't match the certificate.
await assertRejects(
() => tlsHandshake(tlsConn),
() => tlsConn.handshake(),
Deno.errors.InvalidData,
"CertNotValidForName",
);

View file

@ -23,6 +23,16 @@
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({
port,
hostname = "127.0.0.1",
@ -41,13 +51,13 @@
certChain,
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() {
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,
alpnProtocols,
});
return new TLSListener(res.rid, res.localAddr);
return new TlsListener(res.rid, res.localAddr);
}
async function startTls(
@ -80,13 +90,14 @@
certFile,
caCerts,
});
return new Conn(res.rid, res.remoteAddr, res.localAddr);
return new TlsConn(res.rid, res.remoteAddr, res.localAddr);
}
window.__bootstrap.tls = {
startTls,
listenTls,
connectTls,
TLSListener,
TlsConn,
TlsListener,
};
})(this);

View file

@ -33,6 +33,13 @@ declare namespace Deno {
[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 {
/** The local address of the connection. */
readonly localAddr: Addr;
@ -45,6 +52,13 @@ declare namespace Deno {
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 {
/** The port to listen on. */
port: number;
@ -90,7 +104,7 @@ declare namespace Deno {
* ```
*
* Requires `allow-net` permission. */
export function listenTls(options: ListenTlsOptions): Listener;
export function listenTls(options: ListenTlsOptions): TlsListener;
export interface ConnectOptions {
/** The port to connect to. */
@ -150,7 +164,7 @@ declare namespace Deno {
*
* Requires `allow-net` permission.
*/
export function connectTls(options: ConnectTlsOptions): Promise<Conn>;
export function connectTls(options: ConnectTlsOptions): Promise<TlsConn>;
/** Shutdown socket send operations.
*