From 0e841b45c2fc7babef16b9810877287f8123f563 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Tue, 3 Aug 2021 09:01:31 +0200 Subject: [PATCH] stream: don't emit 'data' after 'error' or 'close' As per doc we should not emit further events after 'close' and only 'close' after 'error'. Fixes: https://github.com/nodejs/node/issues/39630 PR-URL: https://github.com/nodejs/node/pull/39639 Reviewed-By: Matteo Collina Reviewed-By: Benjamin Gruenbaum Reviewed-By: Antoine du Hamel Reviewed-By: Ruben Bridgewater --- lib/internal/streams/readable.js | 5 +- test/parallel/test-stream-readable-destroy.js | 82 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 9a3d5c4556b039..947a5525ae3307 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -276,6 +276,8 @@ function readableAddChunk(stream, chunk, encoding, addToFront) { if (addToFront) { if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT()); + else if (state.destroyed || state.errored) + return false; else addChunk(stream, state, chunk, true); } else if (state.ended) { @@ -316,6 +318,7 @@ function addChunk(stream, state, chunk, addToFront) { } else { state.awaitDrainWriters = null; } + state.dataEmitted = true; stream.emit('data', chunk); } else { @@ -542,7 +545,7 @@ Readable.prototype.read = function(n) { endReadable(this); } - if (ret !== null) { + if (ret !== null && !state.errorEmitted && !state.closeEmitted) { state.dataEmitted = true; this.emit('data', ret); } diff --git a/test/parallel/test-stream-readable-destroy.js b/test/parallel/test-stream-readable-destroy.js index 9ba3f9cd3653d6..ffea6cb9c869f3 100644 --- a/test/parallel/test-stream-readable-destroy.js +++ b/test/parallel/test-stream-readable-destroy.js @@ -319,3 +319,85 @@ const assert = require('assert'); })(), /AbortError/); setTimeout(() => controller.abort(), 0); } + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('error', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(new Error('asd')); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.unshift('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.unshift('asd'); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.resume(); + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.push('asd'); +}