• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * refer:
3 *   * @atimb "Real keep-alive HTTP agent": https://gist.github.com/2963672
4 *   * https://github.com/joyent/node/blob/master/lib/http.js
5 *   * https://github.com/joyent/node/blob/master/lib/https.js
6 *   * https://github.com/joyent/node/blob/master/lib/_http_agent.js
7 */
8
9'use strict';
10
11const OriginalAgent = require('./_http_agent').Agent;
12const ms = require('humanize-ms');
13
14class Agent extends OriginalAgent {
15  constructor(options) {
16    options = options || {};
17    options.keepAlive = options.keepAlive !== false;
18    // default is keep-alive and 15s free socket timeout
19    if (options.freeSocketKeepAliveTimeout === undefined) {
20      options.freeSocketKeepAliveTimeout = 15000;
21    }
22    // Legacy API: keepAliveTimeout should be rename to `freeSocketKeepAliveTimeout`
23    if (options.keepAliveTimeout) {
24      options.freeSocketKeepAliveTimeout = options.keepAliveTimeout;
25    }
26    options.freeSocketKeepAliveTimeout = ms(options.freeSocketKeepAliveTimeout);
27
28    // Sets the socket to timeout after timeout milliseconds of inactivity on the socket.
29    // By default is double free socket keepalive timeout.
30    if (options.timeout === undefined) {
31      options.timeout = options.freeSocketKeepAliveTimeout * 2;
32      // make sure socket default inactivity timeout >= 30s
33      if (options.timeout < 30000) {
34        options.timeout = 30000;
35      }
36    }
37    options.timeout = ms(options.timeout);
38
39    super(options);
40
41    this.createSocketCount = 0;
42    this.createSocketCountLastCheck = 0;
43
44    this.createSocketErrorCount = 0;
45    this.createSocketErrorCountLastCheck = 0;
46
47    this.closeSocketCount = 0;
48    this.closeSocketCountLastCheck = 0;
49
50    // socket error event count
51    this.errorSocketCount = 0;
52    this.errorSocketCountLastCheck = 0;
53
54    this.requestCount = 0;
55    this.requestCountLastCheck = 0;
56
57    this.timeoutSocketCount = 0;
58    this.timeoutSocketCountLastCheck = 0;
59
60    this.on('free', s => {
61      this.requestCount++;
62      // last enter free queue timestamp
63      s.lastFreeTime = Date.now();
64    });
65    this.on('timeout', () => {
66      this.timeoutSocketCount++;
67    });
68    this.on('close', () => {
69      this.closeSocketCount++;
70    });
71    this.on('error', () => {
72      this.errorSocketCount++;
73    });
74  }
75
76  createSocket(req, options, cb) {
77    super.createSocket(req, options, (err, socket) => {
78      if (err) {
79        this.createSocketErrorCount++;
80        return cb(err);
81      }
82      if (this.keepAlive) {
83        // Disable Nagle's algorithm: http://blog.caustik.com/2012/04/08/scaling-node-js-to-100k-concurrent-connections/
84        // https://fengmk2.com/benchmark/nagle-algorithm-delayed-ack-mock.html
85        socket.setNoDelay(true);
86      }
87      this.createSocketCount++;
88      cb(null, socket);
89    });
90  }
91
92  get statusChanged() {
93    const changed = this.createSocketCount !== this.createSocketCountLastCheck ||
94      this.createSocketErrorCount !== this.createSocketErrorCountLastCheck ||
95      this.closeSocketCount !== this.closeSocketCountLastCheck ||
96      this.errorSocketCount !== this.errorSocketCountLastCheck ||
97      this.timeoutSocketCount !== this.timeoutSocketCountLastCheck ||
98      this.requestCount !== this.requestCountLastCheck;
99    if (changed) {
100      this.createSocketCountLastCheck = this.createSocketCount;
101      this.createSocketErrorCountLastCheck = this.createSocketErrorCount;
102      this.closeSocketCountLastCheck = this.closeSocketCount;
103      this.errorSocketCountLastCheck = this.errorSocketCount;
104      this.timeoutSocketCountLastCheck = this.timeoutSocketCount;
105      this.requestCountLastCheck = this.requestCount;
106    }
107    return changed;
108  }
109
110  getCurrentStatus() {
111    return {
112      createSocketCount: this.createSocketCount,
113      createSocketErrorCount: this.createSocketErrorCount,
114      closeSocketCount: this.closeSocketCount,
115      errorSocketCount: this.errorSocketCount,
116      timeoutSocketCount: this.timeoutSocketCount,
117      requestCount: this.requestCount,
118      freeSockets: inspect(this.freeSockets),
119      sockets: inspect(this.sockets),
120      requests: inspect(this.requests),
121    };
122  }
123}
124
125module.exports = Agent;
126
127function inspect(obj) {
128  const res = {};
129  for (const key in obj) {
130    res[key] = obj[key].length;
131  }
132  return res;
133}
134