http: fix resource leak

Fixes #2069
This commit is contained in:
koichik 2011-11-29 20:55:05 +09:00
parent 0de6ec5f08
commit 7aa5924dc6
4 changed files with 41 additions and 21 deletions

View File

@ -896,6 +896,10 @@ function Agent(options) {
var name = host + ':' + port; var name = host + ':' + port;
if (self.requests[name] && self.requests[name].length) { if (self.requests[name] && self.requests[name].length) {
self.requests[name].shift().onSocket(socket); self.requests[name].shift().onSocket(socket);
if (self.requests[name].length === 0) {
// don't leak
delete this.requests[name];
}
} else { } else {
// If there are no pending requests just destroy the // If there are no pending requests just destroy the
// socket and it will get removed from the pool. This // socket and it will get removed from the pool. This
@ -963,11 +967,11 @@ Agent.prototype.removeSocket = function(s, name, host, port) {
var index = this.sockets[name].indexOf(s); var index = this.sockets[name].indexOf(s);
if (index !== -1) { if (index !== -1) {
this.sockets[name].splice(index, 1); this.sockets[name].splice(index, 1);
} if (this.sockets[name].length === 0) {
} else if (this.sockets[name] && this.sockets[name].length === 0) {
// don't leak // don't leak
delete this.sockets[name]; delete this.sockets[name];
delete this.requests[name]; }
}
} }
if (this.requests[name] && this.requests[name].length) { if (this.requests[name] && this.requests[name].length) {
// If we have pending requests and a socket gets closed a new one // If we have pending requests and a socket gets closed a new one

View File

@ -52,9 +52,12 @@ function request(i) {
var socket = req.socket; var socket = req.socket;
socket.on('close', function() { socket.on('close', function() {
++count; ++count;
if (count < max) {
assert.equal(http.globalAgent.sockets[name].length, max - count); assert.equal(http.globalAgent.sockets[name].length, max - count);
assert.equal(http.globalAgent.sockets[name].indexOf(socket), -1); assert.equal(http.globalAgent.sockets[name].indexOf(socket), -1);
if (count === max) { } else {
assert(!http.globalAgent.sockets.hasOwnProperty(name));
assert(!http.globalAgent.requests.hasOwnProperty(name));
server.close(); server.close();
} }
}); });

View File

@ -22,10 +22,8 @@
var common = require('../common'); var common = require('../common');
var assert = require('assert'); var assert = require('assert');
var http = require('http'); var http = require('http');
var util = require('util');
var body = 'hello world\n'; var body = 'hello world\n';
var headers = {'connection': 'keep-alive'};
var server = http.createServer(function(req, res) { var server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Length': body.length}); res.writeHead(200, {'Content-Length': body.length});
@ -34,23 +32,37 @@ var server = http.createServer(function(req, res) {
}); });
var connectCount = 0; var connectCount = 0;
var name = 'localhost:' + common.PORT;
var agent = new http.Agent({maxSockets: 1});
var headers = {'connection': 'keep-alive'};
server.listen(common.PORT, function() { server.listen(common.PORT, function() {
var agent = new http.Agent({maxSockets: 1}); http.get({
var request = http.request({method: 'GET', path: '/', headers: headers, port: common.PORT, agent: agent}, function() { path: '/', headers: headers, port: common.PORT, agent: agent
assert.equal(1, agent.sockets['localhost:' + common.PORT].length); }, function(response) {
assert.equal(agent.sockets[name].length, 1);
assert.equal(agent.requests[name].length, 2);
}); });
request.end();
request = http.request({method: 'GET', path: '/', headers: headers, port: common.PORT, agent: agent}, function() { http.get({
assert.equal(1, agent.sockets['localhost:' + common.PORT].length); path: '/', headers: headers, port: common.PORT, agent: agent
}, function(response) {
assert.equal(agent.sockets[name].length, 1);
assert.equal(agent.requests[name].length, 1);
}); });
request.end();
request = http.request({method: 'GET', path: '/', headers: headers, port: common.PORT, agent: agent}, function(response) { http.get({
path: '/', headers: headers, port: common.PORT, agent: agent
}, function(response) {
response.on('end', function() { response.on('end', function() {
assert.equal(1, agent.sockets['localhost:' + common.PORT].length); assert.equal(agent.sockets[name].length, 1);
assert(!agent.requests.hasOwnProperty(name));
server.close(); server.close();
}); });
}); });
request.end(); });
process.on('exit', function() {
assert(!agent.sockets.hasOwnProperty(name));
assert(!agent.requests.hasOwnProperty(name));
}); });

View File

@ -59,6 +59,7 @@ srv.listen(common.PORT, '127.0.0.1', function() {
'upgrade': 'websocket' 'upgrade': 'websocket'
} }
}; };
var name = options.host + ':' + options.port;
var req = http.request(options); var req = http.request(options);
req.end(); req.end();
@ -73,11 +74,11 @@ srv.listen(common.PORT, '127.0.0.1', function() {
'connection': 'upgrade', 'connection': 'upgrade',
'upgrade': 'websocket' }; 'upgrade': 'websocket' };
assert.deepEqual(expectedHeaders, res.headers); assert.deepEqual(expectedHeaders, res.headers);
assert.equal(http.globalAgent.sockets[options.host + ':' + options.port].length, 1); assert.equal(http.globalAgent.sockets[name].length, 1);
process.nextTick(function() { process.nextTick(function() {
// Make sure this request got removed from the pool. // Make sure this request got removed from the pool.
assert.equal(http.globalAgent.sockets[options.host + ':' + options.port].length, 0); assert(!http.globalAgent.sockets.hasOwnProperty(name));
socket.end(); socket.end();
srv.close(); srv.close();