stream: disallow stream methods on finished stream
Invoke callback with ERR_STREAM_ALREADY_FINISHED error if `end()` is called on a finished stream. PR-URL: https://github.com/nodejs/node/pull/28687 Refs: https://github.com/nodejs/node/issues/28667 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
85898e0aca
commit
db706da235
@ -1703,6 +1703,12 @@ An attempt was made to call [`stream.pipe()`][] on a [`Writable`][] stream.
|
||||
A stream method was called that cannot complete because the stream was
|
||||
destroyed using `stream.destroy()`.
|
||||
|
||||
<a id="ERR_STREAM_ALREADY_FINISHED"></a>
|
||||
### ERR_STREAM_ALREADY_FINISHED
|
||||
|
||||
A stream method was called that cannot complete because the stream was
|
||||
finished.
|
||||
|
||||
<a id="ERR_STREAM_NULL_VALUES"></a>
|
||||
### ERR_STREAM_NULL_VALUES
|
||||
|
||||
|
@ -46,6 +46,7 @@ const {
|
||||
ERR_INVALID_CHAR,
|
||||
ERR_METHOD_NOT_IMPLEMENTED,
|
||||
ERR_STREAM_CANNOT_PIPE,
|
||||
ERR_STREAM_ALREADY_FINISHED,
|
||||
ERR_STREAM_WRITE_AFTER_END
|
||||
},
|
||||
hideStackFrames
|
||||
@ -704,6 +705,13 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
|
||||
}
|
||||
|
||||
if (this.finished) {
|
||||
if (typeof callback === 'function') {
|
||||
if (!this.writableFinished) {
|
||||
this.on('finish', callback);
|
||||
} else {
|
||||
callback(new ERR_STREAM_ALREADY_FINISHED('end'));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ const {
|
||||
ERR_MULTIPLE_CALLBACK,
|
||||
ERR_STREAM_CANNOT_PIPE,
|
||||
ERR_STREAM_DESTROYED,
|
||||
ERR_STREAM_ALREADY_FINISHED,
|
||||
ERR_STREAM_NULL_VALUES,
|
||||
ERR_STREAM_WRITE_AFTER_END,
|
||||
ERR_UNKNOWN_ENCODING
|
||||
@ -591,6 +592,13 @@ Writable.prototype.end = function(chunk, encoding, cb) {
|
||||
// Ignore unnecessary end() calls.
|
||||
if (!state.ending)
|
||||
endWritable(this, state, cb);
|
||||
else if (typeof cb === 'function') {
|
||||
if (!state.finished) {
|
||||
this.once('finish', cb);
|
||||
} else {
|
||||
cb(new ERR_STREAM_ALREADY_FINISHED('end'));
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -1128,6 +1128,9 @@ E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error);
|
||||
E('ERR_SRI_PARSE',
|
||||
'Subresource Integrity string %s had an unexpected at %d',
|
||||
SyntaxError);
|
||||
E('ERR_STREAM_ALREADY_FINISHED',
|
||||
'Cannot call %s after a stream was finished',
|
||||
Error);
|
||||
E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error);
|
||||
E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error);
|
||||
E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);
|
||||
|
27
test/parallel/test-http-outgoing-end-multiple.js
Normal file
27
test/parallel/test-http-outgoing-end-multiple.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
|
||||
const server = http.createServer(common.mustCall(function(req, res) {
|
||||
res.end('testing ended state', common.mustCall());
|
||||
res.end(common.mustCall());
|
||||
res.on('finish', common.mustCall(() => {
|
||||
res.end(common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED');
|
||||
server.close();
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
|
||||
server.listen(0);
|
||||
|
||||
server.on('listening', common.mustCall(function() {
|
||||
http
|
||||
.request({
|
||||
port: server.address().port,
|
||||
method: 'GET',
|
||||
path: '/'
|
||||
})
|
||||
.end();
|
||||
}));
|
20
test/parallel/test-stream-writable-end-multiple.js
Normal file
20
test/parallel/test-stream-writable-end-multiple.js
Normal file
@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const stream = require('stream');
|
||||
|
||||
const writable = new stream.Writable();
|
||||
|
||||
writable._write = (chunk, encoding, cb) => {
|
||||
setTimeout(() => cb(), 10);
|
||||
};
|
||||
|
||||
writable.end('testing ended state', common.mustCall());
|
||||
writable.end(common.mustCall());
|
||||
writable.on('finish', common.mustCall(() => {
|
||||
writable.end(common.mustCall((err) => {
|
||||
assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED');
|
||||
}));
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user