Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 28763f1

Browse files
committedMay 4, 2023
net: fix family autoselection timeout handling.
1 parent 76ae7be commit 28763f1

File tree

2 files changed

+80
-16
lines changed

2 files changed

+80
-16
lines changed
 

‎lib/net.js

+19-13
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ let autoSelectFamilyAttemptTimeoutDefault = 250;
134134

135135
const { clearTimeout, setTimeout } = require('timers');
136136
const { kTimeout } = require('internal/timers');
137-
const kTimeoutTriggered = Symbol('kTimeoutTriggered');
138137

139138
const DEFAULT_IPV4_ADDR = '0.0.0.0';
140139
const DEFAULT_IPV6_ADDR = '::';
@@ -1106,9 +1105,10 @@ function internalConnectMultiple(context, canceled) {
11061105

11071106
assert(self.connecting);
11081107

1109-
const handle = context.current === 0 ? self._handle : new TCP(TCPConstants.SOCKET);
1108+
const current = context.current++;
1109+
const handle = current === 0 ? self._handle : new TCP(TCPConstants.SOCKET);
11101110
const { localPort, port, flags } = context;
1111-
const { address, family: addressType } = context.addresses[context.current++];
1111+
const { address, family: addressType } = context.addresses[current];
11121112
let localAddress;
11131113
let err;
11141114

@@ -1135,7 +1135,7 @@ function internalConnectMultiple(context, canceled) {
11351135
debug('connect/multiple: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
11361136

11371137
const req = new TCPConnectWrap();
1138-
req.oncomplete = FunctionPrototypeBind(afterConnectMultiple, undefined, context);
1138+
req.oncomplete = FunctionPrototypeBind(afterConnectMultiple, undefined, context, current);
11391139
req.address = address;
11401140
req.port = port;
11411141
req.localAddress = localAddress;
@@ -1162,8 +1162,12 @@ function internalConnectMultiple(context, canceled) {
11621162
return;
11631163
}
11641164

1165-
// If the attempt has not returned an error, start the connection timer
1166-
context[kTimeout] = setTimeout(internalConnectMultipleTimeout, context.timeout, context, req);
1165+
if (current < context.addresses.length - 1) {
1166+
debug('connect/multiple: setting the attempt timeout to %d ms', context.timeout);
1167+
1168+
// If the attempt has not returned an error, start the connection timer
1169+
context[kTimeout] = setTimeout(internalConnectMultipleTimeout, context.timeout, context, req, handle);
1170+
}
11671171
}
11681172

11691173
Socket.prototype.connect = function(...args) {
@@ -1478,7 +1482,6 @@ function lookupAndConnectMultiple(
14781482
localPort,
14791483
timeout,
14801484
[kTimeout]: null,
1481-
[kTimeoutTriggered]: false,
14821485
errors: [],
14831486
};
14841487

@@ -1581,18 +1584,19 @@ function afterConnect(status, handle, req, readable, writable) {
15811584
}
15821585
}
15831586

1584-
function afterConnectMultiple(context, status, handle, req, readable, writable) {
1587+
function afterConnectMultiple(context, current, status, handle, req, readable, writable) {
1588+
// Make sure another connection is not spawned
1589+
clearTimeout(context[kTimeout]);
1590+
15851591
// One of the connection has completed and correctly dispatched but after timeout, ignore this one
1586-
if (context[kTimeoutTriggered]) {
1592+
if (status === 0 && current !== context.current - 1) {
15871593
debug('connect/multiple: ignoring successful but timedout connection to %s:%s', req.address, req.port);
15881594
handle.close();
15891595
return;
15901596
}
15911597

15921598
const self = context.socket;
15931599

1594-
// Make sure another connection is not spawned
1595-
clearTimeout(context[kTimeout]);
15961600

15971601
// Some error occurred, add to the list of exceptions
15981602
if (status !== 0) {
@@ -1633,8 +1637,10 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
16331637
afterConnect(status, handle, req, readable, writable);
16341638
}
16351639

1636-
function internalConnectMultipleTimeout(context, req) {
1637-
context[kTimeoutTriggered] = true;
1640+
function internalConnectMultipleTimeout(context, req, handle) {
1641+
debug('connect/multiple: connection to %s:%s timed out', req.address, req.port);
1642+
req.oncomplete = undefined;
1643+
handle.close();
16381644
internalConnectMultiple(context);
16391645
}
16401646

‎test/parallel/test-net-autoselectfamily.js

+61-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ function _lookup(resolver, hostname, options, cb) {
3636
});
3737
}
3838

39-
function createDnsServer(ipv6Addr, ipv4Addr, cb) {
39+
function createDnsServer(ipv6Addrs, ipv4Addrs, cb) {
40+
if (!Array.isArray(ipv6Addrs)) {
41+
ipv6Addrs = [ipv6Addrs];
42+
}
43+
44+
if (!Array.isArray(ipv4Addrs)) {
45+
ipv4Addrs = [ipv4Addrs];
46+
}
47+
4048
// Create a DNS server which replies with a AAAA and a A record for the same host
4149
const socket = dgram.createSocket('udp4');
4250

@@ -49,8 +57,8 @@ function createDnsServer(ipv6Addr, ipv4Addr, cb) {
4957
id: parsed.id,
5058
questions: parsed.questions,
5159
answers: [
52-
{ type: 'AAAA', address: ipv6Addr, ttl: 123, domain: 'example.org' },
53-
{ type: 'A', address: ipv4Addr, ttl: 123, domain: 'example.org' },
60+
...ipv6Addrs.map((address) => ({ type: 'AAAA', address, ttl: 123, domain: 'example.org' })),
61+
...ipv4Addrs.map((address) => ({ type: 'A', address, ttl: 123, domain: 'example.org' })),
5462
]
5563
}), port, address);
5664
}));
@@ -106,6 +114,56 @@ function createDnsServer(ipv6Addr, ipv4Addr, cb) {
106114
}));
107115
}
108116

117+
// Test that only the last successfull connection is establishedr
118+
{
119+
createDnsServer(
120+
'::1',
121+
['104.20.22.46', '104.20.23.46', '127.0.0.1'],
122+
common.mustCall(function({ dnsServer, lookup }) {
123+
const ipv4Server = createServer((socket) => {
124+
socket.on('data', common.mustCall(() => {
125+
socket.write('response-ipv4');
126+
socket.end();
127+
}));
128+
});
129+
130+
ipv4Server.listen(0, '127.0.0.1', common.mustCall(() => {
131+
const port = ipv4Server.address().port;
132+
133+
const connection = createConnection({
134+
host: 'example.org',
135+
port: port,
136+
lookup,
137+
autoSelectFamily: true,
138+
autoSelectFamilyAttemptTimeout,
139+
});
140+
141+
let response = '';
142+
connection.setEncoding('utf-8');
143+
144+
connection.on('ready', common.mustCall(() => {
145+
assert.deepStrictEqual(
146+
connection.autoSelectFamilyAttemptedAddresses,
147+
[`::1:${port}`, `104.20.22.46:${port}`, `104.20.23.46:${port}`, `127.0.0.1:${port}`]
148+
);
149+
}));
150+
151+
connection.on('data', (chunk) => {
152+
response += chunk;
153+
});
154+
155+
connection.on('end', common.mustCall(() => {
156+
assert.strictEqual(response, 'response-ipv4');
157+
ipv4Server.close();
158+
dnsServer.close();
159+
}));
160+
161+
connection.write('request');
162+
}));
163+
})
164+
);
165+
}
166+
109167
// Test that IPV4 is NOT reached if IPV6 is reachable
110168
if (common.hasIPv6) {
111169
createDnsServer('::1', '127.0.0.1', common.mustCall(function({ dnsServer, lookup }) {

0 commit comments

Comments
 (0)
Please sign in to comment.