diff --git a/CHANGELOG.md b/CHANGELOG.md index b8f35848..c3e56571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [v0.x.x](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.x.x) +- feat(proxy): support proxy creation without context. +- fix(connect mounting): use connect's `path` configuration to mount proxy. + ## [v0.13.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.13.0) - feat(context): custom context matcher; when simple `path` matching is not sufficient. diff --git a/README.md b/README.md index b293dd03..ed958b1a 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ $ npm install --save-dev http-proxy-middleware ## Core concept Configure the proxy middleware. + ```javascript var proxy = require('http-proxy-middleware'); @@ -150,6 +151,19 @@ proxy('http://www.example.org:8000/api', {changeOrigin:true}); // proxy('/api', {target: 'http://www.example.org:8000', changeOrigin: true}); ``` +### app.use(path, proxy) + +If you want to use the server's `app.use` `path` parameter to match requests; + +Create and mount the proxy without the http-proxy-middleware `context` parameter: +```javascript +app.use('/api', proxy({target:'http://www.example.org', changeOrigin:true})); +``` + +`app.use` documentation: +* express: http://expressjs.com/en/4x/api.html#app.use +* connect: https://github.com/senchalabs/connect#mount-middleware + ## WebSocket ```javascript @@ -227,6 +241,9 @@ var server = app.listen(3000); } ``` +### Events +Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events): + * **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling. ```javascript function onError(err, req, res) { @@ -280,7 +297,9 @@ var server = app.listen(3000); * (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead. -The following options are provided by the underlying [http-proxy](https://www.npmjs.com/package/http-proxy). +### http-proxy options + +The following options are provided by the underlying [http-proxy](https://github.com/nodejitsu/node-http-proxy#options). * **option.target**: url string to be parsed with the url module * **option.forward**: url string to be parsed with the url module diff --git a/index.js b/index.js index 37938501..27ad8348 100644 --- a/index.js +++ b/index.js @@ -35,9 +35,7 @@ var httpProxyMiddleware = function(context, opts) { function middleware(req, res, next) { // https://github.com/chimurai/http-proxy-middleware/issues/17 - if (req.baseUrl) { - req.url = req.originalUrl; - } + req.url = req.originalUrl; if (contextMatcher.match(config.context, req.url, req)) { var activeProxyOptions = prepareProxyRequest(req); diff --git a/lib/config-factory.js b/lib/config-factory.js index 4496be5f..e86611d6 100644 --- a/lib/config-factory.js +++ b/lib/config-factory.js @@ -13,9 +13,14 @@ function createConfig(context, opts) { options: {} }; - var useShortHand = isShortHand(context); - - if (useShortHand) { + // app.use('/api', proxy({target:'http://localhost:9000'})); + if (isContexless(context, opts)) { + config.context = '/'; + config.options = _.assign(config.options, context); + } + // app.use('/api', proxy('http://localhost:9000')); + // app.use(proxy('http://localhost:9000/api')); + else if (isStringShortHand(context)) { var oUrl = url.parse(context); var target = [oUrl.protocol, '//', oUrl.host].join(''); @@ -25,7 +30,7 @@ function createConfig(context, opts) { if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') { config.options.ws = true; } - + // app.use('/api', proxy({target:'http://localhost:9000'})); } else { config.context = context; config.options = _.assign(config.options, opts); @@ -41,14 +46,40 @@ function createConfig(context, opts) { config.options = mapLegacyProxyHostOption(config.options); return config; -}; +} -function isShortHand(context) { +/** + * Checks if a String only target/config is provided. + * This can be just the host or with the optional path. + * + * @example + * app.use('/api', proxy('http://localhost:9000')); + app.use(proxy('http://localhost:9000/api')); + * + * @param {String} context [description] + * @return {Boolean} [description] + */ +function isStringShortHand(context) { if (_.isString(context)) { return (url.parse(context).host) ? true : false; } } +/** + * Checks if a Object only config is provided, without a context. + * In this case the all paths will be proxied. + * + * @example + * app.use('/api', proxy({target:'http://localhost:9000'})); + * + * @param {Object} context [description] + * @param {*} opts [description] + * @return {Boolean} [description] + */ +function isContexless(context, opts) { + return (_.isPlainObject(context) && _.isEmpty(opts)); +} + function mapLegacyProxyHostOption(options) { // set options.headers.host when option.proxyHost is provided if (options.proxyHost) { diff --git a/recipes/basic.md b/recipes/basic.md index 0f76e3f4..ac688f0a 100644 --- a/recipes/basic.md +++ b/recipes/basic.md @@ -10,3 +10,23 @@ var apiProxy = proxy('/api', {target: 'http://localhost:3000'}); // | | // context options ``` + +## Alternative configuration + +The proxy behavior of the following examples are **exactly** the same; Just different ways to configure it. + +```javascript +app.use(proxy('/api', {target: 'http://localhost:3000', changeOrigin:true})); +``` + +```javascript +app.use(proxy('http://localhost:3000/api', {changeOrigin:true})); +``` + +```javascript +app.use('/api', proxy('http://localhost:3000', {changeOrigin:true})); +``` + +```javascript +app.use('/api', proxy({target: 'http://localhost:3000', changeOrigin:true})); +``` diff --git a/test/config-factory.spec.js b/test/config-factory.spec.js index 0a64f9a2..22cf2a98 100644 --- a/test/config-factory.spec.js +++ b/test/config-factory.spec.js @@ -3,103 +3,120 @@ var configFactory = require('../lib/config-factory'); describe('configFactory', function() { var result; + var createConfig = configFactory.createConfig; describe('createConfig()', function() { - describe('classic api', function() { + describe('classic config', function() { var context = '/api'; var options = {target: 'http://www.example.org'}; beforeEach(function() { - result = configFactory.createConfig(context, options); + result = createConfig(context, options); }); - it('should return on config object', function() { + it('should return config object', function() { expect(result).to.have.all.keys('context', 'options'); }); - it('should return on config object with context', function() { + it('should return config object with context', function() { expect(result.context).to.equal(context); }); - it('should return on config object with options', function() { + it('should return 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'); - }); + describe('shorthand String', function() { + describe('shorthand String config', function() { + beforeEach(function() { + result = 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 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 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'}); + it('should return 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'); - }); + describe('shorthand String config for whole domain', function() { + beforeEach(function() { + result = createConfig('http://www.example.org:8000'); + }); - it('should return on config object with context', function() { - expect(result.context).to.equal('/'); + it('should return config object with context', function() { + expect(result.context).to.equal('/'); + }); }); - }); - describe('shorthand api for websocket url', function() { - beforeEach(function() { - result = configFactory.createConfig('ws://www.example.org:8000'); - }); + describe('shorthand String config for websocket url', function() { + beforeEach(function() { + result = createConfig('ws://www.example.org:8000'); + }); - it('should return on config object with context', function() { - expect(result.context).to.equal('/'); - }); + it('should return config object with context', function() { + expect(result.context).to.equal('/'); + }); - it('should return on options with ws = true', function() { - expect(result.options.ws).to.equal(true); + it('should return options with ws = true', function() { + expect(result.options.ws).to.equal(true); + }); }); - }); - describe('shorthand api for secure websocket url', function() { - beforeEach(function() { - result = configFactory.createConfig('wss://www.example.org:8000'); - }); + describe('shorthand String config for secure websocket url', function() { + beforeEach(function() { + result = createConfig('wss://www.example.org:8000'); + }); - it('should return on config object with context', function() { - expect(result.context).to.equal('/'); - }); + it('should return config object with context', function() { + expect(result.context).to.equal('/'); + }); - it('should return on options with ws = true', function() { - expect(result.options.ws).to.equal(true); + it('should return options with ws = true', function() { + expect(result.options.ws).to.equal(true); + }); }); - }); - describe('shorthand api with globbing', function() { - beforeEach(function() { - result = configFactory.createConfig('http://www.example.org:8000/api/*.json'); + describe('shorthand String config with globbing', function() { + beforeEach(function() { + result = createConfig('http://www.example.org:8000/api/*.json'); + }); + + it('should return config object with context', function() { + expect(result.context).to.equal('/api/*.json'); + }); }); - it('should return on config object with context', function() { - expect(result.context).to.equal('/api/*.json'); + describe('shorthand String config with options', function() { + beforeEach(function() { + result = createConfig('http://www.example.org:8000/api', {changeOrigin: true}); + }); + + it('should return config object with additional options', function() { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000', changeOrigin: true}); + }); }); }); - describe('shorthand api with options', function() { + describe('shorthand Object config', function() { beforeEach(function() { - result = configFactory.createConfig('http://www.example.org:8000/api', {changeOrigin: true}); + result = createConfig({target: 'http://www.example.org:8000'}); }); - it('should return on config object with additional options', function() { - expect(result.options).to.deep.equal({target: 'http://www.example.org:8000', changeOrigin: true}); + it('should set the proxy path to everything', function() { + expect(result.context).to.equal('/'); + }); + + it('should return config object', function() { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}); }); }); @@ -107,7 +124,7 @@ describe('configFactory', function() { var fn; beforeEach(function() { fn = function() { - configFactory.createConfig('/api'); + createConfig('/api'); }; }); @@ -119,7 +136,7 @@ describe('configFactory', function() { describe('faulty config. mixing classic with shorthand', function() { var fn; beforeEach(function() { - result = configFactory.createConfig('http://localhost:3000/api', {target: 'http://localhost:8000'}); + result = createConfig('http://localhost:3000/api', {target: 'http://localhost:8000'}); }); it('should use the target in the configuration as target', function() { diff --git a/test/http-proxy-middleware.spec.js b/test/http-proxy-middleware.spec.js index 26c8ea72..759d7544 100644 --- a/test/http-proxy-middleware.spec.js +++ b/test/http-proxy-middleware.spec.js @@ -20,7 +20,7 @@ describe('context matching', function() { var middleware; - var mockReq = {url: '/foo/bar'}; + var mockReq = {url: '/foo/bar', originalUrl: '/foo/bar'}; var mockRes = {}; var mockNext = function() { // mockNext will be called when request is not proxied