Skip to content

Commit c8b5f22

Browse files
committed
net: rework autoSelectFamily implementation
1 parent ecd385e commit c8b5f22

10 files changed

+76
-52
lines changed

lib/_tls_wrap.js

-11
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ const EE = require('events');
5454
const net = require('net');
5555
const tls = require('tls');
5656
const common = require('_tls_common');
57-
const { kWrapConnectedHandle } = require('internal/net');
5857
const JSStreamSocket = require('internal/js_stream_socket');
5958
const { Buffer } = require('buffer');
6059
let debug = require('internal/util/debuglog').debuglog('tls', (fn) => {
@@ -633,16 +632,6 @@ TLSSocket.prototype._wrapHandle = function(wrap, handle) {
633632
return res;
634633
};
635634

636-
TLSSocket.prototype[kWrapConnectedHandle] = function(handle) {
637-
this._handle = this._wrapHandle(null, handle);
638-
this.ssl = this._handle;
639-
this._init();
640-
641-
if (this._tlsOptions.enableTrace) {
642-
this._handle.enableTrace();
643-
}
644-
};
645-
646635
// This eliminates a cyclic reference to TLSWrap
647636
// Ref: https://github.com/nodejs/node/commit/f7620fb96d339f704932f9bb9a0dceb9952df2d4
648637
function defineHandleReading(socket, handle) {

lib/internal/net.js

-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ function makeSyncWrite(fd) {
6767
}
6868

6969
module.exports = {
70-
kWrapConnectedHandle: Symbol('wrapConnectedHandle'),
7170
isIP,
7271
isIPv4,
7372
isIPv6,

lib/net.js

+25-21
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ let debug = require('internal/util/debuglog').debuglog('net', (fn) => {
4242
debug = fn;
4343
});
4444
const {
45-
kWrapConnectedHandle,
4645
isIP,
4746
isIPv4,
4847
isIPv6,
@@ -53,7 +52,8 @@ const assert = require('internal/assert');
5352
const {
5453
UV_EADDRINUSE,
5554
UV_EINVAL,
56-
UV_ENOTCONN
55+
UV_ENOTCONN,
56+
UV_ECANCELED
5757
} = internalBinding('uv');
5858

5959
const { Buffer } = require('buffer');
@@ -1064,43 +1064,54 @@ function internalConnect(
10641064
}
10651065

10661066

1067-
function internalConnectMultiple(context) {
1067+
function internalConnectMultiple(context, canceled) {
10681068
clearTimeout(context[kTimeout]);
10691069
const self = context.socket;
1070-
assert(self.connecting);
10711070

10721071
// All connections have been tried without success, destroy with error
1073-
if (context.current === context.addresses.length) {
1072+
if (canceled || context.current === context.addresses.length) {
10741073
self.destroy(aggregateErrors(context.errors));
10751074
return;
10761075
}
10771076

1077+
assert(self.connecting);
1078+
1079+
// Reset the TCP handle when trying other addresses
1080+
if (context.current > 0) {
1081+
if (self?.[kHandle]?._parent) {
1082+
self[kHandle]._parent.reinitialize();
1083+
} else {
1084+
self._handle.reinitialize();
1085+
}
1086+
}
1087+
10781088
const { localPort, port, flags } = context;
10791089
const { address, family: addressType } = context.addresses[context.current++];
1080-
const handle = new TCP(TCPConstants.SOCKET);
10811090
let localAddress;
10821091
let err;
10831092

10841093
if (localPort) {
10851094
if (addressType === 4) {
10861095
localAddress = DEFAULT_IPV4_ADDR;
1087-
err = handle.bind(localAddress, localPort);
1096+
err = self._handle.bind(localAddress, localPort);
10881097
} else { // addressType === 6
10891098
localAddress = DEFAULT_IPV6_ADDR;
1090-
err = handle.bind6(localAddress, localPort, flags);
1099+
err = self._handle.bind6(localAddress, localPort, flags);
10911100
}
10921101

10931102
debug('connect/multiple: binding to localAddress: %s and localPort: %d (addressType: %d)',
10941103
localAddress, localPort, addressType);
10951104

1096-
err = checkBindError(err, localPort, handle);
1105+
err = checkBindError(err, localPort, self._handle);
10971106
if (err) {
10981107
ArrayPrototypePush(context.errors, exceptionWithHostPort(err, 'bind', localAddress, localPort));
10991108
internalConnectMultiple(context);
11001109
return;
11011110
}
11021111
}
11031112

1113+
debug('connect/multiple: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
1114+
11041115
const req = new TCPConnectWrap();
11051116
req.oncomplete = FunctionPrototypeBind(afterConnectMultiple, undefined, context);
11061117
req.address = address;
@@ -1111,9 +1122,9 @@ function internalConnectMultiple(context) {
11111122
ArrayPrototypePush(self.autoSelectFamilyAttemptedAddresses, `${address}:${port}`);
11121123

11131124
if (addressType === 4) {
1114-
err = handle.connect(req, address, port);
1125+
err = self._handle.connect(req, address, port);
11151126
} else {
1116-
err = handle.connect6(req, address, port);
1127+
err = self._handle.connect6(req, address, port);
11171128
}
11181129

11191130
if (err) {
@@ -1337,6 +1348,8 @@ function lookupAndConnectMultiple(self, async_id_symbol, lookup, host, options,
13371348
if (!self.connecting) {
13381349
return;
13391350
} else if (err) {
1351+
self.emit('lookup', err, undefined, undefined, host);
1352+
13401353
// net.createConnection() creates a net.Socket object and immediately
13411354
// calls net.Socket.connect() on it (that's us). There are no event
13421355
// listeners registered yet so defer the error event to the next tick.
@@ -1529,7 +1542,7 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
15291542
ArrayPrototypePush(context.errors, ex);
15301543

15311544
// Try the next address
1532-
internalConnectMultiple(context);
1545+
internalConnectMultiple(context, status === UV_ECANCELED);
15331546
return;
15341547
}
15351548

@@ -1540,15 +1553,6 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
15401553
return;
15411554
}
15421555

1543-
// Perform initialization sequence on the handle, then move on with the regular callback
1544-
self._handle = handle;
1545-
initSocketHandle(self);
1546-
1547-
if (self[kWrapConnectedHandle]) {
1548-
self[kWrapConnectedHandle](handle);
1549-
initSocketHandle(self); // This is called again to initialize the TLSWrap
1550-
}
1551-
15521556
if (hasObserver('net')) {
15531557
startPerf(
15541558
self,

src/tcp_wrap.cc

+12
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ void TCPWrap::Initialize(Local<Object> target,
104104
SetProtoMethod(isolate, t, "setNoDelay", SetNoDelay);
105105
SetProtoMethod(isolate, t, "setKeepAlive", SetKeepAlive);
106106
SetProtoMethod(isolate, t, "reset", Reset);
107+
SetProtoMethod(isolate, t, "reinitialize", Reinitialize);
107108

108109
#ifdef _WIN32
109110
SetProtoMethod(isolate, t, "setSimultaneousAccepts", SetSimultaneousAccepts);
@@ -142,6 +143,7 @@ void TCPWrap::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
142143
registry->Register(SetNoDelay);
143144
registry->Register(SetKeepAlive);
144145
registry->Register(Reset);
146+
registry->Register(Reinitialize);
145147
#ifdef _WIN32
146148
registry->Register(SetSimultaneousAccepts);
147149
#endif
@@ -181,6 +183,16 @@ TCPWrap::TCPWrap(Environment* env, Local<Object> object, ProviderType provider)
181183
// Suggestion: uv_tcp_init() returns void.
182184
}
183185

186+
void TCPWrap::Reinitialize(const FunctionCallbackInfo<Value>& args) {
187+
TCPWrap* wrap;
188+
ASSIGN_OR_RETURN_UNWRAP(&wrap,
189+
args.Holder(),
190+
args.GetReturnValue().Set(UV_EBADF));
191+
192+
Environment* env = wrap->env();
193+
int r = uv_tcp_init(env->event_loop(), &wrap->handle_);
194+
CHECK_EQ(r, 0);
195+
}
184196

185197
void TCPWrap::SetNoDelay(const FunctionCallbackInfo<Value>& args) {
186198
TCPWrap* wrap;

src/tcp_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> {
7272
ProviderType provider);
7373

7474
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
75+
static void Reinitialize(const v8::FunctionCallbackInfo<v8::Value>& args);
7576
static void SetNoDelay(const v8::FunctionCallbackInfo<v8::Value>& args);
7677
static void SetKeepAlive(const v8::FunctionCallbackInfo<v8::Value>& args);
7778
static void Bind(const v8::FunctionCallbackInfo<v8::Value>& args);

test/parallel/test-http2-ping-settings-heapdump.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ const v8 = require('v8');
1111
// after it is destroyed, either because they are detached from it or have been
1212
// destroyed themselves.
1313

14+
// We use an higher autoSelectFamilyAttemptTimeout in this test as the v8.getHeapSnapshot().resume()
15+
// will slow the connection flow and we don't want the second connection attempt to start.
16+
let autoSelectFamilyAttemptTimeout = common.platformTimeout(1000);
17+
if (common.isWindows) {
18+
// Some of the windows machines in the CI need more time to establish connection
19+
autoSelectFamilyAttemptTimeout = common.platformTimeout(10000);
20+
}
21+
1422
for (const variant of ['ping', 'settings']) {
1523
const server = http2.createServer();
1624
server.on('session', common.mustCall((session) => {
@@ -30,7 +38,7 @@ for (const variant of ['ping', 'settings']) {
3038
}));
3139

3240
server.listen(0, common.mustCall(() => {
33-
const client = http2.connect(`http://localhost:${server.address().port}`,
41+
const client = http2.connect(`http://localhost:${server.address().port}`, { autoSelectFamilyAttemptTimeout },
3442
common.mustCall());
3543
client.on('error', (err) => {
3644
// We destroy the session so it's possible to get ECONNRESET here.

test/parallel/test-net-dns-custom-lookup.js

+16-3
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,22 @@ function check(addressType, cb) {
2626

2727
function lookup(host, dnsopts, cb) {
2828
dnsopts.family = addressType;
29+
2930
if (addressType === 4) {
3031
process.nextTick(function() {
31-
cb(null, common.localhostIPv4, 4);
32+
if (dnsopts.all) {
33+
cb(null, [{ address: common.localhostIPv4, family: 4 }]);
34+
} else {
35+
cb(null, common.localhostIPv4, 4);
36+
}
3237
});
3338
} else {
3439
process.nextTick(function() {
35-
cb(null, '::1', 6);
40+
if (dnsopts.all) {
41+
cb(null, [{ address: '::1', family: 6 }]);
42+
} else {
43+
cb(null, '::1', 6);
44+
}
3645
});
3746
}
3847
}
@@ -48,7 +57,11 @@ check(4, function() {
4857
host: 'localhost',
4958
port: 80,
5059
lookup(host, dnsopts, cb) {
51-
cb(null, undefined, 4);
60+
if (dnsopts.all) {
61+
cb(null, [{ address: undefined, family: 4 }]);
62+
} else {
63+
cb(null, undefined, 4);
64+
}
5265
}
5366
}).on('error', common.expectsError({ code: 'ERR_INVALID_IP_ADDRESS' }));
5467
}

test/parallel/test-net-dns-lookup.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ const server = net.createServer(function(client) {
3131

3232
server.listen(0, common.mustCall(function() {
3333
net.connect(this.address().port, 'localhost')
34-
.on('lookup', common.mustCall(function(err, ip, type, host) {
34+
.on('lookup', common.mustCallAtLeast(function(err, ip, type, host) {
3535
assert.strictEqual(err, null);
3636
assert.match(ip, /^(127\.0\.0\.1|::1)$/);
3737
assert.match(type.toString(), /^(4|6)$/);
3838
assert.strictEqual(host, 'localhost');
39-
}));
39+
}, 1));
4040
}));

test/parallel/test-net-options-lookup.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ function connectDoesNotThrow(input) {
3636
{
3737
// Verify that an error is emitted when an invalid address family is returned.
3838
const s = connectDoesNotThrow((host, options, cb) => {
39-
cb(null, '127.0.0.1', 100);
39+
if (options.all) {
40+
cb(null, [{ address: '127.0.0.1', family: 100 }]);
41+
} else {
42+
cb(null, '127.0.0.1', 100);
43+
}
4044
});
4145

4246
s.on('error', common.expectsError({

test/parallel/test-net-server-reset.js

+6-12
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,11 @@ server.on('close', common.mustCall());
2020

2121
assert.strictEqual(server, server.listen(0, () => {
2222
net.createConnection(server.address().port)
23-
.on('error', common.mustCall(
24-
common.expectsError({
25-
code: 'ECONNRESET',
26-
name: 'Error'
27-
}))
28-
);
23+
.on('error', common.mustCall((error) => {
24+
assert.strictEqual(error.code, 'ECONNRESET');
25+
}));
2926
net.createConnection(server.address().port)
30-
.on('error', common.mustCall(
31-
common.expectsError({
32-
code: 'ECONNRESET',
33-
name: 'Error'
34-
}))
35-
);
27+
.on('error', common.mustCall((error) => {
28+
assert.strictEqual(error.code, 'ECONNRESET');
29+
}));
3630
}));

0 commit comments

Comments
 (0)