From 6f1a28ab5b01a459345c815847d40fe471b2f990 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sun, 14 Jul 2019 13:29:54 +0200 Subject: [PATCH] http: emit ECONNRESET error if no 'aborted' listener --- doc/api/http.md | 4 +++- lib/_http_client.js | 4 +++- test/parallel/test-domain-multi.js | 1 + test/parallel/test-http-abort-stream-end.js | 1 + test/parallel/test-http-agent-remove.js | 3 ++- .../test-http-catch-uncaughtexception.js | 1 + test/parallel/test-http-client-abort.js | 1 + .../test-http-client-aborted-event.js | 1 + .../test-http-client-response-reset.js | 23 +++++++++++++++++++ .../test-http-flush-response-headers.js | 1 + test/parallel/test-http-response-close.js | 1 + test/parallel/test-https-close.js | 1 + test/parallel/test-stream-pipeline.js | 1 + 13 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-http-client-response-reset.js diff --git a/doc/api/http.md b/doc/api/http.md index c2ec3a7fd03770..e2ef0ea9976725 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -2217,7 +2217,9 @@ will be emitted in the following order: * `'data'` any number of times, on the `res` object * (`req.abort()` called here) * `'abort'` -* `'aborted'` on the `res` object +* `'aborted'` on the `res` object if `'aborted'` listener exists, otherwise + `'error'` with an error with message `'Error: aborted'` and code + `'ECONNRESET'` * `'close'` * `'end'` on the `res` object * `'close'` on the `res` object diff --git a/lib/_http_client.js b/lib/_http_client.js index 67e14fae20ad42..ba60e8f4200ff4 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -360,7 +360,9 @@ function socketCloseListener() { // Socket closed before we emitted 'end' below. if (!res.complete) { res.aborted = true; - res.emit('aborted'); + if (!res.emit('aborted')) { + res.emit('error', connResetException('aborted')); + } } req.emit('close'); if (res.readable) { diff --git a/test/parallel/test-domain-multi.js b/test/parallel/test-domain-multi.js index 63701150f8c331..bce1b15a33754e 100644 --- a/test/parallel/test-domain-multi.js +++ b/test/parallel/test-domain-multi.js @@ -68,6 +68,7 @@ const server = http.createServer((req, res) => { req.on('response', (res) => { // Add the response object to the C domain c.add(res); + res.on('aborted', common.mustCall()); res.pipe(process.stdout); }); diff --git a/test/parallel/test-http-abort-stream-end.js b/test/parallel/test-http-abort-stream-end.js index 04c4062faa2ea9..d106bec81d9ee7 100644 --- a/test/parallel/test-http-abort-stream-end.js +++ b/test/parallel/test-http-abort-stream-end.js @@ -40,6 +40,7 @@ const server = http.createServer(common.mustCall((req, res) => { server.listen(0, () => { const res = common.mustCall((res) => { + res.on('error', common.mustCall()); res.on('data', (chunk) => { size += chunk.length; assert(!req.aborted, 'got data after abort'); diff --git a/test/parallel/test-http-agent-remove.js b/test/parallel/test-http-agent-remove.js index 24fc7fcb8282fe..0a23380c941b10 100644 --- a/test/parallel/test-http-agent-remove.js +++ b/test/parallel/test-http-agent-remove.js @@ -11,7 +11,8 @@ const server = http.createServer(mustCall((req, res) => { server.listen(0, mustCall(() => { const req = http.get({ port: server.address().port - }, mustCall(() => { + }, mustCall((res) => { + res.on('error', mustCall()); const { socket } = req; socket.emit('agentRemove'); strictEqual(socket._httpMessage, req); diff --git a/test/parallel/test-http-catch-uncaughtexception.js b/test/parallel/test-http-catch-uncaughtexception.js index 1366b6e26ea37f..19e9daab4d29a5 100644 --- a/test/parallel/test-http-catch-uncaughtexception.js +++ b/test/parallel/test-http-catch-uncaughtexception.js @@ -16,6 +16,7 @@ const server = http.createServer(function(req, res) { }).listen(0, function() { http.get({ port: this.address().port }, function(res) { res.resume(); + res.on('error', common.mustCall()); throw new Error('get did fail'); }).on('close', function() { server.close(); diff --git a/test/parallel/test-http-client-abort.js b/test/parallel/test-http-client-abort.js index f767189ea9d593..aaab9631aaa6a3 100644 --- a/test/parallel/test-http-client-abort.js +++ b/test/parallel/test-http-client-abort.js @@ -48,6 +48,7 @@ server.listen(0, common.mustCall(() => { requests.push( http.get(options, common.mustCall((res) => { res.resume(); + res.on('error', common.mustCall()); reqCountdown.dec(); }))); } diff --git a/test/parallel/test-http-client-aborted-event.js b/test/parallel/test-http-client-aborted-event.js index 1e7feb7d5860f6..a3c4ad445dcc3f 100644 --- a/test/parallel/test-http-client-aborted-event.js +++ b/test/parallel/test-http-client-aborted-event.js @@ -16,5 +16,6 @@ server.listen(0, common.mustCall(function() { server.close(); serverRes.destroy(); res.on('aborted', common.mustCall()); + res.on('error', common.mustNotCall()); })); })); diff --git a/test/parallel/test-http-client-response-reset.js b/test/parallel/test-http-client-response-reset.js new file mode 100644 index 00000000000000..0f1ff45dab09fb --- /dev/null +++ b/test/parallel/test-http-client-response-reset.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +let serverRes; +const server = http.Server(function(req, res) { + res.write('Part of my res.'); + serverRes = res; +}); + +server.listen(0, common.mustCall(function() { + http.get({ + port: this.address().port, + headers: { connection: 'keep-alive' } + }, common.mustCall(function(res) { + server.close(); + serverRes.destroy(); + res.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ECONNRESET'); + })); + })); +})); diff --git a/test/parallel/test-http-flush-response-headers.js b/test/parallel/test-http-flush-response-headers.js index 0f0a1387b56733..6c14600e0f5c6f 100644 --- a/test/parallel/test-http-flush-response-headers.js +++ b/test/parallel/test-http-flush-response-headers.js @@ -22,6 +22,7 @@ server.listen(0, common.localhostIPv4, function() { function onResponse(res) { assert.strictEqual(res.headers.foo, 'bar'); res.destroy(); + res.on('error', common.mustCall()); server.close(); } }); diff --git a/test/parallel/test-http-response-close.js b/test/parallel/test-http-response-close.js index 4c36003357980b..c62a7b59a3b1d0 100644 --- a/test/parallel/test-http-response-close.js +++ b/test/parallel/test-http-response-close.js @@ -39,6 +39,7 @@ const http = require('http'); res.on('data', common.mustCall(() => { res.destroy(); })); + res.on('error', common.mustCall()); res.on('close', common.mustCall(() => { server.close(); })); diff --git a/test/parallel/test-https-close.js b/test/parallel/test-https-close.js index d9ed2ba1de90a6..21467322915251 100644 --- a/test/parallel/test-https-close.js +++ b/test/parallel/test-https-close.js @@ -48,6 +48,7 @@ server.listen(0, () => { const req = https.request(requestOptions, (res) => { res.on('data', () => {}); + res.on('aborted', common.mustCall()); setImmediate(shutdown); }); req.end(); diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index ef5a39fddd881f..8bdbf275e273ed 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -187,6 +187,7 @@ const { promisify } = require('util'); req.end(); req.on('response', (res) => { + res.on('aborted', common.mustCall()); setImmediate(() => { res.destroy(); server.close();