'use strict';
const { once } = require('events');
const {
validateAbortSignal,
validateObject,
} = require('internal/validators');
const { kEmptyObject } = require('internal/util');
// Lazily loaded to avoid a require cycle with the `net` module, which exposes
// this namespace through its `promises` getter.
let net;
function lazyNet() {
net ??= require('net');
return net;
}
// Resolves with a connected `net.Socket` once the `'connect'` event fires, and
// rejects if the connection fails or the optional `signal` is aborted.
async function connect(...args) {
const lazy = lazyNet();
const options = lazy._normalizeArgs(args)[0];
const { signal } = options;
if (signal !== undefined) {
validateAbortSignal(signal, 'options.signal');
signal.throwIfAborted();
}
// Strip the signal so the socket does not also install its own abort
// handling; rejecting and destroying below fully tears the socket down.
const socket = lazy.connect({ ...options, signal: undefined });
try {
await once(socket, 'connect', signal !== undefined ? { signal } : kEmptyObject);
} catch (err) {
socket.destroy();
throw err;
}
return socket;
}
// Creates a server and resolves with it once it is listening, rejecting if it
// fails to bind or the optional `signal` is aborted.
async function listen(options = kEmptyObject) {
validateObject(options, 'options');
const { signal, connectionListener } = options;
if (signal !== undefined) {
validateAbortSignal(signal, 'options.signal');
signal.throwIfAborted();
}
const lazy = lazyNet();
const server = lazy.createServer(options, connectionListener);
try {
// Default to an ephemeral port when no listen target is supplied, matching
// `server.listen()` with no arguments; passing a target-less options object
// (the `{}` default, or e.g. `{ signal }`) straight to listen() would throw
// ERR_INVALID_ARG_VALUE. `signal` is passed through so net installs its own
// close-on-abort handler: the signal aborts the server for its entire
// lifetime, not just the pending listen.
const hasListenTarget = options.port !== undefined ||
options.path !== undefined ||
options.fd !== undefined ||
options.handle !== undefined;
server.listen(hasListenTarget ? options : { ...options, port: 0 });
await once(server, 'listening', signal !== undefined ? { signal } : kEmptyObject);
} catch (err) {
// On abort, net's signal handler already closes the server, so closing
// again would be redundant; on other failures (e.g. a bind error) there
// is no such handler, so close it here.
if (!signal?.aborted) {
server.close();
}
throw err;
}
return server;
}
module.exports = {
connect,
listen,
get isIP() { return lazyNet().isIP; },
get isIPv4() { return lazyNet().isIPv4; },
get isIPv6() { return lazyNet().isIPv6; },
get BlockList() { return lazyNet().BlockList; },
get SocketAddress() { return lazyNet().SocketAddress; },
};