diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 8e6a303a2e27a5..591ff8440537c0 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -384,12 +384,12 @@ ChildProcess.prototype.spawn = function(options) { continue; } - // The stream is already cloned and piped, thus close it. + // The stream is already cloned and piped, thus stop its readable side, + // otherwise we might attempt to read from the stream when at the same time + // the child process does. if (stream.type === 'wrap') { - stream.handle.close(); - if (stream._stdio && stream._stdio instanceof EventEmitter) { - stream._stdio.emit('close'); - } + stream.handle.readStop(); + stream._stdio.pause(); continue; } diff --git a/test/parallel/test-child-process-server-close.js b/test/parallel/test-child-process-server-close.js index ec95fed67b4fa7..d70926f2e8278e 100644 --- a/test/parallel/test-child-process-server-close.js +++ b/test/parallel/test-child-process-server-close.js @@ -8,11 +8,11 @@ const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); const server = net.createServer((conn) => { - conn.on('close', common.mustCall()); - spawn(process.execPath, ['-v'], { stdio: ['ignore', conn, 'ignore'] - }).on('close', common.mustCall()); + }).on('close', common.mustCall(() => { + conn.end(); + })); }).listen(common.PIPE, () => { const client = net.connect(common.PIPE, common.mustCall()); client.on('data', () => { diff --git a/test/parallel/test-child-process-stdio-merge-stdouts-into-cat.js b/test/parallel/test-child-process-stdio-merge-stdouts-into-cat.js new file mode 100644 index 00000000000000..0158cbc2271aec --- /dev/null +++ b/test/parallel/test-child-process-stdio-merge-stdouts-into-cat.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/27097. +// Check that (cat [p1] ; cat [p2]) | cat [p3] works. + +const { spawn } = require('child_process'); +const p3 = spawn('cat', { stdio: ['pipe', 'pipe', process.stderr] }); +const p1 = spawn('cat', { stdio: ['pipe', p3.stdin, process.stderr] }); +const p2 = spawn('cat', { stdio: ['pipe', p3.stdin, process.stderr] }); +p3.stdout.setEncoding('utf8'); + +// Write three different chunks: +// - 'hello' from this process to p1 to p3 back to us +// - 'world' from this process to p2 to p3 back to us +// - 'foobar' from this process to p3 back to us +// Do so sequentially in order to avoid race conditions. +p1.stdin.end('hello\n'); +p3.stdout.once('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, 'hello\n'); + p2.stdin.end('world\n'); + p3.stdout.once('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, 'world\n'); + p3.stdin.end('foobar\n'); + p3.stdout.once('data', common.mustCall((chunk) => { + assert.strictEqual(chunk, 'foobar\n'); + })); + })); +}));