Skip to content

Commit f6bf3f8

Browse files
guybedfordaduh95
authored andcommitted
net: support sync connect for BoundSocket
When a net.Socket is constructed from a net.BoundSocket, support synchronous connect(). Signed-off-by: Guy Bedford <guybedford@gmail.com> PR-URL: #64375 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent dada3ae commit f6bf3f8

3 files changed

Lines changed: 53 additions & 0 deletions

File tree

β€Ždoc/api/net.mdβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,11 @@ Adoption transfers ownership of the socket; afterwards `address()` and `close()`
16981698
throw [`ERR_SOCKET_HANDLE_ADOPTED`][]. A handle that is never adopted must be
16991699
closed to avoid leaking the socket.
17001700

1701+
When an adopted `BoundSocket` connects to a numeric IP literal, `connect(2)` is
1702+
issued synchronously, so [`socket.localAddress`][] is resolved once
1703+
[`socket.connect()`][] returns. Connection failures are still reported via a
1704+
deferred `'error'` event.
1705+
17011706
```mjs
17021707
importnetfrom'node:net';
17031708

@@ -2283,6 +2288,7 @@ net.isIPv6('fhqwhgads'); // returns false
22832288
[`socket.connecting`]: #socketconnecting
22842289
[`socket.destroy()`]: #socketdestroyerror
22852290
[`socket.end()`]: #socketenddata-encoding-callback
2291+
[`socket.localAddress`]: #socketlocaladdress
22862292
[`socket.pause()`]: #socketpause
22872293
[`socket.resume()`]: #socketresume
22882294
[`socket.setEncoding()`]: #socketsetencodingencoding

β€Žlib/net.jsβ€Ž

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,17 @@ function lookupAndConnect(self, options) {
16301630
// If host is an IP, skip performing a lookup
16311631
constaddressType=isIP(host);
16321632
if(addressType){
1633+
// An adopted BoundSocket needs no lookup: issue connect(2) in-tick so the
1634+
// kernel-resolved concrete source address is observable synchronously.
1635+
// Connection failures are still reported via a deferred 'error' event.
1636+
if(self[kBoundSource]){
1637+
defaultTriggerAsyncIdScope(
1638+
self[async_id_symbol],
1639+
internalConnect,
1640+
self,host,port,addressType,localAddress,localPort,
1641+
);
1642+
return;
1643+
}
16331644
defaultTriggerAsyncIdScope(self[async_id_symbol],process.nextTick,()=>{
16341645
if(self.connecting)
16351646
defaultTriggerAsyncIdScope(

β€Žtest/parallel/test-net-boundsocket.jsβ€Ž

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,42 @@ if (!common.isWindows && process.getuid() !== 0) {
178178
client.destroy();
179179
}
180180

181+
// Adopted BoundSocket: connect(2) is synchronous, so localAddress/localPort
182+
// resolve to the concrete source in-tick, before the 'connect' event.
183+
{
184+
constserver=net.createServer();
185+
server.listen(0,'127.0.0.1',common.mustCall(()=>{
186+
constserverPort=server.address().port;
187+
constbound=newnet.BoundSocket({host: '127.0.0.1',port: 0});
188+
constlocalPort=bound.address().port;
189+
constclient=newnet.Socket({handle: bound});
190+
191+
client.connect({host: '127.0.0.1',port: serverPort});
192+
193+
assert.strictEqual(client.localAddress,'127.0.0.1');
194+
assert.strictEqual(client.localPort,localPort);
195+
196+
client.on('connect',common.mustCall(()=>{
197+
assert.strictEqual(client.localAddress,'127.0.0.1');
198+
assert.strictEqual(client.localPort,localPort);
199+
client.destroy();
200+
server.close();
201+
}));
202+
}));
203+
}
204+
205+
// A connect failure is reported via a deferred 'error' event, never thrown
206+
// synchronously. An IPv4-bound handle connecting to an IPv6 literal fails on
207+
// address family mismatch.
208+
{
209+
constbound=newnet.BoundSocket({host: '127.0.0.1',port: 0});
210+
constclient=newnet.Socket({handle: bound});
211+
client.connect({host: '::1',port: 1});
212+
client.on('error',common.mustCall((err)=>{
213+
assert.strictEqual(err.syscall,'connect');
214+
}));
215+
}
216+
181217
// reusePort: SO_REUSEPORT permits multiple listeners on the same port. Support
182218
// is platform-dependent, so probe first.
183219
{

0 commit comments

Comments
Β (0)