Skip to content

Commit

Permalink
feat(router): support for custom router function
Browse files Browse the repository at this point in the history
  • Loading branch information
chimurai committed Jun 12, 2016
1 parent 009f90d commit 3afd492
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 76 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Changelog

## [develop](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.16.0)
- feat(router): deprecate and renamed `proxyTable` option to `router`
- feat(router): support for custom `router` function.
- feat(router): deprecate and renamed `proxyTable` option to `router`.

## [v0.15.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.2)
- fix(websocket): fixes websocket upgrade
- fix(websocket): fixes websocket upgrade.

## [v0.15.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.1)
- feat(pathRewrite): expose `req` object to pathRewrite function.
Expand Down
29 changes: 16 additions & 13 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,36 +71,39 @@ function HttpProxyMiddleware(context, opts) {
function prepareProxyRequest(req) {
// store uri before it gets rewritten for logging
var originalPath = req.url;
var newProxyOptions = _.cloneDeep(proxyOptions);

// Apply in order:
// 1. option.router
// 2. option.pathRewrite
var alteredProxyOptions = __applyRouterOption(req, proxyOptions);
__applyPathRewrite(pathRewriter, req);
__applyRouter(req, newProxyOptions);
__applyPathRewrite(req, pathRewriter);

// debug logging for both http(s) and websockets
if (proxyOptions.logLevel === 'debug') {
var arrow = getArrow(originalPath, req.url, proxyOptions.target, alteredProxyOptions.target);
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, alteredProxyOptions.target);
var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target);
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
}

return alteredProxyOptions;
return newProxyOptions;
}

// Modify option.target when router present.
// return altered options
function __applyRouterOption(req) {
var result = proxyOptions;
function __applyRouter(req, options) {
var newTarget;

if (proxyOptions.router) {
result = Router.createProxyOptions(req, proxyOptions);
}
if (options.router) {
newTarget = Router.getTarget(req, options);

return result;
if (newTarget) {
logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
options.target = newTarget;
}
}
}

// rewrite path
function __applyPathRewrite(pathRewriter, req) {
function __applyPathRewrite(req, pathRewriter) {
if (pathRewriter) {
var path = pathRewriter(req.url, req);

Expand Down
30 changes: 14 additions & 16 deletions lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,42 @@ var _ = require('lodash');
var logger = require('./logger.js').getInstance();

module.exports = {
createProxyOptions: createProxyOptions
getTarget: getTarget
};

function createProxyOptions(req, config) {
function getTarget(req, config) {
var newTarget;
var router = config.router;
var result = _.clone(config);

if (router) {
var newTarget = getTargetFromProxyTable(req, router);
if (newTarget) {
logger.debug('[HPM] router new target: %s -> "%s"', config.target, newTarget);
result = _.assign(result, {target: newTarget}); // override option.target
}
if (_.isPlainObject(router)) {
newTarget = getTargetFromProxyTable(req, router);
} else if (_.isFunction(router)) {
newTarget = router(req);
}

return result;
return newTarget;
}

function getTargetFromProxyTable(req, router) {
function getTargetFromProxyTable(req, table) {
var result;
var host = req.headers.host;
var path = req.url;

var hostAndPath = host + path;

_.forIn(router, function(value, key) {
_.forIn(table, function(value, key) {
if (containsPath(key)) {

if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api'
result = router[key];
logger.debug('[HPM] router match: %s -> "%s"', hostAndPath, result);
result = table[key];
logger.debug('[HPM] Router table match: "%s"', key);
return false;
}
} else {

if (key === host) { // match 'localhost:3000'
result = router[key];
logger.debug('[HPM] router match: %s -> "%s"', host, result);
result = table[key];
logger.debug('[HPM] Router table match: "%s"', host);
return false;
}

Expand Down
104 changes: 59 additions & 45 deletions test/unit/router.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,52 @@ var expect = require('chai').expect;
var router = require('./_libs').router;

describe('router unit test', function() {
var req, config, result;

describe('router.createProxyOptions', function() {
var req, config, result;
beforeEach(function() {
req = {
headers: {
host: 'localhost'
},
url: '/'
};

beforeEach(function() {
req = {
headers: {
host: 'localhost'
},
url: '/'
};
config = {
target: 'http://localhost:6000'
};

});

config = {
describe('router.getTarget from function', function() {
var request;

beforeEach(function() {
proxyOptionWithRouter = {
target: 'http://localhost:6000',
changeOrigin: true // other options should be returned, such as changeOrigin
router: function(req) {
request = req;
return 'http://foobar.com:666';
}
};

configProxyTable = {
result = router.getTarget(req, proxyOptionWithRouter);
});

describe('custom dynamic router function', function() {
it('should provide the request object for dynamic routing', function() {
expect(request.headers.host).to.equal('localhost');
expect(request.url).to.equal('/');
});
it('should return new target', function() {
expect(result).to.equal('http://foobar.com:666');
});
});
});

describe('router.getTarget from table', function() {
beforeEach(function() {
proxyOptionWithRouter = {
target: 'http://localhost:6000',
changeOrigin: true, // other options should be returned, such as changeOrigin
router: {
'alpha.localhost': 'http://localhost:6001',
'beta.localhost': 'http://localhost:6002',
Expand All @@ -36,84 +62,72 @@ describe('router unit test', function() {

describe('without router config', function() {
it('should return the normal target when router not present in config', function() {
result = router.createProxyOptions(req, config);
expect(result.target).to.equal('http://localhost:6000');
expect(result).not.to.equal(config); // should return cloned object
expect(result).to.deep.equal(config); // clone content should match
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, config);
expect(result).to.equal(undefined);
});
});

describe('with just the host in router config', function() {
it('should target http://localhost:6001 when for router:"alpha.localhost"', function() {
req.headers.host = 'alpha.localhost';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6001');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6001');
});

it('should target http://localhost:6002 when for router:"beta.localhost"', function() {
req.headers.host = 'beta.localhost';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6002');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6002');
});
});

describe('with host and host + path config', function() {
it('should target http://localhost:6004 without path', function() {
req.headers.host = 'gamma.localhost';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6004');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6004');
});

it('should target http://localhost:6003 exact path match', function() {
req.headers.host = 'gamma.localhost';
req.url = '/api';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6003');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6003');
});

it('should target http://localhost:6004 when contains path', function() {
req.headers.host = 'gamma.localhost';
req.url = '/api/books/123';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6003');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6003');
});
});

describe('with just the path', function() {
it('should target http://localhost:6005 with just a path as router config', function() {
req.url = '/rest';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6005');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6005');
});

it('should target http://localhost:6005 with just a path as router config', function() {
req.url = '/rest/deep/path';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6005');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6005');
});

it('should target http://localhost:6000 path in not present in router config', function() {
req.url = '/unknow-path';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6000');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal(undefined);
});
});

describe('matching order of router config', function() {
it('should return first matching target when similar paths are configured', function() {
req.url = '/some/specific/path';
result = router.createProxyOptions(req, configProxyTable);
expect(result.target).to.equal('http://localhost:6006');
expect(result.changeOrigin).to.be.true;
result = router.getTarget(req, proxyOptionWithRouter);
expect(result).to.equal('http://localhost:6006');
});
});

Expand Down

0 comments on commit 3afd492

Please sign in to comment.