Skip to content

Commit 6f95d70

Browse files
committed
http: don't emit error after destroy or multiple times
1 parent dfcaf18 commit 6f95d70

File tree

3 files changed

+36
-35
lines changed

3 files changed

+36
-35
lines changed

doc/api/deprecations.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,8 +2511,8 @@ changes:
25112511
25122512
Type: Documentation-only
25132513
2514-
`[ClientRequest.destroy()`][] should be the same as
2515-
`[ClientRequest.abort()`][]. Make ClientRequest more streamlike by deprecating
2514+
[`ClientRequest.destroy()`][] should be the same as
2515+
[`ClientRequest.abort()`][]. Make ClientRequest more streamlike by deprecating
25162516
abort().
25172517
25182518
[`--http-parser=legacy`]: cli.html#cli_http_parser_library

doc/api/http.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ deprecated: REPLACEME
553553
554554
Marks the request as aborting. Calling this will cause remaining data
555555
in the response to be dropped and the socket to be destroyed. After
556-
calling this method no further errors will be emitted.
556+
calling this method, no further errors will be emitted.
557557

558558
### request.aborted
559559
<!-- YAML
@@ -2165,22 +2165,24 @@ In the case of a connection error, the following events will be emitted:
21652165
* `'error'`
21662166
* `'close'`
21672167

2168-
If `req.abort()` is called before the connection succeeds, the following events
2169-
will be emitted in the following order:
2168+
If `req.destroy()` is called before the connection succeeds, the following
2169+
events will be emitted in the following order:
21702170

21712171
* `'socket'`
2172-
* (`req.abort()` called here)
2172+
* (`req.destroy(err)` called here)
21732173
* `'abort'`
2174+
* `'error'` if `err` was provided.
21742175
* `'close'`
21752176

2176-
If `req.abort()` is called after the response is received, the following events
2177-
will be emitted in the following order:
2177+
If `req.destroy()` is called after the response is received, the following
2178+
events will be emitted in the following order:
21782179

21792180
* `'socket'`
21802181
* `'response'`
21812182
* `'data'` any number of times, on the `res` object
2182-
* (`req.abort()` called here)
2183+
* (`req.destroy(err)` called here)
21832184
* `'abort'`
2185+
* `'error'` if `err` was provided.
21842186
* `'aborted'` on the `res` object
21852187
* `'close'`
21862188
* `'end'` on the `res` object

lib/_http_client.js

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ function ClientRequest(input, options, cb) {
190190
}
191191

192192
this._ended = false;
193+
this._errorEmitted = false;
193194
this.res = null;
194195
this.aborted = false;
195196
this.timeoutCb = null;
@@ -265,7 +266,7 @@ function ClientRequest(input, options, cb) {
265266
return;
266267
called = true;
267268
if (err) {
268-
process.nextTick(() => this.emit('error', err));
269+
process.nextTick(emitError, this, err);
269270
return;
270271
}
271272
this.onSocket(socket);
@@ -324,6 +325,11 @@ ClientRequest.prototype.destroy = function destroy(error) {
324325
this.res._dump();
325326
}
326327

328+
if (!error) {
329+
// No more errors after destroy has been called without error.
330+
this._errorEmitted = true;
331+
}
332+
327333
// In the event that we don't have a socket, we will pop out of
328334
// the request queue through handling in onSocket.
329335
if (this.socket) {
@@ -337,6 +343,13 @@ ClientRequest.prototype.abort = function abort() {
337343
this.destroy();
338344
};
339345

346+
function emitError(req, err) {
347+
if (!req._errorEmitted) {
348+
req._errorEmitted = true;
349+
req.emit('error', err);
350+
}
351+
}
352+
340353
function emitAbortNT() {
341354
this.emit('abort');
342355
}
@@ -371,15 +384,10 @@ function socketCloseListener() {
371384
res.emit('close');
372385
}
373386
} else {
374-
if (!req.socket._hadError) {
375-
// This socket error fired before we started to
376-
// receive a response. The error needs to
377-
// fire on the request.
378-
req.socket._hadError = true;
379-
if (!req.aborted) {
380-
req.emit('error', connResetException('socket hang up'));
381-
}
382-
}
387+
// This socket error fired before we started to
388+
// receive a response. The error needs to
389+
// fire on the request.
390+
emitError(req, connResetException('socket hang up'));
383391
req.emit('close');
384392
}
385393

@@ -401,12 +409,7 @@ function socketErrorListener(err) {
401409
debug('SOCKET ERROR:', err.message, err.stack);
402410

403411
if (req) {
404-
// For Safety. Some additional errors might fire later on
405-
// and we need to make sure we don't double-fire the error event.
406-
req.socket._hadError = true;
407-
if (!req.aborted) {
408-
req.emit('error', err);
409-
}
412+
emitError(req, err);
410413
}
411414

412415
// Handle any pending data
@@ -436,13 +439,10 @@ function socketOnEnd() {
436439
const req = this._httpMessage;
437440
const parser = this.parser;
438441

439-
if (!req.res && !req.socket._hadError) {
442+
if (!req.res) {
440443
// If we don't have a response then we know that the socket
441444
// ended prematurely and we need to emit an error on the request.
442-
req.socket._hadError = true;
443-
if (!req.aborted) {
444-
req.emit('error', connResetException('socket hang up'));
445-
}
445+
emitError(req, connResetException('socket hang up'));
446446
}
447447
if (parser) {
448448
parser.finish();
@@ -464,10 +464,7 @@ function socketOnData(d) {
464464
debug('parse error', ret);
465465
freeParser(parser, req, socket);
466466
socket.destroy();
467-
req.socket._hadError = true;
468-
if (!req.aborted) {
469-
req.emit('error', ret);
470-
}
467+
emitError(req, ret);
471468
} else if (parser.incoming && parser.incoming.upgrade) {
472469
// Upgrade (if status code 101) or CONNECT
473470
var bytesParsed = ret;
@@ -725,10 +722,12 @@ function onSocketNT(req, socket) {
725722
socket.destroy(req._destroyError);
726723
} else {
727724
if (req._destroyError) {
728-
req.emit('error', req._destroyError);
725+
emitError(req, req._destroyError);
729726
}
727+
req.emit('close');
730728
socket.emit('free');
731729
}
730+
req._destroyError = null;
732731
} else {
733732
tickOnSocket(req, socket);
734733
}

0 commit comments

Comments
 (0)