mirror of
https://github.com/denoland/deno.git
synced 2025-09-22 10:22:34 +00:00

This commit adds `tcpBacklog` argument to `Deno.listen`, `Deno.listenTls` and `Deno.serve` APIs. The argument specifies maximum number of pending connections in the listen queue, and by default is set to 511. Users that expect huge bursts of traffic can customize this option to a higher value. Ref https://github.com/denoland/deno/pull/30471 Closes https://github.com/denoland/deno/issues/30388
267 lines
6.5 KiB
JavaScript
267 lines
6.5 KiB
JavaScript
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
import { core, internals, primordials } from "ext:core/mod.js";
|
|
const { internalRidSymbol } = core;
|
|
import {
|
|
op_net_accept_tls,
|
|
op_net_connect_tls,
|
|
op_net_listen_tls,
|
|
op_tls_cert_resolver_create,
|
|
op_tls_cert_resolver_poll,
|
|
op_tls_cert_resolver_resolve,
|
|
op_tls_cert_resolver_resolve_error,
|
|
op_tls_handshake,
|
|
op_tls_key_null,
|
|
op_tls_key_static,
|
|
op_tls_peer_certificate,
|
|
op_tls_start,
|
|
} from "ext:core/ops";
|
|
const {
|
|
ObjectDefineProperty,
|
|
TypeError,
|
|
Symbol,
|
|
SymbolFor,
|
|
} = primordials;
|
|
|
|
import { Conn, Listener, validatePort } from "ext:deno_net/01_net.js";
|
|
|
|
const _getPeerCertificate = Symbol("getPeerCertificate");
|
|
|
|
class TlsConn extends Conn {
|
|
#rid = 0;
|
|
|
|
constructor(rid, remoteAddr, localAddr) {
|
|
super(rid, remoteAddr, localAddr);
|
|
ObjectDefineProperty(this, internalRidSymbol, {
|
|
__proto__: null,
|
|
enumerable: false,
|
|
value: rid,
|
|
});
|
|
this.#rid = rid;
|
|
}
|
|
|
|
handshake() {
|
|
return op_tls_handshake(this.#rid);
|
|
}
|
|
|
|
[_getPeerCertificate](detailed = false) {
|
|
return op_tls_peer_certificate(this.#rid, detailed);
|
|
}
|
|
}
|
|
|
|
async function connectTls({
|
|
port,
|
|
hostname = "127.0.0.1",
|
|
transport = "tcp",
|
|
caCerts = [],
|
|
alpnProtocols = undefined,
|
|
keyFormat = undefined,
|
|
cert = undefined,
|
|
key = undefined,
|
|
unsafelyDisableHostnameVerification = false,
|
|
}) {
|
|
if (transport !== "tcp") {
|
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
|
}
|
|
|
|
const keyPair = loadTlsKeyPair("Deno.connectTls", {
|
|
keyFormat,
|
|
cert,
|
|
key,
|
|
});
|
|
// TODO(mmastrac): We only expose this feature via symbol for now. This should actually be a feature
|
|
// in Deno.connectTls, however.
|
|
const serverName = arguments[0][serverNameSymbol] ?? null;
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_connect_tls(
|
|
{ hostname, port },
|
|
{ caCerts, alpnProtocols, serverName, unsafelyDisableHostnameVerification },
|
|
keyPair,
|
|
);
|
|
localAddr.transport = "tcp";
|
|
remoteAddr.transport = "tcp";
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
|
}
|
|
|
|
class TlsListener extends Listener {
|
|
#rid = 0;
|
|
|
|
constructor(rid, addr) {
|
|
super(rid, addr);
|
|
ObjectDefineProperty(this, internalRidSymbol, {
|
|
__proto__: null,
|
|
enumerable: false,
|
|
value: rid,
|
|
});
|
|
this.#rid = rid;
|
|
}
|
|
|
|
async accept() {
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_accept_tls(
|
|
this.#rid,
|
|
);
|
|
localAddr.transport = "tcp";
|
|
remoteAddr.transport = "tcp";
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if this object has the shape of one of the certified key material
|
|
* interfaces.
|
|
*/
|
|
function hasTlsKeyPairOptions(options) {
|
|
// TODO(mmastrac): remove this temporary symbol when the API lands
|
|
if (options[resolverSymbol] !== undefined) {
|
|
return true;
|
|
}
|
|
return (options.cert !== undefined || options.key !== undefined);
|
|
}
|
|
|
|
/**
|
|
* Loads a TLS keypair from one of the various options. If no key material is provided,
|
|
* returns a special Null keypair.
|
|
*/
|
|
function loadTlsKeyPair(api, {
|
|
keyFormat,
|
|
cert,
|
|
key,
|
|
}) {
|
|
// TODO(mmastrac): remove this temporary symbol when the API lands
|
|
if (arguments[1][resolverSymbol] !== undefined) {
|
|
return createTlsKeyResolver(arguments[1][resolverSymbol]);
|
|
}
|
|
|
|
// Check for "pem" format
|
|
if (keyFormat !== undefined && keyFormat !== "pem") {
|
|
throw new TypeError(
|
|
`If "keyFormat" is specified, it must be "pem": received "${keyFormat}"`,
|
|
);
|
|
}
|
|
|
|
if (cert !== undefined && key === undefined) {
|
|
throw new TypeError(
|
|
`If \`cert\` is specified, \`key\` must be specified as well for \`${api}\``,
|
|
);
|
|
}
|
|
if (cert === undefined && key !== undefined) {
|
|
throw new TypeError(
|
|
`If \`key\` is specified, \`cert\` must be specified as well for \`${api}\``,
|
|
);
|
|
}
|
|
|
|
if (cert !== undefined) {
|
|
return op_tls_key_static(cert, key);
|
|
} else {
|
|
return op_tls_key_null();
|
|
}
|
|
}
|
|
|
|
function listenTls({
|
|
port,
|
|
hostname = "0.0.0.0",
|
|
transport = "tcp",
|
|
alpnProtocols = undefined,
|
|
reusePort = false,
|
|
tcpBacklog = 511,
|
|
}) {
|
|
if (transport !== "tcp") {
|
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
|
}
|
|
port = validatePort(port);
|
|
|
|
if (!hasTlsKeyPairOptions(arguments[0])) {
|
|
throw new TypeError(
|
|
"A key and certificate are required for `Deno.listenTls`",
|
|
);
|
|
}
|
|
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
|
|
const { 0: rid, 1: localAddr } = op_net_listen_tls(
|
|
{ hostname, port },
|
|
{ alpnProtocols, reusePort, tcpBacklog },
|
|
keyPair,
|
|
);
|
|
return new TlsListener(rid, localAddr);
|
|
}
|
|
|
|
// deno-lint-ignore require-await
|
|
async function startTls(
|
|
conn,
|
|
{
|
|
hostname = "127.0.0.1",
|
|
caCerts = [],
|
|
alpnProtocols = undefined,
|
|
unsafelyDisableHostnameVerification = false,
|
|
} = { __proto__: null },
|
|
) {
|
|
return startTlsInternal(conn, {
|
|
hostname,
|
|
caCerts,
|
|
alpnProtocols,
|
|
unsafelyDisableHostnameVerification,
|
|
});
|
|
}
|
|
|
|
function startTlsInternal(
|
|
conn,
|
|
{
|
|
hostname = "127.0.0.1",
|
|
caCerts = [],
|
|
alpnProtocols = undefined,
|
|
keyPair = null,
|
|
rejectUnauthorized,
|
|
unsafelyDisableHostnameVerification,
|
|
},
|
|
) {
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = op_tls_start({
|
|
rid: conn[internalRidSymbol],
|
|
hostname,
|
|
caCerts,
|
|
alpnProtocols,
|
|
rejectUnauthorized,
|
|
unsafelyDisableHostnameVerification,
|
|
}, keyPair);
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
|
}
|
|
|
|
const resolverSymbol = SymbolFor("unstableSniResolver");
|
|
const serverNameSymbol = SymbolFor("unstableServerName");
|
|
|
|
function createTlsKeyResolver(callback) {
|
|
const { 0: resolver, 1: lookup } = op_tls_cert_resolver_create();
|
|
(async () => {
|
|
while (true) {
|
|
const sni = await op_tls_cert_resolver_poll(lookup);
|
|
if (typeof sni !== "string") {
|
|
break;
|
|
}
|
|
try {
|
|
const key = await callback(sni);
|
|
if (!hasTlsKeyPairOptions(key)) {
|
|
op_tls_cert_resolver_resolve_error(lookup, sni, "Invalid key");
|
|
} else {
|
|
const resolved = loadTlsKeyPair("Deno.listenTls", key);
|
|
op_tls_cert_resolver_resolve(lookup, sni, resolved);
|
|
}
|
|
} catch (e) {
|
|
op_tls_cert_resolver_resolve_error(lookup, sni, e.message);
|
|
}
|
|
}
|
|
})();
|
|
return resolver;
|
|
}
|
|
|
|
internals.resolverSymbol = resolverSymbol;
|
|
internals.serverNameSymbol = serverNameSymbol;
|
|
internals.createTlsKeyResolver = createTlsKeyResolver;
|
|
internals.getPeerCertificate = _getPeerCertificate;
|
|
|
|
export {
|
|
connectTls,
|
|
hasTlsKeyPairOptions,
|
|
listenTls,
|
|
loadTlsKeyPair,
|
|
startTls,
|
|
startTlsInternal,
|
|
TlsConn,
|
|
TlsListener,
|
|
};
|