• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1module.exports = ForeverAgent
2ForeverAgent.SSL = ForeverAgentSSL
3
4var util = require('util')
5  , Agent = require('http').Agent
6  , net = require('net')
7  , tls = require('tls')
8  , AgentSSL = require('https').Agent
9
10function getConnectionName(host, port) {
11  var name = ''
12  if (typeof host === 'string') {
13    name = host + ':' + port
14  } else {
15    // For node.js v012.0 and iojs-v1.5.1, host is an object. And any existing localAddress is part of the connection name.
16    name = host.host + ':' + host.port + ':' + (host.localAddress ? (host.localAddress + ':') : ':')
17  }
18  return name
19}
20
21function ForeverAgent(options) {
22  var self = this
23  self.options = options || {}
24  self.requests = {}
25  self.sockets = {}
26  self.freeSockets = {}
27  self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets
28  self.minSockets = self.options.minSockets || ForeverAgent.defaultMinSockets
29  self.on('free', function(socket, host, port) {
30    var name = getConnectionName(host, port)
31
32    if (self.requests[name] && self.requests[name].length) {
33      self.requests[name].shift().onSocket(socket)
34    } else if (self.sockets[name].length < self.minSockets) {
35      if (!self.freeSockets[name]) self.freeSockets[name] = []
36      self.freeSockets[name].push(socket)
37
38      // if an error happens while we don't use the socket anyway, meh, throw the socket away
39      var onIdleError = function() {
40        socket.destroy()
41      }
42      socket._onIdleError = onIdleError
43      socket.on('error', onIdleError)
44    } else {
45      // If there are no pending requests just destroy the
46      // socket and it will get removed from the pool. This
47      // gets us out of timeout issues and allows us to
48      // default to Connection:keep-alive.
49      socket.destroy()
50    }
51  })
52
53}
54util.inherits(ForeverAgent, Agent)
55
56ForeverAgent.defaultMinSockets = 5
57
58
59ForeverAgent.prototype.createConnection = net.createConnection
60ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest
61ForeverAgent.prototype.addRequest = function(req, host, port) {
62  var name = getConnectionName(host, port)
63
64  if (typeof host !== 'string') {
65    var options = host
66    port = options.port
67    host = options.host
68  }
69
70  if (this.freeSockets[name] && this.freeSockets[name].length > 0 && !req.useChunkedEncodingByDefault) {
71    var idleSocket = this.freeSockets[name].pop()
72    idleSocket.removeListener('error', idleSocket._onIdleError)
73    delete idleSocket._onIdleError
74    req._reusedSocket = true
75    req.onSocket(idleSocket)
76  } else {
77    this.addRequestNoreuse(req, host, port)
78  }
79}
80
81ForeverAgent.prototype.removeSocket = function(s, name, host, port) {
82  if (this.sockets[name]) {
83    var index = this.sockets[name].indexOf(s)
84    if (index !== -1) {
85      this.sockets[name].splice(index, 1)
86    }
87  } else if (this.sockets[name] && this.sockets[name].length === 0) {
88    // don't leak
89    delete this.sockets[name]
90    delete this.requests[name]
91  }
92
93  if (this.freeSockets[name]) {
94    var index = this.freeSockets[name].indexOf(s)
95    if (index !== -1) {
96      this.freeSockets[name].splice(index, 1)
97      if (this.freeSockets[name].length === 0) {
98        delete this.freeSockets[name]
99      }
100    }
101  }
102
103  if (this.requests[name] && this.requests[name].length) {
104    // If we have pending requests and a socket gets closed a new one
105    // needs to be created to take over in the pool for the one that closed.
106    this.createSocket(name, host, port).emit('free')
107  }
108}
109
110function ForeverAgentSSL (options) {
111  ForeverAgent.call(this, options)
112}
113util.inherits(ForeverAgentSSL, ForeverAgent)
114
115ForeverAgentSSL.prototype.createConnection = createConnectionSSL
116ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest
117
118function createConnectionSSL (port, host, options) {
119  if (typeof port === 'object') {
120    options = port;
121  } else if (typeof host === 'object') {
122    options = host;
123  } else if (typeof options === 'object') {
124    options = options;
125  } else {
126    options = {};
127  }
128
129  if (typeof port === 'number') {
130    options.port = port;
131  }
132
133  if (typeof host === 'string') {
134    options.host = host;
135  }
136
137  return tls.connect(options);
138}
139