Browse Source

child_process: support options in send()

This commit adds an options object to process.send(). The same
object is propagated to process._send(), the _handleQueue, and the
send() and postSend() functions of the handle converter.

Fixes: https://github.com/nodejs/node/issues/4271
PR-URL: https://github.com/nodejs/node/pull/5283
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
process-exit-stdio-flushing
cjihrig 9 years ago
parent
commit
1952844f45
  1. 6
      doc/api/child_process.markdown
  2. 3
      doc/api/process.markdown
  3. 49
      lib/internal/child_process.js
  4. 25
      test/parallel/test-child-process-send-type-error.js

6
doc/api/child_process.markdown

@ -750,10 +750,11 @@ console.log(`Spawned child pid: ${grep.pid}`);
grep.stdin.end();
```
### child.send(message[, sendHandle][, callback])
### child.send(message[, sendHandle[, options]][, callback])
* `message` {Object}
* `sendHandle` {Handle}
* `options` {Object}
* `callback` {Function}
* Return: {Boolean}
@ -801,6 +802,9 @@ passing a TCP server or socket object to the child process. The child will
receive the object as the second argument passed to the callback function
registered on the `process.on('message')` event.
The `options` argument, if present, is an object used to parameterize the
sending of certain types of handles.
The optional `callback` is a function that is invoked after the message is
sent but before the child may have received it. The function is called with a
single argument: `null` on success, or an [`Error`][] object on failure.

3
doc/api/process.markdown

@ -825,10 +825,11 @@ In custom builds from non-release versions of the source tree, only the
`name` property may be present. The additional properties should not be
relied upon to exist.
## process.send(message[, sendHandle][, callback])
## process.send(message[, sendHandle[, options]][, callback])
* `message` {Object}
* `sendHandle` {Handle object}
* `options` {Object}
* `callback` {Function}
* Return: {Boolean}

49
lib/internal/child_process.js

@ -35,7 +35,7 @@ const handleConversion = {
'net.Native': {
simultaneousAccepts: true,
send: function(message, handle) {
send: function(message, handle, options) {
return handle;
},
@ -47,7 +47,7 @@ const handleConversion = {
'net.Server': {
simultaneousAccepts: true,
send: function(message, server) {
send: function(message, server, options) {
return server._handle;
},
@ -60,7 +60,7 @@ const handleConversion = {
},
'net.Socket': {
send: function(message, socket) {
send: function(message, socket, options) {
if (!socket._handle)
return;
@ -90,7 +90,7 @@ const handleConversion = {
return handle;
},
postSend: function(handle) {
postSend: function(handle, options) {
// Close the Socket handle after sending it
if (handle)
handle.close();
@ -117,7 +117,7 @@ const handleConversion = {
'dgram.Native': {
simultaneousAccepts: false,
send: function(message, handle) {
send: function(message, handle, options) {
return handle;
},
@ -129,7 +129,7 @@ const handleConversion = {
'dgram.Socket': {
simultaneousAccepts: false,
send: function(message, socket) {
send: function(message, socket, options) {
message.dgramType = socket.type;
return socket._handle;
@ -466,7 +466,7 @@ function setupChannel(target, channel) {
target._handleQueue = null;
queue.forEach(function(args) {
target._send(args.message, args.handle, false, args.callback);
target._send(args.message, args.handle, args.options, args.callback);
});
// Process a pending disconnect (if any).
@ -498,13 +498,23 @@ function setupChannel(target, channel) {
});
});
target.send = function(message, handle, callback) {
target.send = function(message, handle, options, callback) {
if (typeof handle === 'function') {
callback = handle;
handle = undefined;
options = undefined;
} else if (typeof options === 'function') {
callback = options;
options = undefined;
} else if (options !== undefined &&
(options === null || typeof options !== 'object')) {
throw new TypeError('"options" argument must be an object');
}
options = Object.assign({swallowErrors: false}, options);
if (this.connected) {
return this._send(message, handle, false, callback);
return this._send(message, handle, options, callback);
}
const ex = new Error('channel closed');
if (typeof callback === 'function') {
@ -515,12 +525,17 @@ function setupChannel(target, channel) {
return false;
};
target._send = function(message, handle, swallowErrors, callback) {
target._send = function(message, handle, options, callback) {
assert(this.connected || this._channel);
if (message === undefined)
throw new TypeError('"message" argument cannot be undefined');
// Support legacy function signature
if (typeof options === 'boolean') {
options = {swallowErrors: options};
}
// package messages with a handle object
if (handle) {
// this message will be handled by an internalMessage event handler
@ -549,6 +564,7 @@ function setupChannel(target, channel) {
this._handleQueue.push({
callback: callback,
handle: handle,
options: options,
message: message.msg,
});
return this._handleQueue.length === 1;
@ -557,8 +573,10 @@ function setupChannel(target, channel) {
var obj = handleConversion[message.type];
// convert TCP object to native handle object
handle =
handleConversion[message.type].send.call(target, message, handle);
handle = handleConversion[message.type].send.call(target,
message,
handle,
options);
// If handle was sent twice, or it is impossible to get native handle
// out of it - just send a text without the handle.
@ -575,6 +593,7 @@ function setupChannel(target, channel) {
this._handleQueue.push({
callback: callback,
handle: null,
options: options,
message: message,
});
return this._handleQueue.length === 1;
@ -593,7 +612,7 @@ function setupChannel(target, channel) {
if (this.async === true)
control.unref();
if (obj && obj.postSend)
obj.postSend(handle);
obj.postSend(handle, options);
if (typeof callback === 'function')
callback(null);
};
@ -605,9 +624,9 @@ function setupChannel(target, channel) {
} else {
// Cleanup handle on error
if (obj && obj.postSend)
obj.postSend(handle);
obj.postSend(handle, options);
if (!swallowErrors) {
if (!options.swallowErrors) {
const ex = errnoException(err, 'write');
if (typeof callback === 'function') {
process.nextTick(callback, ex);

25
test/parallel/test-child-process-send-type-error.js

@ -0,0 +1,25 @@
'use strict';
require('../common');
const assert = require('assert');
const cp = require('child_process');
function noop() {}
function fail(proc, args) {
assert.throws(() => {
proc.send.apply(proc, args);
}, /"options" argument must be an object/);
}
let target = process;
if (process.argv[2] !== 'child')
target = cp.fork(__filename, ['child']);
fail(target, ['msg', null, null]);
fail(target, ['msg', null, '']);
fail(target, ['msg', null, 'foo']);
fail(target, ['msg', null, 0]);
fail(target, ['msg', null, NaN]);
fail(target, ['msg', null, 1]);
fail(target, ['msg', null, null, noop]);
Loading…
Cancel
Save