Skip to content

Commit 3afd492

Browse files
committed
feat(router): support for custom router function
1 parent 009f90d commit 3afd492

4 files changed

Lines changed: 92 additions & 76 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Changelog
22

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

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

910
## [v0.15.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.1)
1011
- feat(pathRewrite): expose `req` object to pathRewrite function.

lib/index.js

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,36 +71,39 @@ function HttpProxyMiddleware(context, opts) {
7171
function prepareProxyRequest(req) {
7272
// store uri before it gets rewritten for logging
7373
var originalPath = req.url;
74+
var newProxyOptions = _.cloneDeep(proxyOptions);
7475

7576
// Apply in order:
7677
// 1. option.router
7778
// 2. option.pathRewrite
78-
var alteredProxyOptions = __applyRouterOption(req, proxyOptions);
79-
__applyPathRewrite(pathRewriter, req);
79+
__applyRouter(req, newProxyOptions);
80+
__applyPathRewrite(req, pathRewriter);
8081

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

87-
return alteredProxyOptions;
88+
return newProxyOptions;
8889
}
8990

9091
// Modify option.target when router present.
91-
// return altered options
92-
function __applyRouterOption(req) {
93-
var result = proxyOptions;
92+
function __applyRouter(req, options) {
93+
var newTarget;
9494

95-
if (proxyOptions.router) {
96-
result = Router.createProxyOptions(req, proxyOptions);
97-
}
95+
if (options.router) {
96+
newTarget = Router.getTarget(req, options);
9897

99-
return result;
98+
if (newTarget) {
99+
logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
100+
options.target = newTarget;
101+
}
102+
}
100103
}
101104

102105
// rewrite path
103-
function __applyPathRewrite(pathRewriter, req) {
106+
function __applyPathRewrite(req, pathRewriter) {
104107
if (pathRewriter) {
105108
var path = pathRewriter(req.url, req);
106109

lib/router.js

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,42 @@ var _ = require('lodash');
22
var logger = require('./logger.js').getInstance();
33

44
module.exports = {
5-
createProxyOptions: createProxyOptions
5+
getTarget: getTarget
66
};
77

8-
function createProxyOptions(req, config) {
8+
function getTarget(req, config) {
9+
var newTarget;
910
var router = config.router;
10-
var result = _.clone(config);
1111

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

20-
return result;
18+
return newTarget;
2119
}
2220

23-
function getTargetFromProxyTable(req, router) {
21+
function getTargetFromProxyTable(req, table) {
2422
var result;
2523
var host = req.headers.host;
2624
var path = req.url;
2725

2826
var hostAndPath = host + path;
2927

30-
_.forIn(router, function(value, key) {
28+
_.forIn(table, function(value, key) {
3129
if (containsPath(key)) {
3230

3331
if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api'
34-
result = router[key];
35-
logger.debug('[HPM] router match: %s -> "%s"', hostAndPath, result);
32+
result = table[key];
33+
logger.debug('[HPM] Router table match: "%s"', key);
3634
return false;
3735
}
3836
} else {
3937

4038
if (key === host) { // match 'localhost:3000'
41-
result = router[key];
42-
logger.debug('[HPM] router match: %s -> "%s"', host, result);
39+
result = table[key];
40+
logger.debug('[HPM] Router table match: "%s"', host);
4341
return false;
4442
}
4543

test/unit/router.spec.js

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,52 @@ var expect = require('chai').expect;
22
var router = require('./_libs').router;
33

44
describe('router unit test', function() {
5+
var req, config, result;
56

6-
describe('router.createProxyOptions', function() {
7-
var req, config, result;
7+
beforeEach(function() {
8+
req = {
9+
headers: {
10+
host: 'localhost'
11+
},
12+
url: '/'
13+
};
814

9-
beforeEach(function() {
10-
req = {
11-
headers: {
12-
host: 'localhost'
13-
},
14-
url: '/'
15-
};
15+
config = {
16+
target: 'http://localhost:6000'
17+
};
18+
19+
});
1620

17-
config = {
21+
describe('router.getTarget from function', function() {
22+
var request;
23+
24+
beforeEach(function() {
25+
proxyOptionWithRouter = {
1826
target: 'http://localhost:6000',
19-
changeOrigin: true // other options should be returned, such as changeOrigin
27+
router: function(req) {
28+
request = req;
29+
return 'http://foobar.com:666';
30+
}
2031
};
2132

22-
configProxyTable = {
33+
result = router.getTarget(req, proxyOptionWithRouter);
34+
});
35+
36+
describe('custom dynamic router function', function() {
37+
it('should provide the request object for dynamic routing', function() {
38+
expect(request.headers.host).to.equal('localhost');
39+
expect(request.url).to.equal('/');
40+
});
41+
it('should return new target', function() {
42+
expect(result).to.equal('http://foobar.com:666');
43+
});
44+
});
45+
});
46+
47+
describe('router.getTarget from table', function() {
48+
beforeEach(function() {
49+
proxyOptionWithRouter = {
2350
target: 'http://localhost:6000',
24-
changeOrigin: true, // other options should be returned, such as changeOrigin
2551
router: {
2652
'alpha.localhost': 'http://localhost:6001',
2753
'beta.localhost': 'http://localhost:6002',
@@ -36,84 +62,72 @@ describe('router unit test', function() {
3662

3763
describe('without router config', function() {
3864
it('should return the normal target when router not present in config', function() {
39-
result = router.createProxyOptions(req, config);
40-
expect(result.target).to.equal('http://localhost:6000');
41-
expect(result).not.to.equal(config); // should return cloned object
42-
expect(result).to.deep.equal(config); // clone content should match
43-
expect(result.changeOrigin).to.be.true;
65+
result = router.getTarget(req, config);
66+
expect(result).to.equal(undefined);
4467
});
4568
});
4669

4770
describe('with just the host in router config', function() {
4871
it('should target http://localhost:6001 when for router:"alpha.localhost"', function() {
4972
req.headers.host = 'alpha.localhost';
50-
result = router.createProxyOptions(req, configProxyTable);
51-
expect(result.target).to.equal('http://localhost:6001');
52-
expect(result.changeOrigin).to.be.true;
73+
result = router.getTarget(req, proxyOptionWithRouter);
74+
expect(result).to.equal('http://localhost:6001');
5375
});
5476

5577
it('should target http://localhost:6002 when for router:"beta.localhost"', function() {
5678
req.headers.host = 'beta.localhost';
57-
result = router.createProxyOptions(req, configProxyTable);
58-
expect(result.target).to.equal('http://localhost:6002');
59-
expect(result.changeOrigin).to.be.true;
79+
result = router.getTarget(req, proxyOptionWithRouter);
80+
expect(result).to.equal('http://localhost:6002');
6081
});
6182
});
6283

6384
describe('with host and host + path config', function() {
6485
it('should target http://localhost:6004 without path', function() {
6586
req.headers.host = 'gamma.localhost';
66-
result = router.createProxyOptions(req, configProxyTable);
67-
expect(result.target).to.equal('http://localhost:6004');
68-
expect(result.changeOrigin).to.be.true;
87+
result = router.getTarget(req, proxyOptionWithRouter);
88+
expect(result).to.equal('http://localhost:6004');
6989
});
7090

7191
it('should target http://localhost:6003 exact path match', function() {
7292
req.headers.host = 'gamma.localhost';
7393
req.url = '/api';
74-
result = router.createProxyOptions(req, configProxyTable);
75-
expect(result.target).to.equal('http://localhost:6003');
76-
expect(result.changeOrigin).to.be.true;
94+
result = router.getTarget(req, proxyOptionWithRouter);
95+
expect(result).to.equal('http://localhost:6003');
7796
});
7897

7998
it('should target http://localhost:6004 when contains path', function() {
8099
req.headers.host = 'gamma.localhost';
81100
req.url = '/api/books/123';
82-
result = router.createProxyOptions(req, configProxyTable);
83-
expect(result.target).to.equal('http://localhost:6003');
84-
expect(result.changeOrigin).to.be.true;
101+
result = router.getTarget(req, proxyOptionWithRouter);
102+
expect(result).to.equal('http://localhost:6003');
85103
});
86104
});
87105

88106
describe('with just the path', function() {
89107
it('should target http://localhost:6005 with just a path as router config', function() {
90108
req.url = '/rest';
91-
result = router.createProxyOptions(req, configProxyTable);
92-
expect(result.target).to.equal('http://localhost:6005');
93-
expect(result.changeOrigin).to.be.true;
109+
result = router.getTarget(req, proxyOptionWithRouter);
110+
expect(result).to.equal('http://localhost:6005');
94111
});
95112

96113
it('should target http://localhost:6005 with just a path as router config', function() {
97114
req.url = '/rest/deep/path';
98-
result = router.createProxyOptions(req, configProxyTable);
99-
expect(result.target).to.equal('http://localhost:6005');
100-
expect(result.changeOrigin).to.be.true;
115+
result = router.getTarget(req, proxyOptionWithRouter);
116+
expect(result).to.equal('http://localhost:6005');
101117
});
102118

103119
it('should target http://localhost:6000 path in not present in router config', function() {
104120
req.url = '/unknow-path';
105-
result = router.createProxyOptions(req, configProxyTable);
106-
expect(result.target).to.equal('http://localhost:6000');
107-
expect(result.changeOrigin).to.be.true;
121+
result = router.getTarget(req, proxyOptionWithRouter);
122+
expect(result).to.equal(undefined);
108123
});
109124
});
110125

111126
describe('matching order of router config', function() {
112127
it('should return first matching target when similar paths are configured', function() {
113128
req.url = '/some/specific/path';
114-
result = router.createProxyOptions(req, configProxyTable);
115-
expect(result.target).to.equal('http://localhost:6006');
116-
expect(result.changeOrigin).to.be.true;
129+
result = router.getTarget(req, proxyOptionWithRouter);
130+
expect(result).to.equal('http://localhost:6006');
117131
});
118132
});
119133

0 commit comments

Comments
 (0)