http: don't emit error after close
Refs: https://github.com/nodejs/node/issues/33591 PR-URL: https://github.com/nodejs/node/pull/33654 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
fdf10adef8
commit
30cc54275d
@ -49,7 +49,7 @@ const Agent = require('_http_agent');
|
|||||||
const { Buffer } = require('buffer');
|
const { Buffer } = require('buffer');
|
||||||
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
|
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
|
||||||
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
|
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
|
||||||
const { kOutHeaders, kNeedDrain } = require('internal/http');
|
const { kOutHeaders, kNeedDrain, kClosed } = require('internal/http');
|
||||||
const { connResetException, codes } = require('internal/errors');
|
const { connResetException, codes } = require('internal/errors');
|
||||||
const {
|
const {
|
||||||
ERR_HTTP_HEADERS_SENT,
|
ERR_HTTP_HEADERS_SENT,
|
||||||
@ -385,6 +385,7 @@ function _destroy(req, socket, err) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
req.emit('error', err);
|
req.emit('error', err);
|
||||||
}
|
}
|
||||||
|
req[kClosed] = true;
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,6 +428,7 @@ function socketCloseListener() {
|
|||||||
res.emit('error', connResetException('aborted'));
|
res.emit('error', connResetException('aborted'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
req[kClosed] = true;
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
if (!res.aborted && res.readable) {
|
if (!res.aborted && res.readable) {
|
||||||
res.on('end', function() {
|
res.on('end', function() {
|
||||||
@ -446,6 +448,7 @@ function socketCloseListener() {
|
|||||||
req.socket._hadError = true;
|
req.socket._hadError = true;
|
||||||
req.emit('error', connResetException('socket hang up'));
|
req.emit('error', connResetException('socket hang up'));
|
||||||
}
|
}
|
||||||
|
req[kClosed] = true;
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,6 +553,7 @@ function socketOnData(d) {
|
|||||||
|
|
||||||
req.emit(eventName, res, socket, bodyHead);
|
req.emit(eventName, res, socket, bodyHead);
|
||||||
req.destroyed = true;
|
req.destroyed = true;
|
||||||
|
req[kClosed] = true;
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
} else {
|
} else {
|
||||||
// Requested Upgrade or used CONNECT method, but have no handler.
|
// Requested Upgrade or used CONNECT method, but have no handler.
|
||||||
@ -721,6 +725,7 @@ function requestOnPrefinish() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function emitFreeNT(req) {
|
function emitFreeNT(req) {
|
||||||
|
req[kClosed] = true;
|
||||||
req.emit('close');
|
req.emit('close');
|
||||||
if (req.res) {
|
if (req.res) {
|
||||||
req.res.emit('close');
|
req.res.emit('close');
|
||||||
|
@ -36,7 +36,7 @@ const assert = require('internal/assert');
|
|||||||
const EE = require('events');
|
const EE = require('events');
|
||||||
const Stream = require('stream');
|
const Stream = require('stream');
|
||||||
const internalUtil = require('internal/util');
|
const internalUtil = require('internal/util');
|
||||||
const { kOutHeaders, utcDate, kNeedDrain } = require('internal/http');
|
const { kOutHeaders, utcDate, kNeedDrain, kClosed } = require('internal/http');
|
||||||
const { Buffer } = require('buffer');
|
const { Buffer } = require('buffer');
|
||||||
const common = require('_http_common');
|
const common = require('_http_common');
|
||||||
const checkIsHttpToken = common._checkIsHttpToken;
|
const checkIsHttpToken = common._checkIsHttpToken;
|
||||||
@ -117,6 +117,7 @@ function OutgoingMessage() {
|
|||||||
this.finished = false;
|
this.finished = false;
|
||||||
this._headerSent = false;
|
this._headerSent = false;
|
||||||
this[kCorked] = 0;
|
this[kCorked] = 0;
|
||||||
|
this[kClosed] = false;
|
||||||
|
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this._header = null;
|
this._header = null;
|
||||||
@ -663,7 +664,9 @@ function onError(msg, err, callback) {
|
|||||||
|
|
||||||
function emitErrorNt(msg, err, callback) {
|
function emitErrorNt(msg, err, callback) {
|
||||||
callback(err);
|
callback(err);
|
||||||
if (typeof msg.emit === 'function') msg.emit('error', err);
|
if (typeof msg.emit === 'function' && !msg[kClosed]) {
|
||||||
|
msg.emit('error', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function write_(msg, chunk, encoding, callback, fromEnd) {
|
function write_(msg, chunk, encoding, callback, fromEnd) {
|
||||||
@ -690,7 +693,11 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
onError(msg, err, callback);
|
if (!msg.destroyed) {
|
||||||
|
onError(msg, err, callback);
|
||||||
|
} else {
|
||||||
|
process.nextTick(callback, err);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ const { OutgoingMessage } = require('_http_outgoing');
|
|||||||
const {
|
const {
|
||||||
kOutHeaders,
|
kOutHeaders,
|
||||||
kNeedDrain,
|
kNeedDrain,
|
||||||
|
kClosed,
|
||||||
emitStatistics
|
emitStatistics
|
||||||
} = require('internal/http');
|
} = require('internal/http');
|
||||||
const {
|
const {
|
||||||
@ -212,6 +213,7 @@ function onServerResponseClose() {
|
|||||||
// Fortunately, that requires only a single if check. :-)
|
// Fortunately, that requires only a single if check. :-)
|
||||||
if (this._httpMessage) {
|
if (this._httpMessage) {
|
||||||
this._httpMessage.destroyed = true;
|
this._httpMessage.destroyed = true;
|
||||||
|
this._httpMessage[kClosed] = true;
|
||||||
this._httpMessage.emit('close');
|
this._httpMessage.emit('close');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -729,6 +731,7 @@ function resOnFinish(req, res, socket, state, server) {
|
|||||||
|
|
||||||
function emitCloseNT(self) {
|
function emitCloseNT(self) {
|
||||||
self.destroyed = true;
|
self.destroyed = true;
|
||||||
|
self[kClosed] = true;
|
||||||
self.emit('close');
|
self.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ function emitStatistics(statistics) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
kOutHeaders: Symbol('kOutHeaders'),
|
kOutHeaders: Symbol('kOutHeaders'),
|
||||||
kNeedDrain: Symbol('kNeedDrain'),
|
kNeedDrain: Symbol('kNeedDrain'),
|
||||||
|
kClosed: Symbol('kClosed'),
|
||||||
nowDate,
|
nowDate,
|
||||||
utcDate,
|
utcDate,
|
||||||
emitStatistics
|
emitStatistics
|
||||||
|
@ -10,13 +10,8 @@ const OutgoingMessage = http.OutgoingMessage;
|
|||||||
assert.strictEqual(msg.destroyed, false);
|
assert.strictEqual(msg.destroyed, false);
|
||||||
msg.destroy();
|
msg.destroy();
|
||||||
assert.strictEqual(msg.destroyed, true);
|
assert.strictEqual(msg.destroyed, true);
|
||||||
let callbackCalled = false;
|
|
||||||
msg.write('asd', common.mustCall((err) => {
|
msg.write('asd', common.mustCall((err) => {
|
||||||
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
|
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
|
||||||
callbackCalled = true;
|
|
||||||
}));
|
|
||||||
msg.on('error', common.mustCall((err) => {
|
|
||||||
assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED');
|
|
||||||
assert.strictEqual(callbackCalled, true);
|
|
||||||
}));
|
}));
|
||||||
|
msg.on('error', common.mustNotCall());
|
||||||
}
|
}
|
||||||
|
24
test/parallel/test-http-outgoing-destroyed.js
Normal file
24
test/parallel/test-http-outgoing-destroyed.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
const server = http.createServer(common.mustCall((req, res) => {
|
||||||
|
req.pipe(res);
|
||||||
|
res.on('error', common.mustNotCall());
|
||||||
|
res.on('close', common.mustCall(() => {
|
||||||
|
res.end('asd');
|
||||||
|
process.nextTick(() => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
})).listen(0, () => {
|
||||||
|
http
|
||||||
|
.request({
|
||||||
|
port: server.address().port,
|
||||||
|
method: 'PUT'
|
||||||
|
})
|
||||||
|
.on('response', (res) => {
|
||||||
|
res.destroy();
|
||||||
|
})
|
||||||
|
.write('asd');
|
||||||
|
});
|
@ -8,19 +8,19 @@ const http = require('http');
|
|||||||
const server = http.createServer(handle);
|
const server = http.createServer(handle);
|
||||||
|
|
||||||
function handle(req, res) {
|
function handle(req, res) {
|
||||||
res.on('error', common.mustCall((err) => {
|
res.on('error', common.mustNotCall());
|
||||||
common.expectsError({
|
|
||||||
code: 'ERR_STREAM_WRITE_AFTER_END',
|
|
||||||
name: 'Error'
|
|
||||||
})(err);
|
|
||||||
server.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
res.write('hello');
|
res.write('hello');
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
setImmediate(common.mustCall(() => {
|
setImmediate(common.mustCall(() => {
|
||||||
res.write('world');
|
res.write('world', common.mustCall((err) => {
|
||||||
|
common.expectsError({
|
||||||
|
code: 'ERR_STREAM_WRITE_AFTER_END',
|
||||||
|
name: 'Error'
|
||||||
|
})(err);
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,19 +6,23 @@ const http = require('http');
|
|||||||
const server = http.createServer(handle);
|
const server = http.createServer(handle);
|
||||||
|
|
||||||
function handle(req, res) {
|
function handle(req, res) {
|
||||||
res.on('error', common.mustCall((err) => {
|
res.on('error', common.mustNotCall());
|
||||||
common.expectsError({
|
|
||||||
code: 'ERR_STREAM_WRITE_AFTER_END',
|
|
||||||
name: 'Error'
|
|
||||||
})(err);
|
|
||||||
server.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
res.write('hello');
|
res.write('hello');
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
setImmediate(common.mustCall(() => {
|
setImmediate(common.mustCall(() => {
|
||||||
res.end('world');
|
res.end('world');
|
||||||
|
process.nextTick(() => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
res.write('world', common.mustCall((err) => {
|
||||||
|
common.expectsError({
|
||||||
|
code: 'ERR_STREAM_WRITE_AFTER_END',
|
||||||
|
name: 'Error'
|
||||||
|
})(err);
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user