Skip to content

Commit

Permalink
http: end with data can cause write after end
Browse files Browse the repository at this point in the history
Calling end() with data while ending should trigger
a write after end error.

PR-URL: #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>
  • Loading branch information
ronag authored and sbaayel committed Oct 10, 2023
1 parent 629981a commit 97fea3d
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 20 deletions.
47 changes: 27 additions & 20 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
}
Expand All @@ -767,13 +759,28 @@ 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);
else
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();
Expand Down
27 changes: 27 additions & 0 deletions test/parallel/test-http-server-write-end-after-end.js
Original file line number Diff line number Diff line change
@@ -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}`);
}));

0 comments on commit 97fea3d

Please sign in to comment.