Skip to content

Commit ac5fa2c

Browse files
Fishrock123targos
authored andcommitted
child_process: truncate output when maxBuffer is exceeded
Preserves truncated output for `child_process.exec()` when `maxBuffer` is exceeded. This is particularly useful for commands which have indistinguishable error codes for what output they produce. PR-URL: #24951 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 90a64ab commit ac5fa2c

File tree

3 files changed

+45
-16
lines changed

3 files changed

+45
-16
lines changed

doc/api/child_process.md

+12-8
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ changes:
147147
`'/bin/sh'` on UNIX, `process.env.ComSpec` on Windows.
148148
* `timeout` {number} **Default:** `0`
149149
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
150-
stderr. If exceeded, the child process is terminated. See caveat at
151-
[`maxBuffer` and Unicode][]. **Default:** `200 * 1024`.
150+
stderr. If exceeded, the child process is terminated and any output is
151+
truncated. See caveat at [`maxBuffer` and Unicode][].
152+
**Default:** `200 * 1024`.
152153
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
153154
* `uid` {number} Sets the user identity of the process (see setuid(2)).
154155
* `gid` {number} Sets the group identity of the process (see setgid(2)).
@@ -245,8 +246,9 @@ changes:
245246
* `encoding` {string} **Default:** `'utf8'`
246247
* `timeout` {number} **Default:** `0`
247248
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
248-
stderr. If exceeded, the child process is terminated. See caveat at
249-
[`maxBuffer` and Unicode][]. **Default:** `200 * 1024`.
249+
stderr. If exceeded, the child process is terminated and any output is
250+
truncated. See caveat at [`maxBuffer` and Unicode][].
251+
**Default:** `200 * 1024`.
250252
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
251253
* `uid` {number} Sets the user identity of the process (see setuid(2)).
252254
* `gid` {number} Sets the group identity of the process (see setgid(2)).
@@ -779,8 +781,9 @@ changes:
779781
* `killSignal` {string|integer} The signal value to be used when the spawned
780782
process will be killed. **Default:** `'SIGTERM'`.
781783
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
782-
stderr. If exceeded, the child process is terminated. See caveat at
783-
[`maxBuffer` and Unicode][]. **Default:** `200 * 1024`.
784+
stderr. If exceeded, the child process is terminated and any output is
785+
truncated. See caveat at [`maxBuffer` and Unicode][].
786+
**Default:** `200 * 1024`.
784787
* `encoding` {string} The encoding used for all stdio inputs and outputs.
785788
**Default:** `'buffer'`.
786789
* `windowsHide` {boolean} Hide the subprocess console window that would
@@ -842,8 +845,9 @@ changes:
842845
* `killSignal` {string|integer} The signal value to be used when the spawned
843846
process will be killed. **Default:** `'SIGTERM'`.
844847
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
845-
stderr. If exceeded, the child process is terminated. See caveat at
846-
[`maxBuffer` and Unicode][]. **Default:** `200 * 1024`.
848+
stderr. If exceeded, the child process is terminated and any output is
849+
truncated. See caveat at [`maxBuffer` and Unicode][].
850+
**Default:** `200 * 1024`.
847851
* `encoding` {string} The encoding used for all stdio inputs and outputs.
848852
**Default:** `'buffer'`.
849853
* `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses

lib/child_process.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,15 @@ exports.execFile = function execFile(file /* , args, options, callback */) {
345345

346346
child.stdout.on('data', function onChildStdout(chunk) {
347347
var encoding = child.stdout._readableState.encoding;
348-
stdoutLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length;
348+
const length = encoding ?
349+
Buffer.byteLength(chunk, encoding) :
350+
chunk.length;
351+
stdoutLen += length;
349352

350353
if (stdoutLen > options.maxBuffer) {
354+
const truncatedLen = options.maxBuffer - (stdoutLen - length);
355+
_stdout.push(chunk.slice(0, truncatedLen));
356+
351357
ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stdout');
352358
kill();
353359
} else {
@@ -362,9 +368,15 @@ exports.execFile = function execFile(file /* , args, options, callback */) {
362368

363369
child.stderr.on('data', function onChildStderr(chunk) {
364370
var encoding = child.stderr._readableState.encoding;
365-
stderrLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length;
371+
const length = encoding ?
372+
Buffer.byteLength(chunk, encoding) :
373+
chunk.length;
374+
stderrLen += length;
366375

367376
if (stderrLen > options.maxBuffer) {
377+
const truncatedLen = options.maxBuffer - (stderrLen - length);
378+
_stderr.push(chunk.slice(0, truncatedLen));
379+
368380
ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stderr');
369381
kill();
370382
} else {

test/parallel/test-child-process-exec-maxBuffer.js

+19-6
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ function runChecks(err, stdio, streamName, expected) {
2222
}
2323

2424
{
25-
const cmd = 'echo "hello world"';
25+
const cmd = 'echo hello world';
2626

2727
cp.exec(
2828
cmd,
2929
{ maxBuffer: 5 },
3030
common.mustCall((err, stdout, stderr) => {
31-
runChecks(err, { stdout, stderr }, 'stdout', '');
31+
runChecks(err, { stdout, stderr }, 'stdout', 'hello');
3232
})
3333
);
3434
}
@@ -42,7 +42,7 @@ const unicode = '中文测试'; // length = 4, byte length = 12
4242
cmd,
4343
{ maxBuffer: 10 },
4444
common.mustCall((err, stdout, stderr) => {
45-
runChecks(err, { stdout, stderr }, 'stdout', '');
45+
runChecks(err, { stdout, stderr }, 'stdout', '中文测试\n');
4646
})
4747
);
4848
}
@@ -54,7 +54,7 @@ const unicode = '中文测试'; // length = 4, byte length = 12
5454
cmd,
5555
{ maxBuffer: 3 },
5656
common.mustCall((err, stdout, stderr) => {
57-
runChecks(err, { stdout, stderr }, 'stderr', '');
57+
runChecks(err, { stdout, stderr }, 'stderr', '中文测');
5858
})
5959
);
6060
}
@@ -66,7 +66,7 @@ const unicode = '中文测试'; // length = 4, byte length = 12
6666
cmd,
6767
{ encoding: null, maxBuffer: 10 },
6868
common.mustCall((err, stdout, stderr) => {
69-
runChecks(err, { stdout, stderr }, 'stdout', '');
69+
runChecks(err, { stdout, stderr }, 'stdout', '中文测试\n');
7070
})
7171
);
7272

@@ -80,9 +80,22 @@ const unicode = '中文测试'; // length = 4, byte length = 12
8080
cmd,
8181
{ encoding: null, maxBuffer: 3 },
8282
common.mustCall((err, stdout, stderr) => {
83-
runChecks(err, { stdout, stderr }, 'stderr', '');
83+
runChecks(err, { stdout, stderr }, 'stderr', '中文测');
8484
})
8585
);
8686

8787
child.stderr.setEncoding('utf-8');
8888
}
89+
90+
{
91+
const cmd = `"${process.execPath}" -e "console.error('${unicode}');"`;
92+
93+
cp.exec(
94+
cmd,
95+
{ encoding: null, maxBuffer: 5 },
96+
common.mustCall((err, stdout, stderr) => {
97+
const buf = Buffer.from(unicode).slice(0, 5);
98+
runChecks(err, { stdout, stderr }, 'stderr', buf);
99+
})
100+
);
101+
}

0 commit comments

Comments
 (0)