aboutsummaryrefslogtreecommitdiff
path: root/node_modules/agentkeepalive/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/agentkeepalive/lib')
-rw-r--r--node_modules/agentkeepalive/lib/_http_agent.js334
-rw-r--r--node_modules/agentkeepalive/lib/agent.js97
-rw-r--r--node_modules/agentkeepalive/lib/https_agent.js95
-rw-r--r--node_modules/agentkeepalive/lib/utils.js31
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;