3 const net = require('net');
4 const util = require('util');
5 const EventEmitter = require('events');
6 const debug = util.debuglog('http');
10 // The largest departure from the previous implementation is that
11 // an Agent instance holds connections for a variable number of host:ports.
12 // Surprisingly, this is still API compatible as far as third parties are
13 // concerned. The only code that really notices the difference is the
16 // Another departure is that all code related to HTTP parsing is in
17 // ClientRequest.onSocket(). The Agent is now *strictly*
18 // concerned with managing a connection pool.
20 function Agent(options) {
21 if (!(this instanceof Agent))
22 return new Agent(options);
24 EventEmitter.call(this);
28 self.defaultPort = 80;
29 self.protocol = 'http:';
31 self.options = util._extend({}, options);
33 // don't confuse net and make it think that we're connecting to a pipe
34 self.options.path = null;
37 self.freeSockets = {};
38 self.keepAliveMsecs = self.options.keepAliveMsecs || 1000;
39 self.keepAlive = self.options.keepAlive || false;
40 self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
41 self.maxFreeSockets = self.options.maxFreeSockets || 256;
43 self.on('free', function(socket, options) {
44 var name = self.getName(options);
45 debug('agent.on(free)', name);
47 if (!socket.destroyed &&
48 self.requests[name] && self.requests[name].length) {
49 self.requests[name].shift().onSocket(socket);
50 if (self.requests[name].length === 0) {
52 delete self.requests[name];
55 // If there are no pending requests, then put it in
56 // the freeSockets pool, but only if we're allowed to do so.
57 var req = socket._httpMessage;
59 req.shouldKeepAlive &&
61 self.options.keepAlive) {
62 var freeSockets = self.freeSockets[name];
63 var freeLen = freeSockets ? freeSockets.length : 0;
65 if (self.sockets[name])
66 count += self.sockets[name].length;
68 if (count > self.maxSockets || freeLen >= self.maxFreeSockets) {
69 self.removeSocket(socket, options);
72 freeSockets = freeSockets || [];
73 self.freeSockets[name] = freeSockets;
74 socket.setKeepAlive(true, self.keepAliveMsecs);
76 socket._httpMessage = null;
77 self.removeSocket(socket, options);
78 freeSockets.push(socket);
81 self.removeSocket(socket, options);
88 util.inherits(Agent, EventEmitter);
89 exports.Agent = Agent;
91 Agent.defaultMaxSockets = Infinity;
93 Agent.prototype.createConnection = net.createConnection;
95 // Get the key for a given set of request options
96 Agent.prototype.getName = function(options) {
97 var name = options.host || 'localhost';
101 name += options.port;
104 if (options.localAddress)
105 name += options.localAddress;
110 Agent.prototype.addRequest = function(req, options) {
111 // Legacy API: addRequest(req, host, port, path)
112 if (typeof options === 'string') {
120 var name = this.getName(options);
121 if (!this.sockets[name]) {
122 this.sockets[name] = [];
125 var freeLen = this.freeSockets[name] ? this.freeSockets[name].length : 0;
126 var sockLen = freeLen + this.sockets[name].length;
129 // we have a free socket, so use that.
130 var socket = this.freeSockets[name].shift();
131 debug('have free socket');
134 if (!this.freeSockets[name].length)
135 delete this.freeSockets[name];
138 req.onSocket(socket);
139 this.sockets[name].push(socket);
140 } else if (sockLen < this.maxSockets) {
141 debug('call onSocket', sockLen, freeLen);
142 // If we are under maxSockets create a new one.
143 req.onSocket(this.createSocket(req, options));
145 debug('wait for socket');
146 // We are over limit so we'll add it to the queue.
147 if (!this.requests[name]) {
148 this.requests[name] = [];
150 this.requests[name].push(req);
154 Agent.prototype.createSocket = function(req, options) {
156 options = util._extend({}, options);
157 options = util._extend(options, self.options);
159 if (!options.servername) {
160 options.servername = options.host;
162 var hostHeader = req.getHeader('host');
164 options.servername = hostHeader.replace(/:.*$/, '');
169 var name = self.getName(options);
170 options._agentKey = name;
172 debug('createConnection', name, options);
173 options.encoding = null;
174 var s = self.createConnection(options);
175 if (!self.sockets[name]) {
176 self.sockets[name] = [];
178 this.sockets[name].push(s);
179 debug('sockets', name, this.sockets[name].length);
182 self.emit('free', s, options);
184 s.on('free', onFree);
186 function onClose(err) {
187 debug('CLIENT socket onClose');
188 // This is the only place where sockets get removed from the Agent.
189 // If you want to remove a socket from the pool, just close it.
190 // All socket errors end in a close event anyway.
191 self.removeSocket(s, options);
193 s.on('close', onClose);
195 function onRemove() {
196 // We need this function for cases like HTTP 'upgrade'
197 // (defined by WebSockets) where we need to remove a socket from the
198 // pool because it'll be locked up indefinitely
199 debug('CLIENT socket onRemove');
200 self.removeSocket(s, options);
201 s.removeListener('close', onClose);
202 s.removeListener('free', onFree);
203 s.removeListener('agentRemove', onRemove);
205 s.on('agentRemove', onRemove);
209 Agent.prototype.removeSocket = function(s, options) {
210 var name = this.getName(options);
211 debug('removeSocket', name, 'destroyed:', s.destroyed);
212 var sets = [this.sockets];
214 // If the socket was destroyed, remove it from the free buffers too.
216 sets.push(this.freeSockets);
218 for (var sk = 0; sk < sets.length; sk++) {
219 var sockets = sets[sk];
222 var index = sockets[name].indexOf(s);
224 sockets[name].splice(index, 1);
226 if (sockets[name].length === 0)
227 delete sockets[name];
232 if (this.requests[name] && this.requests[name].length) {
233 debug('removeSocket, have a request, make a socket');
234 var req = this.requests[name][0];
235 // If we have pending requests and a socket gets closed make a new one
236 this.createSocket(req, options).emit('free');
240 Agent.prototype.destroy = function() {
241 var sets = [this.freeSockets, this.sockets];
242 for (var s = 0; s < sets.length; s++) {
244 var keys = Object.keys(set);
245 for (var v = 0; v < keys.length; v++) {
246 var setName = set[keys[v]];
247 for (var n = 0; n < setName.length; n++) {
248 setName[n].destroy();
254 exports.globalAgent = new Agent();