Skip to content

Commit 5f80df8

Browse files
ronagTrott
authored andcommitted
http: do not emit end after aborted
IncomingMessage will no longer emit end after aborted. PR-URL: #27984 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 4ca61f4 commit 5f80df8

7 files changed

+44
-11
lines changed

doc/api/http.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,25 @@ In the case of a connection error, the following events will be emitted:
22522252
* `'error'`
22532253
* `'close'`
22542254

2255+
In the case of a premature connection close before the response is received,
2256+
the following events will be emitted in the following order:
2257+
2258+
* `'socket'`
2259+
* `'error'` with an error with message `'Error: socket hang up'` and code
2260+
`'ECONNRESET'`
2261+
* `'close'`
2262+
2263+
In the case of a premature connection close after the response is received,
2264+
the following events will be emitted in the following order:
2265+
2266+
* `'socket'`
2267+
* `'response'`
2268+
* `'data'` any number of times, on the `res` object
2269+
* (connection closed here)
2270+
* `'aborted'` on the `res` object
2271+
* `'close'`
2272+
* `'close'` on the `res` object
2273+
22552274
If `req.abort()` is called before the connection succeeds, the following events
22562275
will be emitted in the following order:
22572276

@@ -2272,7 +2291,6 @@ will be emitted in the following order:
22722291
* `'abort'`
22732292
* `'aborted'` on the `res` object
22742293
* `'close'`
2275-
* `'end'` on the `res` object
22762294
* `'close'` on the `res` object
22772295

22782296
Setting the `timeout` option or using the `setTimeout()` function will

lib/_http_client.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ function socketCloseListener() {
364364
res.emit('aborted');
365365
}
366366
req.emit('close');
367-
if (res.readable) {
367+
if (!res.aborted && res.readable) {
368368
res.on('end', function() {
369369
this.emit('close');
370370
});

test/parallel/test-http-abort-client.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ server.listen(0, common.mustCall(() => {
3939
serverRes.destroy();
4040

4141
res.resume();
42-
res.on('end', common.mustCall());
42+
res.on('end', common.mustNotCall());
4343
res.on('aborted', common.mustCall());
4444
res.on('close', common.mustCall());
4545
res.socket.on('close', common.mustCall());

test/parallel/test-http-client-spurious-aborted.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,18 @@ function download() {
5656
if (res.complete) res.readable = true;
5757
callback();
5858
};
59-
res.on('end', common.mustCall(() => {
60-
reqCountdown.dec();
61-
}));
62-
res.on('aborted', () => {
63-
aborted = true;
64-
});
59+
if (!abortRequest) {
60+
res.on('end', common.mustCall(() => {
61+
reqCountdown.dec();
62+
}));
63+
} else {
64+
res.on('aborted', common.mustCall(() => {
65+
aborted = true;
66+
reqCountdown.dec();
67+
writable.end();
68+
}));
69+
}
70+
6571
res.on('error', common.mustNotCall());
6672
writable.on('finish', () => {
6773
assert.strictEqual(aborted, abortRequest);

test/parallel/test-http-header-overflow.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
// Flags: --expose-internals
2+
13
'use strict';
24
const { expectsError, mustCall } = require('../common');
35
const assert = require('assert');
46
const { createServer, maxHeaderSize } = require('http');
57
const { createConnection } = require('net');
68

9+
const { getOptionValue } = require('internal/options');
10+
711
const CRLF = '\r\n';
812
const DUMMY_HEADER_NAME = 'Cookie: ';
913
const DUMMY_HEADER_VALUE = 'a'.repeat(
@@ -17,11 +21,14 @@ const PAYLOAD = PAYLOAD_GET + CRLF +
1721
const server = createServer();
1822

1923
server.on('connection', mustCall((socket) => {
24+
// Legacy parser gives sligthly different response.
25+
// This discripancy is not fixed on purpose.
26+
const legacy = getOptionValue('--http-parser') === 'legacy';
2027
socket.on('error', expectsError({
2128
type: Error,
2229
message: 'Parse Error: Header overflow',
2330
code: 'HPE_HEADER_OVERFLOW',
24-
bytesParsed: maxHeaderSize + PAYLOAD_GET.length,
31+
bytesParsed: maxHeaderSize + PAYLOAD_GET.length - (legacy ? -1 : 0),
2532
rawPacket: Buffer.from(PAYLOAD)
2633
}));
2734
}));

test/parallel/test-http-response-no-headers.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function test(httpVersion, callback) {
5151
body += data;
5252
});
5353

54+
res.on('aborted', common.mustNotCall());
5455
res.on('end', common.mustCall(function() {
5556
assert.strictEqual(body, expected[httpVersion]);
5657
server.close();

test/parallel/test-http-response-status-message.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

2222
'use strict';
23-
require('../common');
23+
const common = require('../common');
2424
const assert = require('assert');
2525
const http = require('http');
2626
const net = require('net');
@@ -71,6 +71,7 @@ function runTest(testCaseIndex) {
7171
console.log(`client: actual status message: ${response.statusMessage}`);
7272
assert.strictEqual(testCase.statusMessage, response.statusMessage);
7373

74+
response.on('aborted', common.mustNotCall());
7475
response.on('end', function() {
7576
countdown.dec();
7677
if (testCaseIndex + 1 < testCases.length) {

0 commit comments

Comments
 (0)