http: end with data can cause write after end

Calling end() with data while ending should trigger
a write after end error.

PR-URL: https://github.com/nodejs/node/pull/28666
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
Robert Nagy 2019-07-13 14:04:37 +02:00
parent a7c523e26d
commit c776a37791
2 changed files with 54 additions and 20 deletions

View File

@ -641,17 +641,20 @@ OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
return ret;
};
function writeAfterEnd(msg, callback) {
const err = new ERR_STREAM_WRITE_AFTER_END();
const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined;
defaultTriggerAsyncIdScope(triggerAsyncId,
process.nextTick,
writeAfterEndNT,
msg,
err,
callback);
}
function write_(msg, chunk, encoding, callback, fromEnd) {
if (msg.finished) {
const err = new ERR_STREAM_WRITE_AFTER_END();
const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined;
defaultTriggerAsyncIdScope(triggerAsyncId,
process.nextTick,
writeAfterEndNT,
msg,
err,
callback);
writeAfterEnd(msg, callback);
return true;
}
@ -748,17 +751,6 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
encoding = null;
}
if (this.finished) {
if (typeof callback === 'function') {
if (!this.writableFinished) {
this.on('finish', callback);
} else {
callback(new ERR_STREAM_ALREADY_FINISHED('end'));
}
}
return this;
}
if (this.socket) {
this.socket.cork();
}
@ -767,6 +759,12 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
}
if (this.finished) {
writeAfterEnd(this, callback);
return this;
}
if (!this._header) {
if (typeof chunk === 'string')
this._contentLength = Buffer.byteLength(chunk, encoding);
@ -774,6 +772,15 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
this._contentLength = chunk.length;
}
write_(this, chunk, encoding, null, true);
} else if (this.finished) {
if (typeof callback === 'function') {
if (!this.writableFinished) {
this.on('finish', callback);
} else {
callback(new ERR_STREAM_ALREADY_FINISHED('end'));
}
}
return this;
} else if (!this._header) {
this._contentLength = 0;
this._implicitHeader();

View File

@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
const http = require('http');
const server = http.createServer(handle);
function handle(req, res) {
res.on('error', common.mustCall((err) => {
common.expectsError({
code: 'ERR_STREAM_WRITE_AFTER_END',
name: 'Error'
})(err);
server.close();
}));
res.write('hello');
res.end();
setImmediate(common.mustCall(() => {
res.end('world');
}));
}
server.listen(0, common.mustCall(() => {
http.get(`http://localhost:${server.address().port}`);
}));