Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: don't emit end after aborted #27984

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -2196,6 +2196,25 @@ In the case of a connection error, the following events will be emitted:
* `'error'`
* `'close'`

In the case of a premature connection close before the response is received,
the following events will be emitted in the following order:

* `'socket'`
* `'error'` with an error with message `'Error: socket hang up'` and code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the actual error message here is useful/relevant as long as the code is mentioned.

`'ECONNRESET'`
* `'close'`

In the case of a premature connection close after the response is received,
the following events will be emitted in the following order:

* `'socket'`
* `'response'`
* `'data'` any number of times, on the `res` object
* (connection closed here)
* `'aborted'` on the `res` object
* `'close'`
* `'close'` on the `res` object

If `req.abort()` is called before the connection succeeds, the following events
will be emitted in the following order:

Expand All @@ -2216,7 +2235,6 @@ will be emitted in the following order:
* `'abort'`
* `'aborted'` on the `res` object
* `'close'`
* `'end'` on the `res` object
* `'close'` on the `res` object

Setting the `timeout` option or using the `setTimeout()` function will
Expand Down
2 changes: 1 addition & 1 deletion lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ function socketCloseListener() {
res.emit('aborted');
}
req.emit('close');
if (res.readable) {
if (!res.aborted && res.readable) {
ronag marked this conversation as resolved.
Show resolved Hide resolved
res.on('end', function() {
this.emit('close');
});
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-http-abort-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ server.listen(0, common.mustCall(() => {
serverRes.destroy();

res.resume();
res.on('end', common.mustCall());
res.on('end', common.mustNotCall());
res.on('aborted', common.mustCall());
res.on('close', common.mustCall());
res.socket.on('close', common.mustCall());
Expand Down
18 changes: 12 additions & 6 deletions test/parallel/test-http-client-spurious-aborted.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,18 @@ function download() {
if (res.complete) res.readable = true;
callback();
};
res.on('end', common.mustCall(() => {
reqCountdown.dec();
}));
res.on('aborted', () => {
aborted = true;
});
if (!abortRequest) {
res.on('end', common.mustCall(() => {
reqCountdown.dec();
}));
} else {
res.on('aborted', common.mustCall(() => {
aborted = true;
reqCountdown.dec();
writable.end();
}));
}

res.on('error', common.mustNotCall());
writable.on('finish', () => {
assert.strictEqual(aborted, abortRequest);
Expand Down
9 changes: 8 additions & 1 deletion test/parallel/test-http-header-overflow.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// Flags: --expose-internals

'use strict';
const { expectsError, mustCall } = require('../common');
const assert = require('assert');
const { createServer, maxHeaderSize } = require('http');
const { createConnection } = require('net');

const { getOptionValue } = require('internal/options');

const CRLF = '\r\n';
const DUMMY_HEADER_NAME = 'Cookie: ';
const DUMMY_HEADER_VALUE = 'a'.repeat(
Expand All @@ -17,11 +21,14 @@ const PAYLOAD = PAYLOAD_GET + CRLF +
const server = createServer();

server.on('connection', mustCall((socket) => {
// Legacy parser gives sligthly different response.
// This discripancy is not fixed on purpose.
const legacy = getOptionValue('--http-parser') === 'legacy';
socket.on('error', expectsError({
type: Error,
message: 'Parse Error: Header overflow',
code: 'HPE_HEADER_OVERFLOW',
bytesParsed: maxHeaderSize + PAYLOAD_GET.length,
bytesParsed: maxHeaderSize + PAYLOAD_GET.length - (legacy ? -1 : 0),
rawPacket: Buffer.from(PAYLOAD)
}));
}));
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-http-response-no-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function test(httpVersion, callback) {
body += data;
});

res.on('aborted', common.mustNotCall());
res.on('end', common.mustCall(function() {
assert.strictEqual(body, expected[httpVersion]);
server.close();
Expand Down
3 changes: 2 additions & 1 deletion test/parallel/test-http-response-status-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';
require('../common');
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
Expand Down Expand Up @@ -71,6 +71,7 @@ function runTest(testCaseIndex) {
console.log(`client: actual status message: ${response.statusMessage}`);
assert.strictEqual(testCase.statusMessage, response.statusMessage);

response.on('aborted', common.mustNotCall());
response.on('end', function() {
countdown.dec();
if (testCaseIndex + 1 < testCases.length) {
Expand Down