From 22adaaa297b82c2d2d4eb53c3b5d7363d2e80d1a Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sat, 13 Jul 2019 14:04:37 +0200 Subject: [PATCH 1/2] http: end with data can cause write after end --- lib/_http_outgoing.js | 47 +++++++++++-------- .../test-http-server-write-end-after-end.js | 27 +++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 test/parallel/test-http-server-write-end-after-end.js diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 0dc13f19fe918c..55561bef7b0792 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -620,17 +620,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; } @@ -724,17 +727,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(); } @@ -743,6 +735,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); @@ -750,6 +748,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(); diff --git a/test/parallel/test-http-server-write-end-after-end.js b/test/parallel/test-http-server-write-end-after-end.js new file mode 100644 index 00000000000000..d5c703906568cf --- /dev/null +++ b/test/parallel/test-http-server-write-end-after-end.js @@ -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', + type: 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}`); +})); From 4e00a869bb20e0bfaf6878217ea8f78e6ea0d391 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Wed, 1 Jan 2020 14:39:02 +0100 Subject: [PATCH 2/2] Update test/parallel/test-http-server-write-end-after-end.js --- test/parallel/test-http-server-write-end-after-end.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-http-server-write-end-after-end.js b/test/parallel/test-http-server-write-end-after-end.js index d5c703906568cf..37fbe062f12b69 100644 --- a/test/parallel/test-http-server-write-end-after-end.js +++ b/test/parallel/test-http-server-write-end-after-end.js @@ -9,7 +9,7 @@ function handle(req, res) { res.on('error', common.mustCall((err) => { common.expectsError({ code: 'ERR_STREAM_WRITE_AFTER_END', - type: Error + name: 'Error' })(err); server.close(); }));