Skip to content

Commit 351006c

Browse files
committed
feat(pathRewrite): support for custom pathRewrite function. (chimurai#74)
1 parent 0cb4e37 commit 351006c

File tree

6 files changed

+111
-21
lines changed

6 files changed

+111
-21
lines changed

CHANGELOG.md

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

3+
## [v0.x.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.x.0)
4+
- feat(pathRewrite): support for custom pathRewrite function.
5+
36
## [v0.14.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.14.0)
47
- feat(proxy): support proxy creation without context.
58
- fix(connect mounting): use connect's `path` configuration to mount proxy.

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ var server = app.listen(3000);
192192

193193
## Options
194194

195-
* **option.pathRewrite**: object, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths.
195+
* **option.pathRewrite**: object/function, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths.
196196
```javascript
197197
// rewrite path
198198
pathRewrite: {"^/old/api" : "/new/api"}
@@ -202,6 +202,9 @@ var server = app.listen(3000);
202202

203203
// add base path
204204
pathRewrite: {"^/" : "/basepath/"}
205+
206+
// custom rewriting
207+
pathRewrite: function (path) { return path.replace('/api', '/base/api') }
205208
```
206209

207210
* **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.

index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ var httpProxyMiddleware = function(context, opts) {
7777
// store uri before it gets rewritten for logging
7878
var originalPath = req.url;
7979

80-
// apply apply option.proxyTable & option.pathRewrite
80+
// Apply in order:
81+
// 1. option.proxyTable
82+
// 2. option.pathRewrite
8183
var alteredProxyOptions = __applyProxyTableOption(req, proxyOptions);
8284
__applyPathRewrite(req, pathRewriter);
8385

@@ -105,7 +107,13 @@ var httpProxyMiddleware = function(context, opts) {
105107
// rewrite path
106108
function __applyPathRewrite(req) {
107109
if (pathRewriter) {
108-
req.url = pathRewriter(req.url);
110+
var path = pathRewriter(req.url);
111+
112+
if (path) {
113+
req.url = path;
114+
} else {
115+
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
116+
}
109117
}
110118
}
111119

lib/path-rewriter.js

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,28 @@ module.exports = {
77

88
/**
99
* Create rewrite function, to cache parsed rewrite rules.
10+
*
11+
* @returns {function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
1012
*/
11-
function createPathRewriter(config) {
13+
function createPathRewriter(rewriteConfig) {
14+
var rulesCache;
1215

13-
if (config === undefined || config === null) {
16+
if (!isValidRewriteConfig(rewriteConfig)) {
1417
return;
1518
}
1619

17-
var rules = parsePathRewriteRules(config);
18-
19-
return rewritePath;
20+
if (_.isFunction(rewriteConfig)) {
21+
var customRewriteFn = rewriteConfig;
22+
return customRewriteFn;
23+
} else {
24+
rulesCache = parsePathRewriteRules(rewriteConfig);
25+
return rewritePath;
26+
}
2027

2128
function rewritePath(path) {
2229
var result = path;
2330

24-
_.forEach(rules,function(rule) {
31+
_.forEach(rulesCache, function(rule) {
2532
if (rule.regex.test(path)) {
2633
result = result.replace(rule.regex, rule.value);
2734
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result);
@@ -33,21 +40,31 @@ function createPathRewriter(config) {
3340
}
3441
}
3542

36-
function parsePathRewriteRules(config) {
37-
var rules = [];
38-
39-
if (_.isPlainObject(config) === false) {
40-
throw new Error('[HPM] Invalid pathRewrite config. Expecting an object literal with pathRewrite configuration');
43+
function isValidRewriteConfig(rewriteConfig) {
44+
if (_.isFunction(rewriteConfig)) {
45+
return true;
46+
} else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) {
47+
return true;
48+
} else if (_.isUndefined(rewriteConfig) ||
49+
_.isNull(rewriteConfig) ||
50+
_.isEqual(rewriteConfig, {})) {
51+
return false;
4152
} else {
53+
throw new Error('[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function');
54+
}
55+
}
4256

43-
_.forIn(config, function(value, key) {
57+
function parsePathRewriteRules(rewriteConfig) {
58+
var rules = [];
59+
60+
if (_.isPlainObject(rewriteConfig)) {
61+
_.forIn(rewriteConfig, function(value, key) {
4462
rules.push({
4563
regex: new RegExp(key),
46-
value: config[key]
64+
value: rewriteConfig[key]
4765
});
48-
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, config[key]);
66+
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]);
4967
});
50-
5168
}
5269

5370
return rules;

recipes/pathRewrite.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Modify request paths before requests are send to the target.
77
- [rewrite paths](#rewrite-paths)
88
- [remove paths](#remove-paths)
99
- [add paths](#add-paths)
10+
- [custom rewrite function](#custom-rewrite-function)
1011

1112
<!-- /MarkdownTOC -->
1213

@@ -67,3 +68,25 @@ var apiProxy = proxy('/api', options);
6768

6869
// `/api/lorum/ipsum` -> `http://localhost:3000/extra/api/lorum/ipsum`
6970
```
71+
72+
## custom rewrite function
73+
74+
Implement you own path rewrite logic.
75+
When rewrite function is not return a path (undefined), the original path will be used.
76+
77+
```javascript
78+
var proxy = require("http-proxy-middleware");
79+
80+
var rewriteFn = function (path) {
81+
return path.replace('/api/foo', '/api/bar');
82+
}
83+
84+
var options = {
85+
target: 'http://localhost:3000',
86+
pathRewrite: rewriteFn
87+
};
88+
89+
var apiProxy = proxy('/api', options);
90+
91+
// `/api/foo/lorum/ipsum` -> `http://localhost:3000/api/bar/lorum/ipsum`
92+
```

test/path-rewriter.spec.js

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ describe('Path rewriting', function() {
66
var result;
77
var config;
88

9-
describe('Configuration and usage', function() {
9+
describe('Rewrite rules configuration and usage', function() {
1010

1111
beforeEach(function() {
1212
config = {
@@ -55,7 +55,7 @@ describe('Path rewriting', function() {
5555
});
5656
});
5757

58-
describe('add base path to requests', function() {
58+
describe('Rewrite rule: add base path to requests', function() {
5959

6060
beforeEach(function() {
6161
config = {
@@ -73,6 +73,39 @@ describe('Path rewriting', function() {
7373
});
7474
});
7575

76+
describe('Rewrite function', function() {
77+
var rewriter;
78+
79+
beforeEach(function() {
80+
rewriter = function(fn) {
81+
var rewriteFn = pathRewriter.create(fn);
82+
var requestPath = '/123/456';
83+
return rewriteFn(requestPath);
84+
};
85+
});
86+
87+
it('should return unmodified path', function() {
88+
var rewriteFn = function(path) {
89+
return path;
90+
};
91+
expect(rewriter(rewriteFn)).to.equal('/123/456');
92+
});
93+
94+
it('should return alternative path', function() {
95+
var rewriteFn = function(path) {
96+
return '/foo/bar';
97+
};
98+
expect(rewriter(rewriteFn)).to.equal('/foo/bar');
99+
});
100+
101+
it('should return replaced path', function() {
102+
var rewriteFn = function(path) {
103+
return path.replace('/456', '/789');
104+
};
105+
expect(rewriter(rewriteFn)).to.equal('/123/789');
106+
});
107+
});
108+
76109
describe('Invalid configuration', function() {
77110
var badFn;
78111

@@ -93,7 +126,6 @@ describe('Path rewriting', function() {
93126
it('should throw when bad config is provided', function() {
94127
expect(badFn(123)).to.throw(Error);
95128
expect(badFn('abc')).to.throw(Error);
96-
expect(badFn(function() {})).to.throw(Error);
97129
expect(badFn([])).to.throw(Error);
98130
expect(badFn([1,2,3])).to.throw(Error);
99131
});
@@ -102,6 +134,10 @@ describe('Path rewriting', function() {
102134
expect(badFn({})).to.not.throw(Error);
103135
});
104136

137+
it('should not throw when function config is provided', function() {
138+
expect(badFn(function() {})).to.not.throw(Error);
139+
});
140+
105141
});
106142
});
107143

0 commit comments

Comments
 (0)