2323
2424const {
2525 ArrayIsArray,
26+ ArrayPrototypeIncludes,
2627 ArrayPrototypeIndexOf,
2728 ArrayPrototypePush,
2829 Boolean,
@@ -97,6 +98,7 @@ const {
9798 ERR_INVALID_HANDLE_TYPE ,
9899 ERR_SERVER_ALREADY_LISTEN ,
99100 ERR_SERVER_NOT_RUNNING ,
101+ ERR_SOCKET_CONNECTION_TIMEOUT ,
100102 ERR_SOCKET_CLOSED ,
101103 ERR_SOCKET_CLOSED_BEFORE_CONNECTION ,
102104 ERR_MISSING_ARGS ,
@@ -127,7 +129,7 @@ let cluster;
127129let dns ;
128130let BlockList ;
129131let SocketAddress ;
130- let autoSelectFamilyDefault = getOptionValue ( '--enable- network-family-autoselection' ) ;
132+ let autoSelectFamilyDefault = getOptionValue ( '--network-family-autoselection' ) ;
131133let autoSelectFamilyAttemptTimeoutDefault = 250 ;
132134
133135const { clearTimeout, setTimeout } = require ( 'timers' ) ;
@@ -1092,6 +1094,11 @@ function internalConnectMultiple(context, canceled) {
10921094
10931095 // All connections have been tried without success, destroy with error
10941096 if ( canceled || context . current === context . addresses . length ) {
1097+ if ( context . errors . length === 0 ) {
1098+ self . destroy ( new ERR_SOCKET_CONNECTION_TIMEOUT ( ) ) ;
1099+ return ;
1100+ }
1101+
10951102 self . destroy ( aggregateErrors ( context . errors ) ) ;
10961103 return ;
10971104 }
@@ -1322,6 +1329,7 @@ function lookupAndConnect(self, options) {
13221329 options ,
13231330 dnsopts ,
13241331 port ,
1332+ localAddress ,
13251333 localPort ,
13261334 autoSelectFamilyAttemptTimeout ,
13271335 ) ;
@@ -1364,7 +1372,9 @@ function lookupAndConnect(self, options) {
13641372 } ) ;
13651373}
13661374
1367- function lookupAndConnectMultiple ( self , async_id_symbol , lookup , host , options , dnsopts , port , localPort , timeout ) {
1375+ function lookupAndConnectMultiple (
1376+ self , async_id_symbol , lookup , host , options , dnsopts , port , localAddress , localPort , timeout ,
1377+ ) {
13681378 defaultTriggerAsyncIdScope ( self [ async_id_symbol ] , function emitLookup ( ) {
13691379 lookup ( host , dnsopts , function emitLookup ( err , addresses ) {
13701380 // It's possible we were destroyed while looking this up.
@@ -1385,6 +1395,7 @@ function lookupAndConnectMultiple(self, async_id_symbol, lookup, host, options,
13851395 // Filter addresses by only keeping the one which are either IPv4 or IPV6.
13861396 // The first valid address determines which group has preference on the
13871397 // alternate family sorting which happens later.
1398+ const validAddresses = [ [ ] , [ ] ] ;
13881399 const validIps = [ [ ] , [ ] ] ;
13891400 let destinations ;
13901401 for ( let i = 0 , l = addresses . length ; i < l ; i ++ ) {
@@ -1397,12 +1408,19 @@ function lookupAndConnectMultiple(self, async_id_symbol, lookup, host, options,
13971408 destinations = addressType === 6 ? { 6 : 0 , 4 : 1 } : { 4 : 0 , 6 : 1 } ;
13981409 }
13991410
1400- ArrayPrototypePush ( validIps [ destinations [ addressType ] ] , address ) ;
1411+ const destination = destinations [ addressType ] ;
1412+
1413+ // Only try an address once
1414+ if ( ! ArrayPrototypeIncludes ( validIps [ destination ] , ip ) ) {
1415+ ArrayPrototypePush ( validAddresses [ destination ] , address ) ;
1416+ ArrayPrototypePush ( validIps [ destination ] , ip ) ;
1417+ }
14011418 }
14021419 }
14031420
1421+
14041422 // When no AAAA or A records are available, fail on the first one
1405- if ( ! validIps [ 0 ] . length && ! validIps [ 1 ] . length ) {
1423+ if ( ! validAddresses [ 0 ] . length && ! validAddresses [ 1 ] . length ) {
14061424 const { address : firstIp , family : firstAddressType } = addresses [ 0 ] ;
14071425
14081426 if ( ! isIP ( firstIp ) ) {
@@ -1420,16 +1438,36 @@ function lookupAndConnectMultiple(self, async_id_symbol, lookup, host, options,
14201438
14211439 // Sort addresses alternating families
14221440 const toAttempt = [ ] ;
1423- for ( let i = 0 , l = MathMax ( validIps [ 0 ] . length , validIps [ 1 ] . length ) ; i < l ; i ++ ) {
1424- if ( i in validIps [ 0 ] ) {
1425- ArrayPrototypePush ( toAttempt , validIps [ 0 ] [ i ] ) ;
1441+ for ( let i = 0 , l = MathMax ( validAddresses [ 0 ] . length , validAddresses [ 1 ] . length ) ; i < l ; i ++ ) {
1442+ if ( i in validAddresses [ 0 ] ) {
1443+ ArrayPrototypePush ( toAttempt , validAddresses [ 0 ] [ i ] ) ;
14261444 }
1427- if ( i in validIps [ 1 ] ) {
1428- ArrayPrototypePush ( toAttempt , validIps [ 1 ] [ i ] ) ;
1445+ if ( i in validAddresses [ 1 ] ) {
1446+ ArrayPrototypePush ( toAttempt , validAddresses [ 1 ] [ i ] ) ;
14291447 }
14301448 }
14311449
1450+ if ( toAttempt . length === 1 ) {
1451+ debug ( 'connect/multiple: only one address found, switching back to single connection' ) ;
1452+ const { address : ip , family : addressType } = toAttempt [ 0 ] ;
1453+
1454+ self . _unrefTimer ( ) ;
1455+ defaultTriggerAsyncIdScope (
1456+ self [ async_id_symbol ] ,
1457+ internalConnect ,
1458+ self ,
1459+ ip ,
1460+ port ,
1461+ addressType ,
1462+ localAddress ,
1463+ localPort ,
1464+ ) ;
1465+
1466+ return ;
1467+ }
1468+
14321469 self . autoSelectFamilyAttemptedAddresses = [ ] ;
1470+ debug ( 'connect/multiple: will try the following addresses' , toAttempt ) ;
14331471
14341472 const context = {
14351473 socket : self ,
@@ -1543,6 +1581,13 @@ function afterConnect(status, handle, req, readable, writable) {
15431581}
15441582
15451583function afterConnectMultiple ( context , status , handle , req , readable , writable ) {
1584+ // One of the connection has completed and correctly dispatched but after timeout, ignore this one
1585+ if ( context [ kTimeoutTriggered ] ) {
1586+ debug ( 'connect/multiple: ignoring successful but timedout connection to %s:%s' , req . address , req . port ) ;
1587+ handle . close ( ) ;
1588+ return ;
1589+ }
1590+
15461591 const self = context . socket ;
15471592
15481593 // Make sure another connection is not spawned
@@ -1571,13 +1616,6 @@ function afterConnectMultiple(context, status, handle, req, readable, writable)
15711616 return ;
15721617 }
15731618
1574- // One of the connection has completed and correctly dispatched but after timeout, ignore this one
1575- if ( context [ kTimeoutTriggered ] ) {
1576- debug ( 'connect/multiple: ignoring successful but timedout connection to %s:%s' , req . address , req . port ) ;
1577- handle . close ( ) ;
1578- return ;
1579- }
1580-
15811619 if ( context . current > 1 && self [ kReinitializeHandle ] ) {
15821620 self [ kReinitializeHandle ] ( handle ) ;
15831621 handle = self . _handle ;
0 commit comments