From 217366e308d6759eb6a1f7ed5c7d584a201cb988 Mon Sep 17 00:00:00 2001 From: Elad Nava Date: Thu, 20 Jan 2022 22:24:30 -0500 Subject: [PATCH] cluster: respect listen backlog set by workers PR-URL: https://github.com/nodejs/node/pull/41623 Co-authored-by: Ouyang Yadong Reviewed-By: Ouyang Yadong Reviewed-By: Matteo Collina --- doc/api/net.md | 4 ++ lib/internal/cluster/round_robin_handle.js | 7 +-- lib/net.js | 1 + .../test-cluster-net-listen-backlog.js | 45 +++++++++++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-cluster-net-listen-backlog.js diff --git a/doc/api/net.md b/doc/api/net.md index 99864eeb9fa..70303407fb1 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -464,6 +464,10 @@ server.listen({ }); ``` +When `exclusive` is `true` and the underlying handle is shared, it is +possible that several workers query a handle with different backlogs. +In this case, the first `backlog` passed to the master process will be used. + Starting an IPC server as root may cause the server path to be inaccessible for unprivileged users. Using `readableAll` and `writableAll` will make the server accessible for all users. diff --git a/lib/internal/cluster/round_robin_handle.js b/lib/internal/cluster/round_robin_handle.js index 2bf008939e7..9d242cc60ad 100644 --- a/lib/internal/cluster/round_robin_handle.js +++ b/lib/internal/cluster/round_robin_handle.js @@ -15,7 +15,7 @@ const { constants } = internalBinding('tcp_wrap'); module.exports = RoundRobinHandle; -function RoundRobinHandle(key, address, { port, fd, flags }) { +function RoundRobinHandle(key, address, { port, fd, flags, backlog }) { this.key = key; this.all = new SafeMap(); this.free = new SafeMap(); @@ -24,16 +24,17 @@ function RoundRobinHandle(key, address, { port, fd, flags }) { this.server = net.createServer(assert.fail); if (fd >= 0) - this.server.listen({ fd }); + this.server.listen({ fd, backlog }); else if (port >= 0) { this.server.listen({ port, host: address, // Currently, net module only supports `ipv6Only` option in `flags`. ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY), + backlog, }); } else - this.server.listen(address); // UNIX socket path. + this.server.listen(address, backlog); // UNIX socket path. this.server.once('listening', () => { this.handle = this.server._handle; diff --git a/lib/net.js b/lib/net.js index 3e1577ffa8b..3bbd3c73333 100644 --- a/lib/net.js +++ b/lib/net.js @@ -1386,6 +1386,7 @@ function listenInCluster(server, address, port, addressType, addressType: addressType, fd: fd, flags, + backlog, }; // Get the primary's server handle, and listen on it diff --git a/test/parallel/test-cluster-net-listen-backlog.js b/test/parallel/test-cluster-net-listen-backlog.js new file mode 100644 index 00000000000..090552fd1e1 --- /dev/null +++ b/test/parallel/test-cluster-net-listen-backlog.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +// Monkey-patch `net.Server.listen` +const net = require('net'); +const cluster = require('cluster'); + +// Force round-robin scheduling policy +// as Windows defaults to SCHED_NONE +// https://nodejs.org/docs/latest/api/cluster.html#clusterschedulingpolicy +cluster.schedulingPolicy = cluster.SCHED_RR; + +// Ensures that the `backlog` is used to create a `net.Server`. +const kExpectedBacklog = 127; +if (cluster.isMaster) { + const listen = net.Server.prototype.listen; + + net.Server.prototype.listen = common.mustCall( + function(...args) { + const options = args[0]; + if (typeof options === 'object') { + assert(options.backlog, kExpectedBacklog); + } else { + assert(args[1], kExpectedBacklog); + } + return listen.call(this, ...args); + } + ); + + const worker = cluster.fork(); + worker.on('message', () => { + worker.disconnect(); + }); +} else { + const server = net.createServer(); + + server.listen({ + host: common.localhostIPv4, + port: 0, + backlog: kExpectedBacklog, + }, common.mustCall(() => { + process.send(true); + })); +}