From 8275ccca49785d161fcb9f882d722cf56dc456a3 Mon Sep 17 00:00:00 2001 From: Marco Bartolucci Date: Sat, 25 Feb 2017 17:54:17 +0100 Subject: [PATCH 1/3] Added host-based rules matchingy --- index.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6c552e5..6380253 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,11 @@ /** * This is a constructor for a HttpProxyRules instance. - * @param {Object} options Takes in a `rules` obj, (optional) `default` target + * @param {Object} options Takes in a `rules` obj, a `hosts` obj, (optional) `default` target */ function HttpProxyRules(options) { this.rules = options.rules; + this.hosts = options.hosts; this.default = options.default || null; return this; @@ -55,4 +56,23 @@ HttpProxyRules.prototype.match = function match(req) { return target; } +/** + * This function will modify the `req` object if a matching host is found. + * We also return the new endpoint string if a match is found. + * @param {Object} options Takes in a `req` object. + */ +HttpProxyRules.prototype.matchHost = function matchHost(req) { + var hosts = this.hosts; + var target = this.default; + reqHost = req.headers['host']; + + for (var host in hosts) { + if (host === reqHost) { + target = hosts[host]; + break; + } + } + return target; +} + module.exports = HttpProxyRules; From 30bac7b4379cf53f01a486dddb7f1b82aaddaf58 Mon Sep 17 00:00:00 2001 From: Marco Bartolucci Date: Sat, 25 Feb 2017 18:56:00 +0100 Subject: [PATCH 2/3] Added test for host-based rules --- example/simple.js | 10 ++++++++++ test/index.tests.js | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/example/simple.js b/example/simple.js index 7bbf296..f735ea7 100644 --- a/example/simple.js +++ b/example/simple.js @@ -11,6 +11,9 @@ module.exports = function spawnReverseProxy(cb) { '.*/test': 'http://localhost:8080/cool', // Rule (1) '.*/test2/': 'http://localhost:8080/cool2/' // Rule (2) }, + hosts: { + 'testhost': 'http://localhost:8080/testhost' + }, default: 'http://localhost:8080' // default target }); @@ -30,6 +33,13 @@ module.exports = function spawnReverseProxy(cb) { }); } + var target = proxyRules.matchHost(req); + if (target) { + return proxy.web(req, res, { + target: target + }); + } + res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('The request url and path did not match any of the listed rules!'); }).listen(6010, cb); diff --git a/test/index.tests.js b/test/index.tests.js index d31fda4..897ebff 100644 --- a/test/index.tests.js +++ b/test/index.tests.js @@ -19,7 +19,9 @@ describe('Proxy Routes', function () { res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ // response includes the url that you tried to access - translatedPath: req.url + translatedPath: req.url, + translatedHost: req.headers['host'], + translatedPort: req.socket.localPort })); }).listen(targetPort, function mockServerReady() { spawnReverseProxy(function proxyServerReady() { @@ -98,4 +100,19 @@ describe('Proxy Routes', function () { }); + it('should translate the port correctly', function (done) { + request({ + url: 'http://127.0.0.1:' + proxyServerPort + '/testhost', + json: true, + headers: { + 'Host': 'testhost' + } + }, function processResp(err, res, body) { + + expect(res.statusCode).to.equal(200); + expect(body.translatedPort + body.translatedPath).to.equal('8080/testhost'); + done(); + }); + }); + }); From 0ff8053588988ef7b266b879dcaa2bd1e2657725 Mon Sep 17 00:00:00 2001 From: Marco Bartolucci Date: Sat, 25 Feb 2017 19:04:46 +0100 Subject: [PATCH 3/3] Updated README.md --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index c2643d2..c35a9cc 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,43 @@ npm install http-proxy-rules --save }).listen(6010, cb); ``` +## Example of host-based rule + +```js + var http = require('http'), + httpProxy = require('http-proxy'), + HttpProxyRules = require('http-proxy-rules'); + + // Set up proxy rules instance + var proxyRules = new HttpProxyRules({ + hosts: { + 'foo.com': 'http://bar.com:8080', // Rule (1) + 'foobar.com': 'http://localhost:1234/' // Rule (2) + }, + default: 'http://localhost:8080' // default target + }); + + // Create reverse proxy instance + var proxy = httpProxy.createProxy(); + + // Create http server that leverages reverse proxy instance + // and proxy rules to proxy requests to different targets + http.createServer(function(req, res) { + + // a match method is exposed on the proxy rules instance + // to test a request to see if it matches against one of the specified rules + var target = proxyRules.matchHost(req); + if (target) { + return proxy.web(req, res, { + target: target + }); + } + + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end('The request host did not match any of the listed rules!'); + }).listen(6010, cb); +``` + Given the object we used to initialize the `HttpProxyRules` instance above, here are some [**examples**](test/index.tests.js#L38) of how sample url paths would be translated. ## Options @@ -56,11 +93,14 @@ You can initialize a new `http-proxy-rules` instance with the following options: ```js { rules: {}, // See notes below + hosts: {}, // See notes below default: '' // (optional) if no rules matched, translate url path to specified default } ``` The rules object contains a set of key-value pairs mapping a regex-supported url path to a target route. The module only tries to match the visited url path, and not the entire url, with a specified rule. The target route must include the protocol (e.g., http) and the FQDN. See the [tests](test/index.tests.js) for examples of how incoming route url paths may be translated with the use of this module. +The hosts object contains a set of key-value pairs mapping a host to a target host:port. + ## Other Notes * `(?:\\W|$)` is appended to the end of the regex-supported url path, so that if there is a key like `.*/test` in the rules, the module matches paths `/test`, `/test/`, `/test?` but not `/testing`. * As long as object keys continued to be ordered in V8, if there are multiple rules that match against a given url path, the module will pick the matching rule listed first for the translation.