From ebb9f0dc11cf120dc479da012a81dd11299984ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=CC=81=20F=2E=20Romaniello?= Date: Thu, 31 Dec 2015 11:51:46 -0300 Subject: [PATCH] http: handle errors on idle sockets This change adds a new event handler to the `error` event of the socket after it has been used by the http_client. The purpose of this change is to catch errors on *keep alived* connections from idle sockets that otherwise will cause an uncaugh error event on the application. Fix: #3595 PR-URL: https://github.com/nodejs/node/pull/4482 Reviewed-By: Fedor Indutny --- lib/_http_client.js | 9 +++ .../parallel/test-http-agent-error-on-idle.js | 55 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/parallel/test-http-agent-error-on-idle.js diff --git a/lib/_http_client.js b/lib/_http_client.js index a5cccbdef80f8b..108b38891aabb1 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -274,6 +274,13 @@ function socketErrorListener(err) { socket.destroy(); } +function freeSocketErrorListener(err) { + var socket = this; + debug('SOCKET ERROR on FREE socket:', err.message, err.stack); + socket.destroy(); + socket.emit('agentRemove'); +} + function socketOnEnd() { var socket = this; var req = this._httpMessage; @@ -440,6 +447,7 @@ function responseOnEnd() { } socket.removeListener('close', socketCloseListener); socket.removeListener('error', socketErrorListener); + socket.once('error', freeSocketErrorListener); // Mark this socket as available, AFTER user-added end // handlers have a chance to run. process.nextTick(emitFreeNT, socket); @@ -474,6 +482,7 @@ function tickOnSocket(req, socket) { } parser.onIncoming = parserOnIncomingClient; + socket.removeListener('error', freeSocketErrorListener); socket.on('error', socketErrorListener); socket.on('data', socketOnData); socket.on('end', socketOnEnd); diff --git a/test/parallel/test-http-agent-error-on-idle.js b/test/parallel/test-http-agent-error-on-idle.js new file mode 100644 index 00000000000000..e3388ee0dcbd96 --- /dev/null +++ b/test/parallel/test-http-agent-error-on-idle.js @@ -0,0 +1,55 @@ +'use strict'; +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var Agent = http.Agent; + +var agent = new Agent({ + keepAlive: true, +}); + +var requestParams = { + host: 'localhost', + port: common.PORT, + agent: agent, + path: '/' +}; + +var socketKey = agent.getName(requestParams); + +function get(callback) { + return http.get(requestParams, callback); +} + +var server = http.createServer(function(req, res) { + res.end('hello world'); +}); + +server.listen(common.PORT, function() { + get(function(res) { + assert.equal(res.statusCode, 200); + res.resume(); + res.on('end', function() { + process.nextTick(function() { + var freeSockets = agent.freeSockets[socketKey]; + assert.equal(freeSockets.length, 1, + 'expect a free socket on ' + socketKey); + + //generate a random error on the free socket + var freeSocket = freeSockets[0]; + freeSocket.emit('error', new Error('ECONNRESET: test')); + + get(done); + }); + }); + }); +}); + +function done() { + assert.equal(Object.keys(agent.freeSockets).length, 0, + 'expect the freeSockets pool to be empty'); + + agent.destroy(); + server.close(); + process.exit(0); +}