index.js (4176B)
1 module.exports = ForeverAgent 2 ForeverAgent.SSL = ForeverAgentSSL 3 4 var util = require('util') 5 , Agent = require('http').Agent 6 , net = require('net') 7 , tls = require('tls') 8 , AgentSSL = require('https').Agent 9 10 function 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 21 function 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 } 54 util.inherits(ForeverAgent, Agent) 55 56 ForeverAgent.defaultMinSockets = 5 57 58 59 ForeverAgent.prototype.createConnection = net.createConnection 60 ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest 61 ForeverAgent.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 81 ForeverAgent.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 110 function ForeverAgentSSL (options) { 111 ForeverAgent.call(this, options) 112 } 113 util.inherits(ForeverAgentSSL, ForeverAgent) 114 115 ForeverAgentSSL.prototype.createConnection = createConnectionSSL 116 ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest 117 118 function 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 }