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:
parent
c27ef0ac7b
commit
cf9c4f0031
4 changed files with 47 additions and 30 deletions
4
cli/dts/lib.deno.unstable.d.ts
vendored
4
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -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.
|
||||||
|
|
|
@ -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",
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
18
ext/net/lib.deno_net.d.ts
vendored
18
ext/net/lib.deno_net.d.ts
vendored
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue