You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.1 KiB

  1. module.exports = ForeverAgent
  2. ForeverAgent.SSL = ForeverAgentSSL
  3. var util = require('util')
  4. , Agent = require('http').Agent
  5. , net = require('net')
  6. , tls = require('tls')
  7. , AgentSSL = require('https').Agent
  8. function getConnectionName(host, port) {
  9. var name = ''
  10. if (typeof host === 'string') {
  11. name = host + ':' + port
  12. } else {
  13. // For node.js v012.0 and iojs-v1.5.1, host is an object. And any existing localAddress is part of the connection name.
  14. name = host.host + ':' + host.port + ':' + (host.localAddress ? (host.localAddress + ':') : ':')
  15. }
  16. return name
  17. }
  18. function ForeverAgent(options) {
  19. var self = this
  20. self.options = options || {}
  21. self.requests = {}
  22. self.sockets = {}
  23. self.freeSockets = {}
  24. self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets
  25. self.minSockets = self.options.minSockets || ForeverAgent.defaultMinSockets
  26. self.on('free', function(socket, host, port) {
  27. var name = getConnectionName(host, port)
  28. if (self.requests[name] && self.requests[name].length) {
  29. self.requests[name].shift().onSocket(socket)
  30. } else if (self.sockets[name].length < self.minSockets) {
  31. if (!self.freeSockets[name]) self.freeSockets[name] = []
  32. self.freeSockets[name].push(socket)
  33. // if an error happens while we don't use the socket anyway, meh, throw the socket away
  34. var onIdleError = function() {
  35. socket.destroy()
  36. }
  37. socket._onIdleError = onIdleError
  38. socket.on('error', onIdleError)
  39. } else {
  40. // If there are no pending requests just destroy the
  41. // socket and it will get removed from the pool. This
  42. // gets us out of timeout issues and allows us to
  43. // default to Connection:keep-alive.
  44. socket.destroy()
  45. }
  46. })
  47. }
  48. util.inherits(ForeverAgent, Agent)
  49. ForeverAgent.defaultMinSockets = 5
  50. ForeverAgent.prototype.createConnection = net.createConnection
  51. ForeverAgent.prototype.addRequestNoreuse = Agent.prototype.addRequest
  52. ForeverAgent.prototype.addRequest = function(req, host, port) {
  53. var name = getConnectionName(host, port)
  54. if (typeof host !== 'string') {
  55. var options = host
  56. port = options.port
  57. host = options.host
  58. }
  59. if (this.freeSockets[name] && this.freeSockets[name].length > 0 && !req.useChunkedEncodingByDefault) {
  60. var idleSocket = this.freeSockets[name].pop()
  61. idleSocket.removeListener('error', idleSocket._onIdleError)
  62. delete idleSocket._onIdleError
  63. req._reusedSocket = true
  64. req.onSocket(idleSocket)
  65. } else {
  66. this.addRequestNoreuse(req, host, port)
  67. }
  68. }
  69. ForeverAgent.prototype.removeSocket = function(s, name, host, port) {
  70. if (this.sockets[name]) {
  71. var index = this.sockets[name].indexOf(s)
  72. if (index !== -1) {
  73. this.sockets[name].splice(index, 1)
  74. }
  75. } else if (this.sockets[name] && this.sockets[name].length === 0) {
  76. // don't leak
  77. delete this.sockets[name]
  78. delete this.requests[name]
  79. }
  80. if (this.freeSockets[name]) {
  81. var index = this.freeSockets[name].indexOf(s)
  82. if (index !== -1) {
  83. this.freeSockets[name].splice(index, 1)
  84. if (this.freeSockets[name].length === 0) {
  85. delete this.freeSockets[name]
  86. }
  87. }
  88. }
  89. if (this.requests[name] && this.requests[name].length) {
  90. // If we have pending requests and a socket gets closed a new one
  91. // needs to be created to take over in the pool for the one that closed.
  92. this.createSocket(name, host, port).emit('free')
  93. }
  94. }
  95. function ForeverAgentSSL (options) {
  96. ForeverAgent.call(this, options)
  97. }
  98. util.inherits(ForeverAgentSSL, ForeverAgent)
  99. ForeverAgentSSL.prototype.createConnection = createConnectionSSL
  100. ForeverAgentSSL.prototype.addRequestNoreuse = AgentSSL.prototype.addRequest
  101. function createConnectionSSL (port, host, options) {
  102. if (typeof port === 'object') {
  103. options = port;
  104. } else if (typeof host === 'object') {
  105. options = host;
  106. } else if (typeof options === 'object') {
  107. options = options;
  108. } else {
  109. options = {};
  110. }
  111. if (typeof port === 'number') {
  112. options.port = port;
  113. }
  114. if (typeof host === 'string') {
  115. options.host = host;
  116. }
  117. return tls.connect(options);
  118. }