diff options
Diffstat (limited to 'node_modules/agentkeepalive/lib')
| -rw-r--r-- | node_modules/agentkeepalive/lib/_http_agent.js | 334 | ||||
| -rw-r--r-- | node_modules/agentkeepalive/lib/agent.js | 97 | ||||
| -rw-r--r-- | node_modules/agentkeepalive/lib/https_agent.js | 95 | ||||
| -rw-r--r-- | node_modules/agentkeepalive/lib/utils.js | 31 |
4 files changed, 557 insertions, 0 deletions
diff --git a/node_modules/agentkeepalive/lib/_http_agent.js b/node_modules/agentkeepalive/lib/_http_agent.js new file mode 100644 index 00000000..3c20b028 --- /dev/null +++ b/node_modules/agentkeepalive/lib/_http_agent.js @@ -0,0 +1,334 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// copy from https://github.com/nodejs/node/blob/v4.x/lib/_http_agent.js + +'use strict'; + +var net = require('net'); +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var debug = require('./utils').debug; + +// New Agent code. + +// The largest departure from the previous implementation is that +// an Agent instance holds connections for a variable number of host:ports. +// Surprisingly, this is still API compatible as far as third parties are +// concerned. The only code that really notices the difference is the +// request object. + +// Another departure is that all code related to HTTP parsing is in +// ClientRequest.onSocket(). The Agent is now *strictly* +// concerned with managing a connection pool. + +function Agent(options) { + if (!(this instanceof Agent)) + return new Agent(options); + + EventEmitter.call(this); + + var self = this; + + self.defaultPort = 80; + self.protocol = 'http:'; + + self.options = util._extend({}, options); + + // don't confuse net and make it think that we're connecting to a pipe + self.options.path = null; + self.requests = {}; + self.sockets = {}; + self.freeSockets = {}; + self.keepAliveMsecs = self.options.keepAliveMsecs || 1000; + self.keepAlive = self.options.keepAlive || false; + // free keep-alive socket timeout. By default free socket do not have a timeout. + // keepAliveTimeout should be rename to `freeSocketKeepAliveTimeout` + self.keepAliveTimeout = self.options.keepAliveTimeout || 0; + // working socket timeout. By default working socket do not have a timeout. + self.timeout = self.options.timeout || 0; + self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets; + self.maxFreeSockets = self.options.maxFreeSockets || 256; + + self.on('free', function(socket, options) { + var name = self.getName(options); + debug('agent.on(free)', name); + + if (!socket.destroyed && + self.requests[name] && self.requests[name].length) { + self.requests[name].shift().onSocket(socket); + if (self.requests[name].length === 0) { + // don't leak + delete self.requests[name]; + } + debug('continue handle next request'); + } else { + // If there are no pending requests, then put it in + // the freeSockets pool, but only if we're allowed to do so. + var req = socket._httpMessage; + if (req && + req.shouldKeepAlive && + !socket.destroyed && + self.options.keepAlive) { + var freeSockets = self.freeSockets[name]; + var freeLen = freeSockets ? freeSockets.length : 0; + var count = freeLen; + if (self.sockets[name]) + count += self.sockets[name].length; + // console.log(count, freeLen, self.maxSockets, self.maxFreeSockets) + if (count > self.maxSockets || freeLen >= self.maxFreeSockets) { + // console.log('hit max sockets', count, freeLen, self.maxSockets, self.maxFreeSockets); + self.removeSocket(socket, options); + socket.destroy(); + } else { + freeSockets = freeSockets || []; + self.freeSockets[name] = freeSockets; + socket.setKeepAlive(true, self.keepAliveMsecs); + socket.unref && socket.unref(); + socket._httpMessage = null; + self.removeSocket(socket, options); + freeSockets.push(socket); + + // Add a default error handler to avoid Unhandled 'error' event throw on idle socket + // https://github.com/node-modules/agentkeepalive/issues/25 + // https://github.com/nodejs/node/pull/4482 (fixed in >= 4.4.0 and >= 5.4.0) + if (socket.listeners('error').length === 0) { + socket.once('error', freeSocketErrorListener); + } + + // set free keepalive timer + socket.setTimeout(self.keepAliveTimeout); + } + } else { + self.removeSocket(socket, options); + socket.destroy(); + } + } + }); +} + +util.inherits(Agent, EventEmitter); +exports.Agent = Agent; + +function freeSocketErrorListener(err) { + var socket = this; + debug('SOCKET ERROR on FREE socket:', err.message, err.stack); + socket.destroy(); + socket.emit('agentRemove'); +} + +Agent.defaultMaxSockets = Infinity; + +Agent.prototype.createConnection = net.createConnection; + +// Get the key for a given set of request options +Agent.prototype.getName = function(options) { + var name = ''; + + if (options.host) + name += options.host; + else + name += 'localhost'; + + name += ':'; + if (options.port) + name += options.port; + name += ':'; + if (options.localAddress) + name += options.localAddress; + name += ':'; + return name; +}; + +Agent.prototype.addRequest = function(req, options) { + // Legacy API: addRequest(req, host, port, path) + if (typeof options === 'string') { + options = { + host: options, + port: arguments[2], + path: arguments[3] + }; + } + + options = util._extend({}, options); + options = util._extend(options, this.options); + + var name = this.getName(options); + if (!this.sockets[name]) { + this.sockets[name] = []; + } + + var freeLen = this.freeSockets[name] ? this.freeSockets[name].length : 0; + var sockLen = freeLen + this.sockets[name].length; + + if (freeLen) { + // we have a free socket, so use that. + var socket = this.freeSockets[name].shift(); + debug('have free socket'); + + socket.removeListener('error', freeSocketErrorListener); + + // restart the default timer + socket.setTimeout(this.timeout); + + // don't leak + if (!this.freeSockets[name].length) + delete this.freeSockets[name]; + + socket.ref && socket.ref(); + req.onSocket(socket); + this.sockets[name].push(socket); + } else if (sockLen < this.maxSockets) { + debug('call onSocket', sockLen, freeLen); + // If we are under maxSockets create a new one. + req.onSocket(this.createSocket(req, options)); + } else { + debug('wait for socket'); + // We are over limit so we'll add it to the queue. + if (!this.requests[name]) { + this.requests[name] = []; + } + this.requests[name].push(req); + } +}; + +Agent.prototype.createSocket = function(req, options) { + var self = this; + options = util._extend({}, options); + options = util._extend(options, self.options); + + if (!options.servername) { + options.servername = options.host; + if (req) { + var hostHeader = req.getHeader('host'); + if (hostHeader) { + options.servername = hostHeader.replace(/:.*$/, ''); + } + } + } + + var name = self.getName(options); + + debug('createConnection', name, options); + options.encoding = null; + var s = self.createConnection(options); + if (!self.sockets[name]) { + self.sockets[name] = []; + } + this.sockets[name].push(s); + debug('sockets', name, this.sockets[name].length); + + function onFree() { + self.emit('free', s, options); + } + s.on('free', onFree); + + function onClose(err) { + debug('CLIENT socket onClose'); + // fix: socket.destroyed always be undefined on 0.10.x + if (typeof s.destroyed !== 'boolean') { + s.destroyed = true; + } + + // This is the only place where sockets get removed from the Agent. + // If you want to remove a socket from the pool, just close it. + // All socket errors end in a close event anyway. + self.removeSocket(s, options); + self.emit('close'); + } + s.on('close', onClose); + + function onTimeout() { + debug('CLIENT socket onTimeout'); + s.destroy(); + // Remove it from freeSockets immediately to prevent new requests from being sent through this socket. + self.removeSocket(s, options); + self.emit('timeout'); + } + s.on('timeout', onTimeout); + // set the default timer + s.setTimeout(self.timeout); + + function onRemove() { + // We need this function for cases like HTTP 'upgrade' + // (defined by WebSockets) where we need to remove a socket from the + // pool because it'll be locked up indefinitely + debug('CLIENT socket onRemove'); + self.removeSocket(s, options); + s.removeListener('close', onClose); + s.removeListener('free', onFree); + s.removeListener('agentRemove', onRemove); + // remove timer + s.setTimeout(0, onTimeout); + } + s.on('agentRemove', onRemove); + return s; +}; + +Agent.prototype.removeSocket = function(s, options) { + var freeLen, sockLen; + var name = this.getName(options); + debug('removeSocket', name, 'destroyed:', s.destroyed); + var sets = [this.sockets]; + + // If the socket was destroyed, remove it from the free buffers too. + if (s.destroyed) + sets.push(this.freeSockets); + + for (var sk = 0; sk < sets.length; sk++) { + var sockets = sets[sk]; + if (sockets[name]) { + var index = sockets[name].indexOf(s); + if (index !== -1) { + sockets[name].splice(index, 1); + // Don't leak + if (sockets[name].length === 0) + delete sockets[name]; + } + } + } + + freeLen = this.freeSockets[name] ? this.freeSockets[name].length : 0; + sockLen = freeLen + this.sockets[name] ? this.sockets[name].length : 0; + + if (this.requests[name] && this.requests[name].length && sockLen < this.maxSockets) { + debug('removeSocket, have a request, make a socket'); + var req = this.requests[name][0]; + // If we have pending requests and a socket gets closed make a new one + this.createSocket(req, options).emit('free'); + } +}; + +Agent.prototype.destroy = function() { + var sets = [this.freeSockets, this.sockets]; + for (var s = 0; s < sets.length; s++) { + var set = sets[s]; + var keys = Object.keys(set); + for (var v = 0; v < keys.length; v++) { + var setName = set[keys[v]]; + for (var n = 0; n < setName.length; n++) { + setName[n].destroy(); + } + } + } +}; + +exports.globalAgent = new Agent(); diff --git a/node_modules/agentkeepalive/lib/agent.js b/node_modules/agentkeepalive/lib/agent.js new file mode 100644 index 00000000..ad4917f0 --- /dev/null +++ b/node_modules/agentkeepalive/lib/agent.js @@ -0,0 +1,97 @@ +/** + * refer: + * * @atimb "Real keep-alive HTTP agent": https://gist.github.com/2963672 + * * https://github.com/joyent/node/blob/master/lib/http.js + * * https://github.com/joyent/node/blob/master/lib/https.js + * * https://github.com/joyent/node/blob/master/lib/_http_agent.js + * + * Copyright(c) 2012 - 2014 fengmk2 <fengmk2@gmail.com> + * Copyright(c) node-modules + * MIT Licensed + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var https = require('https'); +var utils = require('./utils'); +var OriginalAgent = require('./_http_agent').Agent; +var OriginalHttpsAgent = https.Agent; + +module.exports = Agent; + +function Agent(options) { + if (!(this instanceof Agent)) { + return new Agent(options); + } + + options = options || {}; + options.keepAlive = options.keepAlive !== false; + // default is keep-alive and 15s free socket timeout + if (options.keepAliveTimeout === undefined) { + options.keepAliveTimeout = 15000; + } + // default timeout is double keepalive timeout + if (options.timeout === undefined) { + options.timeout = options.keepAliveTimeout * 2; + } + + OriginalAgent.call(this, options); + + var self = this; + self.createSocketCount = 0; + self.closeSocketCount = 0; + // socket error event count + self.errorSocketCount = 0; + self.requestCount = 0; + self.timeoutSocketCount = 0; + self.on('free', function () { + self.requestCount++; + }); + self.on('timeout', function () { + self.timeoutSocketCount++; + }); + self.on('close', function () { + self.closeSocketCount++; + }); + self.on('error', function () { + self.errorSocketCount++; + }); +} + +utils.inherits(Agent, OriginalAgent); + +Agent.prototype.createSocket = function (req, options) { + var socket = OriginalAgent.prototype.createSocket.call(this, req, options); + if (this.keepAlive) { + // Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/ + // http://fengmk2.com/benchmark/nagle-algorithm-delayed-ack-mock.html + socket.setNoDelay(true); + } + this.createSocketCount++; + return socket; +}; + +Agent.prototype.getCurrentStatus = function () { + return { + createSocketCount: this.createSocketCount, + closeSocketCount: this.closeSocketCount, + errorSocketCount: this.errorSocketCount, + timeoutSocketCount: this.timeoutSocketCount, + requestCount: this.requestCount, + freeSockets: inspect(this.freeSockets), + sockets: inspect(this.sockets), + requests: inspect(this.requests) + }; +}; + +function inspect(obj) { + var res = {}; + for (var key in obj) { + res[key] = obj[key].length; + } + return res; +} diff --git a/node_modules/agentkeepalive/lib/https_agent.js b/node_modules/agentkeepalive/lib/https_agent.js new file mode 100644 index 00000000..36b32d66 --- /dev/null +++ b/node_modules/agentkeepalive/lib/https_agent.js @@ -0,0 +1,95 @@ +/** + * Https Agent base on custom http agent + * + * Copyright(c) node-modules and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 <m@fengmk2.com> (http://fengmk2.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var https = require('https'); +var utils = require('./utils'); +var HttpAgent = require('./agent'); +var OriginalHttpsAgent = https.Agent; + +var HttpsAgent; + +if (utils.isNode10) { + // node v0.10 + HttpsAgent = function HttpsAgent(options) { + HttpAgent.call(this, options); + this.defaultPort = 443; + this.protocol = 'https:'; + }; + + utils.inherits(HttpsAgent, HttpAgent); + + HttpsAgent.prototype.createConnection = https.globalAgent.createConnection; + HttpsAgent.prototype.getName = function(options) { + var name = HttpAgent.prototype.getName.call(this, options); + + name += ':'; + if (options.ca) + name += options.ca; + + name += ':'; + if (options.cert) + name += options.cert; + + name += ':'; + if (options.ciphers) + name += options.ciphers; + + name += ':'; + if (options.key) + name += options.key; + + name += ':'; + if (options.pfx) + name += options.pfx; + + name += ':'; + if (options.rejectUnauthorized !== undefined) + name += options.rejectUnauthorized; + + return name; + }; +} else { + HttpsAgent = function HttpsAgent(options) { + HttpAgent.call(this, options); + this.defaultPort = 443; + this.protocol = 'https:'; + this.maxCachedSessions = this.options.maxCachedSessions; + if (this.maxCachedSessions === undefined) + this.maxCachedSessions = 100; + + this._sessionCache = { + map: {}, + list: [] + }; + }; + + utils.inherits(HttpsAgent, HttpAgent); + + [ + 'createConnection', + 'getName', + '_getSession', + '_cacheSession', + // https://github.com/nodejs/node/pull/4982 + '_evictSession', + ].forEach(function(method) { + if (typeof OriginalHttpsAgent.prototype[method] === 'function') { + HttpsAgent.prototype[method] = OriginalHttpsAgent.prototype[method]; + } + }); +} + +module.exports = HttpsAgent; diff --git a/node_modules/agentkeepalive/lib/utils.js b/node_modules/agentkeepalive/lib/utils.js new file mode 100644 index 00000000..7f79f9c1 --- /dev/null +++ b/node_modules/agentkeepalive/lib/utils.js @@ -0,0 +1,31 @@ +/** + * Copyright(c) node-modules and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 <m@fengmk2.com> (http://fengmk2.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var util = require('util'); +var debug; +var node10 = process.version.indexOf('v0.10.') === 0; + +if (node10) { + debug = function () { + if (process.env.NODE_DEBUG && /agentkeepalive/.test(process.env.NODE_DEBUG)) { + console.log.apply(console.log, arguments); + } + }; +} else { + debug = util.debuglog('agentkeepalive'); +} + +exports.debug = debug; +exports.isNode10 = node10; +exports.inherits = util.inherits; |
