From 13542bab1cb684740e167db672dd90686ac7465a Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Wed, 19 Aug 2015 20:52:24 +0200 Subject: [PATCH 1/3] refactor: config-factory.js to handle proxy options --- README.md | 2 +- index.js | 23 ++++++----------------- lib/config-factory.js | 39 +++++++++++++++++++++++++++++++++++++++ lib/handlers.js | 2 +- 4 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 lib/config-factory.js diff --git a/README.md b/README.md index 2a9d2b2b..ec03f3a5 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ $ npm test $ npm run cover ``` -### Changlog +### Changelog * [v0.6.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.6.0) - support proxyTable * [v0.5.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.5.0) - support subscribing to http-proxy error- and proxyRes-event * [v0.4.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.4.0) - support websocket diff --git a/index.js b/index.js index d61f9797..23d4f574 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ var _ = require('lodash'); var httpProxy = require('http-proxy'); +var configFactory = require('./lib/config-factory'); var handlers = require('./lib/handlers'); var contextMatcher = require('./lib/context-matcher'); var PathRewriter = require('./lib/path-rewriter'); @@ -7,24 +8,12 @@ var ProxyTable = require('./lib/proxy-table'); var httpProxyMiddleware = function (context, opts) { var isWsUpgradeListened = false; - var proxyOptions = opts || {}; - - // Legacy option.proxyHost - // set options.headers.host when option.proxyHost is provided - if (proxyOptions.proxyHost) { - console.log('*************************************'); - console.log('[HPM] Deprecated "option.proxyHost"'); - console.log(' Use "option.changeOrigin" or "option.headers.host" instead'); - console.log(' "option.proxyHost" will be removed in future release.'); - console.log('*************************************'); - - proxyOptions.headers = proxyOptions.headers || {}; - proxyOptions.headers.host = proxyOptions.proxyHost; - } + var config = configFactory.createConfig(context, opts); + var proxyOptions = config.options; // create proxy var proxy = httpProxy.createProxyServer(proxyOptions); - console.log('[HPM] Proxy created:', context, ' -> ', proxyOptions.target); + console.log('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target); var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided @@ -56,7 +45,7 @@ var httpProxyMiddleware = function (context, opts) { return middleware; function middleware (req, res, next) { - if (contextMatcher.match(context, req.url)) { + if (contextMatcher.match(config.context, req.url)) { if (proxyOptions.proxyTable) { // change option.target when proxyTable present. proxy.web(req, res, ProxyTable.createProxyOptions(req, proxyOptions)); @@ -82,7 +71,7 @@ var httpProxyMiddleware = function (context, opts) { isWsUpgradeListened = true; req.connection.server.on('upgrade', function (req, socket, head) { - if (contextMatcher.match(context, req.url)) { + if (contextMatcher.match(config.context, req.url)) { if (pathRewriter) { req.url = pathRewriter(req.url); } diff --git a/lib/config-factory.js b/lib/config-factory.js new file mode 100644 index 00000000..b01af294 --- /dev/null +++ b/lib/config-factory.js @@ -0,0 +1,39 @@ +var _ = require('lodash'); + +module.exports = { + createConfig : createConfig +} + +function createConfig (context, opts) { + var config = { + context : undefined, + options : {} + }; + + config.context = context; + + if (opts) { + _.assign(config.options, opts); + } + + config.options = mapLegacyProxyHostOption(config.options); + + return config; +}; + +// Legacy option.proxyHost +function mapLegacyProxyHostOption (options) { + // set options.headers.host when option.proxyHost is provided + if (options.proxyHost) { + console.log('*************************************'); + console.log('[HPM] Deprecated "option.proxyHost"'); + console.log(' Use "option.changeOrigin" or "option.headers.host" instead'); + console.log(' "option.proxyHost" will be removed in future release.'); + console.log('*************************************'); + + options.headers = options.headers || {}; + options.headers.host = options.proxyHost; + } + + return options; +} diff --git a/lib/handlers.js b/lib/handlers.js index 12b43636..7a242758 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,5 +1,5 @@ module.exports = { - proxyError : proxyError + proxyError : proxyError } function proxyError (err, req, res) { From 92f72c3acaf956b7935c7f27a7447e8d8bc20900 Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Wed, 19 Aug 2015 21:59:50 +0200 Subject: [PATCH 2/3] feat: shorthand --- lib/config-factory.js | 31 ++++++++-- test/config-factory.spec.js | 95 ++++++++++++++++++++++++++++++ test/http-proxy-middleware.spec.js | 32 ++++++++++ 3 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 test/config-factory.spec.js diff --git a/lib/config-factory.js b/lib/config-factory.js index b01af294..6fcc1e1e 100644 --- a/lib/config-factory.js +++ b/lib/config-factory.js @@ -1,27 +1,46 @@ -var _ = require('lodash'); +var _ = require('lodash'); +var url = require('url'); module.exports = { createConfig : createConfig -} +}; function createConfig (context, opts) { + // structure of config object to be returned var config = { context : undefined, options : {} }; - config.context = context; + var useShortHand = isShortHand(context); - if (opts) { - _.assign(config.options, opts); + if (useShortHand) { + var oUrl = url.parse(context); + var target = [oUrl.protocol, '//', oUrl.host].join(''); + + config.context = oUrl.pathname; + config.options = _.assign(config.options, {target:target}, opts); + } else { + config.context = context; + config.options = _.assign(config.options, opts); } + // Legacy option.proxyHost config.options = mapLegacyProxyHostOption(config.options); + if (!config.options.target) { + throw new Error('[HPM] Missing "target" option. Example: {target: "http://www.example.org"}'); + } + return config; }; -// Legacy option.proxyHost +function isShortHand (context) { + if (_.isString(context)) { + return (url.parse(context).host) ? true : false; + } +} + function mapLegacyProxyHostOption (options) { // set options.headers.host when option.proxyHost is provided if (options.proxyHost) { diff --git a/test/config-factory.spec.js b/test/config-factory.spec.js new file mode 100644 index 00000000..2906a157 --- /dev/null +++ b/test/config-factory.spec.js @@ -0,0 +1,95 @@ +var expect = require('chai').expect; +var configFactory = require('../lib/config-factory'); + +describe('configFactory', function () { + var result; + + describe('createConfig()', function () { + + describe('classic api', function () { + var context = '/api'; + var options = {target: 'http://www.example.org'}; + + beforeEach(function () { + result = configFactory.createConfig(context, options); + }); + + it('should return on config object', function () { + expect(result).to.have.all.keys('context', 'options'); + }); + + it('should return on config object with context', function () { + expect(result.context).to.equal(context); + }); + + it('should return on config object with options', function () { + expect(result.options).to.deep.equal(options); + }); + }); + + describe('shorthand api', function () { + beforeEach(function () { + result = configFactory.createConfig('http://www.example.org:8000/api'); + }); + + it('should return on config object', function () { + expect(result).to.have.all.keys('context', 'options'); + }); + + it('should return on config object with context', function () { + expect(result.context).to.equal('/api'); + }); + + it('should return on config object with options', function () { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}); + }); + }); + + describe('shorthand api for whole domain', function () { + beforeEach(function () { + result = configFactory.createConfig('http://www.example.org:8000'); + }); + + it('should return on config object with context', function () { + expect(result.context).to.equal('/'); + }); + }); + + describe('shorthand api with globbing', function () { + beforeEach(function () { + result = configFactory.createConfig('http://www.example.org:8000/api/*.json'); + }); + + it('should return on config object with context', function () { + expect(result.context).to.equal('/api/*.json'); + }); + }); + + describe('shorthand api with options', function () { + beforeEach(function () { + result = configFactory.createConfig('http://www.example.org:8000/api', {changeOrigin: true}); + }); + + it('should return on config object with additional options', function () { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000', changeOrigin: true}); + }); + }); + + describe('missing option.target', function () { + var fn + beforeEach(function () { + fn = function () { + configFactory.createConfig('/api'); + } + }); + + it('should throw an error when target option is missing', function () { + expect(fn).to.throw(Error); + }); + }); + + + }); + +}); + diff --git a/test/http-proxy-middleware.spec.js b/test/http-proxy-middleware.spec.js index 3310f4cf..71aed606 100644 --- a/test/http-proxy-middleware.spec.js +++ b/test/http-proxy-middleware.spec.js @@ -462,6 +462,38 @@ describe('http-proxy-middleware in actual server', function () { }); }); + describe('shorthand usage', function () { + var proxyServer, targetServer; + var responseBody; + + beforeEach(function (done) { + var mw_proxy = proxyMiddleware('http://localhost:8000/api'); + 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) { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + afterEach(function () { + proxyServer.close(); + targetServer.close(); + }); + + it('should have proxy with shorthand configuration', function () { + expect(responseBody).to.equal('/api/foo/bar'); + }); + }); + }); From 3da46aa382215b150899268f00a1c1d8db11594b Mon Sep 17 00:00:00 2001 From: Steven Chim Date: Wed, 19 Aug 2015 22:37:44 +0200 Subject: [PATCH 3/3] docs: shorthand --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec03f3a5..097c9e30 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,10 @@ var proxy = proxyMiddleware('/api', {target: 'http://www.example.org'}); // | | // context options -// 'proxy' is now ready to be used in a server. +// 'proxy' is now ready to be used in a server. + +// shorthand syntax for the example above: +// proxyMiddleware('http://www.example.org/api'); ``` * **context**: matches provided context against request-urls' path. @@ -39,8 +42,10 @@ A simple example with express server. var express = require('express'); var proxyMiddleware = require('http-proxy-middleware'); -// configure proxy middleware +// configure proxy middleware context var context = '/api'; // requests with this path will be proxied + +// configure proxy middleware options var options = { target: 'http://www.example.org', // target host changeOrigin: true, // needed for virtual hosted sites @@ -64,7 +69,7 @@ var app = express(); app.listen(3000); ``` -See [working examples](#more-examples). +Check out [working examples](#more-examples). **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`. @@ -91,6 +96,22 @@ Request URL's [ _path-absolute_ and _query_](https://tools.ietf.org/html/rfc3986 * `['/api/**', '!**/bad.json']` exclusion +### Shorthand +Use the shorthand syntax for simple use cases. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed. + +```javascript +proxyMiddleware('http://www.example.org:8000/api'); +// proxyMiddleware('/api', {target: 'http://www.example.org:8000'}); + + +proxyMiddleware('http://www.example.org:8000/api/books/*/**.json'); +// proxyMiddleware('/api/books/*/**.json', {target: 'http://www.example.org:8000'}); + + +proxyMiddleware('http://www.example.org:8000/api', {changeOrigin:true}); +// proxyMiddleware('/api', {target: 'http://www.example.org:8000', changeOrigin: true}); +``` + ### Options * **option.pathRewrite**: object, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths. ```javascript