Skip to content

Commit 82d88a8

Browse files
jakecastelliwh0
authored andcommitted
stream: pipeline wait for close before calling the callback
The pipeline should wait for close event to finish before calling the callback. The `finishCount` should not below 0 when calling finish function. Fixes: #51540 Co-authored-by: wh0 <[email protected]> PR-URL: #53462 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Robert Nagy <[email protected]>
1 parent 508abfe commit 82d88a8

File tree

2 files changed

+50
-4
lines changed

2 files changed

+50
-4
lines changed

lib/internal/streams/pipeline.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ function pipelineImpl(streams, callback, opts) {
224224
finishImpl(err, --finishCount === 0);
225225
}
226226

227+
function finishOnlyHandleError(err) {
228+
finishImpl(err, false);
229+
}
230+
227231
function finishImpl(err, final) {
228232
if (err && (!error || error.code === 'ERR_STREAM_PREMATURE_CLOSE')) {
229233
error = err;
@@ -273,7 +277,7 @@ function pipelineImpl(streams, callback, opts) {
273277
err.name !== 'AbortError' &&
274278
err.code !== 'ERR_STREAM_PREMATURE_CLOSE'
275279
) {
276-
finish(err);
280+
finishOnlyHandleError(err);
277281
}
278282
}
279283
stream.on('error', onError);
@@ -366,7 +370,7 @@ function pipelineImpl(streams, callback, opts) {
366370
} else if (isNodeStream(stream)) {
367371
if (isReadableNodeStream(ret)) {
368372
finishCount += 2;
369-
const cleanup = pipe(ret, stream, finish, { end });
373+
const cleanup = pipe(ret, stream, finish, finishOnlyHandleError, { end });
370374
if (isReadable(stream) && isLastStream) {
371375
lastStreamCleanup.push(cleanup);
372376
}
@@ -409,12 +413,12 @@ function pipelineImpl(streams, callback, opts) {
409413
return ret;
410414
}
411415

412-
function pipe(src, dst, finish, { end }) {
416+
function pipe(src, dst, finish, finishOnlyHandleError, { end }) {
413417
let ended = false;
414418
dst.on('close', () => {
415419
if (!ended) {
416420
// Finish if the destination closes before the source has completed.
417-
finish(new ERR_STREAM_PREMATURE_CLOSE());
421+
finishOnlyHandleError(new ERR_STREAM_PREMATURE_CLOSE());
418422
}
419423
});
420424

test/parallel/test-stream-pipeline.js

+42
Original file line numberDiff line numberDiff line change
@@ -1664,5 +1664,47 @@ const tsp = require('timers/promises');
16641664
pipeline(r, w, common.mustCall((err) => {
16651665
assert.strictEqual(err, undefined);
16661666
}));
1667+
}
1668+
1669+
{
1670+
// See https://github.com/nodejs/node/issues/51540 for the following 2 tests
1671+
const src = new Readable();
1672+
const dst = new Writable({
1673+
destroy(error, cb) {
1674+
// Takes a while to destroy
1675+
setImmediate(cb);
1676+
},
1677+
});
1678+
1679+
pipeline(src, dst, (err) => {
1680+
assert.strictEqual(src.closed, true);
1681+
assert.strictEqual(dst.closed, true);
1682+
assert.strictEqual(err.message, 'problem');
1683+
});
1684+
src.destroy(new Error('problem'));
1685+
}
16671686

1687+
{
1688+
const src = new Readable();
1689+
const dst = new Writable({
1690+
destroy(error, cb) {
1691+
// Takes a while to destroy
1692+
setImmediate(cb);
1693+
},
1694+
});
1695+
const passThroughs = [];
1696+
for (let i = 0; i < 10; i++) {
1697+
passThroughs.push(new PassThrough());
1698+
}
1699+
1700+
pipeline(src, ...passThroughs, dst, (err) => {
1701+
assert.strictEqual(src.closed, true);
1702+
assert.strictEqual(dst.closed, true);
1703+
assert.strictEqual(err.message, 'problem');
1704+
1705+
for (let i = 0; i < passThroughs.length; i++) {
1706+
assert.strictEqual(passThroughs[i].closed, true);
1707+
}
1708+
});
1709+
src.destroy(new Error('problem'));
16681710
}

0 commit comments

Comments
 (0)