Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

child_process: allow Infinity as maxBuffer value #10769

Merged
merged 2 commits into from
Jan 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions lib/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ exports.execFile = function(file /*, args, options, callback*/) {
throw new TypeError('Incorrect value of args option');
}

// Validate the timeout, if present.
validateTimeout(options.timeout);

// Validate maxBuffer, if present.
validateMaxBuffer(options.maxBuffer);

var child = spawn(file, args, {
cwd: options.cwd,
env: options.env,
Expand Down Expand Up @@ -466,31 +472,17 @@ function spawnSync(/*file, args, options*/) {
debug('spawnSync', opts.args, options);

// Validate the timeout, if present.
if (options.timeout != null &&
!(Number.isInteger(options.timeout) && options.timeout >= 0)) {
throw new TypeError('"timeout" must be an unsigned integer');
}
validateTimeout(options.timeout);

// Validate maxBuffer, if present.
if (options.maxBuffer != null &&
!(Number.isInteger(options.maxBuffer) && options.maxBuffer >= 0)) {
throw new TypeError('"maxBuffer" must be an unsigned integer');
}
validateMaxBuffer(options.maxBuffer);

options.file = opts.file;
options.args = opts.args;
options.envPairs = opts.envPairs;

// Validate the kill signal, if present.
if (typeof options.killSignal === 'string' ||
typeof options.killSignal === 'number') {
options.killSignal = lookupSignal(options.killSignal);

if (options.killSignal === 0)
throw new RangeError('"killSignal" cannot be 0');
} else if (options.killSignal != null) {
throw new TypeError('"killSignal" must be a string or number');
}
// Validate and translate the kill signal, if present.
options.killSignal = validateKillSignal(options.killSignal);

options.stdio = _validateStdio(options.stdio || 'pipe', true).stdio;

Expand Down Expand Up @@ -600,3 +592,31 @@ function execSync(command /*, options*/) {
return ret.stdout;
}
exports.execSync = execSync;


function validateTimeout(timeout) {
if (timeout != null && !(Number.isInteger(timeout) && timeout >= 0)) {
throw new TypeError('"timeout" must be an unsigned integer');
}
}


function validateMaxBuffer(maxBuffer) {
if (maxBuffer != null && !(typeof maxBuffer === 'number' && maxBuffer >= 0)) {
throw new TypeError('"maxBuffer" must be a positive number');
}
}


function validateKillSignal(killSignal) {
if (typeof killSignal === 'string' || typeof killSignal === 'number') {
killSignal = lookupSignal(killSignal);

if (killSignal === 0)
throw new RangeError('"killSignal" cannot be 0');
} else if (killSignal != null) {
throw new TypeError('"killSignal" must be a string or number');
}

return killSignal;
}
4 changes: 2 additions & 2 deletions src/spawn_sync.cc
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,8 @@ int SyncProcessRunner::ParseOptions(Local<Value> js_value) {

Local<Value> js_max_buffer = js_options->Get(env()->max_buffer_string());
if (IsSet(js_max_buffer)) {
CHECK(js_max_buffer->IsUint32());
max_buffer_ = js_max_buffer->Uint32Value();
CHECK(js_max_buffer->IsNumber());
max_buffer_ = js_max_buffer->NumberValue();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm not sure that JavaScript Infinity will convert to C++ properly here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so JavaScript Infinity translates to std::numeric_limits<double>::infinity() in C++. So, I changed max_buffer_ to a double.

}

Local<Value> js_kill_signal = js_options->Get(env()->kill_signal_string());
Expand Down
2 changes: 1 addition & 1 deletion src/spawn_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class SyncProcessRunner {
static void KillTimerCallback(uv_timer_t* handle);
static void KillTimerCloseCallback(uv_handle_t* handle);

size_t max_buffer_;
double max_buffer_;
uint64_t timeout_;
int kill_signal_;

Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-child-process-exec-maxBuffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ function checkFactory(streamName) {
});
}

{
const cmd = `"${process.execPath}" -e "console.log('hello world');"`;
const options = { maxBuffer: Infinity };

cp.exec(cmd, options, common.mustCall((err, stdout, stderr) => {
assert.ifError(err);
assert.strictEqual(stdout.trim(), 'hello world');
assert.strictEqual(stderr, '');
}));
}

{
const cmd = 'echo "hello world"';

Expand Down
27 changes: 16 additions & 11 deletions test/parallel/test-child-process-spawnsync-maxbuf.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
'use strict';
require('../common');
const assert = require('assert');

const spawnSync = require('child_process').spawnSync;

const msgOut = 'this is stdout';

// This is actually not os.EOL?
Expand All @@ -14,14 +12,21 @@ const args = [
`console.log("${msgOut}");`
];

const options = {
maxBuffer: 1
};
// Verify that an error is returned if maxBuffer is surpassed.
{
const ret = spawnSync(process.execPath, args, { maxBuffer: 1 });

assert.ok(ret.error, 'maxBuffer should error');
assert.strictEqual(ret.error.errno, 'ENOBUFS');
// We can have buffers larger than maxBuffer because underneath we alloc 64k
// that matches our read sizes.
assert.deepStrictEqual(ret.stdout, msgOutBuf);
}

const ret = spawnSync(process.execPath, args, options);
// Verify that a maxBuffer size of Infinity works.
{
const ret = spawnSync(process.execPath, args, { maxBuffer: Infinity });

assert.ok(ret.error, 'maxBuffer should error');
assert.strictEqual(ret.error.errno, 'ENOBUFS');
// We can have buffers larger than maxBuffer because underneath we alloc 64k
// that matches our read sizes
assert.deepStrictEqual(ret.stdout, msgOutBuf);
assert.ifError(ret.error);
assert.deepStrictEqual(ret.stdout, msgOutBuf);
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,17 @@ if (!common.isWindows) {

{
// Validate the maxBuffer option
const err = /^TypeError: "maxBuffer" must be an unsigned integer$/;
const err = /^TypeError: "maxBuffer" must be a positive number$/;

pass('maxBuffer', undefined);
pass('maxBuffer', null);
pass('maxBuffer', 1);
pass('maxBuffer', 0);
fail('maxBuffer', 3.14, err);
pass('maxBuffer', Infinity);
pass('maxBuffer', 3.14);
fail('maxBuffer', -1, err);
fail('maxBuffer', NaN, err);
fail('maxBuffer', Infinity, err);
fail('maxBuffer', -Infinity, err);
fail('maxBuffer', true, err);
fail('maxBuffer', false, err);
fail('maxBuffer', __dirname, err);
Expand Down