Skip to content

Commit f4ffcb8

Browse files
committed
net: reuse handle with resetting
1 parent 8fa810f commit f4ffcb8

13 files changed

+73
-57
lines changed

doc/api/net.md

+1
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ For TCP connections, available `options` are:
941941
option before timing out and trying the next address.
942942
Ignored if the `family` option is not `0` or if `localAddress` is set.
943943
Connection errors are not emitted if at least one connection succeeds.
944+
If all connections attempts fails a single `AggregateError` with all failed attempts is emitted.
944945
**Default:** initially `true`, but it can be changed at runtime using [`net.setDefaultAutoSelectFamily(value)`][]
945946
or via the command line option `--no-network-family-autoselection`.
946947
* `autoSelectFamilyAttemptTimeout` {number}: The amount of time in milliseconds to wait

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

+23-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,36 +1064,45 @@ 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);
@@ -1111,9 +1120,9 @@ function internalConnectMultiple(context) {
11111120
ArrayPrototypePush(self.autoSelectFamilyAttemptedAddresses, `${address}:${port}`);
11121121

11131122
if (addressType === 4) {
1114-
err = handle.connect(req, address, port);
1123+
err = self._handle.connect(req, address, port);
11151124
} else {
1116-
err = handle.connect6(req, address, port);
1125+
err = self._handle.connect6(req, address, port);
11171126
}
11181127

11191128
if (err) {
@@ -1337,6 +1346,8 @@ function lookupAndConnectMultiple(self, async_id_symbol, lookup, host, options,
13371346
if (!self.connecting) {
13381347
return;
13391348
} else if (err) {
1349+
self.emit('lookup', err, undefined, undefined, host);
1350+
13401351
// net.createConnection() creates a net.Socket object and immediately
13411352
// calls net.Socket.connect() on it (that's us). There are no event
13421353
// listeners registered yet so defer the error event to the next tick.
@@ -1529,7 +1540,7 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
15291540
ArrayPrototypePush(context.errors, ex);
15301541

15311542
// Try the next address
1532-
internalConnectMultiple(context);
1543+
internalConnectMultiple(context, status === UV_ECANCELED);
15331544
return;
15341545
}
15351546

@@ -1540,15 +1551,6 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
15401551
return;
15411552
}
15421553

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-
15521554
if (hasObserver('net')) {
15531555
startPerf(
15541556
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-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
}));

test/parallel/test-tls-connect-hints-option.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ tls.connect({
2525
port: 42,
2626
lookup: common.mustCall((host, options) => {
2727
assert.strictEqual(host, 'localhost');
28-
assert.deepStrictEqual(options, { family: undefined, hints });
28+
assert.deepStrictEqual(options, { family: undefined, hints, all: true });
2929
}),
3030
hints
3131
});

test/sequential/test-http-econnrefused.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function ping() {
135135
console.log(`Error making ping req: ${error}`);
136136
hadError = true;
137137
assert.ok(!gotEnd);
138-
afterPing(error.message);
138+
afterPing(error.errors[0].message);
139139
});
140140
}
141141

test/sequential/test-net-better-error-messages-port.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ const c = net.createConnection(common.PORT);
77

88
c.on('connect', common.mustNotCall());
99

10-
c.on('error', common.mustCall(function(e) {
11-
assert.strictEqual(e.code, 'ECONNREFUSED');
12-
assert.strictEqual(e.port, common.PORT);
13-
assert.match(e.address, /^(127\.0\.0\.1|::1)$/);
10+
c.on('error', common.mustCall(function(error) {
11+
const failedAttempt = error.errors[0];
12+
assert.strictEqual(failedAttempt.code, 'ECONNREFUSED');
13+
assert.strictEqual(failedAttempt.port, common.PORT);
14+
assert.match(failedAttempt.address, /^(127\.0\.0\.1|::1)$/);
1415
}));

0 commit comments

Comments
 (0)