Skip to content

Commit 30cc542

Browse files
committed
http: don't emit error after close
Refs: #33591 PR-URL: #33654 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent fdf10ad commit 30cc542

8 files changed

+64
-25
lines changed

lib/_http_client.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const Agent = require('_http_agent');
4949
const { Buffer } = require('buffer');
5050
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
5151
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
52-
const { kOutHeaders, kNeedDrain } = require('internal/http');
52+
const { kOutHeaders, kNeedDrain, kClosed } = require('internal/http');
5353
const { connResetException, codes } = require('internal/errors');
5454
const {
5555
ERR_HTTP_HEADERS_SENT,
@@ -385,6 +385,7 @@ function _destroy(req, socket, err) {
385385
if (err) {
386386
req.emit('error', err);
387387
}
388+
req[kClosed] = true;
388389
req.emit('close');
389390
}
390391
}
@@ -427,6 +428,7 @@ function socketCloseListener() {
427428
res.emit('error', connResetException('aborted'));
428429
}
429430
}
431+
req[kClosed] = true;
430432
req.emit('close');
431433
if (!res.aborted && res.readable) {
432434
res.on('end', function() {
@@ -446,6 +448,7 @@ function socketCloseListener() {
446448
req.socket._hadError = true;
447449
req.emit('error', connResetException('socket hang up'));
448450
}
451+
req[kClosed] = true;
449452
req.emit('close');
450453
}
451454

@@ -550,6 +553,7 @@ function socketOnData(d) {
550553

551554
req.emit(eventName, res, socket, bodyHead);
552555
req.destroyed = true;
556+
req[kClosed] = true;
553557
req.emit('close');
554558
} else {
555559
// Requested Upgrade or used CONNECT method, but have no handler.
@@ -721,6 +725,7 @@ function requestOnPrefinish() {
721725
}
722726

723727
function emitFreeNT(req) {
728+
req[kClosed] = true;
724729
req.emit('close');
725730
if (req.res) {
726731
req.res.emit('close');

lib/_http_outgoing.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const assert = require('internal/assert');
3636
const EE = require('events');
3737
const Stream = require('stream');
3838
const internalUtil = require('internal/util');
39-
const { kOutHeaders, utcDate, kNeedDrain } = require('internal/http');
39+
const { kOutHeaders, utcDate, kNeedDrain, kClosed } = require('internal/http');
4040
const { Buffer } = require('buffer');
4141
const common = require('_http_common');
4242
const checkIsHttpToken = common._checkIsHttpToken;
@@ -117,6 +117,7 @@ function OutgoingMessage() {
117117
this.finished = false;
118118
this._headerSent = false;
119119
this[kCorked] = 0;
120+
this[kClosed] = false;
120121

121122
this.socket = null;
122123
this._header = null;
@@ -663,7 +664,9 @@ function onError(msg, err, callback) {
663664

664665
function emitErrorNt(msg, err, callback) {
665666
callback(err);
666-
if (typeof msg.emit === 'function') msg.emit('error', err);
667+
if (typeof msg.emit === 'function' && !msg[kClosed]) {
668+
msg.emit('error', err);
669+
}
667670
}
668671

669672
function write_(msg, chunk, encoding, callback, fromEnd) {
@@ -690,7 +693,11 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
690693
}
691694

692695
if (err) {
693-
onError(msg, err, callback);
696+
if (!msg.destroyed) {
697+
onError(msg, err, callback);
698+
} else {
699+
process.nextTick(callback, err);
700+
}
694701
return false;
695702
}
696703

lib/_http_server.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const { OutgoingMessage } = require('_http_outgoing');
4949
const {
5050
kOutHeaders,
5151
kNeedDrain,
52+
kClosed,
5253
emitStatistics
5354
} = require('internal/http');
5455
const {
@@ -212,6 +213,7 @@ function onServerResponseClose() {
212213
// Fortunately, that requires only a single if check. :-)
213214
if (this._httpMessage) {
214215
this._httpMessage.destroyed = true;
216+
this._httpMessage[kClosed] = true;
215217
this._httpMessage.emit('close');
216218
}
217219
}
@@ -729,6 +731,7 @@ function resOnFinish(req, res, socket, state, server) {
729731

730732
function emitCloseNT(self) {
731733
self.destroyed = true;
734+
self[kClosed] = true;
732735
self.emit('close');
733736
}
734737

lib/internal/http.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function emitStatistics(statistics) {
5151
module.exports = {
5252
kOutHeaders: Symbol('kOutHeaders'),
5353
kNeedDrain: Symbol('kNeedDrain'),
54+
kClosed: Symbol('kClosed'),
5455
nowDate,
5556
utcDate,
5657
emitStatistics

test/parallel/test-http-outgoing-destroy.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,8 @@ const OutgoingMessage = http.OutgoingMessage;
1010
assert.strictEqual(msg.destroyed, false);
1111
msg.destroy();
1212
assert.strictEqual(msg.destroyed, true);
13-
let callbackCalled = false;
1413
msg.write('asd', common.mustCall((err) => {
1514
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
16-
callbackCalled = true;
17-
}));
18-
msg.on('error', common.mustCall((err) => {
19-
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
20-
assert.strictEqual(callbackCalled, true);
2115
}));
16+
msg.on('error', common.mustNotCall());
2217
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
const common = require('../common');
3+
const http = require('http');
4+
5+
const server = http.createServer(common.mustCall((req, res) => {
6+
req.pipe(res);
7+
res.on('error', common.mustNotCall());
8+
res.on('close', common.mustCall(() => {
9+
res.end('asd');
10+
process.nextTick(() => {
11+
server.close();
12+
});
13+
}));
14+
})).listen(0, () => {
15+
http
16+
.request({
17+
port: server.address().port,
18+
method: 'PUT'
19+
})
20+
.on('response', (res) => {
21+
res.destroy();
22+
})
23+
.write('asd');
24+
});

test/parallel/test-http-server-write-after-end.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ const http = require('http');
88
const server = http.createServer(handle);
99

1010
function handle(req, res) {
11-
res.on('error', common.mustCall((err) => {
12-
common.expectsError({
13-
code: 'ERR_STREAM_WRITE_AFTER_END',
14-
name: 'Error'
15-
})(err);
16-
server.close();
17-
}));
11+
res.on('error', common.mustNotCall());
1812

1913
res.write('hello');
2014
res.end();
2115

2216
setImmediate(common.mustCall(() => {
23-
res.write('world');
17+
res.write('world', common.mustCall((err) => {
18+
common.expectsError({
19+
code: 'ERR_STREAM_WRITE_AFTER_END',
20+
name: 'Error'
21+
})(err);
22+
server.close();
23+
}));
2424
}));
2525
}
2626

test/parallel/test-http-server-write-end-after-end.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@ const http = require('http');
66
const server = http.createServer(handle);
77

88
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-
}));
9+
res.on('error', common.mustNotCall());
1610

1711
res.write('hello');
1812
res.end();
1913

2014
setImmediate(common.mustCall(() => {
2115
res.end('world');
16+
process.nextTick(() => {
17+
server.close();
18+
});
19+
res.write('world', common.mustCall((err) => {
20+
common.expectsError({
21+
code: 'ERR_STREAM_WRITE_AFTER_END',
22+
name: 'Error'
23+
})(err);
24+
server.close();
25+
}));
2226
}));
2327
}
2428

0 commit comments

Comments
 (0)