Skip to content

Commit 97fea3d

Browse files
ronagsbaayel
authored andcommitted
http: end with data can cause write after end
Calling end() with data while ending should trigger a write after end error. PR-URL: nodejs#28666 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 629981a commit 97fea3d

File tree

2 files changed

+54
-20
lines changed

2 files changed

+54
-20
lines changed

lib/_http_outgoing.js

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -641,17 +641,20 @@ OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
641641
return ret;
642642
};
643643

644+
function writeAfterEnd(msg, callback) {
645+
const err = new ERR_STREAM_WRITE_AFTER_END();
646+
const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined;
647+
defaultTriggerAsyncIdScope(triggerAsyncId,
648+
process.nextTick,
649+
writeAfterEndNT,
650+
msg,
651+
err,
652+
callback);
653+
}
654+
644655
function write_(msg, chunk, encoding, callback, fromEnd) {
645656
if (msg.finished) {
646-
const err = new ERR_STREAM_WRITE_AFTER_END();
647-
const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined;
648-
defaultTriggerAsyncIdScope(triggerAsyncId,
649-
process.nextTick,
650-
writeAfterEndNT,
651-
msg,
652-
err,
653-
callback);
654-
657+
writeAfterEnd(msg, callback);
655658
return true;
656659
}
657660

@@ -748,17 +751,6 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
748751
encoding = null;
749752
}
750753

751-
if (this.finished) {
752-
if (typeof callback === 'function') {
753-
if (!this.writableFinished) {
754-
this.on('finish', callback);
755-
} else {
756-
callback(new ERR_STREAM_ALREADY_FINISHED('end'));
757-
}
758-
}
759-
return this;
760-
}
761-
762754
if (this.socket) {
763755
this.socket.cork();
764756
}
@@ -767,13 +759,28 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
767759
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
768760
throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
769761
}
762+
763+
if (this.finished) {
764+
writeAfterEnd(this, callback);
765+
return this;
766+
}
767+
770768
if (!this._header) {
771769
if (typeof chunk === 'string')
772770
this._contentLength = Buffer.byteLength(chunk, encoding);
773771
else
774772
this._contentLength = chunk.length;
775773
}
776774
write_(this, chunk, encoding, null, true);
775+
} else if (this.finished) {
776+
if (typeof callback === 'function') {
777+
if (!this.writableFinished) {
778+
this.on('finish', callback);
779+
} else {
780+
callback(new ERR_STREAM_ALREADY_FINISHED('end'));
781+
}
782+
}
783+
return this;
777784
} else if (!this._header) {
778785
this._contentLength = 0;
779786
this._implicitHeader();
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
6+
const server = http.createServer(handle);
7+
8+
function handle(req, res) {
9+
res.on('error', common.mustCall((err) => {
10+
common.expectsError({
11+
code: 'ERR_STREAM_WRITE_AFTER_END',
12+
name: 'Error'
13+
})(err);
14+
server.close();
15+
}));
16+
17+
res.write('hello');
18+
res.end();
19+
20+
setImmediate(common.mustCall(() => {
21+
res.end('world');
22+
}));
23+
}
24+
25+
server.listen(0, common.mustCall(() => {
26+
http.get(`http://localhost:${server.address().port}`);
27+
}));

0 commit comments

Comments
 (0)