@@ -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' ) ;
@@ -482,6 +483,10 @@ function Socket(options) {
482483 }
483484 }
484485
486+ if ( options . signal ) {
487+ addClientAbortSignalOption ( this , options ) ;
488+ }
489+
485490 // Reserve properties
486491 this . server = null ;
487492 this . _server = null ;
@@ -1091,6 +1096,11 @@ function internalConnectMultiple(context, canceled) {
10911096 clearTimeout ( context [ kTimeout ] ) ;
10921097 const self = context . socket ;
10931098
1099+ // We were requested to abort. Stop all operations
1100+ if ( self . _aborted ) {
1101+ return ;
1102+ }
1103+
10941104 // All connections have been tried without success, destroy with error
10951105 if ( canceled || context . current === context . addresses . length ) {
10961106 if ( context . errors . length === 0 ) {
@@ -1105,7 +1115,11 @@ function internalConnectMultiple(context, canceled) {
11051115 assert ( self . connecting ) ;
11061116
11071117 const current = context . current ++ ;
1108- const handle = current === 0 ? self . _handle : new TCP ( TCPConstants . SOCKET ) ;
1118+
1119+ if ( current > 0 ) {
1120+ self [ kReinitializeHandle ] ( new TCP ( TCPConstants . SOCKET ) ) ;
1121+ }
1122+
11091123 const { localPort, port, flags } = context ;
11101124 const { address, family : addressType } = context . addresses [ current ] ;
11111125 let localAddress ;
@@ -1114,16 +1128,16 @@ function internalConnectMultiple(context, canceled) {
11141128 if ( localPort ) {
11151129 if ( addressType === 4 ) {
11161130 localAddress = DEFAULT_IPV4_ADDR ;
1117- err = handle . bind ( localAddress , localPort ) ;
1131+ err = self . _handle . bind ( localAddress , localPort ) ;
11181132 } else { // addressType === 6
11191133 localAddress = DEFAULT_IPV6_ADDR ;
1120- err = handle . bind6 ( localAddress , localPort , flags ) ;
1134+ err = self . _handle . bind6 ( localAddress , localPort , flags ) ;
11211135 }
11221136
11231137 debug ( 'connect/multiple: binding to localAddress: %s and localPort: %d (addressType: %d)' ,
11241138 localAddress , localPort , addressType ) ;
11251139
1126- err = checkBindError ( err , localPort , handle ) ;
1140+ err = checkBindError ( err , localPort , self . _handle ) ;
11271141 if ( err ) {
11281142 ArrayPrototypePush ( context . errors , exceptionWithHostPort ( err , 'bind' , localAddress , localPort ) ) ;
11291143 internalConnectMultiple ( context ) ;
@@ -1143,9 +1157,9 @@ function internalConnectMultiple(context, canceled) {
11431157 ArrayPrototypePush ( self . autoSelectFamilyAttemptedAddresses , `${ address } :${ port } ` ) ;
11441158
11451159 if ( addressType === 4 ) {
1146- err = handle . connect ( req , address , port ) ;
1160+ err = self . _handle . connect ( req , address , port ) ;
11471161 } else {
1148- err = handle . connect6 ( req , address , port ) ;
1162+ err = self . _handle . connect6 ( req , address , port ) ;
11491163 }
11501164
11511165 if ( err ) {
@@ -1165,7 +1179,7 @@ function internalConnectMultiple(context, canceled) {
11651179 debug ( 'connect/multiple: setting the attempt timeout to %d ms' , context . timeout ) ;
11661180
11671181 // If the attempt has not returned an error, start the connection timer
1168- context [ kTimeout ] = setTimeout ( internalConnectMultipleTimeout , context . timeout , context , req , handle ) ;
1182+ context [ kTimeout ] = setTimeout ( internalConnectMultipleTimeout , context . timeout , context , req , self . _handle ) ;
11691183 }
11701184}
11711185
@@ -1183,6 +1197,15 @@ Socket.prototype.connect = function(...args) {
11831197 const options = normalized [ 0 ] ;
11841198 const cb = normalized [ 1 ] ;
11851199
1200+ if ( cb !== null ) {
1201+ this . once ( 'connect' , cb ) ;
1202+ }
1203+
1204+ // If the parent is already connecting, do not attempt to connect again
1205+ if ( this . _parent && this . _parent . connecting ) {
1206+ return this ;
1207+ }
1208+
11861209 // options.port === null will be checked later.
11871210 if ( options . port === undefined && options . path == null )
11881211 throw new ERR_MISSING_ARGS ( [ 'options' , 'port' , 'path' ] ) ;
@@ -1207,10 +1230,6 @@ Socket.prototype.connect = function(...args) {
12071230 initSocketHandle ( this ) ;
12081231 }
12091232
1210- if ( cb !== null ) {
1211- this . once ( 'connect' , cb ) ;
1212- }
1213-
12141233 this . _unrefTimer ( ) ;
12151234
12161235 this . connecting = true ;
@@ -1583,7 +1602,47 @@ function afterConnect(status, handle, req, readable, writable) {
15831602 }
15841603}
15851604
1605+ function addClientAbortSignalOption ( self , options ) {
1606+ validateAbortSignal ( options . signal , 'options.signal' ) ;
1607+ const { signal } = options ;
1608+
1609+ function onAbort ( ) {
1610+ signal . removeEventListener ( 'abort' , onAbort ) ;
1611+ self . _aborted = true ;
1612+ }
1613+
1614+ if ( signal . aborted ) {
1615+ process . nextTick ( onAbort ) ;
1616+ } else {
1617+ process . nextTick ( ( ) => {
1618+ signal . addEventListener ( 'abort' , onAbort ) ;
1619+ } ) ;
1620+ }
1621+ }
1622+
1623+ function createConnectionError ( req , status ) {
1624+ let details ;
1625+
1626+ if ( req . localAddress && req . localPort ) {
1627+ details = req . localAddress + ':' + req . localPort ;
1628+ }
1629+
1630+ const ex = exceptionWithHostPort ( status ,
1631+ 'connect' ,
1632+ req . address ,
1633+ req . port ,
1634+ details ) ;
1635+ if ( details ) {
1636+ ex . localAddress = req . localAddress ;
1637+ ex . localPort = req . localPort ;
1638+ }
1639+
1640+ return ex ;
1641+ }
1642+
15861643function afterConnectMultiple ( context , current , status , handle , req , readable , writable ) {
1644+ debug ( 'connect/multiple: connection attempt to %s:%s completed with status %s' , req . address , req . port , status ) ;
1645+
15871646 // Make sure another connection is not spawned
15881647 clearTimeout ( context [ kTimeout ] ) ;
15891648
@@ -1596,35 +1655,15 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
15961655
15971656 const self = context . socket ;
15981657
1599-
16001658 // Some error occurred, add to the list of exceptions
16011659 if ( status !== 0 ) {
1602- let details ;
1603- if ( req . localAddress && req . localPort ) {
1604- details = req . localAddress + ':' + req . localPort ;
1605- }
1606- const ex = exceptionWithHostPort ( status ,
1607- 'connect' ,
1608- req . address ,
1609- req . port ,
1610- details ) ;
1611- if ( details ) {
1612- ex . localAddress = req . localAddress ;
1613- ex . localPort = req . localPort ;
1614- }
1615-
1616- ArrayPrototypePush ( context . errors , ex ) ;
1660+ ArrayPrototypePush ( context . errors , createConnectionError ( req , status ) ) ;
16171661
16181662 // Try the next address
16191663 internalConnectMultiple ( context , status === UV_ECANCELED ) ;
16201664 return ;
16211665 }
16221666
1623- if ( context . current > 1 && self [ kReinitializeHandle ] ) {
1624- self [ kReinitializeHandle ] ( handle ) ;
1625- handle = self . _handle ;
1626- }
1627-
16281667 if ( hasObserver ( 'net' ) ) {
16291668 startPerf (
16301669 self ,
@@ -1633,17 +1672,18 @@ function afterConnectMultiple(context, current, status, handle, req, readable, w
16331672 ) ;
16341673 }
16351674
1636- afterConnect ( status , handle , req , readable , writable ) ;
1675+ afterConnect ( status , self . _handle , req , readable , writable ) ;
16371676}
16381677
16391678function internalConnectMultipleTimeout ( context , req , handle ) {
16401679 debug ( 'connect/multiple: connection to %s:%s timed out' , req . address , req . port ) ;
16411680 req . oncomplete = undefined ;
1681+ ArrayPrototypePush ( context . errors , createConnectionError ( req , UV_ETIMEDOUT ) ) ;
16421682 handle . close ( ) ;
16431683 internalConnectMultiple ( context ) ;
16441684}
16451685
1646- function addAbortSignalOption ( self , options ) {
1686+ function addServerAbortSignalOption ( self , options ) {
16471687 if ( options ?. signal === undefined ) {
16481688 return ;
16491689 }
@@ -1932,7 +1972,7 @@ Server.prototype.listen = function(...args) {
19321972 listenInCluster ( this , null , - 1 , - 1 , backlogFromArgs ) ;
19331973 return this ;
19341974 }
1935- addAbortSignalOption ( this , options ) ;
1975+ addServerAbortSignalOption ( this , options ) ;
19361976 // (handle[, backlog][, cb]) where handle is an object with a fd
19371977 if ( typeof options . fd === 'number' && options . fd >= 0 ) {
19381978 listenInCluster ( this , null , null , null , backlogFromArgs , options . fd ) ;
0 commit comments