diff --git a/lib/_http_client.js b/lib/_http_client.js index cea02eefa30763..a5a869b327c79b 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -502,6 +502,7 @@ function socketOnData(d) { !statusIsInformational(parser.incoming.statusCode)) { socket.removeListener('data', socketOnData); socket.removeListener('end', socketOnEnd); + socket.removeListener('drain', ondrain); freeParser(parser, req, socket); } } @@ -609,6 +610,7 @@ function responseKeepAlive(res, req) { } socket.removeListener('close', socketCloseListener); socket.removeListener('error', socketErrorListener); + socket.removeListener('drain', ondrain); socket.once('error', freeSocketErrorListener); // There are cases where _handle === null. Avoid those. Passing null to // nextTick() will call getDefaultTriggerAsyncId() to retrieve the id. diff --git a/test/parallel/test-http-agent-keepalive.js b/test/parallel/test-http-agent-keepalive.js index 5902c5867968cf..8cfc568b1e76e4 100644 --- a/test/parallel/test-http-agent-keepalive.js +++ b/test/parallel/test-http-agent-keepalive.js @@ -52,7 +52,7 @@ function get(path, callback) { port: server.address().port, agent: agent, path: path - }, callback); + }, callback).on('socket', common.mustCall(checkListeners)); } function checkDataAndSockets(body) { @@ -134,3 +134,12 @@ server.listen(0, common.mustCall(() => { })); })); })); + +// Check for listener leaks when reusing sockets. +function checkListeners(socket) { + assert.strictEqual(socket.listenerCount('data'), 1); + assert.strictEqual(socket.listenerCount('drain'), 1); + assert.strictEqual(socket.listenerCount('error'), 1); + // Sockets have onReadableStreamEnd. + assert.strictEqual(socket.listenerCount('end'), 2); +}