@@ -56,6 +56,7 @@ const {
5656 UV_EINVAL ,
5757 UV_ENOTCONN ,
5858 UV_ECANCELED ,
59+ UV_ETIMEDOUT ,
5960} = internalBinding ( 'uv' ) ;
6061
6162const { Buffer } = require ( 'buffer' ) ;
@@ -483,6 +484,10 @@ function Socket(options) {
483484 }
484485 }
485486
487+ if ( options . signal ) {
488+ addClientAbortSignalOption ( this , options ) ;
489+ }
490+
486491 // Reserve properties
487492 this . server = null ;
488493 this . _server = null ;
@@ -1092,6 +1097,11 @@ function internalConnectMultiple(context, canceled) {
10921097 clearTimeout ( context [ kTimeout ] ) ;
10931098 const self = context . socket ;
10941099
1100+ // We were requested to abort. Stop all operations
1101+ if ( self . _aborted ) {
1102+ return ;
1103+ }
1104+
10951105 // All connections have been tried without success, destroy with error
10961106 if ( canceled || context . current === context . addresses . length ) {
10971107 if ( context . errors . length === 0 ) {
@@ -1106,7 +1116,11 @@ function internalConnectMultiple(context, canceled) {
11061116 assert ( self . connecting ) ;
11071117
11081118 const current = context . current ++ ;
1109- const handle = current === 0 ? self . _handle : new TCP ( TCPConstants . SOCKET ) ;
1119+
1120+ if ( current > 0 ) {
1121+ self [ kReinitializeHandle ] ( new TCP ( TCPConstants . SOCKET ) ) ;
1122+ }
1123+
11101124 const { localPort, port, flags } = context ;
11111125 const { address, family : addressType } = context . addresses [ current ] ;
11121126 let localAddress ;
@@ -1115,16 +1129,16 @@ function internalConnectMultiple(context, canceled) {
11151129 if ( localPort ) {
11161130 if ( addressType === 4 ) {
11171131 localAddress = DEFAULT_IPV4_ADDR ;
1118- err = handle . bind ( localAddress , localPort ) ;
1132+ err = self . _handle . bind ( localAddress , localPort ) ;
11191133 } else { // addressType === 6
11201134 localAddress = DEFAULT_IPV6_ADDR ;
1121- err = handle . bind6 ( localAddress , localPort , flags ) ;
1135+ err = self . _handle . bind6 ( localAddress , localPort , flags ) ;
11221136 }
11231137
11241138 debug ( 'connect/multiple: binding to localAddress: %s and localPort: %d (addressType: %d)' ,
11251139 localAddress , localPort , addressType ) ;
11261140
1127- err = checkBindError ( err , localPort , handle ) ;
1141+ err = checkBindError ( err , localPort , self . _handle ) ;
11281142 if ( err ) {
11291143 ArrayPrototypePush ( context . errors , exceptionWithHostPort ( err , 'bind' , localAddress , localPort ) ) ;
11301144 internalConnectMultiple ( context ) ;
@@ -1144,9 +1158,9 @@ function internalConnectMultiple(context, canceled) {
11441158 ArrayPrototypePush ( self . autoSelectFamilyAttemptedAddresses , `${ address } :${ port } ` ) ;
11451159
11461160 if ( addressType === 4 ) {
1147- err = handle . connect ( req , address , port ) ;
1161+ err = self . _handle . connect ( req , address , port ) ;
11481162 } else {
1149- err = handle . connect6 ( req , address , port ) ;
1163+ err = self . _handle . connect6 ( req , address , port ) ;
11501164 }
11511165
11521166 if ( err ) {
@@ -1166,7 +1180,7 @@ function internalConnectMultiple(context, canceled) {
11661180 debug ( 'connect/multiple: setting the attempt timeout to %d ms' , context . timeout ) ;
11671181
11681182 // If the attempt has not returned an error, start the connection timer
1169- context [ kTimeout ] = setTimeout ( internalConnectMultipleTimeout , context . timeout , context , req , handle ) ;
1183+ context [ kTimeout ] = setTimeout ( internalConnectMultipleTimeout , context . timeout , context , req , self . _handle ) ;
11701184 }
11711185}
11721186
@@ -1184,6 +1198,15 @@ Socket.prototype.connect = function(...args) {
11841198 const options = normalized [ 0 ] ;
11851199 const cb = normalized [ 1 ] ;
11861200
1201+ if ( cb !== null ) {
1202+ this . once ( 'connect' , cb ) ;
1203+ }
1204+
1205+ // If the parent is already connecting, do not attempt to connect again
1206+ if ( this . _parent && this . _parent . connecting ) {
1207+ return this ;
1208+ }
1209+
11871210 // options.port === null will be checked later.
11881211 if ( options . port === undefined && options . path == null )
11891212 throw new ERR_MISSING_ARGS ( [ 'options' , 'port' , 'path' ] ) ;
@@ -1208,10 +1231,6 @@ Socket.prototype.connect = function(...args) {
12081231 initSocketHandle ( this ) ;
12091232 }
12101233
1211- if ( cb !== null ) {
1212- this . once ( 'connect' , cb ) ;
1213- }
1214-
12151234 this . _unrefTimer ( ) ;
12161235
12171236 this . connecting = true ;
@@ -1584,7 +1603,47 @@ function afterConnect(status, handle, req, readable, writable) {
15841603 }
15851604}
15861605
1606+ function addClientAbortSignalOption ( self , options ) {
1607+ validateAbortSignal ( options . signal , 'options.signal' ) ;
1608+ const { signal } = options ;
1609+
1610+ function onAbort ( ) {
1611+ signal . removeEventListener ( 'abort' , onAbort ) ;
1612+ self . _aborted = true ;
1613+ }
1614+
1615+ if ( signal . aborted ) {
1616+ process . nextTick ( onAbort ) ;
1617+ } else {
1618+ process . nextTick ( ( ) => {
1619+ signal . addEventListener ( 'abort' , onAbort ) ;
1620+ } ) ;
1621+ }
1622+ }
1623+
1624+ function createConnectionError ( req , status ) {
1625+ let details ;
1626+
1627+ if ( req . localAddress && req . localPort ) {
1628+ details = req . localAddress + ':' + req . localPort ;
1629+ }
1630+
1631+ const ex = exceptionWithHostPort ( status ,
1632+ 'connect' ,
1633+ req . address ,
1634+ req . port ,
1635+ details ) ;
1636+ if ( details ) {
1637+ ex . localAddress = req . localAddress ;
1638+ ex . localPort = req . localPort ;
1639+ }
1640+
1641+ return ex ;
1642+ }
1643+
15871644function afterConnectMultiple ( context , current , status , handle , req , readable , writable ) {
1645+ debug ( 'connect/multiple: connection attempt to %s:%s completed with status %s' , req . address , req . port , status ) ;
1646+
15881647 // Make sure another connection is not spawned
15891648 clearTimeout ( context [ kTimeout ] ) ;
15901649
@@ -1597,35 +1656,15 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
15971656
15981657 const self = context . socket ;
15991658
1600-
16011659 // Some error occurred, add to the list of exceptions
16021660 if ( status !== 0 ) {
1603- let details ;
1604- if ( req . localAddress && req . localPort ) {
1605- details = req . localAddress + ':' + req . localPort ;
1606- }
1607- const ex = exceptionWithHostPort ( status ,
1608- 'connect' ,
1609- req . address ,
1610- req . port ,
1611- details ) ;
1612- if ( details ) {
1613- ex . localAddress = req . localAddress ;
1614- ex . localPort = req . localPort ;
1615- }
1616-
1617- ArrayPrototypePush ( context . errors , ex ) ;
1661+ ArrayPrototypePush ( context . errors , createConnectionError ( req , status ) ) ;
16181662
16191663 // Try the next address
16201664 internalConnectMultiple ( context , status === UV_ECANCELED ) ;
16211665 return ;
16221666 }
16231667
1624- if ( context . current > 1 && self [ kReinitializeHandle ] ) {
1625- self [ kReinitializeHandle ] ( handle ) ;
1626- handle = self . _handle ;
1627- }
1628-
16291668 if ( hasObserver ( 'net' ) ) {
16301669 startPerf (
16311670 self ,
@@ -1634,17 +1673,18 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
16341673 ) ;
16351674 }
16361675
1637- afterConnect ( status , handle , req , readable , writable ) ;
1676+ afterConnect ( status , self . _handle , req , readable , writable ) ;
16381677}
16391678
16401679function internalConnectMultipleTimeout ( context , req , handle ) {
16411680 debug ( 'connect/multiple: connection to %s:%s timed out' , req . address , req . port ) ;
16421681 req . oncomplete = undefined ;
1682+ ArrayPrototypePush ( context . errors , createConnectionError ( req , UV_ETIMEDOUT ) ) ;
16431683 handle . close ( ) ;
16441684 internalConnectMultiple ( context ) ;
16451685}
16461686
1647- function addAbortSignalOption ( self , options ) {
1687+ function addServerAbortSignalOption ( self , options ) {
16481688 if ( options ?. signal === undefined ) {
16491689 return ;
16501690 }
@@ -1933,7 +1973,7 @@ Server.prototype.listen = function(...args) {
19331973 listenInCluster ( this , null , - 1 , - 1 , backlogFromArgs ) ;
19341974 return this ;
19351975 }
1936- addAbortSignalOption ( this , options ) ;
1976+ addServerAbortSignalOption ( this , options ) ;
19371977 // (handle[, backlog][, cb]) where handle is an object with a fd
19381978 if ( typeof options . fd === 'number' && options . fd >= 0 ) {
19391979 listenInCluster ( this , null , null , null , backlogFromArgs , options . fd ) ;
0 commit comments