Skip to content

Commit 65f6f06

Browse files
committed
http: add agent.maxFreeSockets option
1 parent a1ea8a2 commit 65f6f06

File tree

2 files changed

+117
-6
lines changed

2 files changed

+117
-6
lines changed

lib/_http_agent.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ function Agent(options) {
5959
self.keepAliveMsecs = self.options.keepAliveMsecs || 1000;
6060
self.keepAlive = self.options.keepAlive || false;
6161
self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
62+
self.maxFreeSockets = self.options.maxFreeSockets || 256;
6263

6364
self.on('free', function(socket, options) {
6465
var name = self.getName(options);
@@ -80,21 +81,25 @@ function Agent(options) {
8081
!socket.destroyed &&
8182
self.options.keepAlive) {
8283
var freeSockets = self.freeSockets[name];
83-
var count = freeSockets ? freeSockets.length : 0;
84+
var freeLen = freeSockets ? freeSockets.length : 0;
85+
var count = freeLen;
8486
if (self.sockets[name])
8587
count += self.sockets[name].length;
8688

87-
if (count > self.maxSockets) {
89+
if (count >= self.maxSockets || freeLen >= self.maxFreeSockets) {
90+
self.removeSocket(socket, options);
8891
socket.destroy();
8992
} else {
9093
freeSockets = freeSockets || [];
9194
self.freeSockets[name] = freeSockets;
9295
socket.setKeepAlive(true, self.keepAliveMsecs);
9396
socket.unref();
9497
socket._httpMessage = null;
98+
self.removeSocket(socket, options);
9599
freeSockets.push(socket);
96100
}
97101
} else {
102+
self.removeSocket(socket, options);
98103
socket.destroy();
99104
}
100105
}
@@ -146,19 +151,23 @@ Agent.prototype.addRequest = function(req, options) {
146151
this.sockets[name] = [];
147152
}
148153

149-
if (this.freeSockets[name] && this.freeSockets[name].length) {
150-
debug('have free socket');
154+
var freeLen = this.freeSockets[name] ? this.freeSockets[name].length : 0;
155+
var sockLen = freeLen + this.sockets[name].length;
156+
157+
if (freeLen) {
151158
// we have a free socket, so use that.
152159
var socket = this.freeSockets[name].shift();
160+
debug('have free socket');
153161

154162
// don't leak
155163
if (!this.freeSockets[name].length)
156164
delete this.freeSockets[name];
157165

158166
socket.ref();
159167
req.onSocket(socket);
160-
} else if (this.sockets[name].length < this.maxSockets) {
161-
debug('call onSocket');
168+
this.sockets[name].push(socket);
169+
} else if (sockLen < this.maxSockets) {
170+
debug('call onSocket', sockLen, freeLen);
162171
// If we are under maxSockets create a new one.
163172
req.onSocket(this.createSocket(req, options));
164173
} else {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var common = require('../common');
23+
var assert = require('assert');
24+
25+
var http = require('http');
26+
27+
28+
var serverSockets = [];
29+
var server = http.createServer(function(req, res) {
30+
if (serverSockets.indexOf(req.socket) === -1) {
31+
serverSockets.push(req.socket);
32+
}
33+
res.end(req.url);
34+
});
35+
server.listen(common.PORT);
36+
37+
var agent = http.Agent({
38+
keepAlive: true,
39+
maxSockets: 5,
40+
maxFreeSockets: 2
41+
});
42+
43+
// make 10 requests in parallel,
44+
// then 10 more when they all finish.
45+
function makeReqs(n, cb) {
46+
for (var i = 0; i < n; i++)
47+
makeReq(i, then)
48+
49+
function then(er) {
50+
if (er)
51+
return cb(er);
52+
else if (--n === 0)
53+
setTimeout(cb);
54+
}
55+
}
56+
57+
function makeReq(i, cb) {
58+
agent.request({ port: common.PORT, path: '/' + i }, function(res) {
59+
var data = '';
60+
res.setEncoding('ascii');
61+
res.on('data', function(c) {
62+
data += c;
63+
});
64+
res.on('end', function() {
65+
assert.equal(data, '/' + i);
66+
cb();
67+
});
68+
}).end();
69+
}
70+
71+
var closed = false;
72+
makeReqs(10, function(er) {
73+
assert.ifError(er);
74+
assert.equal(count(agent.freeSockets), 2);
75+
assert.equal(count(agent.sockets), 0);
76+
assert.equal(serverSockets.length, 5);
77+
78+
// now make 10 more reqs.
79+
// should use the 2 free reqs from the pool first.
80+
makeReqs(10, function(er) {
81+
assert.ifError(er);
82+
assert.equal(count(agent.freeSockets), 2);
83+
assert.equal(count(agent.sockets), 0);
84+
assert.equal(serverSockets.length, 8);
85+
86+
agent.destroy();
87+
server.close(function() {
88+
closed = true;
89+
});
90+
});
91+
});
92+
93+
function count(sockets) {
94+
return Object.keys(sockets).reduce(function(n, name) {
95+
return n + sockets[name].length;
96+
}, 0);
97+
}
98+
99+
process.on('exit', function() {
100+
assert(closed);
101+
console.log('ok');
102+
});

0 commit comments

Comments
 (0)