net,dgram: add ipv6Only option for net and dgram
For TCP servers, the dual-stack support is enable by default, i.e. binding host "::" will also make "0.0.0.0" bound. This commit add ipv6Only option in `net.Server.listen()` and `dgram.createSocket()` methods which allows to disable dual-stack support. Support for cluster module is also provided in this commit. Fixes: https://github.com/nodejs/node/issues/17664 PR-URL: https://github.com/nodejs/node/pull/23798 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
91748dd89c
commit
33a25b29a4
@ -601,6 +601,9 @@ changes:
|
|||||||
pr-url: https://github.com/nodejs/node/pull/13623
|
pr-url: https://github.com/nodejs/node/pull/13623
|
||||||
description: The `recvBufferSize` and `sendBufferSize` options are
|
description: The `recvBufferSize` and `sendBufferSize` options are
|
||||||
supported now.
|
supported now.
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/23798
|
||||||
|
description: The `ipv6Only` option is supported.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* `options` {Object} Available options are:
|
* `options` {Object} Available options are:
|
||||||
@ -609,6 +612,9 @@ changes:
|
|||||||
* `reuseAddr` {boolean} When `true` [`socket.bind()`][] will reuse the
|
* `reuseAddr` {boolean} When `true` [`socket.bind()`][] will reuse the
|
||||||
address, even if another process has already bound a socket on it.
|
address, even if another process has already bound a socket on it.
|
||||||
**Default:** `false`.
|
**Default:** `false`.
|
||||||
|
* `ipv6Only` {boolean} Setting `ipv6Only` to `true` will
|
||||||
|
disable dual-stack support, i.e., binding to address `::` won't make
|
||||||
|
`0.0.0.0` be bound. **Default:** `false`.
|
||||||
* `recvBufferSize` {number} - Sets the `SO_RCVBUF` socket value.
|
* `recvBufferSize` {number} - Sets the `SO_RCVBUF` socket value.
|
||||||
* `sendBufferSize` {number} - Sets the `SO_SNDBUF` socket value.
|
* `sendBufferSize` {number} - Sets the `SO_SNDBUF` socket value.
|
||||||
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
|
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
|
||||||
|
@ -252,6 +252,10 @@ Listening on a file descriptor is not supported on Windows.
|
|||||||
#### server.listen(options[, callback])
|
#### server.listen(options[, callback])
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.11.14
|
added: v0.11.14
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/23798
|
||||||
|
description: The `ipv6Only` option is supported.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* `options` {Object} Required. Supports the following properties:
|
* `options` {Object} Required. Supports the following properties:
|
||||||
@ -266,6 +270,9 @@ added: v0.11.14
|
|||||||
for all users. **Default:** `false`
|
for all users. **Default:** `false`
|
||||||
* `writableAll` {boolean} For IPC servers makes the pipe writable
|
* `writableAll` {boolean} For IPC servers makes the pipe writable
|
||||||
for all users. **Default:** `false`
|
for all users. **Default:** `false`
|
||||||
|
* `ipv6Only` {boolean} For TCP servers, setting `ipv6Only` to `true` will
|
||||||
|
disable dual-stack support, i.e., binding to host `::` won't make
|
||||||
|
`0.0.0.0` be bound. **Default:** `false`.
|
||||||
* `callback` {Function} Common parameter of [`server.listen()`][]
|
* `callback` {Function} Common parameter of [`server.listen()`][]
|
||||||
functions.
|
functions.
|
||||||
* Returns: {net.Server}
|
* Returns: {net.Server}
|
||||||
|
@ -54,7 +54,11 @@ const {
|
|||||||
} = require('internal/async_hooks');
|
} = require('internal/async_hooks');
|
||||||
const { UV_UDP_REUSEADDR } = internalBinding('constants').os;
|
const { UV_UDP_REUSEADDR } = internalBinding('constants').os;
|
||||||
|
|
||||||
const { UDP, SendWrap } = internalBinding('udp_wrap');
|
const {
|
||||||
|
constants: { UV_UDP_IPV6ONLY },
|
||||||
|
UDP,
|
||||||
|
SendWrap
|
||||||
|
} = internalBinding('udp_wrap');
|
||||||
|
|
||||||
const BIND_STATE_UNBOUND = 0;
|
const BIND_STATE_UNBOUND = 0;
|
||||||
const BIND_STATE_BINDING = 1;
|
const BIND_STATE_BINDING = 1;
|
||||||
@ -99,6 +103,7 @@ function Socket(type, listener) {
|
|||||||
bindState: BIND_STATE_UNBOUND,
|
bindState: BIND_STATE_UNBOUND,
|
||||||
queue: undefined,
|
queue: undefined,
|
||||||
reuseAddr: options && options.reuseAddr, // Use UV_UDP_REUSEADDR if true.
|
reuseAddr: options && options.reuseAddr, // Use UV_UDP_REUSEADDR if true.
|
||||||
|
ipv6Only: options && options.ipv6Only,
|
||||||
recvBufferSize,
|
recvBufferSize,
|
||||||
sendBufferSize
|
sendBufferSize
|
||||||
};
|
};
|
||||||
@ -270,6 +275,8 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) {
|
|||||||
var flags = 0;
|
var flags = 0;
|
||||||
if (state.reuseAddr)
|
if (state.reuseAddr)
|
||||||
flags |= UV_UDP_REUSEADDR;
|
flags |= UV_UDP_REUSEADDR;
|
||||||
|
if (state.ipv6Only)
|
||||||
|
flags |= UV_UDP_IPV6ONLY;
|
||||||
|
|
||||||
if (cluster.isWorker && !exclusive) {
|
if (cluster.isWorker && !exclusive) {
|
||||||
bindServerHandle(this, {
|
bindServerHandle(this, {
|
||||||
|
@ -3,10 +3,11 @@ const assert = require('assert');
|
|||||||
const net = require('net');
|
const net = require('net');
|
||||||
const { sendHelper } = require('internal/cluster/utils');
|
const { sendHelper } = require('internal/cluster/utils');
|
||||||
const uv = internalBinding('uv');
|
const uv = internalBinding('uv');
|
||||||
|
const { constants } = internalBinding('tcp_wrap');
|
||||||
|
|
||||||
module.exports = RoundRobinHandle;
|
module.exports = RoundRobinHandle;
|
||||||
|
|
||||||
function RoundRobinHandle(key, address, port, addressType, fd) {
|
function RoundRobinHandle(key, address, port, addressType, fd, flags) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.all = new Map();
|
this.all = new Map();
|
||||||
this.free = [];
|
this.free = [];
|
||||||
@ -16,9 +17,14 @@ function RoundRobinHandle(key, address, port, addressType, fd) {
|
|||||||
|
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
this.server.listen({ fd });
|
this.server.listen({ fd });
|
||||||
else if (port >= 0)
|
else if (port >= 0) {
|
||||||
this.server.listen(port, address);
|
this.server.listen({
|
||||||
else
|
port,
|
||||||
|
host: address,
|
||||||
|
// Currently, net module only supports `ipv6Only` option in `flags`.
|
||||||
|
ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY),
|
||||||
|
});
|
||||||
|
} else
|
||||||
this.server.listen(address); // UNIX socket path.
|
this.server.listen(address); // UNIX socket path.
|
||||||
|
|
||||||
this.server.once('listening', () => {
|
this.server.once('listening', () => {
|
||||||
|
@ -15,7 +15,7 @@ function SharedHandle(key, address, port, addressType, fd, flags) {
|
|||||||
if (addressType === 'udp4' || addressType === 'udp6')
|
if (addressType === 'udp4' || addressType === 'udp6')
|
||||||
rval = dgram._createSocketHandle(address, port, addressType, fd, flags);
|
rval = dgram._createSocketHandle(address, port, addressType, fd, flags);
|
||||||
else
|
else
|
||||||
rval = net._createServerHandle(address, port, addressType, fd);
|
rval = net._createServerHandle(address, port, addressType, fd, flags);
|
||||||
|
|
||||||
if (typeof rval === 'number')
|
if (typeof rval === 'number')
|
||||||
this.errno = rval;
|
this.errno = rval;
|
||||||
|
35
lib/net.js
35
lib/net.js
@ -97,6 +97,10 @@ const {
|
|||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
|
function getFlags(ipv6Only) {
|
||||||
|
return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
function createHandle(fd, is_server) {
|
function createHandle(fd, is_server) {
|
||||||
validateInt32(fd, 'fd', 0);
|
validateInt32(fd, 'fd', 0);
|
||||||
const type = TTYWrap.guessHandleType(fd);
|
const type = TTYWrap.guessHandleType(fd);
|
||||||
@ -798,7 +802,7 @@ function checkBindError(err, port, handle) {
|
|||||||
|
|
||||||
|
|
||||||
function internalConnect(
|
function internalConnect(
|
||||||
self, address, port, addressType, localAddress, localPort) {
|
self, address, port, addressType, localAddress, localPort, flags) {
|
||||||
// TODO return promise from Socket.prototype.connect which
|
// TODO return promise from Socket.prototype.connect which
|
||||||
// wraps _connectReq.
|
// wraps _connectReq.
|
||||||
|
|
||||||
@ -812,7 +816,7 @@ function internalConnect(
|
|||||||
err = self._handle.bind(localAddress, localPort);
|
err = self._handle.bind(localAddress, localPort);
|
||||||
} else { // addressType === 6
|
} else { // addressType === 6
|
||||||
localAddress = localAddress || '::';
|
localAddress = localAddress || '::';
|
||||||
err = self._handle.bind6(localAddress, localPort);
|
err = self._handle.bind6(localAddress, localPort, flags);
|
||||||
}
|
}
|
||||||
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
|
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
|
||||||
localAddress, localPort, addressType);
|
localAddress, localPort, addressType);
|
||||||
@ -1148,7 +1152,7 @@ util.inherits(Server, EventEmitter);
|
|||||||
function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
|
function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
|
||||||
|
|
||||||
// Returns handle if it can be created, or error code if it can't
|
// Returns handle if it can be created, or error code if it can't
|
||||||
function createServerHandle(address, port, addressType, fd) {
|
function createServerHandle(address, port, addressType, fd, flags) {
|
||||||
var err = 0;
|
var err = 0;
|
||||||
// assign handle in listen, and clean up if bind or listen fails
|
// assign handle in listen, and clean up if bind or listen fails
|
||||||
var handle;
|
var handle;
|
||||||
@ -1187,14 +1191,14 @@ function createServerHandle(address, port, addressType, fd) {
|
|||||||
debug('bind to', address || 'any');
|
debug('bind to', address || 'any');
|
||||||
if (!address) {
|
if (!address) {
|
||||||
// Try binding to ipv6 first
|
// Try binding to ipv6 first
|
||||||
err = handle.bind6('::', port);
|
err = handle.bind6('::', port, flags);
|
||||||
if (err) {
|
if (err) {
|
||||||
handle.close();
|
handle.close();
|
||||||
// Fallback to ipv4
|
// Fallback to ipv4
|
||||||
return createServerHandle('0.0.0.0', port);
|
return createServerHandle('0.0.0.0', port);
|
||||||
}
|
}
|
||||||
} else if (addressType === 6) {
|
} else if (addressType === 6) {
|
||||||
err = handle.bind6(address, port);
|
err = handle.bind6(address, port, flags);
|
||||||
} else {
|
} else {
|
||||||
err = handle.bind(address, port);
|
err = handle.bind(address, port);
|
||||||
}
|
}
|
||||||
@ -1208,7 +1212,7 @@ function createServerHandle(address, port, addressType, fd) {
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupListenHandle(address, port, addressType, backlog, fd) {
|
function setupListenHandle(address, port, addressType, backlog, fd, flags) {
|
||||||
debug('setupListenHandle', address, port, addressType, backlog, fd);
|
debug('setupListenHandle', address, port, addressType, backlog, fd);
|
||||||
|
|
||||||
// If there is not yet a handle, we need to create one and bind.
|
// If there is not yet a handle, we need to create one and bind.
|
||||||
@ -1222,7 +1226,7 @@ function setupListenHandle(address, port, addressType, backlog, fd) {
|
|||||||
|
|
||||||
// Try to bind to the unspecified IPv6 address, see if IPv6 is available
|
// Try to bind to the unspecified IPv6 address, see if IPv6 is available
|
||||||
if (!address && typeof fd !== 'number') {
|
if (!address && typeof fd !== 'number') {
|
||||||
rval = createServerHandle('::', port, 6, fd);
|
rval = createServerHandle('::', port, 6, fd, flags);
|
||||||
|
|
||||||
if (typeof rval === 'number') {
|
if (typeof rval === 'number') {
|
||||||
rval = null;
|
rval = null;
|
||||||
@ -1235,7 +1239,7 @@ function setupListenHandle(address, port, addressType, backlog, fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rval === null)
|
if (rval === null)
|
||||||
rval = createServerHandle(address, port, addressType, fd);
|
rval = createServerHandle(address, port, addressType, fd, flags);
|
||||||
|
|
||||||
if (typeof rval === 'number') {
|
if (typeof rval === 'number') {
|
||||||
var error = uvExceptionWithHostPort(rval, 'listen', address, port);
|
var error = uvExceptionWithHostPort(rval, 'listen', address, port);
|
||||||
@ -1294,7 +1298,7 @@ function emitListeningNT(self) {
|
|||||||
|
|
||||||
|
|
||||||
function listenInCluster(server, address, port, addressType,
|
function listenInCluster(server, address, port, addressType,
|
||||||
backlog, fd, exclusive) {
|
backlog, fd, exclusive, flags) {
|
||||||
exclusive = !!exclusive;
|
exclusive = !!exclusive;
|
||||||
|
|
||||||
if (cluster === undefined) cluster = require('cluster');
|
if (cluster === undefined) cluster = require('cluster');
|
||||||
@ -1303,7 +1307,7 @@ function listenInCluster(server, address, port, addressType,
|
|||||||
// Will create a new handle
|
// Will create a new handle
|
||||||
// _listen2 sets up the listened handle, it is still named like this
|
// _listen2 sets up the listened handle, it is still named like this
|
||||||
// to avoid breaking code that wraps this method
|
// to avoid breaking code that wraps this method
|
||||||
server._listen2(address, port, addressType, backlog, fd);
|
server._listen2(address, port, addressType, backlog, fd, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1312,7 +1316,7 @@ function listenInCluster(server, address, port, addressType,
|
|||||||
port: port,
|
port: port,
|
||||||
addressType: addressType,
|
addressType: addressType,
|
||||||
fd: fd,
|
fd: fd,
|
||||||
flags: 0
|
flags,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the master's server handle, and listen on it
|
// Get the master's server handle, and listen on it
|
||||||
@ -1330,7 +1334,7 @@ function listenInCluster(server, address, port, addressType,
|
|||||||
server._handle = handle;
|
server._handle = handle;
|
||||||
// _listen2 sets up the listened handle, it is still named like this
|
// _listen2 sets up the listened handle, it is still named like this
|
||||||
// to avoid breaking code that wraps this method
|
// to avoid breaking code that wraps this method
|
||||||
server._listen2(address, port, addressType, backlog, fd);
|
server._listen2(address, port, addressType, backlog, fd, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,6 +1357,7 @@ Server.prototype.listen = function(...args) {
|
|||||||
toNumber(args.length > 2 && args[2]); // (port, host, backlog)
|
toNumber(args.length > 2 && args[2]); // (port, host, backlog)
|
||||||
|
|
||||||
options = options._handle || options.handle || options;
|
options = options._handle || options.handle || options;
|
||||||
|
const flags = getFlags(options.ipv6Only);
|
||||||
// (handle[, backlog][, cb]) where handle is an object with a handle
|
// (handle[, backlog][, cb]) where handle is an object with a handle
|
||||||
if (options instanceof TCP) {
|
if (options instanceof TCP) {
|
||||||
this._handle = options;
|
this._handle = options;
|
||||||
@ -1387,7 +1392,7 @@ Server.prototype.listen = function(...args) {
|
|||||||
// start TCP server listening on host:port
|
// start TCP server listening on host:port
|
||||||
if (options.host) {
|
if (options.host) {
|
||||||
lookupAndListen(this, options.port | 0, options.host, backlog,
|
lookupAndListen(this, options.port | 0, options.host, backlog,
|
||||||
options.exclusive);
|
options.exclusive, flags);
|
||||||
} else { // Undefined host, listens on unspecified address
|
} else { // Undefined host, listens on unspecified address
|
||||||
// Default addressType 4 will be used to search for master server
|
// Default addressType 4 will be used to search for master server
|
||||||
listenInCluster(this, null, options.port | 0, 4,
|
listenInCluster(this, null, options.port | 0, 4,
|
||||||
@ -1434,7 +1439,7 @@ Server.prototype.listen = function(...args) {
|
|||||||
throw new ERR_INVALID_OPT_VALUE('options', util.inspect(options));
|
throw new ERR_INVALID_OPT_VALUE('options', util.inspect(options));
|
||||||
};
|
};
|
||||||
|
|
||||||
function lookupAndListen(self, port, address, backlog, exclusive) {
|
function lookupAndListen(self, port, address, backlog, exclusive, flags) {
|
||||||
if (dns === undefined) dns = require('dns');
|
if (dns === undefined) dns = require('dns');
|
||||||
dns.lookup(address, function doListen(err, ip, addressType) {
|
dns.lookup(address, function doListen(err, ip, addressType) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -1442,7 +1447,7 @@ function lookupAndListen(self, port, address, backlog, exclusive) {
|
|||||||
} else {
|
} else {
|
||||||
addressType = ip ? addressType : 4;
|
addressType = ip ? addressType : 4;
|
||||||
listenInCluster(self, ip, port, addressType,
|
listenInCluster(self, ip, port, addressType,
|
||||||
backlog, undefined, exclusive);
|
backlog, undefined, exclusive, flags);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,7 @@ void TCPWrap::Initialize(Local<Object> target,
|
|||||||
Local<Object> constants = Object::New(env->isolate());
|
Local<Object> constants = Object::New(env->isolate());
|
||||||
NODE_DEFINE_CONSTANT(constants, SOCKET);
|
NODE_DEFINE_CONSTANT(constants, SOCKET);
|
||||||
NODE_DEFINE_CONSTANT(constants, SERVER);
|
NODE_DEFINE_CONSTANT(constants, SERVER);
|
||||||
|
NODE_DEFINE_CONSTANT(constants, UV_TCP_IPV6ONLY);
|
||||||
target->Set(context,
|
target->Set(context,
|
||||||
env->constants_string(),
|
env->constants_string(),
|
||||||
constants).FromJust();
|
constants).FromJust();
|
||||||
@ -252,13 +253,15 @@ void TCPWrap::Bind6(const FunctionCallbackInfo<Value>& args) {
|
|||||||
Environment* env = wrap->env();
|
Environment* env = wrap->env();
|
||||||
node::Utf8Value ip6_address(env->isolate(), args[0]);
|
node::Utf8Value ip6_address(env->isolate(), args[0]);
|
||||||
int port;
|
int port;
|
||||||
|
unsigned int flags;
|
||||||
if (!args[1]->Int32Value(env->context()).To(&port)) return;
|
if (!args[1]->Int32Value(env->context()).To(&port)) return;
|
||||||
|
if (!args[2]->Uint32Value(env->context()).To(&flags)) return;
|
||||||
sockaddr_in6 addr;
|
sockaddr_in6 addr;
|
||||||
int err = uv_ip6_addr(*ip6_address, port, &addr);
|
int err = uv_ip6_addr(*ip6_address, port, &addr);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
err = uv_tcp_bind(&wrap->handle_,
|
err = uv_tcp_bind(&wrap->handle_,
|
||||||
reinterpret_cast<const sockaddr*>(&addr),
|
reinterpret_cast<const sockaddr*>(&addr),
|
||||||
0);
|
flags);
|
||||||
}
|
}
|
||||||
args.GetReturnValue().Set(err);
|
args.GetReturnValue().Set(err);
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,12 @@ void UDPWrap::Initialize(Local<Object> target,
|
|||||||
target->Set(env->context(),
|
target->Set(env->context(),
|
||||||
sendWrapString,
|
sendWrapString,
|
||||||
swt->GetFunction(env->context()).ToLocalChecked()).FromJust();
|
swt->GetFunction(env->context()).ToLocalChecked()).FromJust();
|
||||||
|
|
||||||
|
Local<Object> constants = Object::New(env->isolate());
|
||||||
|
NODE_DEFINE_CONSTANT(constants, UV_UDP_IPV6ONLY);
|
||||||
|
target->Set(context,
|
||||||
|
env->constants_string(),
|
||||||
|
constants).FromJust();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
51
test/parallel/test-cluster-dgram-ipv6only.js
Normal file
51
test/parallel/test-cluster-dgram-ipv6only.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
if (common.isWindows)
|
||||||
|
common.skip('dgram clustering is currently not supported on windows.');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const cluster = require('cluster');
|
||||||
|
const dgram = require('dgram');
|
||||||
|
|
||||||
|
// This test ensures that the `ipv6Only` option in `dgram.createSock()`
|
||||||
|
// works as expected.
|
||||||
|
if (cluster.isMaster) {
|
||||||
|
cluster.fork().on('exit', common.mustCall((code) => {
|
||||||
|
assert.strictEqual(code, 0);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
let waiting = 2;
|
||||||
|
function close() {
|
||||||
|
if (--waiting === 0)
|
||||||
|
cluster.worker.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket1 = dgram.createSocket({
|
||||||
|
type: 'udp6',
|
||||||
|
ipv6Only: true
|
||||||
|
});
|
||||||
|
const socket2 = dgram.createSocket({
|
||||||
|
type: 'udp4',
|
||||||
|
});
|
||||||
|
socket1.on('error', common.mustNotCall());
|
||||||
|
socket2.on('error', common.mustNotCall());
|
||||||
|
|
||||||
|
socket1.bind({
|
||||||
|
port: 0,
|
||||||
|
address: '::',
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
const { port } = socket1.address();
|
||||||
|
socket2.bind({
|
||||||
|
port,
|
||||||
|
address: '0.0.0.0',
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
process.nextTick(() => {
|
||||||
|
socket1.close(close);
|
||||||
|
socket2.close(close);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
55
test/parallel/test-cluster-net-listen-ipv6only-false.js
Normal file
55
test/parallel/test-cluster-net-listen-ipv6only-false.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const cluster = require('cluster');
|
||||||
|
const net = require('net');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
|
// This test ensures that dual-stack support still works for cluster module
|
||||||
|
// when `ipv6Only` is not `true`.
|
||||||
|
const host = '::';
|
||||||
|
const WORKER_COUNT = 3;
|
||||||
|
|
||||||
|
if (cluster.isMaster) {
|
||||||
|
const workers = new Map();
|
||||||
|
let address;
|
||||||
|
|
||||||
|
const countdown = new Countdown(WORKER_COUNT, () => {
|
||||||
|
const socket = net.connect({
|
||||||
|
port: address.port,
|
||||||
|
host: '0.0.0.0',
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
socket.destroy();
|
||||||
|
workers.forEach((worker) => {
|
||||||
|
worker.disconnect();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
socket.on('error', common.mustNotCall());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < WORKER_COUNT; i += 1) {
|
||||||
|
const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
|
||||||
|
assert.strictEqual(statusCode, 0);
|
||||||
|
})).on('listening', common.mustCall((workerAddress) => {
|
||||||
|
if (!address) {
|
||||||
|
address = workerAddress;
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(address.addressType, workerAddress.addressType);
|
||||||
|
assert.strictEqual(address.host, workerAddress.host);
|
||||||
|
assert.strictEqual(address.port, workerAddress.port);
|
||||||
|
}
|
||||||
|
countdown.dec();
|
||||||
|
}));
|
||||||
|
|
||||||
|
workers.set(i, worker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
net.createServer().listen({
|
||||||
|
host,
|
||||||
|
port: 0,
|
||||||
|
}, common.mustCall());
|
||||||
|
}
|
58
test/parallel/test-cluster-net-listen-ipv6only-none.js
Normal file
58
test/parallel/test-cluster-net-listen-ipv6only-none.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const cluster = require('cluster');
|
||||||
|
const net = require('net');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
|
// This test ensures that the `ipv6Only` option in `net.Server.listen()`
|
||||||
|
// works as expected when we use cluster with `SCHED_NONE` schedulingPolicy.
|
||||||
|
cluster.schedulingPolicy = cluster.SCHED_NONE;
|
||||||
|
const host = '::';
|
||||||
|
const WORKER_ACCOUNT = 3;
|
||||||
|
|
||||||
|
if (cluster.isMaster) {
|
||||||
|
const workers = new Map();
|
||||||
|
let address;
|
||||||
|
|
||||||
|
const countdown = new Countdown(WORKER_ACCOUNT, () => {
|
||||||
|
// Make sure the `ipv6Only` option works.
|
||||||
|
const server = net.createServer().listen({
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: address.port,
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
// Exit.
|
||||||
|
server.close();
|
||||||
|
workers.forEach((worker) => {
|
||||||
|
worker.disconnect();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
|
||||||
|
const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
|
||||||
|
assert.strictEqual(statusCode, 0);
|
||||||
|
})).on('listening', common.mustCall((workerAddress) => {
|
||||||
|
if (!address) {
|
||||||
|
address = workerAddress;
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(address.addressType, workerAddress.addressType);
|
||||||
|
assert.strictEqual(address.host, workerAddress.host);
|
||||||
|
assert.strictEqual(address.port, workerAddress.port);
|
||||||
|
}
|
||||||
|
countdown.dec();
|
||||||
|
}));
|
||||||
|
|
||||||
|
workers.set(i, worker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
net.createServer().listen({
|
||||||
|
host,
|
||||||
|
port: 0,
|
||||||
|
ipv6Only: true,
|
||||||
|
}, common.mustCall());
|
||||||
|
}
|
58
test/parallel/test-cluster-net-listen-ipv6only-rr.js
Normal file
58
test/parallel/test-cluster-net-listen-ipv6only-rr.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const cluster = require('cluster');
|
||||||
|
const net = require('net');
|
||||||
|
const Countdown = require('../common/countdown');
|
||||||
|
|
||||||
|
// This test ensures that the `ipv6Only` option in `net.Server.listen()`
|
||||||
|
// works as expected when we use cluster with `SCHED_RR` schedulingPolicy.
|
||||||
|
cluster.schedulingPolicy = cluster.SCHED_RR;
|
||||||
|
const host = '::';
|
||||||
|
const WORKER_ACCOUNT = 3;
|
||||||
|
|
||||||
|
if (cluster.isMaster) {
|
||||||
|
const workers = new Map();
|
||||||
|
let address;
|
||||||
|
|
||||||
|
const countdown = new Countdown(WORKER_ACCOUNT, () => {
|
||||||
|
// Make sure the `ipv6Only` option works.
|
||||||
|
const server = net.createServer().listen({
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: address.port,
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
// Exit.
|
||||||
|
server.close();
|
||||||
|
workers.forEach((worker) => {
|
||||||
|
worker.disconnect();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
|
||||||
|
const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
|
||||||
|
assert.strictEqual(statusCode, 0);
|
||||||
|
})).on('listening', common.mustCall((workerAddress) => {
|
||||||
|
if (!address) {
|
||||||
|
address = workerAddress;
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(address.addressType, workerAddress.addressType);
|
||||||
|
assert.strictEqual(address.host, workerAddress.host);
|
||||||
|
assert.strictEqual(address.port, workerAddress.port);
|
||||||
|
}
|
||||||
|
countdown.dec();
|
||||||
|
}));
|
||||||
|
|
||||||
|
workers.set(i, worker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
net.createServer().listen({
|
||||||
|
host,
|
||||||
|
port: 0,
|
||||||
|
ipv6Only: true,
|
||||||
|
}, common.mustCall());
|
||||||
|
}
|
33
test/parallel/test-dgram-ipv6only.js
Normal file
33
test/parallel/test-dgram-ipv6only.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
|
||||||
|
const dgram = require('dgram');
|
||||||
|
|
||||||
|
// This test ensures that dual-stack support is disabled when
|
||||||
|
// we specify the `ipv6Only` option in `dgram.createSocket()`.
|
||||||
|
const socket = dgram.createSocket({
|
||||||
|
type: 'udp6',
|
||||||
|
ipv6Only: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.bind({
|
||||||
|
port: 0,
|
||||||
|
address: '::',
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
const { port } = socket.address();
|
||||||
|
const client = dgram.createSocket('udp4');
|
||||||
|
|
||||||
|
// We can still bind to '0.0.0.0'.
|
||||||
|
client.bind({
|
||||||
|
port,
|
||||||
|
address: '0.0.0.0',
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
client.close();
|
||||||
|
socket.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
client.on('error', common.mustNotCall());
|
||||||
|
}));
|
30
test/parallel/test-net-listen-ipv6only.js
Normal file
30
test/parallel/test-net-listen-ipv6only.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasIPv6)
|
||||||
|
common.skip('no IPv6 support');
|
||||||
|
|
||||||
|
// This test ensures that dual-stack support is disabled when
|
||||||
|
// we specify the `ipv6Only` option in `net.Server.listen()`.
|
||||||
|
const assert = require('assert');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
const host = '::';
|
||||||
|
const server = net.createServer();
|
||||||
|
server.listen({
|
||||||
|
host,
|
||||||
|
port: 0,
|
||||||
|
ipv6Only: true,
|
||||||
|
}, common.mustCall(() => {
|
||||||
|
const { port } = server.address();
|
||||||
|
const socket = net.connect({
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('connect', common.mustNotCall());
|
||||||
|
socket.on('error', common.mustCall((err) => {
|
||||||
|
assert.strictEqual(err.code, 'ECONNREFUSED');
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
|
}));
|
Loading…
x
Reference in New Issue
Block a user