Commit cfddd12e authored by indexzero's avatar indexzero

[api] Update `.proxyRequest()` and `.proxyWebSocketRequest()` APIs to take an...

[api] Update `.proxyRequest()` and `.proxyWebSocketRequest()` APIs to take an options hash instead of a set of arguments. Add HTTPS support.
parent 6e4bf6a9
...@@ -35,7 +35,11 @@ var util = require('util'), ...@@ -35,7 +35,11 @@ var util = require('util'),
httpProxy.createServer(function (req, res, proxy) { httpProxy.createServer(function (req, res, proxy) {
var buffer = proxy.buffer(req); var buffer = proxy.buffer(req);
setTimeout(function() { setTimeout(function() {
proxy.proxyRequest(req, res, 9000, 'localhost', buffer); proxy.proxyRequest(req, res, {
port: 9000,
host: 'localhost',
buffer: buffer
});
}, 200) }, 200)
}).listen(8002); }).listen(8002);
......
...@@ -36,7 +36,11 @@ var proxy = new httpProxy.HttpProxy(); ...@@ -36,7 +36,11 @@ var proxy = new httpProxy.HttpProxy();
http.createServer(function (req, res) { http.createServer(function (req, res) {
var buffer = proxy.buffer(req); var buffer = proxy.buffer(req);
setTimeout(function() { setTimeout(function() {
proxy.proxyRequest(req, res, 9000, 'localhost', buffer); proxy.proxyRequest(req, res, {
port: 9000,
host: 'localhost',
buffer: buffer
});
}, 200); }, 200);
}).listen(8004); }).listen(8004);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
var util = require('util'), var util = require('util'),
http = require('http'), http = require('http'),
https = require('https'),
events = require('events'), events = require('events'),
ProxyTable = require('./proxy-table').ProxyTable, ProxyTable = require('./proxy-table').ProxyTable,
maxSockets = 100; maxSockets = 100;
...@@ -36,21 +37,46 @@ var util = require('util'), ...@@ -36,21 +37,46 @@ var util = require('util'),
exports.version = [0, 4, 2]; exports.version = [0, 4, 2];
// //
// ### function _getAgent (host, port) // ### function _getAgent (host, port, secure)
// #### @host {string} Host of the agent to get // #### @host {string} Host of the agent to get
// #### @port {number} Port of the agent to get // #### @port {number} Port of the agent to get
// #### @secure {boolean} Value indicating whether or not to use HTTPS
// Retreives an agent from the `http` module // Retreives an agent from the `http` module
// and sets the `maxSockets` property appropriately. // and sets the `maxSockets` property appropriately.
// //
function _getAgent (host, port) { function _getAgent (host, port, secure) {
// var agent = !secure ? http.getAgent(host, port) : https.getAgent({
// TODO (indexzero): Make this configurable for http / https host: host,
// port: port
var agent = http.getAgent(host, port); });
agent.maxSockets = maxSockets; agent.maxSockets = maxSockets;
return agent; return agent;
} }
//
// ### function _getProtocol (outgoing, https)
// #### @outgoing {Object} Outgoing request options
// #### @secure {Object|boolean} Settings for `https`
// Returns the appropriate protocol based on the settings in
// `secure`. If the protocol is `https` this function will update
// the options in `outgoing` as appropriate by adding `ca`, `key`,
// and `cert` if they exist in `secure`.
//
function _getProtocol (outgoing, secure) {
var protocol = secure ? https : http;
if (typeof secure === 'object') {
['ca', 'cert', 'key'].forEach(function (prop) {
if (secure[prop]) {
outgoing[prop] = secure[prop];
}
})
}
return protocol;
}
// //
// ### function getMaxSockets () // ### function getMaxSockets ()
// Returns the maximum number of sockets // Returns the maximum number of sockets
...@@ -104,20 +130,34 @@ exports.createServer = function () { ...@@ -104,20 +130,34 @@ exports.createServer = function () {
proxy = new HttpProxy(options); proxy = new HttpProxy(options);
server = http.createServer(function (req, res) { server = http.createServer(function (req, res) {
proxy.emit('request', req, res, req.headers.host, req.url);
// If we were passed a callback to process the request
// or response in some way, then call it.
if (callback) { if (callback) {
//
// If we were passed a callback to process the request
// or response in some way, then call it.
//
callback(req, res, proxy); callback(req, res, proxy);
} }
else if (port && host) { else if (port && host) {
proxy.proxyRequest(req, res, port, host); //
// If we have a target host and port for the request
// then proxy to the specified location.
//
proxy.proxyRequest(req, res, {
port: port,
host: host
});
} }
else if (proxy.proxyTable) { else if (proxy.proxyTable) {
//
// If the proxy is configured with a ProxyTable
// instance then use that before failing.
//
proxy.proxyRequest(req, res); proxy.proxyRequest(req, res);
} }
else { else {
//
// Otherwise this server is improperly configured.
//
throw new Error('Cannot proxy without port, host, or router.') throw new Error('Cannot proxy without port, host, or router.')
} }
}); });
...@@ -136,10 +176,19 @@ exports.createServer = function () { ...@@ -136,10 +176,19 @@ exports.createServer = function () {
server.on('upgrade', function(req, socket, head) { server.on('upgrade', function(req, socket, head) {
// Tunnel websocket requests too // Tunnel websocket requests too
proxy.proxyWebSocketRequest(req, socket, head, port, host); proxy.proxyWebSocketRequest(req, socket, head, {
port: port,
host: host
});
}); });
} }
//
// Set the proxy on the server so it is available
// to the consumer of the server
//
server.proxy = proxy;
return server; return server;
}; };
...@@ -165,11 +214,12 @@ exports.createServer = function () { ...@@ -165,11 +214,12 @@ exports.createServer = function () {
var HttpProxy = exports.HttpProxy = function (options) { var HttpProxy = exports.HttpProxy = function (options) {
events.EventEmitter.call(this); events.EventEmitter.call(this);
options = options || {}; var self = this;
this.options = options; options = options || {};
this.forward = options.forward;
this.https = options.https;
if (options.router) { if (options.router) {
var self = this;
this.proxyTable = new ProxyTable(options.router, options.silent, options.hostnameOnly); this.proxyTable = new ProxyTable(options.router, options.silent, options.hostnameOnly);
this.proxyTable.on('routes', function (routes) { this.proxyTable.on('routes', function (routes) {
self.emit('routes', routes); self.emit('routes', routes);
...@@ -196,7 +246,7 @@ util.inherits(HttpProxy, events.EventEmitter); ...@@ -196,7 +246,7 @@ util.inherits(HttpProxy, events.EventEmitter);
// //
// __Attribution:__ This approach is based heavily on // __Attribution:__ This approach is based heavily on
// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157). // [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157).
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0. // However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
// This simply chooses to manage the scope of the events on a new Object literal as opposed to // This simply chooses to manage the scope of the events on a new Object literal as opposed to
// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154). // [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
// //
...@@ -238,12 +288,17 @@ HttpProxy.prototype.close = function () { ...@@ -238,12 +288,17 @@ HttpProxy.prototype.close = function () {
// ### function proxyRequest (req, res, [port, host, paused]) // ### function proxyRequest (req, res, [port, host, paused])
// #### @req {ServerRequest} Incoming HTTP Request to proxy. // #### @req {ServerRequest} Incoming HTTP Request to proxy.
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to. // #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
// #### @port {number} **Optional** Port to use on the proxy target host. // #### @options {Object} Options for the outgoing proxy request.
// #### @host {string} **Optional** Host of the proxy target. // options.port {number} Port to use on the proxy target host.
// #### @buffer {Object} **Optional** Result from `httpProxy.buffer(req)` // options.host {string} Host of the proxy target.
// options.buffer {Object} Result from `httpProxy.buffer(req)`
// options.https {Object|boolean} Settings for https.
// //
HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { HttpProxy.prototype.proxyRequest = function (req, res, options) {
var self = this, reverseProxy, location, errState = false, opts; var self = this, errState = false, location, outgoing, protocol, reverseProxy;
// Create an empty options hash if none is passed.
options = options || {};
// //
// Check the proxy table for this instance to see if we need // Check the proxy table for this instance to see if we need
...@@ -251,7 +306,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -251,7 +306,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
// always ignore the proxyTable if an explicit `port` and `host` // always ignore the proxyTable if an explicit `port` and `host`
// arguments are supplied to `proxyRequest`. // arguments are supplied to `proxyRequest`.
// //
if (this.proxyTable && !host) { if (this.proxyTable && !options.host) {
location = this.proxyTable.getProxyLocation(req); location = this.proxyTable.getProxyLocation(req);
// //
...@@ -267,13 +322,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -267,13 +322,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
// When using the ProxyTable in conjunction with an HttpProxy instance // When using the ProxyTable in conjunction with an HttpProxy instance
// only the following arguments are valid: // only the following arguments are valid:
// //
// * `proxy.proxyRequest(req, res, port, host, buffer)`: This will be skipped // * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
// * `proxy.proxyRequest(req, res, buffer)`: Buffer will get updated appropriately // * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
// * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined` // * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
// //
buffer = port; options.port = location.port;
port = location.port; options.host = location.host;
host = location.host;
} }
// //
...@@ -284,14 +338,14 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -284,14 +338,14 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
// //
// Emit the `start` event indicating that we have begun the proxy operation. // Emit the `start` event indicating that we have begun the proxy operation.
// //
this.emit('start', req, res, host, port); this.emit('start', req, res, options);
// //
// If forwarding is enabled for this instance, foward proxy the // If forwarding is enabled for this instance, foward proxy the
// specified request to the address provided in `this.options.forward` // specified request to the address provided in `this.forward`
// //
if (this.options.forward) { if (this.forward) {
this.emit('forward', req, res, this.options.forward.host, this.options.forward.port); this.emit('forward', req, res, this.forward);
this._forwardRequest(req); this._forwardRequest(req);
} }
...@@ -312,10 +366,10 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -312,10 +366,10 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
res.end(); res.end();
} }
var opts = { outgoing = {
host: host, host: options.host,
port: port, port: options.port,
agent: _getAgent(host, port), agent: _getAgent(options.host, options.port, options.https || this.https),
method: req.method, method: req.method,
path: req.url, path: req.url,
headers: req.headers headers: req.headers
...@@ -323,10 +377,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -323,10 +377,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
// Force the `connection` header to be 'close' until // Force the `connection` header to be 'close' until
// node.js core re-implements 'keep-alive'. // node.js core re-implements 'keep-alive'.
opts.headers['connection'] = 'close'; outgoing.headers['connection'] = 'close';
protocol = _getProtocol(outgoing, options.https || this.https);
// Open new HTTP request to internal resource with will act as a reverse proxy pass // Open new HTTP request to internal resource with will act as a reverse proxy pass
reverseProxy = http.request(opts, function (response) { reverseProxy = protocol.request(outgoing, function (response) {
// Process the `reverseProxy` `response` when it's received. // Process the `reverseProxy` `response` when it's received.
if (response.headers.connection) { if (response.headers.connection) {
...@@ -388,8 +444,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -388,8 +444,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
}); });
// If we have been passed buffered data, resume it. // If we have been passed buffered data, resume it.
if (buffer && !errState) { if (options.buffer && !errState) {
buffer.resume(); options.buffer.resume();
} }
}; };
...@@ -397,18 +453,18 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) { ...@@ -397,18 +453,18 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
// ### @private function _forwardRequest (req) // ### @private function _forwardRequest (req)
// #### @req {ServerRequest} Incoming HTTP Request to proxy. // #### @req {ServerRequest} Incoming HTTP Request to proxy.
// Forwards the specified `req` to the location specified // Forwards the specified `req` to the location specified
// by `this.options.forward` ignoring errors and the subsequent response. // by `this.forward` ignoring errors and the subsequent response.
// //
HttpProxy.prototype._forwardRequest = function (req) { HttpProxy.prototype._forwardRequest = function (req) {
var self = this, port, host, forwardProxy, opts; var self = this, port, host, outgoing, protocol, forwardProxy;
port = this.options.forward.port; port = this.forward.port;
host = this.options.forward.host; host = this.forward.host;
opts = { outgoing = {
host: host, host: host,
port: port, port: port,
agent: _getAgent(host, port), agent: _getAgent(host, port, this.forward.https),
method: req.method, method: req.method,
path: req.url, path: req.url,
headers: req.headers headers: req.headers
...@@ -416,10 +472,12 @@ HttpProxy.prototype._forwardRequest = function (req) { ...@@ -416,10 +472,12 @@ HttpProxy.prototype._forwardRequest = function (req) {
// Force the `connection` header to be 'close' until // Force the `connection` header to be 'close' until
// node.js core re-implements 'keep-alive'. // node.js core re-implements 'keep-alive'.
opts.headers['connection'] = 'close'; outgoing.headers['connection'] = 'close';
protocol = _getProtocol(outgoing, this.forward.https);
// Open new HTTP request to internal resource with will act as a reverse proxy pass // Open new HTTP request to internal resource with will act as a reverse proxy pass
forwardProxy = http.request(opts, function (response) { forwardProxy = protocol.request(outgoing, function (response) {
// //
// Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy. // Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning. // Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
...@@ -444,8 +502,8 @@ HttpProxy.prototype._forwardRequest = function (req) { ...@@ -444,8 +502,8 @@ HttpProxy.prototype._forwardRequest = function (req) {
}); });
}; };
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, host, buffer) { HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
var self = this, CRLF = '\r\n'; var self = this, outgoing, errState = false, CRLF = '\r\n';
// WebSocket requests has method = GET // WebSocket requests has method = GET
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') { if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
...@@ -519,24 +577,24 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h ...@@ -519,24 +577,24 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
_socket(socket); _socket(socket);
// Remote host address // Remote host address
var remoteHost = host + (port - 80 === 0 ? '' : ':' + port), var agent = _getAgent(options.host, options.port),
agent = _getAgent(host, port); remoteHost = options.host + (options.port - 80 === 0 ? '' : ':' + options.port);
// Change headers // Change headers
req.headers.host = remoteHost; req.headers.host = remoteHost;
req.headers.origin = 'http://' + host; req.headers.origin = 'http://' + options.host;
var opts = { outgoing = {
host: host, host: options.host,
port: port, port: options.port,
agent: agent, agent: agent,
method: 'GET', method: 'GET',
path: req.url, path: req.url,
headers: req.headers headers: req.headers
} };
// Make the outgoing WebSocket request // Make the outgoing WebSocket request
var request = http.request(opts, function () { }); var request = http.request(outgoing, function () { });
// Not disconnect on update // Not disconnect on update
agent.on('upgrade', function(request, remoteSocket, head) { agent.on('upgrade', function(request, remoteSocket, head) {
...@@ -567,8 +625,8 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h ...@@ -567,8 +625,8 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
data = data.slice(Buffer.byteLength(sdata), data.length); data = data.slice(Buffer.byteLength(sdata), data.length);
// Replace host and origin // Replace host and origin
sdata = sdata.replace(remoteHost, host) sdata = sdata.replace(remoteHost, options.host)
.replace(remoteHost, host); .replace(remoteHost, options.host);
try { try {
// Write printable // Write printable
...@@ -602,7 +660,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h ...@@ -602,7 +660,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
} }
// If we have been passed buffered data, resume it. // If we have been passed buffered data, resume it.
if (buffer && !errState) { if (options.buffer && !errState) {
buffer.resume(); options.buffer.resume();
} }
}; };
\ No newline at end of file
...@@ -100,10 +100,14 @@ TestRunner.prototype.startProxyServer = function (port, targetPort, host, callba ...@@ -100,10 +100,14 @@ TestRunner.prototype.startProxyServer = function (port, targetPort, host, callba
TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) { TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) {
// Initialize the nodeProxy and start proxying the request // Initialize the nodeProxy and start proxying the request
var that = this, proxyServer = httpProxy.createServer(function (req, res, proxy) { var that = this, proxyServer = httpProxy.createServer(function (req, res, proxy) {
var data = proxy.buffer(req); var buffer = proxy.buffer(req);
setTimeout(function () { setTimeout(function () {
proxy.proxyRequest(req, res, targetPort, host, data); proxy.proxyRequest(req, res, {
port: targetPort,
host: host,
buffer: buffer
});
}, latency); }, latency);
}); });
...@@ -135,7 +139,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten ...@@ -135,7 +139,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
proxyServer = http.createServer(function (req, res) { proxyServer = http.createServer(function (req, res) {
var buffer = proxy.buffer(req); var buffer = proxy.buffer(req);
setTimeout(function () { setTimeout(function () {
proxy.proxyRequest(req, res, buffer); proxy.proxyRequest(req, res, {
buffer: buffer
});
}, latency); }, latency);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment