From f765f90ec37defaa2b493f859a982add51e25b76 Mon Sep 17 00:00:00 2001 From: indexzero Date: Sat, 10 Sep 2011 03:55:07 -0400 Subject: [PATCH] [api] Finalized the RoutingProxy API --- lib/node-http-proxy/http-proxy.js | 4 +- lib/node-http-proxy/proxy-table.js | 57 ++++++++-------- lib/node-http-proxy/routing-proxy.js | 98 +++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 37 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index e02be9cad..165fccfc3 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -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'); } @@ -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 diff --git a/lib/node-http-proxy/proxy-table.js b/lib/node-http-proxy/proxy-table.js index a6ecff61c..749ea7d45 100644 --- a/lib/node-http-proxy/proxy-table.js +++ b/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 @@ -29,7 +29,7 @@ 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__ @@ -37,31 +37,34 @@ var util = require('util'), // 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); }); @@ -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 = []; @@ -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. @@ -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 @@ -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], @@ -144,7 +149,7 @@ ProxyTable.prototype.getProxyLocation = function (req) { } } } - + return null; }; diff --git a/lib/node-http-proxy/routing-proxy.js b/lib/node-http-proxy/routing-proxy.js index 969aff531..3938b26f7 100644 --- a/lib/node-http-proxy/routing-proxy.js +++ b/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, @@ -12,11 +20,15 @@ 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); + }); } // @@ -24,6 +36,21 @@ var RoutingProxy = exports.RoutingProxy = function (options) { // 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; }; @@ -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); }; // @@ -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); }; // @@ -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); }; @@ -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); -}; \ No newline at end of file +}; + +// +// ### @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(':'); +} \ No newline at end of file