@@ -29,6 +29,9 @@ module.exports = {
2929 getSocketList
3030} ;
3131
32+ const MAX_HANDLE_RETRANSMISSIONS = 3 ;
33+
34+
3235// this object contain function to convert TCP objects to native handle objects
3336// and back again.
3437const handleConversion = {
@@ -94,17 +97,18 @@ const handleConversion = {
9497 return handle ;
9598 } ,
9699
97- postSend : function ( handle , options , target ) {
100+ postSend : function ( message , handle , options , callback , target ) {
98101 // Store the handle after successfully sending it, so it can be closed
99102 // when the NODE_HANDLE_ACK is received. If the handle could not be sent,
100103 // just close it.
101104 if ( handle && ! options . keepOpen ) {
102105 if ( target ) {
103- // There can only be one _pendingHandle as passing handles are
106+ // There can only be one _pendingMessage as passing handles are
104107 // processed one at a time: handles are stored in _handleQueue while
105108 // waiting for the NODE_HANDLE_ACK of the current passing handle.
106- assert ( ! target . _pendingHandle ) ;
107- target . _pendingHandle = handle ;
109+ assert ( ! target . _pendingMessage ) ;
110+ target . _pendingMessage =
111+ { callback, message, handle, options, retransmissions : 0 } ;
108112 } else {
109113 handle . close ( ) ;
110114 }
@@ -267,6 +271,11 @@ function getHandleWrapType(stream) {
267271 return false ;
268272}
269273
274+ function closePendingHandle ( target ) {
275+ target . _pendingMessage . handle . close ( ) ;
276+ target . _pendingMessage = null ;
277+ }
278+
270279
271280ChildProcess . prototype . spawn = function ( options ) {
272281 const self = this ;
@@ -437,7 +446,7 @@ class Control extends EventEmitter {
437446function setupChannel ( target , channel ) {
438447 target . _channel = channel ;
439448 target . _handleQueue = null ;
440- target . _pendingHandle = null ;
449+ target . _pendingMessage = null ;
441450
442451 const control = new Control ( channel ) ;
443452
@@ -489,16 +498,31 @@ function setupChannel(target, channel) {
489498 // handlers will go through this
490499 target . on ( 'internalMessage' , function ( message , handle ) {
491500 // Once acknowledged - continue sending handles.
492- if ( message . cmd === 'NODE_HANDLE_ACK' ) {
493- if ( target . _pendingHandle ) {
494- target . _pendingHandle . close ( ) ;
495- target . _pendingHandle = null ;
501+ if ( message . cmd === 'NODE_HANDLE_ACK' ||
502+ message . cmd === 'NODE_HANDLE_NACK' ) {
503+
504+ if ( target . _pendingMessage ) {
505+ if ( message . cmd === 'NODE_HANDLE_ACK' ) {
506+ closePendingHandle ( target ) ;
507+ } else if ( target . _pendingMessage . retransmissions ++ ===
508+ MAX_HANDLE_RETRANSMISSIONS ) {
509+ closePendingHandle ( target ) ;
510+ process . emitWarning ( 'Handle did not reach the receiving process ' +
511+ 'correctly' , 'SentHandleNotReceivedWarning' ) ;
512+ }
496513 }
497514
498515 assert ( Array . isArray ( target . _handleQueue ) ) ;
499516 var queue = target . _handleQueue ;
500517 target . _handleQueue = null ;
501518
519+ if ( target . _pendingMessage ) {
520+ target . _send ( target . _pendingMessage . message ,
521+ target . _pendingMessage . handle ,
522+ target . _pendingMessage . options ,
523+ target . _pendingMessage . callback ) ;
524+ }
525+
502526 for ( var i = 0 ; i < queue . length ; i ++ ) {
503527 var args = queue [ i ] ;
504528 target . _send ( args . message , args . handle , args . options , args . callback ) ;
@@ -513,6 +537,12 @@ function setupChannel(target, channel) {
513537
514538 if ( message . cmd !== 'NODE_HANDLE' ) return ;
515539
540+ // It is possible that the handle is not received because of some error on
541+ // ancillary data reception such as MSG_CTRUNC. In this case, report the
542+ // sender about it by sending a NODE_HANDLE_NACK message.
543+ if ( ! handle )
544+ return target . _send ( { cmd : 'NODE_HANDLE_NACK' } , null , true ) ;
545+
516546 // Acknowledge handle receival. Don't emit error events (for example if
517547 // the other side has disconnected) because this call to send() is not
518548 // initiated by the user and it shouldn't be fatal to be unable to ACK
@@ -623,7 +653,8 @@ function setupChannel(target, channel) {
623653 net . _setSimultaneousAccepts ( handle ) ;
624654 }
625655 } else if ( this . _handleQueue &&
626- ! ( message && message . cmd === 'NODE_HANDLE_ACK' ) ) {
656+ ! ( message && ( message . cmd === 'NODE_HANDLE_ACK' ||
657+ message . cmd === 'NODE_HANDLE_NACK' ) ) ) {
627658 // Queue request anyway to avoid out-of-order messages.
628659 this . _handleQueue . push ( {
629660 callback : callback ,
@@ -645,7 +676,7 @@ function setupChannel(target, channel) {
645676 if ( ! this . _handleQueue )
646677 this . _handleQueue = [ ] ;
647678 if ( obj && obj . postSend )
648- obj . postSend ( handle , options , target ) ;
679+ obj . postSend ( message , handle , options , callback , target ) ;
649680 }
650681
651682 req . oncomplete = function ( ) {
@@ -662,7 +693,7 @@ function setupChannel(target, channel) {
662693 } else {
663694 // Cleanup handle on error
664695 if ( obj && obj . postSend )
665- obj . postSend ( handle , options ) ;
696+ obj . postSend ( message , handle , options , callback ) ;
666697
667698 if ( ! options . swallowErrors ) {
668699 const ex = errnoException ( err , 'write' ) ;
@@ -711,10 +742,8 @@ function setupChannel(target, channel) {
711742 // This marks the fact that the channel is actually disconnected.
712743 this . _channel = null ;
713744
714- if ( this . _pendingHandle ) {
715- this . _pendingHandle . close ( ) ;
716- this . _pendingHandle = null ;
717- }
745+ if ( this . _pendingMessage )
746+ closePendingHandle ( this ) ;
718747
719748 var fired = false ;
720749 function finish ( ) {
0 commit comments