diff --git a/README.md b/README.md index 2a9d2b2b..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 @@ -194,7 +215,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..6fcc1e1e --- /dev/null +++ b/lib/config-factory.js @@ -0,0 +1,58 @@ +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 : {} + }; + + var useShortHand = isShortHand(context); + + 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; +}; + +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) { + 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) { 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'); + }); + }); + });