Skip to content

Commit

Permalink
[api] Finalized the RoutingProxy API
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Sep 10, 2011
1 parent 598fe2e commit f765f90
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 37 deletions.
4 changes: 2 additions & 2 deletions lib/node-http-proxy/http-proxy.js
Expand Up @@ -80,7 +80,7 @@ var HttpProxy = exports.HttpProxy = function (options) {
self[key].base = httpProxy._getBase(self[key]);
}

setupProxy('target')
setupProxy('target');
if (this.forward) {
setupProxy('forward');
}
Expand All @@ -102,7 +102,7 @@ var HttpProxy = exports.HttpProxy = function (options) {
//
this.source = options.source || { host: 'localhost', port: 8000 };
this.source.https = this.source.https || options.https;
this.changeOrigin = options.changeOrigin || false;
this.changeOrigin = options.changeOrigin || false;
};

// Inherit from events.EventEmitter
Expand Down
57 changes: 31 additions & 26 deletions lib/node-http-proxy/proxy-table.js
@@ -1,7 +1,7 @@
/*
node-http-proxy.js: Lookup table for proxy targets in node.js
Copyright (c) 2010 Charlie Robbins
Copyright (c) 2010 Charlie Robbins
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down Expand Up @@ -29,39 +29,42 @@ var util = require('util'),
fs = require('fs');

//
// ### function ProxyTable (router, silent)
// ### function ProxyTable (router, silent)
// #### @router {Object} Object containing the host based routes
// #### @silent {Boolean} Value indicating whether we should suppress logs
// #### @hostnameOnly {Boolean} Value indicating if we should route based on __hostname string only__
// Constructor function for the ProxyTable responsible for getting
// locations of proxy targets based on ServerRequest headers; specifically
// the HTTP host header.
//
var ProxyTable = exports.ProxyTable = function (router, silent, hostnameOnly) {
var ProxyTable = exports.ProxyTable = function (options) {
events.EventEmitter.call(this);
this.silent = typeof silent !== 'undefined' ? silent : true;
this.hostnameOnly = typeof hostnameOnly !== 'undefined' ? hostnameOnly : false;
if (typeof router === 'object') {

this.silent = options.silent || options.silent !== true;
this.hostnameOnly = options.hostnameOnly === true;

if (typeof options.router === 'object') {
//
// If we are passed an object literal setup
// the routes with RegExps from the router
// If we are passed an object literal setup
// the routes with RegExps from the router
//
this.setRoutes(router);
this.setRoutes(options.router);
}
else if (typeof router === 'string') {
else if (typeof options.router === 'string') {
//
// If we are passed a string then assume it is a
// If we are passed a string then assume it is a
// file path, parse that file and watch it for changes
//
var self = this;
this.routeFile = router;
this.setRoutes(JSON.parse(fs.readFileSync(router)).router);
this.routeFile = options.router;
this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router);

fs.watchFile(this.routeFile, function () {
fs.readFile(self.routeFile, function (err, data) {
if (err) throw err;
if (err) {
self.emit('error', err);
}

self.setRoutes(JSON.parse(data).router);
self.emit('routes', self.hostnameOnly === false ? self.routes : self.router);
});
Expand All @@ -78,17 +81,17 @@ var ProxyTable = exports.ProxyTable = function (router, silent, hostnameOnly) {
util.inherits(ProxyTable, events.EventEmitter);

//
// ### function setRoutes (router)
// ### function setRoutes (router)
// #### @router {Object} Object containing the host based routes
// Sets the host-based routes to be used by this instance.
// Sets the host-based routes to be used by this instance.
//
ProxyTable.prototype.setRoutes = function (router) {
if (!router) {
throw new Error('Cannot update ProxyTable routes without router.');
}

this.router = router;

if (this.hostnameOnly === false) {
var self = this;
this.routes = [];
Expand All @@ -105,7 +108,7 @@ ProxyTable.prototype.setRoutes = function (router) {
};

//
// ### function getProxyLocation (req)
// ### function getProxyLocation (req)
// #### @req {ServerRequest} The incoming server request to get proxy information about.
// Returns the proxy location based on the HTTP Headers in the ServerRequest `req`
// available to this instance.
Expand All @@ -114,14 +117,14 @@ ProxyTable.prototype.getProxyLocation = function (req) {
if (!req || !req.headers || !req.headers.host) {
return null;
}

var target = req.headers.host.split(':')[0];
if (this.hostnameOnly == true) {
if (this.router.hasOwnProperty(target)) {
var location = this.router[target].split(':'),
host = location[0],
port = location.length === 1 ? 80 : location[1];

return {
port: port,
host: host
Expand All @@ -131,7 +134,9 @@ ProxyTable.prototype.getProxyLocation = function (req) {
else {
target += req.url;
for (var i in this.routes) {
var match, route = this.routes[i];
var route = this.routes[i],
match;

if (match = target.match(route.route)) {
var location = route.target.split(':'),
host = location[0],
Expand All @@ -144,7 +149,7 @@ ProxyTable.prototype.getProxyLocation = function (req) {
}
}
}

return null;
};

Expand Down
98 changes: 89 additions & 9 deletions lib/node-http-proxy/routing-proxy.js
@@ -1,3 +1,11 @@
/*
* routing-proxy.js: A routing proxy consuming a RoutingTable and multiple HttpProxy instances
*
* (C) 2011 Nodejitsu Inc.
* MIT LICENCE
*
*/

var events = require('events'),
util = require('util'),
HttpProxy = require('./http-proxy').HttpProxy,
Expand All @@ -12,18 +20,37 @@ var events = require('events'),
//
var RoutingProxy = exports.RoutingProxy = function (options) {
events.EventEmitter.call(this);

var self = this;
options = options || {};

if (options.router) {
//
// TODO: Consume the RoutingTable for various things: `this.proxyTable`
//
this.proxyTable = new ProxyTable(options);
this.proxyTable.on('routes', function (routes) {
self.emit('routes', routes);
});
}

//
// Create a set of `HttpProxy` objects to be used later on calls
// to `.proxyRequest()` and `.proxyWebSocketRequest()`.
//
this.proxies = {};

//
// Setup default target options (such as `https`).
//
this.target  = {};
this.target.https = options.target && options.target.https;

//
// Setup other default options to be used for instances of
// `HttpProxy` created by this `RoutingProxy` instance.
//
this.source = options.source || { host: 'localhost', port: 8000 };
this.https = this.source.https || options.https;
this.enable = options.enable;
this.forward = options.forward;
};


Expand All @@ -39,7 +66,30 @@ util.inherits(RoutingProxy, events.EventEmitter);
// for the specified `options.host` and `options.port`.
//
RoutingProxy.prototype.add = function (options) {
var self = this,
key = this._getKey(options);

//
// TODO: Consume properties in `options` related to the `ProxyTable`.
//
options.target = options.target || {};
options.target.host = options.target.host || options.host;
options.target.port = options.target.port || options.port;
options.target.https = this.target && this.target.https ||
options.target && options.target.https ||
options.https;

//
// Setup options to pass-thru to the new `HttpProxy` instance
// for the specified `options.host` and `options.port` pair.
//
['https', 'enable', 'forward'].forEach(function (key) {
if (options[key] !== false && self[key]) {
options[key] = self[key];
}
});

this.proxies[key] = new HttpProxy(options);
};

//
Expand All @@ -49,7 +99,7 @@ RoutingProxy.prototype.add = function (options) {
// for the specified `options.host` and `options.port` (if they exist).
//
RoutingProxy.prototype.remove = function (options) {

var key = this._getKey(options);
};

//
Expand Down Expand Up @@ -129,8 +179,13 @@ RoutingProxy.prototype.proxyRequest = function (req, res, options) {
}

var key = options.host + ':' + options.port,
proxy = this.proxies[key] || this._addTarget(options);

proxy;

if (!this.proxies[key]) {
this.add(options);
}

proxy = this.proxies[key];
proxy.proxyRequest(req, res, options.buffer);
};

Expand Down Expand Up @@ -161,7 +216,32 @@ RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, opti
}

var key = options.host + ':' + options.port,
proxy = this.proxies[key] || this._addTarget(options);

proxy;

if (!this.proxies[key]) {
this.add(options);
}

proxy = this.proxies[key];
proxy.proxyWebSocketRequest(req, socket, head, options.buffer);
};
};

//
// ### @private function _getKey (options)
// #### @options {Object} Options to extract the key from
// Ensures that the appropriate options are present in the `options`
// provided and responds with a string key representing the `host`, `port`
// combination contained within.
//
RoutingProxy.prototype._getKey = function (options) {
if (!options || ((!options.host || !options.port)
&& (!options.target || !options.target.host || !options.target.port))) {
throw new Error('options.host and options.port or options.target are required.');
return;
}

return [
options.host || options.target.host,
options.port || options.target.port
].join(':');
}

0 comments on commit f765f90

Please sign in to comment.