From 17e56aa9b17d47c9b318742daaa69dd952deed25 Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Thu, 9 Jul 2015 20:33:28 +0200 Subject: [PATCH 1/5] tests: refactor tests --- test/http-proxy-middleware.spec.js | 158 +++++++++++++++-------------- 1 file changed, 83 insertions(+), 75 deletions(-) diff --git a/test/http-proxy-middleware.spec.js b/test/http-proxy-middleware.spec.js index e9d825d6..7a83dee4 100644 --- a/test/http-proxy-middleware.spec.js +++ b/test/http-proxy-middleware.spec.js @@ -39,22 +39,23 @@ describe('context matching', function () { }); describe('http-proxy-middleware in actual server', function () { - var servers; describe('basic setup', function () { + var proxyServer, targetServer; var targetHeaders; var responseBody; beforeEach(function (done) { - servers = createServers({ - proxy: proxyMiddleware('/api', {target:'http://localhost:8000'}), - sourceMiddleware : function (req, res, next) {next()}, - targetMiddleware: function (req, res, next) { - targetHeaders = req.headers; // store target headers. - res.write('HELLO WEB'); // respond with 'HELLO WEB' - res.end() - }, - }); + var mw_proxy = proxyMiddleware('/api', {target:'http://localhost:8000'}); + + var mw_target = function (req, res, next) { + targetHeaders = req.headers; // store target headers. + res.write('HELLO WEB'); // respond with 'HELLO WEB' + res.end() + }; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); http.get('http://localhost:3000/api/', function (res) { res.on('data', function (chunk) { @@ -64,6 +65,10 @@ describe('http-proxy-middleware in actual server', function () { }); }); + afterEach(function () { + proxyServer.close(); + targetServer.close(); + }); it('should have the same headers.host value', function () { expect(targetHeaders.host).to.equal('localhost:3000'); @@ -75,22 +80,28 @@ describe('http-proxy-middleware in actual server', function () { }); describe('additional request headers', function () { + var proxyServer, targetServer; var targetHeaders; beforeEach(function (done) { - servers = createServers({ - proxy: proxyMiddleware('/api', {target:'http://localhost:8000', headers: {host:'foobar.dev'} }), - sourceMiddleware : function (req, res, next) {next()}, - targetMiddleware: function (req, res, next) { - targetHeaders = req.headers; // host - res.end(); - }, - }); + var mw_proxy = proxyMiddleware('/api', { target:'http://localhost:8000', headers: {host:'foobar.dev'} }); + + var mw_target = function (req, res, next) { + targetHeaders = req.headers; + res.end(); + }; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); http.get('http://localhost:3000/api/', function (res) { done(); }); + }); + afterEach(function () { + proxyServer.close(); + targetServer.close(); }); it('should send request header "host" to target server', function () { @@ -99,38 +110,46 @@ describe('http-proxy-middleware in actual server', function () { }); describe('legacy proxyHost parameter', function () { + var proxyServer, targetServer; var targetHeaders; beforeEach(function (done) { - servers = createServers({ - proxy: proxyMiddleware('/api', {target:'http://localhost:8000', proxyHost: 'foobar.dev'}), - sourceMiddleware : function (req, res, next) {next()}, - targetMiddleware: function (req, res, next) { - targetHeaders = req.headers; // host - res.end(); - }, - }); + var mw_proxy = proxyMiddleware('/api', {target:'http://localhost:8000', proxyHost: 'foobar.dev'}); + + var mw_target = function (req, res, next) { + targetHeaders = req.headers; + res.end(); + }; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); http.get('http://localhost:3000/api/', function (res) { done(); }); + }); + afterEach(function () { + proxyServer.close(); + targetServer.close(); }); + it('should proxy host header to target server', function () { expect(targetHeaders.host).to.equal('foobar.dev'); }); }); describe('Error handling', function () { + var proxyServer, targetServer; var response; beforeEach(function (done) { - servers = createServers({ - proxy: proxyMiddleware('/api', {target:'http://localhost:666'}), // unreachable host on port:666 - sourceMiddleware : function (req, res, next) {next()}, - targetMiddleware: function (req, res, next) {next()}, - }); + var mw_proxy = proxyMiddleware('/api', {target:'http://localhost:666'}); // unreachable host on port:666 + var mw_target = function (req, res, next) {next()}; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); http.get('http://localhost:3000/api/', function (res) { response = res; @@ -138,29 +157,36 @@ describe('http-proxy-middleware in actual server', function () { }); }); + afterEach(function () { + proxyServer.close(); + targetServer.close(); + }); + + it('should handle errors when host is not reachable', function () { expect(response.statusCode).to.equal(500); }); }); describe('Rewrite path', function () { + var proxyServer, targetServer; var responseBody; beforeEach(function (done) { - servers = createServers({ - proxy: proxyMiddleware('/api', { - target:'http://localhost:8000', - pathRewrite: { - '^/api' : '/rest', - '^/remove' : '', - } - }), - sourceMiddleware : function (req, res, next) {next()}, - targetMiddleware: function (req, res, next) { - res.write(req.url); // respond with req.url - res.end() - }, + var mw_proxy = proxyMiddleware('/api', { + target:'http://localhost:8000', + pathRewrite: { + '^/api' : '/rest', + '^/remove' : '' + } }); + var mw_target = function (req, res, next) { + res.write(req.url); // respond with req.url + res.end() + }; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); http.get('http://localhost:3000/api/foo/bar', function (res) { res.on('data', function (chunk) { @@ -170,46 +196,28 @@ describe('http-proxy-middleware in actual server', function () { }); }); - it('should have response body with the rewritten req.url: "/rest/..."', function () { + afterEach(function () { + proxyServer.close(); + targetServer.close(); + }); + + it('should have rewritten path from "/api/foo/bar" to "/rest/foo/bar"', function () { expect(responseBody).to.equal('/rest/foo/bar'); }); }); - afterEach(function () { - closeServers(servers); - servers = null; - }); }); -/** - * source Server: http:localhost:3000 - * target Server: http:localhost:8000 - **/ -function createServers (options) { - var sourceServer, - targetServer; - - // source server - var sourceApp = express(); - sourceApp.use(options.sourceMiddleware); - sourceApp.use(options.proxy); - sourceServer = sourceApp.listen(3000); - +function createServer (portNumber, middleware) { + var app = express(); - // target server - var targetApp = express(); - targetApp.use(options.targetMiddleware); - targetServer = targetApp.listen(8000); - - return { - sourceServer : sourceServer, - targetServer : targetServer, + if (middleware) { + app.use(middleware); } -} -function closeServers (servers) { - servers.sourceServer.close(); - servers.targetServer.close(); + var server = app.listen(portNumber); + + return server; } From 4ffd4c8f5301359d95a60303d98cbf260a302317 Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Thu, 9 Jul 2015 23:15:32 +0200 Subject: [PATCH 2/5] feat: added support for multi path context --- index.js | 2 +- lib/util-context-match.js | 21 +++++++++ lib/utils.js | 15 ++++-- test/context-match.spec.js | 51 ++++++++++++++++++++ test/http-proxy-middleware.spec.js | 74 ++++++++++++++++++++++++++++++ test/utils.spec.js | 21 --------- 6 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 lib/util-context-match.js create mode 100644 test/context-match.spec.js delete mode 100644 test/utils.spec.js diff --git a/index.js b/index.js index bcbdf769..aedc7404 100644 --- a/index.js +++ b/index.js @@ -41,7 +41,7 @@ var httpProxyMiddleware = function (context, opts) { return middleware; function middleware (req, res, next) { - if (utils.hasContext(context, req.url)) { + if (utils.matchContext(context, req.url)) { proxy.web(req, res); } else { next(); diff --git a/lib/util-context-match.js b/lib/util-context-match.js new file mode 100644 index 00000000..b1d73f0d --- /dev/null +++ b/lib/util-context-match.js @@ -0,0 +1,21 @@ +var url = require('url'); + +module.exports = { + matchSinglePath : matchSinglePath, + matchMultiPath : matchMultiPath +} + +function matchSinglePath (context, uri) { + var urlPath = url.parse(uri).path; + return urlPath.indexOf(context) === 0; +} + +function matchMultiPath (contextList, uri) { + for (var i = 0; i < contextList.length; i++) { + var context = contextList[i]; + if (matchSinglePath(context, uri)) { + return true; + } + } + return false; +} diff --git a/lib/utils.js b/lib/utils.js index beb01cfb..180aa777 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,13 +1,20 @@ var url = require('url'); +var utilContextMatch = require('./util-context-match'); module.exports = { - hasContext : hasContext, + matchContext : matchContext, createPathRewriter : createPathRewriter } -function hasContext (context, uri) { - var urlPath = url.parse(uri).path; - return urlPath.indexOf(context) === 0; +function matchContext (context, uri) { + // single path + if (typeof context === 'string') { + return utilContextMatch.matchSinglePath(context, uri); + } + // multi path + else if (Array.isArray(context)) { + return utilContextMatch.matchMultiPath(context, uri); + } } /** diff --git a/test/context-match.spec.js b/test/context-match.spec.js new file mode 100644 index 00000000..1757d6d3 --- /dev/null +++ b/test/context-match.spec.js @@ -0,0 +1,51 @@ +var expect = require('chai').expect; +var utils = require('../lib/utils'); + +describe('Single path matching', function () { + var result; + + it('should return true when the context is present in url', function () { + result = utils.matchContext('/api', 'http://localhost/api/foo/bar'); + expect(result).to.be.true; + }); + + it('should return false when the context is not present in url', function () { + result = utils.matchContext('/abc', 'http://localhost/api/foo/bar'); + expect(result).to.be.false; + }); + + it('should return false when the context is present half way in url', function () { + result = utils.matchContext('/foo', 'http://localhost/api/foo/bar'); + expect(result).to.be.false; + }); + + it('should return false when the context does not start with /', function () { + result = utils.matchContext('api', 'http://localhost/api/foo/bar'); + expect(result).to.be.false; + }); +}); + +describe('Multi path matching', function () { + var result; + + it('should return true when the context is present in url', function () { + result = utils.matchContext(['/api'], 'http://localhost/api/foo/bar'); + expect(result).to.be.true; + }); + + it('should return true when the context is present in url', function () { + result = utils.matchContext(['/api', '/ajax'], 'http://localhost/ajax/foo/bar'); + expect(result).to.be.true; + }); + + it('should return false when the context does not match url', function () { + result = utils.matchContext(['/api', '/ajax'], 'http://localhost/foo/bar'); + expect(result).to.be.false; + }); + + it('should return false when empty array provided', function () { + result = utils.matchContext([], 'http://localhost/api/foo/bar'); + expect(result).to.be.false; + }); + +}); diff --git a/test/http-proxy-middleware.spec.js b/test/http-proxy-middleware.spec.js index 7a83dee4..5f1b10a5 100644 --- a/test/http-proxy-middleware.spec.js +++ b/test/http-proxy-middleware.spec.js @@ -79,6 +79,80 @@ describe('http-proxy-middleware in actual server', function () { }); }); + describe('multi path', function () { + var proxyServer, targetServer; + var targetHeaders; + var response, responseBody; + + beforeEach(function () { + var mw_proxy = proxyMiddleware(['/api', '/ajax'], {target:'http://localhost:8000'}); + + var mw_target = function (req, res, next) { + res.write(req.url); // respond with req.url + res.end() + }; + + proxyServer = createServer(3000, mw_proxy); + targetServer = createServer(8000, mw_target); + }); + + afterEach(function () { + proxyServer.close(); + targetServer.close(); + }); + + describe('request to path A, configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/api/some/endpoint', function (res) { + response = res; + res.on('data', function (chunk) { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + it('should proxy to path A', function () { + expect(response.statusCode).to.equal(200); + expect(responseBody).to.equal('/api/some/endpoint'); + }); + }); + + describe('request to path B, configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/ajax/some/library', function (res) { + response = res; + res.on('data', function (chunk) { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + it('should proxy to path B', function () { + expect(response.statusCode).to.equal(200); + expect(responseBody).to.equal('/ajax/some/library'); + }); + }); + + describe('request to path C, not configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/lorum/ipsum', function (res) { + response = res; + res.on('data', function (chunk) { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + it('should not proxy to this path', function () { + expect(response.statusCode).to.equal(404); + }); + }); + + }); + describe('additional request headers', function () { var proxyServer, targetServer; var targetHeaders; diff --git a/test/utils.spec.js b/test/utils.spec.js deleted file mode 100644 index 31052503..00000000 --- a/test/utils.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -var expect = require('chai').expect; -var utils = require('../lib/utils'); - - -describe('utils.hasContext(context, url)', function () { - - it('should return true when the context is present in url', function () { - var result = utils.hasContext('/api', 'http://localhost/api/foo/bar'); - expect(result).to.be.true; - }); - - it('should return false when the context is not present in url', function () { - var result = utils.hasContext('/abc', 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); - - it('should return false when the context is present half way in url', function () { - var result = utils.hasContext('/foo', 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); -}); From f0acd0410667c9d74a6115231136b0d9dd2b4a8f Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Fri, 10 Jul 2015 21:16:21 +0200 Subject: [PATCH 3/5] test: added tests for multi path docs: updated docs for multi path --- README.md | 19 +++++++++++-------- index.js | 2 +- lib/utils.js | 4 +++- test/context-match.spec.js | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 72d33430..d6879535 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,18 @@ npm install --save-dev http-proxy-middleware ``` ## Core concept -Create and configure the proxy middleware. +Configure the proxy middleware. ```javascript var proxy = proxyMiddleware('/api', {target: 'http://www.example.org'}); // 'proxy' is now ready to be used in a server. ``` +* `context` path to proxy. Example: `'/api'` or `['/api', '/ajax']` for multiple paths. +* `options.target` target host to proxy to. + +Check out available [proxy options](#options). + + ## Example ```javascript @@ -42,9 +48,6 @@ var app = express(); app.listen(3000); ``` -* `context` path to proxy. Example: '/api' -* `options.target` target host to proxy to. (See "[Options](#options)" for all options) - **Tip:** For [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based), you'll need to use the option `changeOrigin` and set it to `true`. ## Compatible servers: @@ -103,10 +106,10 @@ $ npm install $ node examples/connect ``` - Or just [explore the examples sources](https://github.com/chimurai/http-proxy-middleware/tree/master/examples): - * `examples/connect` - Example usage with [connect](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect) - * `examples/express` - Example usage with [express](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express) - * `examples/browser-sync` - Example usage with [browser-sync](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync) + Or just explore the [proxy examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples) sources: + * `examples/connect` - [connect proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect) + * `examples/express` - [express proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express) + * `examples/browser-sync` - [browser-sync proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync) ## Tests diff --git a/index.js b/index.js index aedc7404..0bffa904 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ var httpProxyMiddleware = function (context, opts) { if (proxyOptions.proxyHost) { console.log('*************************************'); console.log('[HPM] Deprecated "option.proxyHost"'); - console.log(' Use "option.headers.host" instead'); + console.log(' Use "option.changeOrigin" or "option.headers.host" instead'); console.log(' "option.proxyHost" will be removed in future release.'); console.log('*************************************'); diff --git a/lib/utils.js b/lib/utils.js index 180aa777..18d7f35b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,9 +12,11 @@ function matchContext (context, uri) { return utilContextMatch.matchSinglePath(context, uri); } // multi path - else if (Array.isArray(context)) { + if (Array.isArray(context)) { return utilContextMatch.matchMultiPath(context, uri); } + + throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]'); } /** diff --git a/test/context-match.spec.js b/test/context-match.spec.js index 1757d6d3..3672a819 100644 --- a/test/context-match.spec.js +++ b/test/context-match.spec.js @@ -49,3 +49,41 @@ describe('Multi path matching', function () { }); }); + + +describe('Test invalid contexts', function () { + var testContext; + + beforeEach(function () { + testContext = function (context) { + return function () { + utils.matchContext(context, 'http://localhost/api/foo/bar'); + }; + }; + }); + + it('should throw error with undefined', function () { + expect(testContext(undefined)).to.throw(Error); + }); + + it('should throw error with null', function () { + expect(testContext(null)).to.throw(Error); + }); + + it('should throw error with object literal', function () { + expect(testContext({})).to.throw(Error); + }); + + it('should throw error with integers', function () { + expect(testContext(123)).to.throw(Error); + }); + + it('should not throw error with string', function () { + expect(testContext('/123')).not.to.throw(Error); + }); + + it('should not throw error with Array', function () { + expect(testContext(['/123'])).not.to.throw(Error); + }); + +}); From 011a91196424da6dd78a715104b73e8529a7c96b Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Fri, 10 Jul 2015 22:55:04 +0200 Subject: [PATCH 4/5] docs: examples --- examples/browser-sync/index.js | 2 +- examples/connect/index.js | 2 +- examples/express/index.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/browser-sync/index.js b/examples/browser-sync/index.js index 1fa9d365..12720a82 100644 --- a/examples/browser-sync/index.js +++ b/examples/browser-sync/index.js @@ -16,7 +16,7 @@ browserSync({ server: { baseDir: "./", port: 3000, - middleware: [proxy], + middleware: [proxy], // add the proxy to browser-sync open: false } }); diff --git a/examples/connect/index.js b/examples/connect/index.js index c3948075..6651a253 100644 --- a/examples/connect/index.js +++ b/examples/connect/index.js @@ -14,7 +14,7 @@ var proxy = proxyMiddleware('/api', { }); var app = connect(); -app.use(proxy); +app.use(proxy); // add the proxy to connect http.createServer(app).listen(3000); diff --git a/examples/express/index.js b/examples/express/index.js index 7512342c..65770243 100644 --- a/examples/express/index.js +++ b/examples/express/index.js @@ -13,7 +13,7 @@ var proxy = proxyMiddleware('/api', { }); var app = express(); -app.use(proxy); +app.use(proxy); // add the proxy to express app.listen(3000); From e568795ea2e931d1d917f9bc7fb531aa45452fac Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Sun, 12 Jul 2015 13:04:09 +0200 Subject: [PATCH 5/5] tests: added more tests --- README.md | 18 +++-- index.js | 15 ++-- ...il-context-match.js => context-matcher.js} | 14 ++++ lib/{utils.js => path-rewriter.js} | 39 +++------- package.json | 2 +- ...-match.spec.js => context-matcher.spec.js} | 20 ++--- test/http-proxy-middleware.spec.js | 10 ++- test/path-rewriter.spec.js | 74 +++++++++++++++++++ 8 files changed, 138 insertions(+), 54 deletions(-) rename lib/{util-context-match.js => context-matcher.js} (55%) rename lib/{utils.js => path-rewriter.js} (50%) rename test/{context-match.spec.js => context-matcher.spec.js} (73%) create mode 100644 test/path-rewriter.spec.js diff --git a/README.md b/README.md index d6879535..3d6de782 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware) [![dependency Status](https://img.shields.io/david/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=devDependencies) [![devDependency Status](https://img.shields.io/david/dev/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=devDependencies) -[![MIT license](https://img.shields.io/npm/l/http-proxy-middleware.svg?style=flat-square)](https://www.npmjs.com/package/http-proxy-middleware) The one-liner proxy middleware for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express) and [browser-sync](https://github.com/BrowserSync/browser-sync) @@ -15,14 +14,21 @@ npm install --save-dev http-proxy-middleware ## Core concept Configure the proxy middleware. ```javascript +var proxyMiddleware = require('http-proxy-middleware'); + var proxy = proxyMiddleware('/api', {target: 'http://www.example.org'}); +// \____/ \________________________________/ +// | | +// context options + // 'proxy' is now ready to be used in a server. ``` -* `context` path to proxy. Example: `'/api'` or `['/api', '/ajax']` for multiple paths. -* `options.target` target host to proxy to. - -Check out available [proxy options](#options). +* **context**: matches provided context against request-urls' path. + Matching requests will be proxied to the target host. + Example: `'/api'` or `['/api', '/ajax']` +* **options.target**: target host to proxy to. + Check out available [proxy options](#options). @@ -60,7 +66,7 @@ http-proxy-middleware is compatible with the following servers: ## Options * (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead. -* **option.pathRewrite**: object, rewrite the url path. Object-keys will be used as _RegEx_ to match paths. +* **option.pathRewrite**: object, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths. ```javascript { diff --git a/index.js b/index.js index 0bffa904..42c60498 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ -var httpProxy = require('http-proxy'); -var utils = require('./lib/utils'); -var handlers = require('./lib/handlers'); +var httpProxy = require('http-proxy'); +var handlers = require('./lib/handlers'); +var contextMatcher = require('./lib/context-matcher'); +var pathRewriter = require('./lib/path-rewriter'); var httpProxyMiddleware = function (context, opts) { @@ -24,10 +25,10 @@ var httpProxyMiddleware = function (context, opts) { // handle option.pathRewrite if (proxyOptions.pathRewrite) { - var pathRewriter = utils.createPathRewriter(proxyOptions.pathRewrite); + var rewriter = pathRewriter.create(proxyOptions.pathRewrite); proxy.on('proxyReq', function (proxyReq, req, res, options) { - handlers.proxyPathRewrite(proxyReq, pathRewriter); + handlers.proxyPathRewrite(proxyReq, rewriter); }); } @@ -36,12 +37,12 @@ var httpProxyMiddleware = function (context, opts) { handlers.proxyError(err, req, res, proxyOptions); }); - console.log('[HPM] Proxy created:', context, proxyOptions.target); + console.log('[HPM] Proxy created:', context, ' -> ', proxyOptions.target); return middleware; function middleware (req, res, next) { - if (utils.matchContext(context, req.url)) { + if (contextMatcher.match(context, req.url)) { proxy.web(req, res); } else { next(); diff --git a/lib/util-context-match.js b/lib/context-matcher.js similarity index 55% rename from lib/util-context-match.js rename to lib/context-matcher.js index b1d73f0d..746fbbbf 100644 --- a/lib/util-context-match.js +++ b/lib/context-matcher.js @@ -1,10 +1,24 @@ var url = require('url'); module.exports = { + match : matchContext, matchSinglePath : matchSinglePath, matchMultiPath : matchMultiPath } +function matchContext (context, uri) { + // single path + if (typeof context === 'string') { + return matchSinglePath(context, uri); + } + // multi path + if (Array.isArray(context)) { + return matchMultiPath(context, uri); + } + + throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]'); +} + function matchSinglePath (context, uri) { var urlPath = url.parse(uri).path; return urlPath.indexOf(context) === 0; diff --git a/lib/utils.js b/lib/path-rewriter.js similarity index 50% rename from lib/utils.js rename to lib/path-rewriter.js index 18d7f35b..ac27b562 100644 --- a/lib/utils.js +++ b/lib/path-rewriter.js @@ -1,22 +1,5 @@ -var url = require('url'); -var utilContextMatch = require('./util-context-match'); - module.exports = { - matchContext : matchContext, - createPathRewriter : createPathRewriter -} - -function matchContext (context, uri) { - // single path - if (typeof context === 'string') { - return utilContextMatch.matchSinglePath(context, uri); - } - // multi path - if (Array.isArray(context)) { - return utilContextMatch.matchMultiPath(context, uri); - } - - throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]'); + create : createPathRewriter } /** @@ -45,16 +28,16 @@ function parsePathRewriteRules (config) { if (isObject(config) === false) { throw new Error('[HPM] Invalid pathRewrite config. Expecting an object literal with pathRewrite configuration'); - } - - for (var key in config) { - if (config.hasOwnProperty(key)) { - rules.push({ - regex : new RegExp(key), - value : config[key] - }); - - console.log('[HPM] Proxy rewrite rule created: "' + key + '" -> "' + config[key] + '"'); + } else { + for (var key in config) { + if (config.hasOwnProperty(key)) { + rules.push({ + regex : new RegExp(key), + value : config[key] + }); + + console.log('[HPM] Proxy rewrite rule created: "' + key + '" -> "' + config[key] + '"'); + } } } diff --git a/package.json b/package.json index a647cb28..d387831b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "http-proxy-middleware", "version": "0.1.0", - "description": "The one-liner http-proxy middleware", + "description": "The one-liner proxy middleware for connect, express and browser-sync", "main": "index.js", "scripts": { "test": "mocha --reporter spec", diff --git a/test/context-match.spec.js b/test/context-matcher.spec.js similarity index 73% rename from test/context-match.spec.js rename to test/context-matcher.spec.js index 3672a819..342d2256 100644 --- a/test/context-match.spec.js +++ b/test/context-matcher.spec.js @@ -1,26 +1,26 @@ var expect = require('chai').expect; -var utils = require('../lib/utils'); +var contextMatcher = require('../lib/context-matcher'); describe('Single path matching', function () { var result; it('should return true when the context is present in url', function () { - result = utils.matchContext('/api', 'http://localhost/api/foo/bar'); + result = contextMatcher.match('/api', 'http://localhost/api/foo/bar'); expect(result).to.be.true; }); it('should return false when the context is not present in url', function () { - result = utils.matchContext('/abc', 'http://localhost/api/foo/bar'); + result = contextMatcher.match('/abc', 'http://localhost/api/foo/bar'); expect(result).to.be.false; }); it('should return false when the context is present half way in url', function () { - result = utils.matchContext('/foo', 'http://localhost/api/foo/bar'); + result = contextMatcher.match('/foo', 'http://localhost/api/foo/bar'); expect(result).to.be.false; }); it('should return false when the context does not start with /', function () { - result = utils.matchContext('api', 'http://localhost/api/foo/bar'); + result = contextMatcher.match('api', 'http://localhost/api/foo/bar'); expect(result).to.be.false; }); }); @@ -29,22 +29,22 @@ describe('Multi path matching', function () { var result; it('should return true when the context is present in url', function () { - result = utils.matchContext(['/api'], 'http://localhost/api/foo/bar'); + result = contextMatcher.match(['/api'], 'http://localhost/api/foo/bar'); expect(result).to.be.true; }); it('should return true when the context is present in url', function () { - result = utils.matchContext(['/api', '/ajax'], 'http://localhost/ajax/foo/bar'); + result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/ajax/foo/bar'); expect(result).to.be.true; }); it('should return false when the context does not match url', function () { - result = utils.matchContext(['/api', '/ajax'], 'http://localhost/foo/bar'); + result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/foo/bar'); expect(result).to.be.false; }); it('should return false when empty array provided', function () { - result = utils.matchContext([], 'http://localhost/api/foo/bar'); + result = contextMatcher.match([], 'http://localhost/api/foo/bar'); expect(result).to.be.false; }); @@ -57,7 +57,7 @@ describe('Test invalid contexts', function () { beforeEach(function () { testContext = function (context) { return function () { - utils.matchContext(context, 'http://localhost/api/foo/bar'); + contextMatcher.match(context, 'http://localhost/api/foo/bar'); }; }; }); diff --git a/test/http-proxy-middleware.spec.js b/test/http-proxy-middleware.spec.js index 5f1b10a5..4303c863 100644 --- a/test/http-proxy-middleware.spec.js +++ b/test/http-proxy-middleware.spec.js @@ -40,15 +40,17 @@ describe('context matching', function () { describe('http-proxy-middleware in actual server', function () { - describe('basic setup', function () { + describe('basic setup, requests to target', function () { var proxyServer, targetServer; var targetHeaders; + var targetUrl; var responseBody; beforeEach(function (done) { var mw_proxy = proxyMiddleware('/api', {target:'http://localhost:8000'}); var mw_target = function (req, res, next) { + targetUrl = req.url; // store target url. targetHeaders = req.headers; // store target headers. res.write('HELLO WEB'); // respond with 'HELLO WEB' res.end() @@ -57,7 +59,7 @@ describe('http-proxy-middleware in actual server', function () { proxyServer = createServer(3000, mw_proxy); targetServer = createServer(8000, mw_target); - http.get('http://localhost:3000/api/', function (res) { + http.get('http://localhost:3000/api/b/c/d;p?q=1&r=[2,3]#s"', function (res) { res.on('data', function (chunk) { responseBody = chunk.toString(); done(); @@ -74,6 +76,10 @@ describe('http-proxy-middleware in actual server', function () { expect(targetHeaders.host).to.equal('localhost:3000'); }); + it('should have proxied the uri-path and uri-query, but not the uri-hash', function () { + expect(targetUrl).to.equal('/api/b/c/d;p?q=1&r=[2,3]'); + }); + it('should have response body: "HELLO WEB"', function () { expect(responseBody).to.equal('HELLO WEB'); }); diff --git a/test/path-rewriter.spec.js b/test/path-rewriter.spec.js new file mode 100644 index 00000000..4595c47e --- /dev/null +++ b/test/path-rewriter.spec.js @@ -0,0 +1,74 @@ +var expect = require('chai').expect; +var pathRewriter = require('../lib/path-rewriter'); + +describe('Path rewriting', function () { + var rewriter; + var result; + + describe('Configuration and usage', function () { + beforeEach(function () { + var config = { + '^/api/old' : '/api/new', + '^/remove' : '', + 'invalid' : 'path/new', + '/valid' : '/path/new' + }; + rewriter = pathRewriter.create(config); + }); + + it('should rewrite path', function () { + result = rewriter('/api/old/index.json'); + expect(result).to.equal('/api/new/index.json'); + }); + + it('should remove path', function () { + result = rewriter('/remove/old/index.json'); + expect(result).to.equal('/old/index.json'); + }); + + it('should leave path intact', function () { + result = rewriter('/foo/bar/index.json'); + expect(result).to.equal('/foo/bar/index.json'); + }); + + it('should not rewrite path when config-key does not match url with test(regex)', function () { + result = rewriter('/invalid/bar/foo.json'); + expect(result).to.equal('/path/new/bar/foo.json'); + expect(result).to.not.equal('/invalid/new/bar/foo.json'); + }); + + it('should rewrite path when config-key does match url with test(regex)', function () { + result = rewriter('/valid/foo/bar.json'); + expect(result).to.equal('/path/new/foo/bar.json'); + }); + }); + + describe('Invalid configuration', function () { + var badFn; + + beforeEach(function () { + badFn = function (config) { + return function () { + pathRewriter.create(config); + }; + }; + }); + + it('should throw when bad config is provided', function () { + expect(badFn()).to.throw(Error); + expect(badFn(null)).to.throw(Error); + expect(badFn(undefined)).to.throw(Error); + expect(badFn(123)).to.throw(Error); + expect(badFn("abc")).to.throw(Error); + expect(badFn(function(){})).to.throw(Error); + expect(badFn([])).to.throw(Error); + expect(badFn([1,2,3])).to.throw(Error); + }); + + it('should not throw when empty Object config is provided', function () { + expect(badFn({})).to.not.throw(Error); + }); + + }); +}); +