mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
child_process: add AbortSignal support
PR-URL: https://github.com/nodejs/node/pull/36308 Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
@@ -250,6 +250,9 @@ lsExample();
|
||||
<!-- YAML
|
||||
added: v0.1.91
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/36308
|
||||
description: AbortSignal support was added.
|
||||
- version: v8.8.0
|
||||
pr-url: https://github.com/nodejs/node/pull/15380
|
||||
description: The `windowsHide` option is supported now.
|
||||
@@ -277,6 +280,7 @@ changes:
|
||||
`'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different
|
||||
shell can be specified as a string. See [Shell requirements][] and
|
||||
[Default Windows shell][]. **Default:** `false` (no shell).
|
||||
* `signal` {AbortSignal} allows aborting the execFile using an AbortSignal
|
||||
* `callback` {Function} Called with the output when process terminates.
|
||||
* `error` {Error}
|
||||
* `stdout` {string|Buffer}
|
||||
@@ -330,6 +334,19 @@ getVersion();
|
||||
function. Any input containing shell metacharacters may be used to trigger
|
||||
arbitrary command execution.**
|
||||
|
||||
If the `signal` option is enabled, calling `.abort()` on the corresponding
|
||||
`AbortController` is similar to calling `.kill()` on the child process except
|
||||
the error passed to the callback will be an `AbortError`:
|
||||
|
||||
```js
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
const child = execFile('node', ['--version'], { signal }, (error) => {
|
||||
console.log(error); // an AbortError
|
||||
});
|
||||
signal.abort();
|
||||
```
|
||||
|
||||
### `child_process.fork(modulePath[, args][, options])`
|
||||
<!-- YAML
|
||||
added: v0.5.0
|
||||
|
||||
@@ -58,15 +58,24 @@ let debug = require('internal/util/debuglog').debuglog(
|
||||
);
|
||||
const { Buffer } = require('buffer');
|
||||
const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap');
|
||||
|
||||
const {
|
||||
AbortError,
|
||||
codes: errorCodes,
|
||||
} = require('internal/errors');
|
||||
const {
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_CHILD_PROCESS_IPC_REQUIRED,
|
||||
ERR_CHILD_PROCESS_STDIO_MAXBUFFER,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_OUT_OF_RANGE
|
||||
} = require('internal/errors').codes;
|
||||
ERR_OUT_OF_RANGE,
|
||||
} = errorCodes;
|
||||
const { clearTimeout, setTimeout } = require('timers');
|
||||
const { validateString, isInt32 } = require('internal/validators');
|
||||
const {
|
||||
validateString,
|
||||
isInt32,
|
||||
validateAbortSignal,
|
||||
} = require('internal/validators');
|
||||
const child_process = require('internal/child_process');
|
||||
const {
|
||||
getValidStdio,
|
||||
@@ -245,6 +254,9 @@ function execFile(file /* , args, options, callback */) {
|
||||
// Validate maxBuffer, if present.
|
||||
validateMaxBuffer(options.maxBuffer);
|
||||
|
||||
// Validate signal, if present
|
||||
validateAbortSignal(options.signal, 'options.signal');
|
||||
|
||||
options.killSignal = sanitizeKillSignal(options.killSignal);
|
||||
|
||||
const child = spawn(file, args, {
|
||||
@@ -362,6 +374,20 @@ function execFile(file /* , args, options, callback */) {
|
||||
timeoutId = null;
|
||||
}, options.timeout);
|
||||
}
|
||||
if (options.signal) {
|
||||
if (options.signal.aborted) {
|
||||
process.nextTick(() => kill());
|
||||
} else {
|
||||
options.signal.addEventListener('abort', () => {
|
||||
if (!ex) {
|
||||
ex = new AbortError();
|
||||
}
|
||||
kill();
|
||||
});
|
||||
const remove = () => options.signal.removeEventListener('abort', kill);
|
||||
child.once('close', remove);
|
||||
}
|
||||
}
|
||||
|
||||
if (child.stdout) {
|
||||
if (encoding)
|
||||
|
||||
@@ -114,6 +114,7 @@ if (!common.isMainThread) {
|
||||
'Internal Binding performance',
|
||||
'Internal Binding symbols',
|
||||
'Internal Binding worker',
|
||||
'NativeModule internal/streams/add-abort-signal',
|
||||
'NativeModule internal/streams/duplex',
|
||||
'NativeModule internal/streams/passthrough',
|
||||
'NativeModule internal/streams/readable',
|
||||
|
||||
@@ -7,6 +7,7 @@ const { getSystemErrorName } = require('util');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const fixture = fixtures.path('exit.js');
|
||||
const echoFixture = fixtures.path('echo.js');
|
||||
const execOpts = { encoding: 'utf8', shell: true };
|
||||
|
||||
{
|
||||
@@ -45,3 +46,16 @@ const execOpts = { encoding: 'utf8', shell: true };
|
||||
// Verify the shell option works properly
|
||||
execFile(process.execPath, [fixture, 0], execOpts, common.mustSucceed());
|
||||
}
|
||||
|
||||
{
|
||||
// Verify that the signal option works properly
|
||||
const ac = new AbortController();
|
||||
const { signal } = ac;
|
||||
|
||||
const callback = common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ABORT_ERR');
|
||||
assert.strictEqual(err.name, 'AbortError');
|
||||
});
|
||||
execFile(process.execPath, [echoFixture, 0], { signal }, callback);
|
||||
ac.abort();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user