mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
net: add connection attempt events
PR-URL: https://github.com/nodejs/node/pull/51045 Fixes: https://github.com/nodejs/node/issues/48763 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
This commit is contained in:
parent
abbdc3efaa
commit
2cb94240f9
|
|
@ -691,6 +691,47 @@ added: v0.1.90
|
||||||
Emitted when a socket connection is successfully established.
|
Emitted when a socket connection is successfully established.
|
||||||
See [`net.createConnection()`][].
|
See [`net.createConnection()`][].
|
||||||
|
|
||||||
|
### Event: `'connectionAttempt'`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `ip` {number} The IP which the socket is attempting to connect to.
|
||||||
|
* `port` {number} The port which the socket is attempting to connect to.
|
||||||
|
* `family` {number} The family of the IP. It can be `6` for IPv6 or `4` for IPv4.
|
||||||
|
|
||||||
|
Emitted when a new connection attempt is started. This may be emitted multiple times
|
||||||
|
if the family autoselection algorithm is enabled in [`socket.connect(options)`][].
|
||||||
|
|
||||||
|
### Event: `'connectionAttemptFailed'`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `ip` {number} The IP which the socket attempted to connect to.
|
||||||
|
* `port` {number} The port which the socket attempted to connect to.
|
||||||
|
* `family` {number} The family of the IP. It can be `6` for IPv6 or `4` for IPv4.
|
||||||
|
\* `error` {Error} The error associated with the failure.
|
||||||
|
|
||||||
|
Emitted when a connection attempt failed. This may be emitted multiple times
|
||||||
|
if the family autoselection algorithm is enabled in [`socket.connect(options)`][].
|
||||||
|
|
||||||
|
### Event: `'connectionAttemptTimeout'`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `ip` {number} The IP which the socket attempted to connect to.
|
||||||
|
* `port` {number} The port which the socket attempted to connect to.
|
||||||
|
* `family` {number} The family of the IP. It can be `6` for IPv6 or `4` for IPv4.
|
||||||
|
|
||||||
|
Emitted when a connection attempt timed out. This is only emitted (and may be
|
||||||
|
emitted multiple times) if the family autoselection algorithm is enabled
|
||||||
|
in [`socket.connect(options)`][].
|
||||||
|
|
||||||
### Event: `'data'`
|
### Event: `'data'`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
@ -963,8 +1004,7 @@ For TCP connections, available `options` are:
|
||||||
obtained IPv6 and IPv4 addresses, in sequence, until a connection is established.
|
obtained IPv6 and IPv4 addresses, in sequence, until a connection is established.
|
||||||
The first returned AAAA address is tried first, then the first returned A address,
|
The first returned AAAA address is tried first, then the first returned A address,
|
||||||
then the second returned AAAA address and so on.
|
then the second returned AAAA address and so on.
|
||||||
Each connection attempt is given the amount of time specified by the `autoSelectFamilyAttemptTimeout`
|
Each connection attempt (but the last one) is given the amount of time specified by the `autoSelectFamilyAttemptTimeout` option before timing out and trying the next address.
|
||||||
option before timing out and trying the next address.
|
|
||||||
Ignored if the `family` option is not `0` or if `localAddress` is set.
|
Ignored if the `family` option is not `0` or if `localAddress` is set.
|
||||||
Connection errors are not emitted if at least one connection succeeds.
|
Connection errors are not emitted if at least one connection succeeds.
|
||||||
If all connections attempts fails, a single `AggregateError` with all failed attempts is emitted.
|
If all connections attempts fails, a single `AggregateError` with all failed attempts is emitted.
|
||||||
|
|
|
||||||
31
lib/net.js
31
lib/net.js
|
|
@ -1058,6 +1058,7 @@ function internalConnect(
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('connect: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
|
debug('connect: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
|
||||||
|
self.emit('connectionAttempt', address, port, addressType);
|
||||||
|
|
||||||
if (addressType === 6 || addressType === 4) {
|
if (addressType === 6 || addressType === 4) {
|
||||||
const req = new TCPConnectWrap();
|
const req = new TCPConnectWrap();
|
||||||
|
|
@ -1066,6 +1067,7 @@ function internalConnect(
|
||||||
req.port = port;
|
req.port = port;
|
||||||
req.localAddress = localAddress;
|
req.localAddress = localAddress;
|
||||||
req.localPort = localPort;
|
req.localPort = localPort;
|
||||||
|
req.addressType = addressType;
|
||||||
|
|
||||||
if (addressType === 4)
|
if (addressType === 4)
|
||||||
err = self._handle.connect(req, address, port);
|
err = self._handle.connect(req, address, port);
|
||||||
|
|
@ -1149,6 +1151,7 @@ function internalConnectMultiple(context, canceled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('connect/multiple: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
|
debug('connect/multiple: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
|
||||||
|
self.emit('connectionAttempt', address, port, addressType);
|
||||||
|
|
||||||
const req = new TCPConnectWrap();
|
const req = new TCPConnectWrap();
|
||||||
req.oncomplete = FunctionPrototypeBind(afterConnectMultiple, undefined, context, current);
|
req.oncomplete = FunctionPrototypeBind(afterConnectMultiple, undefined, context, current);
|
||||||
|
|
@ -1156,6 +1159,7 @@ function internalConnectMultiple(context, canceled) {
|
||||||
req.port = port;
|
req.port = port;
|
||||||
req.localAddress = localAddress;
|
req.localAddress = localAddress;
|
||||||
req.localPort = localPort;
|
req.localPort = localPort;
|
||||||
|
req.addressType = addressType;
|
||||||
|
|
||||||
ArrayPrototypePush(self.autoSelectFamilyAttemptedAddresses, `${address}:${port}`);
|
ArrayPrototypePush(self.autoSelectFamilyAttemptedAddresses, `${address}:${port}`);
|
||||||
|
|
||||||
|
|
@ -1173,7 +1177,10 @@ function internalConnectMultiple(context, canceled) {
|
||||||
details = sockname.address + ':' + sockname.port;
|
details = sockname.address + ':' + sockname.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPrototypePush(context.errors, new ExceptionWithHostPort(err, 'connect', address, port, details));
|
const ex = new ExceptionWithHostPort(err, 'connect', address, port, details);
|
||||||
|
ArrayPrototypePush(context.errors, ex);
|
||||||
|
|
||||||
|
self.emit('connectionAttemptFailed', address, port, addressType, ex);
|
||||||
internalConnectMultiple(context);
|
internalConnectMultiple(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1601,6 +1608,8 @@ function afterConnect(status, handle, req, readable, writable) {
|
||||||
ex.localAddress = req.localAddress;
|
ex.localAddress = req.localAddress;
|
||||||
ex.localPort = req.localPort;
|
ex.localPort = req.localPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.emit('connectionAttemptFailed', req.address, req.port, req.addressType, ex);
|
||||||
self.destroy(ex);
|
self.destroy(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1661,10 +1670,16 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
|
||||||
|
|
||||||
// Some error occurred, add to the list of exceptions
|
// Some error occurred, add to the list of exceptions
|
||||||
if (status !== 0) {
|
if (status !== 0) {
|
||||||
ArrayPrototypePush(context.errors, createConnectionError(req, status));
|
const ex = createConnectionError(req, status);
|
||||||
|
ArrayPrototypePush(context.errors, ex);
|
||||||
|
|
||||||
|
self.emit('connectionAttemptFailed', req.address, req.port, req.addressType, ex);
|
||||||
|
|
||||||
|
// Try the next address, unless we were aborted
|
||||||
|
if (context.socket.connecting) {
|
||||||
|
internalConnectMultiple(context, status === UV_ECANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
// Try the next address
|
|
||||||
internalConnectMultiple(context, status === UV_ECANCELED);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1681,10 +1696,16 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
|
||||||
|
|
||||||
function internalConnectMultipleTimeout(context, req, handle) {
|
function internalConnectMultipleTimeout(context, req, handle) {
|
||||||
debug('connect/multiple: connection to %s:%s timed out', req.address, req.port);
|
debug('connect/multiple: connection to %s:%s timed out', req.address, req.port);
|
||||||
|
context.socket.emit('connectionAttemptTimeout', req.address, req.port, req.addressType);
|
||||||
|
|
||||||
req.oncomplete = undefined;
|
req.oncomplete = undefined;
|
||||||
ArrayPrototypePush(context.errors, createConnectionError(req, UV_ETIMEDOUT));
|
ArrayPrototypePush(context.errors, createConnectionError(req, UV_ETIMEDOUT));
|
||||||
handle.close();
|
handle.close();
|
||||||
internalConnectMultiple(context);
|
|
||||||
|
// Try the next address, unless we were aborted
|
||||||
|
if (context.socket.connecting) {
|
||||||
|
internalConnectMultiple(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addServerAbortSignalOption(self, options) {
|
function addServerAbortSignalOption(self, options) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const { isIP } = require('net');
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
A: 1,
|
A: 1,
|
||||||
|
|
@ -309,6 +310,25 @@ function errorLookupMock(code = mockedErrorCode, syscall = mockedSysCall) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createMockedLookup(...addresses) {
|
||||||
|
addresses = addresses.map((address) => ({ address: address, family: isIP(address) }));
|
||||||
|
|
||||||
|
// Create a DNS server which replies with a AAAA and a A record for the same host
|
||||||
|
return function lookup(hostname, options, cb) {
|
||||||
|
if (options.all === true) {
|
||||||
|
process.nextTick(() => {
|
||||||
|
cb(null, addresses);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.nextTick(() => {
|
||||||
|
cb(null, addresses[0].address, addresses[0].family);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
types,
|
types,
|
||||||
classes,
|
classes,
|
||||||
|
|
@ -317,4 +337,5 @@ module.exports = {
|
||||||
errorLookupMock,
|
errorLookupMock,
|
||||||
mockedErrorCode,
|
mockedErrorCode,
|
||||||
mockedSysCall,
|
mockedSysCall,
|
||||||
|
createMockedLookup,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
128
test/internet/test-net-autoselectfamily-events-failure.js
Normal file
128
test/internet/test-net-autoselectfamily-events-failure.js
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const { addresses: { INET6_IP, INET4_IP } } = require('../common/internet');
|
||||||
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { createConnection } = require('net');
|
||||||
|
|
||||||
|
//
|
||||||
|
// When testing this is MacOS, remember that the last connection will have no timeout at Node.js
|
||||||
|
// level but only at operating system one.
|
||||||
|
//
|
||||||
|
// The default for MacOS is 75 seconds. It can be changed by doing:
|
||||||
|
//
|
||||||
|
// sudo sysctl net.inet.tcp.keepinit=VALUE_IN_MS
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test that all failure events are emitted when trying a single IP (which means autoselectfamily is bypassed)
|
||||||
|
{
|
||||||
|
const pass = common.mustCallAtLeast(1);
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: 10,
|
||||||
|
lookup: createMockedLookup(INET4_IP),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
autoSelectFamilyAttemptTimeout: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('connectionAttempt', (address, port, family) => {
|
||||||
|
assert.strictEqual(address, INET4_IP);
|
||||||
|
assert.strictEqual(port, 10);
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('connectionAttemptFailed', (address, port, family, error) => {
|
||||||
|
assert.strictEqual(address, INET4_IP);
|
||||||
|
assert.strictEqual(port, 10);
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
error.code.match(/ECONNREFUSED|ENETUNREACH|EHOSTUNREACH|ETIMEDOUT/),
|
||||||
|
`Received unexpected error code ${error.code}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('ready', () => {
|
||||||
|
pass();
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('error', () => {
|
||||||
|
pass();
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
pass();
|
||||||
|
process.exit(0);
|
||||||
|
}, 5000).unref();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that all events are emitted when trying multiple IPs
|
||||||
|
{
|
||||||
|
const pass = common.mustCallAtLeast(1);
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: 10,
|
||||||
|
lookup: createMockedLookup(INET6_IP, INET4_IP),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
autoSelectFamilyAttemptTimeout: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const addresses = [
|
||||||
|
{ address: INET6_IP, port: 10, family: 6 },
|
||||||
|
{ address: INET6_IP, port: 10, family: 6 },
|
||||||
|
{ address: INET4_IP, port: 10, family: 4 },
|
||||||
|
{ address: INET4_IP, port: 10, family: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
connection.on('connectionAttempt', (address, port, family) => {
|
||||||
|
const expected = addresses.shift();
|
||||||
|
|
||||||
|
assert.strictEqual(address, expected.address);
|
||||||
|
assert.strictEqual(port, expected.port);
|
||||||
|
assert.strictEqual(family, expected.family);
|
||||||
|
|
||||||
|
pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('connectionAttemptFailed', (address, port, family, error) => {
|
||||||
|
const expected = addresses.shift();
|
||||||
|
|
||||||
|
assert.strictEqual(address, expected.address);
|
||||||
|
assert.strictEqual(port, expected.port);
|
||||||
|
assert.strictEqual(family, expected.family);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
error.code.match(/ECONNREFUSED|ENETUNREACH|EHOSTUNREACH|ETIMEDOUT/),
|
||||||
|
`Received unexpected error code ${error.code}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('ready', () => {
|
||||||
|
pass();
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('error', () => {
|
||||||
|
pass();
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
pass();
|
||||||
|
process.exit(0);
|
||||||
|
}, 5000).unref();
|
||||||
|
|
||||||
|
}
|
||||||
54
test/internet/test-net-autoselectfamily-events-timeout.js
Normal file
54
test/internet/test-net-autoselectfamily-events-timeout.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const { addresses: { INET6_IP, INET4_IP } } = require('../common/internet');
|
||||||
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { createConnection } = require('net');
|
||||||
|
|
||||||
|
//
|
||||||
|
// When testing this is MacOS, remember that the last connection will have no timeout at Node.js
|
||||||
|
// level but only at operating system one.
|
||||||
|
//
|
||||||
|
// The default for MacOS is 75 seconds. It can be changed by doing:
|
||||||
|
//
|
||||||
|
// sudo sysctl net.inet.tcp.keepinit=VALUE_IN_MS
|
||||||
|
//
|
||||||
|
// Depending on the network, it might be impossible to obtain a timeout in 10ms,
|
||||||
|
// which is the minimum value allowed by network family autoselection.
|
||||||
|
// At the time of writing (Dec 2023), the network times out on local machine and in the Node CI,
|
||||||
|
// but it does not on GitHub actions runner.
|
||||||
|
// Therefore, after five seconds we just consider this test as passed.
|
||||||
|
|
||||||
|
// Test that if a connection attempt times out and the socket is destroyed before the
|
||||||
|
// next attempt starts then the process does not crash
|
||||||
|
{
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: 443,
|
||||||
|
lookup: createMockedLookup(INET4_IP, INET6_IP),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
autoSelectFamilyAttemptTimeout: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pass = common.mustCall();
|
||||||
|
|
||||||
|
connection.on('connectionAttemptTimeout', (address, port, family) => {
|
||||||
|
assert.strictEqual(address, INET4_IP);
|
||||||
|
assert.strictEqual(port, 443);
|
||||||
|
assert.strictEqual(family, 4);
|
||||||
|
connection.destroy();
|
||||||
|
pass();
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('ready', () => {
|
||||||
|
pass();
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
pass();
|
||||||
|
process.exit(0);
|
||||||
|
}, 5000).unref();
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,15 @@ const { addresses } = require('../common/internet');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { connect } = require('net');
|
const { connect } = require('net');
|
||||||
|
|
||||||
|
//
|
||||||
|
// When testing this is MacOS, remember that the last connection will have no timeout at Node.js
|
||||||
|
// level but only at operating system one.
|
||||||
|
//
|
||||||
|
// The default for MacOS is 75 seconds. It can be changed by doing:
|
||||||
|
//
|
||||||
|
// sudo sysctl net.inet.tcp.keepinit=VALUE_IN_MS
|
||||||
|
//
|
||||||
|
|
||||||
// Test that when all errors are returned when no connections succeeded and that the close event is emitted
|
// Test that when all errors are returned when no connections succeeded and that the close event is emitted
|
||||||
{
|
{
|
||||||
const connection = connect({
|
const connection = connect({
|
||||||
|
|
|
||||||
|
|
@ -3,100 +3,46 @@
|
||||||
// Flags: --no-network-family-autoselection
|
// Flags: --no-network-family-autoselection
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const dgram = require('dgram');
|
|
||||||
const { Resolver } = require('dns');
|
|
||||||
const { createConnection, createServer } = require('net');
|
const { createConnection, createServer } = require('net');
|
||||||
|
|
||||||
// Test that happy eyeballs algorithm can be enable from command line.
|
|
||||||
|
|
||||||
function _lookup(resolver, hostname, options, cb) {
|
|
||||||
resolver.resolve(hostname, 'ANY', (err, replies) => {
|
|
||||||
assert.notStrictEqual(options.family, 4);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hosts = replies
|
|
||||||
.map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
|
|
||||||
.sort((a, b) => b.family - a.family);
|
|
||||||
|
|
||||||
if (options.all === true) {
|
|
||||||
return cb(null, hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, hosts[0].address, hosts[0].family);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDnsServer(ipv6Addr, ipv4Addr, cb) {
|
|
||||||
// Create a DNS server which replies with a AAAA and a A record for the same host
|
|
||||||
const socket = dgram.createSocket('udp4');
|
|
||||||
|
|
||||||
socket.on('message', common.mustCall((msg, { address, port }) => {
|
|
||||||
const parsed = parseDNSPacket(msg);
|
|
||||||
const domain = parsed.questions[0].domain;
|
|
||||||
assert.strictEqual(domain, 'example.org');
|
|
||||||
|
|
||||||
socket.send(writeDNSPacket({
|
|
||||||
id: parsed.id,
|
|
||||||
questions: parsed.questions,
|
|
||||||
answers: [
|
|
||||||
{ type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
{ type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
]
|
|
||||||
}), port, address);
|
|
||||||
}));
|
|
||||||
|
|
||||||
socket.bind(0, () => {
|
|
||||||
const resolver = new Resolver();
|
|
||||||
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
|
|
||||||
|
|
||||||
cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that IPV4 is NOT reached if IPV6 is not reachable and the option has been disabled via command line
|
// Test that IPV4 is NOT reached if IPV6 is not reachable and the option has been disabled via command line
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port,
|
||||||
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
});
|
});
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
connection.on('ready', common.mustNotCall());
|
||||||
const port = ipv4Server.address().port;
|
connection.on('error', common.mustCall((error) => {
|
||||||
|
assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
|
||||||
|
|
||||||
const connection = createConnection({
|
if (common.hasIPv6) {
|
||||||
host: 'example.org',
|
assert.strictEqual(error.code, 'ECONNREFUSED');
|
||||||
port,
|
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
||||||
lookup,
|
} else if (error.code === 'EAFNOSUPPORT') {
|
||||||
});
|
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
||||||
|
} else if (error.code === 'EUNATCH') {
|
||||||
|
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
||||||
|
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
||||||
|
}
|
||||||
|
|
||||||
connection.on('ready', common.mustNotCall());
|
ipv4Server.close();
|
||||||
connection.on('error', common.mustCall((error) => {
|
|
||||||
assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
|
|
||||||
|
|
||||||
if (common.hasIPv6) {
|
|
||||||
assert.strictEqual(error.code, 'ECONNREFUSED');
|
|
||||||
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
|
||||||
} else if (error.code === 'EAFNOSUPPORT') {
|
|
||||||
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
|
||||||
} else if (error.code === 'EUNATCH') {
|
|
||||||
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
|
||||||
} else {
|
|
||||||
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
|
||||||
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,140 +1,83 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const dgram = require('dgram');
|
|
||||||
const { Resolver } = require('dns');
|
|
||||||
const { createConnection, createServer, setDefaultAutoSelectFamily } = require('net');
|
const { createConnection, createServer, setDefaultAutoSelectFamily } = require('net');
|
||||||
|
|
||||||
// Test that the default for happy eyeballs algorithm is properly respected.
|
|
||||||
|
|
||||||
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
|
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
|
||||||
|
|
||||||
function _lookup(resolver, hostname, options, cb) {
|
|
||||||
resolver.resolve(hostname, 'ANY', (err, replies) => {
|
|
||||||
assert.notStrictEqual(options.family, 4);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hosts = replies
|
|
||||||
.map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
|
|
||||||
.sort((a, b) => b.family - a.family);
|
|
||||||
|
|
||||||
if (options.all === true) {
|
|
||||||
return cb(null, hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, hosts[0].address, hosts[0].family);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDnsServer(ipv6Addr, ipv4Addr, cb) {
|
|
||||||
// Create a DNS server which replies with a AAAA and a A record for the same host
|
|
||||||
const socket = dgram.createSocket('udp4');
|
|
||||||
|
|
||||||
socket.on('message', common.mustCall((msg, { address, port }) => {
|
|
||||||
const parsed = parseDNSPacket(msg);
|
|
||||||
const domain = parsed.questions[0].domain;
|
|
||||||
assert.strictEqual(domain, 'example.org');
|
|
||||||
|
|
||||||
socket.send(writeDNSPacket({
|
|
||||||
id: parsed.id,
|
|
||||||
questions: parsed.questions,
|
|
||||||
answers: [
|
|
||||||
{ type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
{ type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
]
|
|
||||||
}), port, address);
|
|
||||||
}));
|
|
||||||
|
|
||||||
socket.bind(0, () => {
|
|
||||||
const resolver = new Resolver();
|
|
||||||
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
|
|
||||||
|
|
||||||
cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that IPV4 is reached by default if IPV6 is not reachable and the default is enabled
|
// Test that IPV4 is reached by default if IPV6 is not reachable and the default is enabled
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
setDefaultAutoSelectFamily(true);
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: ipv4Server.address().port,
|
||||||
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
|
autoSelectFamilyAttemptTimeout,
|
||||||
});
|
});
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
let response = '';
|
||||||
setDefaultAutoSelectFamily(true);
|
connection.setEncoding('utf-8');
|
||||||
|
|
||||||
const connection = createConnection({
|
connection.on('data', (chunk) => {
|
||||||
host: 'example.org',
|
response += chunk;
|
||||||
port: ipv4Server.address().port,
|
});
|
||||||
lookup,
|
|
||||||
autoSelectFamilyAttemptTimeout,
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = '';
|
connection.on('end', common.mustCall(() => {
|
||||||
connection.setEncoding('utf-8');
|
assert.strictEqual(response, 'response-ipv4');
|
||||||
|
ipv4Server.close();
|
||||||
connection.on('data', (chunk) => {
|
|
||||||
response += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('end', common.mustCall(() => {
|
|
||||||
assert.strictEqual(response, 'response-ipv4');
|
|
||||||
ipv4Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.write('request');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
connection.write('request');
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that IPV4 is not reached by default if IPV6 is not reachable and the default is disabled
|
// Test that IPV4 is not reached by default if IPV6 is not reachable and the default is disabled
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
setDefaultAutoSelectFamily(false);
|
||||||
|
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port,
|
||||||
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
});
|
});
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
connection.on('ready', common.mustNotCall());
|
||||||
setDefaultAutoSelectFamily(false);
|
connection.on('error', common.mustCall((error) => {
|
||||||
|
if (common.hasIPv6) {
|
||||||
|
assert.strictEqual(error.code, 'ECONNREFUSED');
|
||||||
|
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
||||||
|
} else if (error.code === 'EAFNOSUPPORT') {
|
||||||
|
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
||||||
|
} else if (error.code === 'EUNATCH') {
|
||||||
|
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
||||||
|
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
||||||
|
}
|
||||||
|
|
||||||
const port = ipv4Server.address().port;
|
ipv4Server.close();
|
||||||
|
|
||||||
const connection = createConnection({
|
|
||||||
host: 'example.org',
|
|
||||||
port,
|
|
||||||
lookup,
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('ready', common.mustNotCall());
|
|
||||||
connection.on('error', common.mustCall((error) => {
|
|
||||||
if (common.hasIPv6) {
|
|
||||||
assert.strictEqual(error.code, 'ECONNREFUSED');
|
|
||||||
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
|
||||||
} else if (error.code === 'EAFNOSUPPORT') {
|
|
||||||
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
|
||||||
} else if (error.code === 'EUNATCH') {
|
|
||||||
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
|
||||||
} else {
|
|
||||||
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
|
||||||
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,105 +1,52 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const dgram = require('dgram');
|
|
||||||
const { Resolver } = require('dns');
|
|
||||||
const { createConnection, createServer } = require('net');
|
const { createConnection, createServer } = require('net');
|
||||||
|
|
||||||
// Test that happy eyeballs algorithm is properly implemented when a A record is returned first.
|
// Test that happy eyeballs algorithm is properly implemented when a A record is returned first.
|
||||||
|
|
||||||
function _lookup(resolver, hostname, options, cb) {
|
|
||||||
resolver.resolve(hostname, 'ANY', (err, replies) => {
|
|
||||||
assert.notStrictEqual(options.family, 4);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hosts = replies
|
|
||||||
.map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }));
|
|
||||||
|
|
||||||
if (options.all === true) {
|
|
||||||
return cb(null, hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, hosts[0].address, hosts[0].family);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDnsServer(ipv6Addr, ipv4Addr, cb) {
|
|
||||||
// Create a DNS server which replies with a AAAA and a A record for the same host
|
|
||||||
const socket = dgram.createSocket('udp4');
|
|
||||||
|
|
||||||
socket.on('message', common.mustCall((msg, { address, port }) => {
|
|
||||||
const parsed = parseDNSPacket(msg);
|
|
||||||
const domain = parsed.questions[0].domain;
|
|
||||||
assert.strictEqual(domain, 'example.org');
|
|
||||||
|
|
||||||
socket.send(writeDNSPacket({
|
|
||||||
id: parsed.id,
|
|
||||||
questions: parsed.questions,
|
|
||||||
answers: [
|
|
||||||
{ type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
{ type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
|
|
||||||
]
|
|
||||||
}), port, address);
|
|
||||||
}));
|
|
||||||
|
|
||||||
socket.bind(0, () => {
|
|
||||||
const resolver = new Resolver();
|
|
||||||
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
|
|
||||||
|
|
||||||
cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that IPV6 is NOT reached if IPV4 is sorted first
|
|
||||||
if (common.hasIPv6) {
|
if (common.hasIPv6) {
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const ipv6Server = createServer((socket) => {
|
||||||
|
socket.on('data', common.mustNotCall(() => {
|
||||||
|
socket.write('response-ipv6');
|
||||||
|
socket.end();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
ipv6Server.listen(port, '::1', common.mustCall(() => {
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port,
|
||||||
|
lookup: createMockedLookup('127.0.0.1', '::1'),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = '';
|
||||||
|
connection.setEncoding('utf-8');
|
||||||
|
|
||||||
|
connection.on('data', (chunk) => {
|
||||||
|
response += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('end', common.mustCall(() => {
|
||||||
|
assert.strictEqual(response, 'response-ipv4');
|
||||||
|
ipv4Server.close();
|
||||||
|
ipv6Server.close();
|
||||||
}));
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
const ipv6Server = createServer((socket) => {
|
connection.write('request');
|
||||||
socket.on('data', common.mustNotCall(() => {
|
|
||||||
socket.write('response-ipv6');
|
|
||||||
socket.end();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
|
||||||
const port = ipv4Server.address().port;
|
|
||||||
|
|
||||||
ipv6Server.listen(port, '::1', common.mustCall(() => {
|
|
||||||
const connection = createConnection({
|
|
||||||
host: 'example.org',
|
|
||||||
port,
|
|
||||||
lookup,
|
|
||||||
autoSelectFamily: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = '';
|
|
||||||
connection.setEncoding('utf-8');
|
|
||||||
|
|
||||||
connection.on('data', (chunk) => {
|
|
||||||
response += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('end', common.mustCall(() => {
|
|
||||||
assert.strictEqual(response, 'response-ipv4');
|
|
||||||
ipv4Server.close();
|
|
||||||
ipv6Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.write('request');
|
|
||||||
}));
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,136 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const { parseDNSPacket, writeDNSPacket } = require('../common/dns');
|
const { createMockedLookup } = require('../common/dns');
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const dgram = require('dgram');
|
|
||||||
const { Resolver } = require('dns');
|
|
||||||
const { createConnection, createServer } = require('net');
|
const { createConnection, createServer } = require('net');
|
||||||
|
|
||||||
// Test that happy eyeballs algorithm is properly implemented.
|
// Test that happy eyeballs algorithm is properly implemented.
|
||||||
|
|
||||||
// Purposely not using setDefaultAutoSelectFamilyAttemptTimeout here to test the
|
// Purposely not using setDefaultAutoSelectFamilyAttemptTimeout here to test the
|
||||||
// parameter is correctly used in options.
|
// parameter is correctly used in options.
|
||||||
//
|
|
||||||
// Some of the machines in the CI need more time to establish connection
|
// Some of the machines in the CI need more time to establish connection
|
||||||
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
|
const autoSelectFamilyAttemptTimeout = common.defaultAutoSelectFamilyAttemptTimeout;
|
||||||
|
|
||||||
function _lookup(resolver, hostname, options, cb) {
|
|
||||||
resolver.resolve(hostname, 'ANY', (err, replies) => {
|
|
||||||
assert.notStrictEqual(options.family, 4);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hosts = replies
|
|
||||||
.map((r) => ({ address: r.address, family: r.type === 'AAAA' ? 6 : 4 }))
|
|
||||||
.sort((a, b) => b.family - a.family);
|
|
||||||
|
|
||||||
if (options.all === true) {
|
|
||||||
return cb(null, hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cb(null, hosts[0].address, hosts[0].family);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
|
|
||||||
if (!Array.isArray(ipv6Addrs)) {
|
|
||||||
ipv6Addrs = [ipv6Addrs];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(ipv4Addrs)) {
|
|
||||||
ipv4Addrs = [ipv4Addrs];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a DNS server which replies with a AAAA and a A record for the same host
|
|
||||||
const socket = dgram.createSocket('udp4');
|
|
||||||
|
|
||||||
socket.on('message', common.mustCall((msg, { address, port }) => {
|
|
||||||
const parsed = parseDNSPacket(msg);
|
|
||||||
const domain = parsed.questions[0].domain;
|
|
||||||
assert.strictEqual(domain, 'example.org');
|
|
||||||
|
|
||||||
socket.send(writeDNSPacket({
|
|
||||||
id: parsed.id,
|
|
||||||
questions: parsed.questions,
|
|
||||||
answers: [
|
|
||||||
...ipv6Addrs.map((address) => ({ type: 'AAAA', address, ttl: 123, domain: 'example.org' })),
|
|
||||||
...ipv4Addrs.map((address) => ({ type: 'A', address, ttl: 123, domain: 'example.org' })),
|
|
||||||
]
|
|
||||||
}), port, address);
|
|
||||||
}));
|
|
||||||
|
|
||||||
socket.bind(0, () => {
|
|
||||||
const resolver = new Resolver();
|
|
||||||
resolver.setServers([`127.0.0.1:${socket.address().port}`]);
|
|
||||||
|
|
||||||
cb({ dnsServer: socket, lookup: _lookup.bind(null, resolver) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that IPV4 is reached if IPV6 is not reachable
|
// Test that IPV4 is reached if IPV6 is not reachable
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: port,
|
||||||
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
autoSelectFamilyAttemptTimeout,
|
||||||
});
|
});
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
let response = '';
|
||||||
const port = ipv4Server.address().port;
|
connection.setEncoding('utf-8');
|
||||||
|
|
||||||
|
connection.on('ready', common.mustCall(() => {
|
||||||
|
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`, `127.0.0.1:${port}`]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
connection.on('data', (chunk) => {
|
||||||
|
response += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('end', common.mustCall(() => {
|
||||||
|
assert.strictEqual(response, 'response-ipv4');
|
||||||
|
ipv4Server.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
connection.write('request');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that only the last successful connection is established.
|
||||||
|
{
|
||||||
|
const ipv4Server = createServer((socket) => {
|
||||||
|
socket.on('data', common.mustCall(() => {
|
||||||
|
socket.write('response-ipv4');
|
||||||
|
socket.end();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port: port,
|
||||||
|
lookup: createMockedLookup(
|
||||||
|
'2606:4700::6810:85e5', '2606:4700::6810:84e5', '::1',
|
||||||
|
'104.20.22.46', '104.20.23.46', '127.0.0.1',
|
||||||
|
),
|
||||||
|
autoSelectFamily: true,
|
||||||
|
autoSelectFamilyAttemptTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = '';
|
||||||
|
connection.setEncoding('utf-8');
|
||||||
|
|
||||||
|
connection.on('ready', common.mustCall(() => {
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
connection.autoSelectFamilyAttemptedAddresses,
|
||||||
|
[
|
||||||
|
`2606:4700::6810:85e5:${port}`,
|
||||||
|
`104.20.22.46:${port}`,
|
||||||
|
`2606:4700::6810:84e5:${port}`,
|
||||||
|
`104.20.23.46:${port}`,
|
||||||
|
`::1:${port}`,
|
||||||
|
`127.0.0.1:${port}`,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
connection.on('data', (chunk) => {
|
||||||
|
response += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on('end', common.mustCall(() => {
|
||||||
|
assert.strictEqual(response, 'response-ipv4');
|
||||||
|
ipv4Server.close();
|
||||||
|
}));
|
||||||
|
|
||||||
|
connection.write('request');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that IPV4 is NOT reached if IPV6 is reachable
|
||||||
|
if (common.hasIPv6) {
|
||||||
|
const ipv4Server = createServer((socket) => {
|
||||||
|
socket.on('data', common.mustNotCall(() => {
|
||||||
|
socket.write('response-ipv4');
|
||||||
|
socket.end();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const ipv6Server = createServer((socket) => {
|
||||||
|
socket.on('data', common.mustCall(() => {
|
||||||
|
socket.write('response-ipv6');
|
||||||
|
socket.end();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
ipv6Server.listen(port, '::1', common.mustCall(() => {
|
||||||
const connection = createConnection({
|
const connection = createConnection({
|
||||||
host: 'example.org',
|
host: 'example.org',
|
||||||
port: port,
|
port,
|
||||||
lookup,
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
autoSelectFamily: true,
|
autoSelectFamily: true,
|
||||||
autoSelectFamilyAttemptTimeout,
|
autoSelectFamilyAttemptTimeout,
|
||||||
});
|
});
|
||||||
|
|
@ -96,7 +139,7 @@ function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
|
||||||
connection.setEncoding('utf-8');
|
connection.setEncoding('utf-8');
|
||||||
|
|
||||||
connection.on('ready', common.mustCall(() => {
|
connection.on('ready', common.mustCall(() => {
|
||||||
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`, `127.0.0.1:${port}`]);
|
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
connection.on('data', (chunk) => {
|
connection.on('data', (chunk) => {
|
||||||
|
|
@ -104,9 +147,9 @@ function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('end', common.mustCall(() => {
|
connection.on('end', common.mustCall(() => {
|
||||||
assert.strictEqual(response, 'response-ipv4');
|
assert.strictEqual(response, 'response-ipv6');
|
||||||
ipv4Server.close();
|
ipv4Server.close();
|
||||||
dnsServer.close();
|
ipv6Server.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
connection.write('request');
|
connection.write('request');
|
||||||
|
|
@ -114,184 +157,67 @@ function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that only the last successful connection is established.
|
|
||||||
{
|
|
||||||
createDnsServer(
|
|
||||||
['2606:4700::6810:85e5', '2606:4700::6810:84e5', '::1'],
|
|
||||||
['104.20.22.46', '104.20.23.46', '127.0.0.1'],
|
|
||||||
common.mustCall(function({ dnsServer, lookup }) {
|
|
||||||
const ipv4Server = createServer((socket) => {
|
|
||||||
socket.on('data', common.mustCall(() => {
|
|
||||||
socket.write('response-ipv4');
|
|
||||||
socket.end();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
|
||||||
const port = ipv4Server.address().port;
|
|
||||||
|
|
||||||
const connection = createConnection({
|
|
||||||
host: 'example.org',
|
|
||||||
port: port,
|
|
||||||
lookup,
|
|
||||||
autoSelectFamily: true,
|
|
||||||
autoSelectFamilyAttemptTimeout,
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = '';
|
|
||||||
connection.setEncoding('utf-8');
|
|
||||||
|
|
||||||
connection.on('ready', common.mustCall(() => {
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
connection.autoSelectFamilyAttemptedAddresses,
|
|
||||||
[
|
|
||||||
`2606:4700::6810:85e5:${port}`,
|
|
||||||
`104.20.22.46:${port}`,
|
|
||||||
`2606:4700::6810:84e5:${port}`,
|
|
||||||
`104.20.23.46:${port}`,
|
|
||||||
`::1:${port}`,
|
|
||||||
`127.0.0.1:${port}`,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.on('data', (chunk) => {
|
|
||||||
response += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('end', common.mustCall(() => {
|
|
||||||
assert.strictEqual(response, 'response-ipv4');
|
|
||||||
ipv4Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.write('request');
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that IPV4 is NOT reached if IPV6 is reachable
|
|
||||||
if (common.hasIPv6) {
|
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
|
||||||
const ipv4Server = createServer((socket) => {
|
|
||||||
socket.on('data', common.mustNotCall(() => {
|
|
||||||
socket.write('response-ipv4');
|
|
||||||
socket.end();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
const ipv6Server = createServer((socket) => {
|
|
||||||
socket.on('data', common.mustCall(() => {
|
|
||||||
socket.write('response-ipv6');
|
|
||||||
socket.end();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
|
||||||
const port = ipv4Server.address().port;
|
|
||||||
|
|
||||||
ipv6Server.listen(port, '::1', common.mustCall(() => {
|
|
||||||
const connection = createConnection({
|
|
||||||
host: 'example.org',
|
|
||||||
port,
|
|
||||||
lookup,
|
|
||||||
autoSelectFamily: true,
|
|
||||||
autoSelectFamilyAttemptTimeout,
|
|
||||||
});
|
|
||||||
|
|
||||||
let response = '';
|
|
||||||
connection.setEncoding('utf-8');
|
|
||||||
|
|
||||||
connection.on('ready', common.mustCall(() => {
|
|
||||||
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, [`::1:${port}`]);
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.on('data', (chunk) => {
|
|
||||||
response += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('end', common.mustCall(() => {
|
|
||||||
assert.strictEqual(response, 'response-ipv6');
|
|
||||||
ipv4Server.close();
|
|
||||||
ipv6Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
connection.write('request');
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that when all errors are returned when no connections succeeded
|
// Test that when all errors are returned when no connections succeeded
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const connection = createConnection({
|
||||||
const connection = createConnection({
|
host: 'example.org',
|
||||||
host: 'example.org',
|
port: 10,
|
||||||
port: 10,
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
lookup,
|
autoSelectFamily: true,
|
||||||
autoSelectFamily: true,
|
autoSelectFamilyAttemptTimeout,
|
||||||
autoSelectFamilyAttemptTimeout,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
connection.on('ready', common.mustNotCall());
|
connection.on('ready', common.mustNotCall());
|
||||||
connection.on('error', common.mustCall((error) => {
|
connection.on('error', common.mustCall((error) => {
|
||||||
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, ['::1:10', '127.0.0.1:10']);
|
assert.deepStrictEqual(connection.autoSelectFamilyAttemptedAddresses, ['::1:10', '127.0.0.1:10']);
|
||||||
assert.strictEqual(error.constructor.name, 'AggregateError');
|
assert.strictEqual(error.constructor.name, 'AggregateError');
|
||||||
assert.strictEqual(error.errors.length, 2);
|
assert.strictEqual(error.errors.length, 2);
|
||||||
|
|
||||||
const errors = error.errors.map((e) => e.message);
|
const errors = error.errors.map((e) => e.message);
|
||||||
assert.ok(errors.includes('connect ECONNREFUSED 127.0.0.1:10'));
|
assert.ok(errors.includes('connect ECONNREFUSED 127.0.0.1:10'));
|
||||||
|
|
||||||
if (common.hasIPv6) {
|
if (common.hasIPv6) {
|
||||||
assert.ok(errors.includes('connect ECONNREFUSED ::1:10'));
|
assert.ok(errors.includes('connect ECONNREFUSED ::1:10'));
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the option can be disabled
|
// Test that the option can be disabled
|
||||||
{
|
{
|
||||||
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {
|
const ipv4Server = createServer((socket) => {
|
||||||
const ipv4Server = createServer((socket) => {
|
socket.on('data', common.mustCall(() => {
|
||||||
socket.on('data', common.mustCall(() => {
|
socket.write('response-ipv4');
|
||||||
socket.write('response-ipv4');
|
socket.end();
|
||||||
socket.end();
|
}));
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
||||||
|
const port = ipv4Server.address().port;
|
||||||
|
|
||||||
|
const connection = createConnection({
|
||||||
|
host: 'example.org',
|
||||||
|
port,
|
||||||
|
lookup: createMockedLookup('::1', '127.0.0.1'),
|
||||||
|
autoSelectFamily: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
|
connection.on('ready', common.mustNotCall());
|
||||||
const port = ipv4Server.address().port;
|
connection.on('error', common.mustCall((error) => {
|
||||||
|
assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
|
||||||
|
|
||||||
const connection = createConnection({
|
if (common.hasIPv6) {
|
||||||
host: 'example.org',
|
assert.strictEqual(error.code, 'ECONNREFUSED');
|
||||||
port,
|
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
||||||
lookup,
|
} else if (error.code === 'EAFNOSUPPORT') {
|
||||||
autoSelectFamily: false,
|
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
||||||
});
|
} else if (error.code === 'EUNATCH') {
|
||||||
|
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
||||||
|
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
||||||
|
}
|
||||||
|
|
||||||
connection.on('ready', common.mustNotCall());
|
ipv4Server.close();
|
||||||
connection.on('error', common.mustCall((error) => {
|
|
||||||
assert.strictEqual(connection.autoSelectFamilyAttemptedAddresses, undefined);
|
|
||||||
|
|
||||||
if (common.hasIPv6) {
|
|
||||||
assert.strictEqual(error.code, 'ECONNREFUSED');
|
|
||||||
assert.strictEqual(error.message, `connect ECONNREFUSED ::1:${port}`);
|
|
||||||
} else if (error.code === 'EAFNOSUPPORT') {
|
|
||||||
assert.strictEqual(error.message, `connect EAFNOSUPPORT ::1:${port} - Local (undefined:undefined)`);
|
|
||||||
} else if (error.code === 'EUNATCH') {
|
|
||||||
assert.strictEqual(error.message, `connect EUNATCH ::1:${port} - Local (:::0)`);
|
|
||||||
} else {
|
|
||||||
assert.strictEqual(error.code, 'EADDRNOTAVAIL');
|
|
||||||
assert.strictEqual(error.message, `connect EADDRNOTAVAIL ::1:${port} - Local (:::0)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4Server.close();
|
|
||||||
dnsServer.close();
|
|
||||||
}));
|
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user