@ -24,6 +24,8 @@ const errnoException = util._errnoException;
const SocketListSend = SocketList . SocketListSend ;
const SocketListReceive = SocketList . SocketListReceive ;
const MAX_HANDLE_RETRANSMISSIONS = 3 ;
// this object contain function to convert TCP objects to native handle objects
// and back again.
const handleConversion = {
@ -89,17 +91,18 @@ const handleConversion = {
return handle ;
} ,
postSend : function ( handle , options , target ) {
postSend : function ( message , handle , options , callback , target ) {
// Store the handle after successfully sending it, so it can be closed
// when the NODE_HANDLE_ACK is received. If the handle could not be sent,
// just close it.
if ( handle && ! options . keepOpen ) {
if ( target ) {
// There can only be one _pendingHandl e as passing handles are
// There can only be one _pendingMessag e as passing handles are
// processed one at a time: handles are stored in _handleQueue while
// waiting for the NODE_HANDLE_ACK of the current passing handle.
assert ( ! target . _ pendingHandle ) ;
target . _ pendingHandle = handle ;
assert ( ! target . _ pendingMessage ) ;
target . _ pendingMessage =
{ callback , message , handle , options , retransmissions : 0 } ;
} else {
handle . close ( ) ;
}
@ -250,6 +253,11 @@ function getHandleWrapType(stream) {
return false ;
}
function closePendingHandle ( target ) {
target . _ pendingMessage . handle . close ( ) ;
target . _ pendingMessage = null ;
}
ChildProcess . prototype . spawn = function ( options ) {
var ipc ;
@ -435,7 +443,7 @@ function setupChannel(target, channel) {
} ) ;
target . _ handleQueue = null ;
target . _ pendingHandl e = null ;
target . _ pendingMessag e = null ;
const control = new Control ( channel ) ;
@ -491,16 +499,31 @@ function setupChannel(target, channel) {
// handlers will go through this
target . on ( 'internalMessage' , function ( message , handle ) {
// Once acknowledged - continue sending handles.
if ( message . cmd === 'NODE_HANDLE_ACK' ) {
if ( target . _ pendingHandle ) {
target . _ pendingHandle . close ( ) ;
target . _ pendingHandle = null ;
if ( message . cmd === 'NODE_HANDLE_ACK' ||
message . cmd === 'NODE_HANDLE_NACK' ) {
if ( target . _ pendingMessage ) {
if ( message . cmd === 'NODE_HANDLE_ACK' ) {
closePendingHandle ( target ) ;
} else if ( target . _ pendingMessage . retransmissions ++ ===
MAX_HANDLE_RETRANSMISSIONS ) {
closePendingHandle ( target ) ;
process . emitWarning ( 'Handle did not reach the receiving process ' +
'correctly' , 'SentHandleNotReceivedWarning' ) ;
}
}
assert ( Array . isArray ( target . _ handleQueue ) ) ;
var queue = target . _ handleQueue ;
target . _ handleQueue = null ;
if ( target . _ pendingMessage ) {
target . _ send ( target . _ pendingMessage . message ,
target . _ pendingMessage . handle ,
target . _ pendingMessage . options ,
target . _ pendingMessage . callback ) ;
}
for ( var i = 0 ; i < queue . length ; i ++ ) {
var args = queue [ i ] ;
target . _ send ( args . message , args . handle , args . options , args . callback ) ;
@ -515,6 +538,12 @@ function setupChannel(target, channel) {
if ( message . cmd !== 'NODE_HANDLE' ) return ;
// It is possible that the handle is not received because of some error on
// ancillary data reception such as MSG_CTRUNC. In this case, report the
// sender about it by sending a NODE_HANDLE_NACK message.
if ( ! handle )
return target . _ send ( { cmd : 'NODE_HANDLE_NACK' } , null , true ) ;
// Acknowledge handle receival. Don't emit error events (for example if
// the other side has disconnected) because this call to send() is not
// initiated by the user and it shouldn't be fatal to be unable to ACK
@ -625,7 +654,8 @@ function setupChannel(target, channel) {
net . _ setSimultaneousAccepts ( handle ) ;
}
} else if ( this . _ handleQueue &&
! ( message && message . cmd === 'NODE_HANDLE_ACK' ) ) {
! ( message && ( message . cmd === 'NODE_HANDLE_ACK' ||
message . cmd === 'NODE_HANDLE_NACK' ) ) ) {
// Queue request anyway to avoid out-of-order messages.
this . _ handleQueue . push ( {
callback : callback ,
@ -647,7 +677,7 @@ function setupChannel(target, channel) {
if ( ! this . _ handleQueue )
this . _ handleQueue = [ ] ;
if ( obj && obj . postSend )
obj . postSend ( handle , options , target ) ;
obj . postSend ( message , handle , options , callback , target ) ;
}
if ( req . async ) {
@ -663,7 +693,7 @@ function setupChannel(target, channel) {
} else {
// Cleanup handle on error
if ( obj && obj . postSend )
obj . postSend ( handle , options ) ;
obj . postSend ( message , handle , options , callback ) ;
if ( ! options . swallowErrors ) {
const ex = errnoException ( err , 'write' ) ;
@ -712,10 +742,8 @@ function setupChannel(target, channel) {
// This marks the fact that the channel is actually disconnected.
this . channel = null ;
if ( this . _ pendingHandle ) {
this . _ pendingHandle . close ( ) ;
this . _ pendingHandle = null ;
}
if ( this . _ pendingMessage )
closePendingHandle ( this ) ;
var fired = false ;
function finish ( ) {