Skip to content

Commit 92bb9b7

Browse files
authored
feat: router (chimurai#85)
* feat(router): deprecate and rename `proxyTable` option to `router` * feat(router): support for custom `router` function * chore(router): add e2e router test * docs: router
1 parent 2ae1e03 commit 92bb9b7

File tree

13 files changed

+345
-229
lines changed

13 files changed

+345
-229
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
# Changelog
22

3+
## [develop](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.16.0)
4+
- feat(router): support for custom `router` function.
5+
- feat(router): deprecate and renamed `proxyTable` option to `router`.
6+
37
## [v0.15.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.15.2)
4-
- fix(websocket): fixes websocket upgrade
8+
- fix(websocket): fixes websocket upgrade.
59

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

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,21 @@ Providing an alternative way to decide which requests should be proxied; In case
179179
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }
180180
```
181181

182-
* **option.proxyTable**: object, re-target `option.target` based on the request header `host` parameter. `host` can be used in conjunction with `path`. Only one instance of the proxy will be used. The order of the configuration matters.
182+
* **option.router**: object/function, re-target `option.target` for specific requests.
183183
```javascript
184-
proxyTable: {
184+
// Use `host` and/or `path` to match requests. First match will be used.
185+
// The order of the configuration matters.
186+
router: {
185187
'integration.localhost:3000' : 'http://localhost:8001', // host only
186188
'staging.localhost:3000' : 'http://localhost:8002', // host only
187189
'localhost:3000/api' : 'http://localhost:8003', // host + path
188190
'/rest' : 'http://localhost:8004' // path only
189191
}
192+
193+
// Custom router function
194+
router: function(req) {
195+
return 'http://localhost:8004';
196+
}
190197
```
191198

192199
* **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'`
@@ -216,6 +223,7 @@ Providing an alternative way to decide which requests should be proxied; In case
216223
}
217224
```
218225
* (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead.
226+
* (DEPRECATED) **option.proxyTable**: Use `option.router` instead.
219227

220228

221229
### http-proxy events

lib/config-factory.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ function createConfig(context, opts) {
4545
// Legacy option.proxyHost
4646
config.options = mapLegacyProxyHostOption(config.options);
4747

48+
// Legacy option.proxyTable > option.router
49+
config.options = mapLegacyProxyTableOption(config.options);
50+
4851
return config;
4952
}
5053

@@ -96,6 +99,22 @@ function mapLegacyProxyHostOption(options) {
9699
return options;
97100
}
98101

102+
// Warn deprecated proxyTable api usage
103+
function mapLegacyProxyTableOption(options) {
104+
if (options.proxyTable) {
105+
logger.warn('*************************************');
106+
logger.warn('[HPM] Deprecated "option.proxyTable"');
107+
logger.warn(' Use "option.router" instead');
108+
logger.warn(' "option.proxyTable" will be removed in future release.');
109+
logger.warn('*************************************');
110+
111+
options.router = _.clone(options.proxyTable);
112+
_.omit(options, 'proxyTable');
113+
}
114+
115+
return options;
116+
}
117+
99118
function configureLogger(options) {
100119
if (options.logLevel) {
101120
logger.setLevel(options.logLevel);

lib/index.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var configFactory = require('./config-factory');
44
var handlers = require('./handlers');
55
var contextMatcher = require('./context-matcher');
66
var PathRewriter = require('./path-rewriter');
7-
var ProxyTable = require('./proxy-table');
7+
var Router = require('./router');
88
var logger = require('./logger').getInstance();
99
var getArrow = require('./logger').getArrow;
1010

@@ -17,7 +17,7 @@ function HttpProxyMiddleware(context, opts) {
1717
var proxyOptions = config.options;
1818

1919
// create proxy
20-
var proxy = httpProxy.createProxyServer(proxyOptions);
20+
var proxy = httpProxy.createProxyServer({});
2121
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target);
2222

2323
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
@@ -63,44 +63,47 @@ function HttpProxyMiddleware(context, opts) {
6363
}
6464

6565
/**
66-
* Apply option.proxyTable and option.pathRewrite
66+
* Apply option.router and option.pathRewrite
6767
* Order matters:
68-
ProxyTable uses original path for routing;
68+
Router uses original path for routing;
6969
NOT the modified path, after it has been rewritten by pathRewrite
7070
*/
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:
76-
// 1. option.proxyTable
77+
// 1. option.router
7778
// 2. option.pathRewrite
78-
var alteredProxyOptions = __applyProxyTableOption(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

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

95-
if (proxyOptions.proxyTable) {
96-
result = ProxyTable.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/logger.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function Logger() {
149149
*/
150150
function getArrow(originalPath, newPath, originalTarget, newTarget) {
151151
var arrow = ['>'];
152-
var isNewTarget = (originalTarget !== newTarget); // proxyTable
152+
var isNewTarget = (originalTarget !== newTarget); // router
153153
var isNewPath = (originalPath !== newPath); // pathRewrite
154154

155155
if (isNewPath && !isNewTarget) {arrow.unshift('~');} else if (!isNewPath && isNewTarget) {arrow.unshift('=');} else if (isNewPath && isNewTarget) {arrow.unshift('≈');} else {arrow.unshift('-');}

lib/proxy-table.js

Lines changed: 0 additions & 55 deletions
This file was deleted.

lib/router.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var _ = require('lodash');
2+
var logger = require('./logger.js').getInstance();
3+
4+
module.exports = {
5+
getTarget: getTarget
6+
};
7+
8+
function getTarget(req, config) {
9+
var newTarget;
10+
var router = config.router;
11+
12+
if (_.isPlainObject(router)) {
13+
newTarget = getTargetFromProxyTable(req, router);
14+
} else if (_.isFunction(router)) {
15+
newTarget = router(req);
16+
}
17+
18+
return newTarget;
19+
}
20+
21+
function getTargetFromProxyTable(req, table) {
22+
var result;
23+
var host = req.headers.host;
24+
var path = req.url;
25+
26+
var hostAndPath = host + path;
27+
28+
_.forIn(table, function(value, key) {
29+
if (containsPath(key)) {
30+
31+
if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api'
32+
result = table[key];
33+
logger.debug('[HPM] Router table match: "%s"', key);
34+
return false;
35+
}
36+
} else {
37+
38+
if (key === host) { // match 'localhost:3000'
39+
result = table[key];
40+
logger.debug('[HPM] Router table match: "%s"', host);
41+
return false;
42+
}
43+
44+
}
45+
46+
});
47+
48+
return result;
49+
}
50+
51+
function containsPath(v) {
52+
return v.indexOf('/') > -1;
53+
}

recipes/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var options = {
4343
},
4444

4545
// re-target based on the request's host header and/or path
46-
proxyTable: {
46+
router: {
4747
// host[/path] : <new target>
4848
// /path : <new target>
4949
'integration.localhost:8000' : 'http://localhost:8001', // host only

recipes/proxyTable.md renamed to recipes/router.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,45 @@
1-
# proxyTable
1+
# router
22

3-
Use the Proxy Table to proxy requests to a different target based on:
3+
Allows you to route to a different `target` by using a table of a custom function.
4+
5+
<!-- MarkdownTOC autolink=true bracket=round -->
6+
7+
- [Custom router function](#custom-router-function)
8+
- [Proxy Table](#proxy-table)
9+
10+
<!-- /MarkdownTOC -->
11+
12+
13+
## Custom router function
14+
15+
Write your own router to dynamically route to a different `target`.
16+
The `req` object is provided to retrieve contextual data.
17+
18+
```javascript
19+
var express = require('express');
20+
var proxy = require("http-proxy-middleware");
21+
22+
var customRouter = function(req) {
23+
return 'http://www.example.org' // protocol + host
24+
};
25+
26+
var options = {
27+
target: 'http://localhost:8000',
28+
router: customRouter
29+
};
30+
31+
var myProxy = proxy(options);
32+
33+
var app = express();
34+
app.use(myProxy); // add the proxy to express
35+
36+
app.listen(3000);
37+
```
38+
39+
40+
## Proxy Table
41+
42+
Use a Proxy Table to proxy requests to a different `target` based on:
443
* Host [HTTP header](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields).
544
* Request path
645
* Host HTTP header + path
@@ -18,18 +57,18 @@ var proxyTable = {
1857

1958
var options = {
2059
target: 'http://localhost:8000',
21-
proxyTable: proxyTable
60+
router: proxyTable
2261
};
2362

24-
var myProxy = proxy('/', options);
63+
var myProxy = proxy(options);
2564

2665
var app = express();
2766
app.use(myProxy); // add the proxy to express
2867

2968
app.listen(3000);
3069
```
3170

32-
## Example
71+
### Example
3372

3473
In the example above; all requests will be proxied to `http://localhost:8000`.
3574

0 commit comments

Comments
 (0)