Coverage

94%
76
72
4

agent.js

94%
76
72
4
LineHitsSource
1/*!
2 * agentkeepalive - lib/agent.js
3 *
4 * refer:
5 * * @atimb "Real keep-alive HTTP agent": https://gist.github.com/2963672
6 * * https://github.com/joyent/node/blob/master/lib/http.js
7 *
8 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
9 * MIT Licensed
10 */
11
121"use strict";
13
14/**
15 * Module dependencies.
16 */
17
181var http = require('http');
191var https = require('https');
201var util = require('util');
21
221var debug;
231if (process.env.NODE_DEBUG && /agentkeepalive/.test(process.env.NODE_DEBUG)) {
240 debug = function (x) {
250 console.error('agentkeepalive:', x);
26 };
27} else {
281 debug = function () { };
29}
30
311function Agent(options) {
3212 options = options || {};
3312 http.Agent.call(this, options);
34
3512 var self = this;
36 // max requests per keepalive socket, default is 0, no limit.
3712 self.maxKeepAliveRequests = parseInt(options.maxKeepAliveRequests, 10) || 0;
38 // max keep alive time, default 60 seconds.
39 // if set `maxKeepAliveTime = 0`, will disable keepalive feature.
4012 self.maxKeepAliveTime = parseInt(options.maxKeepAliveTime, 10);
4112 if (isNaN(self.maxKeepAliveTime)) {
429 self.maxKeepAliveTime = 60000;
43 }
4412 self.unusedSockets = {};
4512 self.createSocketCount = 0;
4612 self.timeoutSocketCount = 0;
4712 self.requestFinishedCount = 0;
48
49 // override the `free` event listener
5012 self.removeAllListeners('free');
5112 self.on('free', function (socket, host, port, localAddress) {
5227 self.requestFinishedCount++;
5327 socket._requestCount++;
5427 var name = host + ':' + port;
5527 if (localAddress) {
560 name += ':' + localAddress;
57 }
5827 if (self.requests[name] && self.requests[name].length > 0) {
5912 self.requests[name].shift().onSocket(socket);
6012 if (self.requests[name].length === 0) {
61 // don't leak
624 delete self.requests[name];
63 }
64 } else {
65 // If there are no pending requests just destroy the
66 // socket and it will get removed from the pool. This
67 // gets us out of timeout issues and allows us to
68 // default to Connection:keep-alive.
69 // socket.destroy();
7015 if (self.maxKeepAliveTime === 0 ||
71 (self.maxKeepAliveRequests && socket._requestCount >= self.maxKeepAliveRequests)) {
724 socket.destroy();
734 return;
74 }
75
76 // Avoid duplicitive timeout events by removing timeout listeners set on
77 // socket by previous requests. node does not do this normally because it
78 // assumes sockets are too short-lived for it to matter. It becomes a
79 // problem when sockets are being reused. Steps are being taken to fix
80 // this issue upstream in node v0.10.0.
81 //
82 // See https://github.com/joyent/node/commit/451ff1540ab536237e8d751d241d7fc3391a4087
8311 if (self.maxKeepAliveTime && socket._events && Array.isArray(socket._events.timeout)) {
841 socket.removeAllListeners('timeout');
85 // Restore the socket's setTimeout() that was remove as collateral
86 // damage.
871 socket.setTimeout(self.maxKeepAliveTime, socket._maxKeepAliveTimeout);
88 }
89 // keepalive
9011 if (!self.unusedSockets[name]) {
916 self.unusedSockets[name] = [];
92 }
9311 self.unusedSockets[name].push(socket);
94 }
95 });
96}
97
981util.inherits(Agent, http.Agent);
991module.exports = Agent;
100
1011Agent.prototype.addRequest = function (req, host, port, localAddress) {
10227 var name = host + ':' + port;
10327 if (localAddress) {
1040 name += ':' + localAddress;
105 }
10627 if (this.unusedSockets[name] && this.unusedSockets[name].length > 0) {
1075 return req.onSocket(this.unusedSockets[name].shift());
108 }
10922 return http.Agent.prototype.addRequest.call(this, req, host, port, localAddress);
110};
111
1121Agent.prototype.createSocket = function (name, host, port, localAddress, req) {
11312 var self = this;
11412 var socket = http.Agent.prototype.createSocket.call(this, name, host, port, localAddress, req);
11512 socket._requestCount = 0;
11612 if (self.maxKeepAliveTime) {
11711 socket._maxKeepAliveTimeout = function () {
1181 socket.destroy();
1191 self.timeoutSocketCount++;
120 };
12111 socket.setTimeout(self.maxKeepAliveTime, socket._maxKeepAliveTimeout);
122 // Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/
12311 socket.setNoDelay(true);
124 }
12512 this.createSocketCount++;
12612 return socket;
127};
128
1291Agent.prototype.removeSocket = function (socket, name, host, port, localAddress) {
1308 if (this.unusedSockets[name]) {
1314 var unusedIndex = this.unusedSockets[name].indexOf(socket);
1324 if (unusedIndex !== -1) {
1332 this.unusedSockets[name].splice(unusedIndex, 1);
1342 if (this.unusedSockets[name].length === 0) {
135 // don't leak
1362 delete this.unusedSockets[name];
137 }
138 }
139 }
1408 return http.Agent.prototype.removeSocket.call(this, socket, name, host, port, localAddress);
141};
142
1431function HttpsAgent(options) {
1441 Agent.call(this, options);
1451 this.createConnection = https.globalAgent.createConnection;
146}
1471util.inherits(HttpsAgent, Agent);
1481HttpsAgent.prototype.defaultPort = 443;
149
1501Agent.HttpsAgent = HttpsAgent;