|
@ -24,6 +24,7 @@ var url = require('url'); |
|
|
var util = require('util'); |
|
|
var util = require('util'); |
|
|
var EventEmitter = require('events').EventEmitter; |
|
|
var EventEmitter = require('events').EventEmitter; |
|
|
var ClientRequest = require('_http_client').ClientRequest; |
|
|
var ClientRequest = require('_http_client').ClientRequest; |
|
|
|
|
|
var debug = util.debuglog('http'); |
|
|
|
|
|
|
|
|
// New Agent code.
|
|
|
// New Agent code.
|
|
|
|
|
|
|
|
@ -44,7 +45,12 @@ function Agent(options) { |
|
|
EventEmitter.call(this); |
|
|
EventEmitter.call(this); |
|
|
|
|
|
|
|
|
var self = this; |
|
|
var self = this; |
|
|
|
|
|
|
|
|
|
|
|
self.defaultPort = 80; |
|
|
|
|
|
self.protocol = 'http:'; |
|
|
|
|
|
|
|
|
self.options = util._extend({}, options); |
|
|
self.options = util._extend({}, options); |
|
|
|
|
|
|
|
|
// don't confuse net and make it think that we're connecting to a pipe
|
|
|
// don't confuse net and make it think that we're connecting to a pipe
|
|
|
self.options.path = null; |
|
|
self.options.path = null; |
|
|
self.requests = {}; |
|
|
self.requests = {}; |
|
@ -54,11 +60,9 @@ function Agent(options) { |
|
|
self.keepAlive = self.options.keepAlive || false; |
|
|
self.keepAlive = self.options.keepAlive || false; |
|
|
self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets; |
|
|
self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets; |
|
|
|
|
|
|
|
|
self.on('free', function(socket, host, port, localAddress) { |
|
|
self.on('free', function(socket, options) { |
|
|
var name = host + ':' + port; |
|
|
var name = self.getName(options); |
|
|
if (localAddress) { |
|
|
debug('agent.on(free)', name); |
|
|
name += ':' + localAddress; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!socket.destroyed && |
|
|
if (!socket.destroyed && |
|
|
self.requests[name] && self.requests[name].length) { |
|
|
self.requests[name] && self.requests[name].length) { |
|
@ -103,18 +107,38 @@ exports.Agent = Agent; |
|
|
Agent.defaultMaxSockets = Infinity; |
|
|
Agent.defaultMaxSockets = Infinity; |
|
|
|
|
|
|
|
|
Agent.prototype.createConnection = net.createConnection; |
|
|
Agent.prototype.createConnection = net.createConnection; |
|
|
Agent.prototype.defaultPort = 80; |
|
|
|
|
|
Agent.prototype.protocol = 'http:'; |
|
|
// Get the key for a given set of request options
|
|
|
Agent.prototype.addRequest = function(req, host, port, localAddress) { |
|
|
Agent.prototype.getName = function(options) { |
|
|
var name = host + ':' + port; |
|
|
var name = ''; |
|
|
if (localAddress) { |
|
|
|
|
|
name += ':' + localAddress; |
|
|
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) { |
|
|
|
|
|
var host = options.host; |
|
|
|
|
|
var port = options.port; |
|
|
|
|
|
var localAddress = options.localAddress; |
|
|
|
|
|
|
|
|
|
|
|
var name = this.getName(options); |
|
|
if (!this.sockets[name]) { |
|
|
if (!this.sockets[name]) { |
|
|
this.sockets[name] = []; |
|
|
this.sockets[name] = []; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.freeSockets[name] && this.freeSockets[name].length) { |
|
|
if (this.freeSockets[name] && this.freeSockets[name].length) { |
|
|
|
|
|
debug('have free socket'); |
|
|
// we have a free socket, so use that.
|
|
|
// we have a free socket, so use that.
|
|
|
var socket = this.freeSockets[name].shift(); |
|
|
var socket = this.freeSockets[name].shift(); |
|
|
|
|
|
|
|
@ -125,9 +149,11 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { |
|
|
socket.ref(); |
|
|
socket.ref(); |
|
|
req.onSocket(socket); |
|
|
req.onSocket(socket); |
|
|
} else if (this.sockets[name].length < this.maxSockets) { |
|
|
} else if (this.sockets[name].length < this.maxSockets) { |
|
|
|
|
|
debug('call onSocket'); |
|
|
// If we are under maxSockets create a new one.
|
|
|
// If we are under maxSockets create a new one.
|
|
|
req.onSocket(this.createSocket(name, host, port, localAddress, req)); |
|
|
req.onSocket(this.createSocket(req, options)); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
debug('wait for socket'); |
|
|
// We are over limit so we'll add it to the queue.
|
|
|
// We are over limit so we'll add it to the queue.
|
|
|
if (!this.requests[name]) { |
|
|
if (!this.requests[name]) { |
|
|
this.requests[name] = []; |
|
|
this.requests[name] = []; |
|
@ -136,14 +162,12 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
Agent.prototype.createSocket = function(name, host, port, localAddress, req) { |
|
|
Agent.prototype.createSocket = function(req, options) { |
|
|
var self = this; |
|
|
var self = this; |
|
|
var options = util._extend({}, self.options); |
|
|
options = util._extend({}, options); |
|
|
options.port = port; |
|
|
options = util._extend(options, self.options); |
|
|
options.host = host; |
|
|
|
|
|
options.localAddress = localAddress; |
|
|
|
|
|
|
|
|
|
|
|
options.servername = host; |
|
|
options.servername = options.host; |
|
|
if (req) { |
|
|
if (req) { |
|
|
var hostHeader = req.getHeader('host'); |
|
|
var hostHeader = req.getHeader('host'); |
|
|
if (hostHeader) { |
|
|
if (hostHeader) { |
|
@ -151,30 +175,36 @@ Agent.prototype.createSocket = function(name, host, port, localAddress, req) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var name = self.getName(options); |
|
|
|
|
|
|
|
|
|
|
|
debug('createConnection', name, options); |
|
|
var s = self.createConnection(options); |
|
|
var s = self.createConnection(options); |
|
|
if (!self.sockets[name]) { |
|
|
if (!self.sockets[name]) { |
|
|
self.sockets[name] = []; |
|
|
self.sockets[name] = []; |
|
|
} |
|
|
} |
|
|
this.sockets[name].push(s); |
|
|
this.sockets[name].push(s); |
|
|
|
|
|
debug('sockets', name, this.sockets[name].length); |
|
|
|
|
|
|
|
|
function onFree() { |
|
|
function onFree() { |
|
|
self.emit('free', s, host, port, localAddress); |
|
|
self.emit('free', s, options); |
|
|
} |
|
|
} |
|
|
s.on('free', onFree); |
|
|
s.on('free', onFree); |
|
|
|
|
|
|
|
|
function onClose(err) { |
|
|
function onClose(err) { |
|
|
|
|
|
debug('CLIENT socket onClose'); |
|
|
// This is the only place where sockets get removed from the Agent.
|
|
|
// 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.
|
|
|
// If you want to remove a socket from the pool, just close it.
|
|
|
// All socket errors end in a close event anyway.
|
|
|
// All socket errors end in a close event anyway.
|
|
|
self.removeSocket(s, name, host, port, localAddress); |
|
|
self.removeSocket(s, options); |
|
|
} |
|
|
} |
|
|
s.on('close', onClose); |
|
|
s.on('close', onClose); |
|
|
|
|
|
|
|
|
function onRemove() { |
|
|
function onRemove() { |
|
|
// We need this function for cases like HTTP 'upgrade'
|
|
|
// We need this function for cases like HTTP 'upgrade'
|
|
|
// (defined by WebSockets) where we need to remove a socket from the pool
|
|
|
// (defined by WebSockets) where we need to remove a socket from the
|
|
|
// because it'll be locked up indefinitely
|
|
|
// pool because it'll be locked up indefinitely
|
|
|
self.removeSocket(s, name, host, port, localAddress); |
|
|
debug('CLIENT socket onRemove'); |
|
|
|
|
|
self.removeSocket(s, options); |
|
|
s.removeListener('close', onClose); |
|
|
s.removeListener('close', onClose); |
|
|
s.removeListener('free', onFree); |
|
|
s.removeListener('free', onFree); |
|
|
s.removeListener('agentRemove', onRemove); |
|
|
s.removeListener('agentRemove', onRemove); |
|
@ -183,7 +213,9 @@ Agent.prototype.createSocket = function(name, host, port, localAddress, req) { |
|
|
return s; |
|
|
return s; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
Agent.prototype.removeSocket = function(s, name, host, port, localAddress) { |
|
|
Agent.prototype.removeSocket = function(s, options) { |
|
|
|
|
|
var name = this.getName(options); |
|
|
|
|
|
debug('removeSocket', name); |
|
|
if (this.sockets[name]) { |
|
|
if (this.sockets[name]) { |
|
|
var index = this.sockets[name].indexOf(s); |
|
|
var index = this.sockets[name].indexOf(s); |
|
|
if (index !== -1) { |
|
|
if (index !== -1) { |
|
@ -195,9 +227,10 @@ Agent.prototype.removeSocket = function(s, name, host, port, localAddress) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (this.requests[name] && this.requests[name].length) { |
|
|
if (this.requests[name] && this.requests[name].length) { |
|
|
|
|
|
debug('removeSocket, have a request, make a socket'); |
|
|
var req = this.requests[name][0]; |
|
|
var req = this.requests[name][0]; |
|
|
// If we have pending requests and a socket gets closed a new one
|
|
|
// If we have pending requests and a socket gets closed make a new one
|
|
|
this.createSocket(name, host, port, localAddress, req).emit('free'); |
|
|
this.createSocket(req, options).emit('free'); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
@ -216,6 +249,10 @@ Agent.prototype.request = function(options, cb) { |
|
|
if (typeof options === 'string') { |
|
|
if (typeof options === 'string') { |
|
|
options = url.parse(options); |
|
|
options = url.parse(options); |
|
|
} |
|
|
} |
|
|
|
|
|
// don't try to do dns lookups of foo.com:8080, just foo.com
|
|
|
|
|
|
if (options.hostname) { |
|
|
|
|
|
options.host = options.hostname; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (options && options.path && / /.test(options.path)) { |
|
|
if (options && options.path && / /.test(options.path)) { |
|
|
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
|
|
// The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
|
|
@ -229,11 +266,16 @@ Agent.prototype.request = function(options, cb) { |
|
|
throw new Error('Protocol:' + options.protocol + ' not supported.'); |
|
|
throw new Error('Protocol:' + options.protocol + ' not supported.'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
options = util._extend({ agent: this, keepAlive: false }, options); |
|
|
options = util._extend({ |
|
|
|
|
|
agent: this, |
|
|
|
|
|
keepAlive: this.keepAlive |
|
|
|
|
|
}, options); |
|
|
|
|
|
|
|
|
|
|
|
// if it's false, then make a new one, just like this one.
|
|
|
if (options.agent === false) |
|
|
if (options.agent === false) |
|
|
options.agent = new Agent(options); |
|
|
options.agent = new this.constructor(options); |
|
|
|
|
|
|
|
|
|
|
|
debug('agent.request', options); |
|
|
return new ClientRequest(options, cb); |
|
|
return new ClientRequest(options, cb); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|