-
-
Notifications
You must be signed in to change notification settings - Fork 135
Add support for sending events through proxy #397
base: master
Are you sure you want to change the base?
Changes from 5 commits
6cce30b
de6da9e
9a8a4c2
8abbf65
1bc3de5
33fabb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,11 @@ var timeoutReq = require('timed-out'); | |
|
||
var http = require('http'); | ||
var https = require('https'); | ||
|
||
var agentOptions = {keepAlive: true, maxSockets: 100}; | ||
var tunnel = require('tunnel-agent'); | ||
var agentOptions = { | ||
keepAlive: true, | ||
maxSockets: 100 | ||
}; | ||
var httpAgent = new http.Agent(agentOptions); | ||
var httpsAgent = new https.Agent(agentOptions); | ||
|
||
|
@@ -31,14 +34,45 @@ HTTPTransport.prototype.send = function(client, message, headers, eventId, cb) { | |
ca: client.ca, | ||
agent: this.agent | ||
}; | ||
|
||
// set path apprpriately when using http endpoint + proxy, set proxy headers appropriately when using https endpoint + proxy | ||
if (this.options.hasOwnProperty('proxyHost')) { | ||
if (client.dsn.protocol === 'http') { | ||
this.options.path = | ||
'http://' + | ||
client.dsn.host + | ||
':' + | ||
client.dsn.port + | ||
client.dsn.path + | ||
'api/' + | ||
client.dsn.project_id + | ||
'/store/'; | ||
delete options.hostname; // only 'host' should be set when using proxy | ||
|
||
if (this.options.proxyAuth) { | ||
// might be able to use httpOverHttp agent | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could this be commented to be more explanatory? not sure what these two branches mean |
||
this.options.headers['Proxy-Authorization'] = | ||
'Basic ' + Buffer.from(this.options.proxyAuth).toString('base64'); | ||
} | ||
} else { | ||
this.options.agent.proxyOptions.headers = { | ||
'Content-Type': 'application/octet-stream', | ||
host: client.dsn.host + ':' + client.dsn.port | ||
}; | ||
} | ||
} | ||
|
||
for (var key in this.options) { | ||
if (this.options.hasOwnProperty(key)) { | ||
options[key] = this.options[key]; | ||
} | ||
} | ||
|
||
// prevent off heap memory explosion | ||
var _name = this.agent.getName({host: client.dsn.host, port: client.dsn.port}); | ||
var _name = this.agent.getName({ | ||
host: client.dsn.host, | ||
port: client.dsn.port | ||
}); | ||
var _requests = this.agent.requests[_name]; | ||
if (_requests && Object.keys(_requests).length > client.maxReqQueueCount) { | ||
// other feedback strategy | ||
|
@@ -63,7 +97,6 @@ HTTPTransport.prototype.send = function(client, message, headers, eventId, cb) { | |
client.emit('error', e); | ||
cb && cb(e); | ||
} | ||
|
||
// force the socket to drain | ||
var noop = function() {}; | ||
res.on('data', noop); | ||
|
@@ -89,10 +122,47 @@ function HTTPSTransport(options) { | |
this.options = options || {}; | ||
this.agent = httpsAgent; | ||
} | ||
|
||
function HTTPProxyTransport(options) { | ||
this.defaultPort = 80; | ||
this.transport = http; | ||
this.options = options || {}; | ||
this.agent = httpAgent; | ||
this.options.host = options.proxyHost; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about just using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or it may be useful to distinguish |
||
this.options.port = options.proxyPort; | ||
|
||
if (options.proxyUser && options.proxyPassword) { | ||
this.options.proxyAuth = options.proxyUser + ':' + options.proxyPassword; | ||
} | ||
} | ||
|
||
function HTTPSProxyTransport(options) { | ||
this.transport = https; | ||
this.options = options || {}; | ||
this.agent = httpsAgent; | ||
this.options.agent = tunnel.httpsOverHttp({ | ||
proxy: { | ||
host: options.proxyHost, | ||
port: options.proxyPort, | ||
proxyAuth: | ||
options.proxyUser && options.proxyPassword | ||
? options.proxyUser + ':' + options.proxyPassword | ||
: null | ||
}, | ||
keepAlive: agentOptions.keepAlive, | ||
maxSockets: agentOptions.maxSockets | ||
}); | ||
} | ||
|
||
util.inherits(HTTPSTransport, HTTPTransport); | ||
util.inherits(HTTPProxyTransport, HTTPTransport); | ||
util.inherits(HTTPSProxyTransport, HTTPTransport); | ||
|
||
module.exports.http = new HTTPTransport(); | ||
module.exports.https = new HTTPSTransport(); | ||
module.exports.Transport = Transport; | ||
module.exports.HTTPTransport = HTTPTransport; | ||
module.exports.HTTPSTransport = HTTPSTransport; | ||
|
||
module.exports.HTTPProxyTransport = HTTPProxyTransport; | ||
module.exports.HTTPSProxyTransport = HTTPSProxyTransport; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,92 @@ describe('transports', function() { | |
https.agent.maxSockets.should.equal(100); | ||
}); | ||
|
||
it('should set HTTPS proxy transport when proxy config is specified and request is sent', function(done) { | ||
var option = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
proxyHost: 'localhost', | ||
proxyPort: '8080' | ||
}; | ||
|
||
// HTTPS | ||
var httpsProxyTransport = new transports.HTTPSProxyTransport(option); | ||
httpsProxyTransport.options.agent.proxyOptions.should.exist; | ||
|
||
var _cachedAgent = httpsProxyTransport.agent; | ||
var requests = {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't seem to use this object anywhere in tests |
||
for (var i = 0; i < 10; i++) { | ||
requests[i] = 'req'; | ||
} | ||
|
||
httpsProxyTransport.agent = Object.assign({}, _cachedAgent, { | ||
getName: function() { | ||
return 'foo:123'; | ||
}, | ||
requests: { | ||
'foo:123': {} | ||
} | ||
}); | ||
|
||
httpsProxyTransport.send( | ||
{ | ||
dsn: { | ||
host: 'foo', | ||
port: 123 | ||
}, | ||
emit: function() {} | ||
}, | ||
null, | ||
null, | ||
null, | ||
function() { | ||
httpsProxyTransport.options.agent.proxyOptions.headers.host.should.exist; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could also check that it has been created correctly and assert on |
||
done(); | ||
} | ||
); | ||
}); | ||
|
||
it('should set HTTP proxy transport when proxy config is specified and request is sent', function(done) { | ||
var option = { | ||
proxyHost: 'localhost', | ||
proxyPort: '8080' | ||
}; | ||
|
||
// HTTP | ||
var httpProxyTransport = new transports.HTTPProxyTransport(option); | ||
httpProxyTransport.options.host.should.equal('localhost'); | ||
httpProxyTransport.options.port.should.equal('8080'); | ||
|
||
var _cachedAgent = httpProxyTransport.agent; | ||
httpProxyTransport.agent = Object.assign({}, _cachedAgent, { | ||
getName: function() { | ||
return 'foo:123'; | ||
}, | ||
requests: { | ||
'foo:123': {} | ||
} | ||
}); | ||
|
||
httpProxyTransport.options.agent = _cachedAgent; | ||
httpProxyTransport.send( | ||
{ | ||
dsn: { | ||
host: 'foo', | ||
port: 123, | ||
protocol: 'http', | ||
path: '/', | ||
project_id: '1' | ||
}, | ||
emit: function() {} | ||
}, | ||
null, | ||
null, | ||
null, | ||
function() { | ||
httpProxyTransport.options.path.should.equal('http://foo:123/api/1/store/'); | ||
done(); | ||
} | ||
); | ||
}); | ||
|
||
it('should emit error when requests queued over the limit', function(done) { | ||
var http = transports.http; | ||
var _cachedAgent = http.options.agent; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slightly better comment here for what will happen if you send hostname or a link to a resource that justifies why?