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 <matteo.collina@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
Robert Nagy 2021-08-03 09:01:31 +02:00
parent 8bfb4b9815
commit 0e841b45c2
2 changed files with 86 additions and 1 deletions

View File

@ -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);
}

View File

@ -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');
}