Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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`.

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
23 changes: 6 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
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');
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

Expand Down Expand Up @@ -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));
Expand All @@ -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);
}
Expand Down
58 changes: 58 additions & 0 deletions lib/config-factory.js
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion lib/handlers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
proxyError : proxyError
proxyError : proxyError
}

function proxyError (err, req, res) {
Expand Down
95 changes: 95 additions & 0 deletions test/config-factory.spec.js
Original file line number Diff line number Diff line change
@@ -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);
});
});


});

});

32 changes: 32 additions & 0 deletions test/http-proxy-middleware.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});


});

Expand Down