Skip to content

Commit 8fe6008

Browse files
authored
fix(context matching): Use RFC 3986 path in context matching. (excludes query parameters) (#87)
1 parent 09926cd commit 8fe6008

File tree

6 files changed

+60
-31
lines changed

6 files changed

+60
-31
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+
## [develop](https://github.com/chimurai/http-proxy-middleware/releases/tag/develop)
4+
- fix(context matching): Use [RFC 3986 path](https://tools.ietf.org/html/rfc3986#section-3.3) in context matching. (excludes query parameters)
5+
36
## [v0.16.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.16.0)
47
- deprecated(proxyTable): renamed `proxyTable` to `router`.
58
- feat(router): support for custom `router` function.

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,20 @@ var app = express();
127127

128128
## Context matching
129129

130-
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility. Request URL's [ _path-absolute_ and _query_](https://tools.ietf.org/html/rfc3986#section-3) will be used for context matching.
130+
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.
131+
132+
The [RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is be used for context matching.
133+
134+
```
135+
foo://example.com:8042/over/there?name=ferret#nose
136+
\_/ \______________/\_________/ \_________/ \__/
137+
| | | | |
138+
scheme authority path query fragment
139+
```
131140

132141
* **path matching**
133-
- `proxy({...})` or `proxy('/', {...})` - matches any path, all requests will be proxied.
142+
- `proxy({...})` - matches any path, all requests will be proxied.
143+
- `proxy('/', {...})` - matches any path, all requests will be proxied.
134144
- `proxy('/api', {...})` - matches paths starting with `/api`
135145

136146
* **multiple path matching**
@@ -153,8 +163,8 @@ Providing an alternative way to decide which requests should be proxied; In case
153163
/**
154164
* @return {Boolean}
155165
*/
156-
var filter = function (path, req) {
157-
return (path.match('^/api') && req.method === 'GET');
166+
var filter = function (pathname, req) {
167+
return (pathname.match('^/api') && req.method === 'GET');
158168
};
159169

160170
var apiProxy = proxy(filter, {target: 'http://www.example.org'})

lib/context-matcher.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ function matchContext(context, uri, req) {
3333

3434
// custom matching
3535
if (_.isFunction(context)) {
36-
var path = getUrlPath(uri);
37-
return context(path, req);
36+
var pathname = getUrlPathName(uri);
37+
return context(pathname, req);
3838
}
3939

4040
throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]');
@@ -46,13 +46,13 @@ function matchContext(context, uri, req) {
4646
* @return {Boolean}
4747
*/
4848
function matchSingleStringPath(context, uri) {
49-
var path = getUrlPath(uri);
50-
return path.indexOf(context) === 0;
49+
var pathname = getUrlPathName(uri);
50+
return pathname.indexOf(context) === 0;
5151
}
5252

5353
function matchSingleGlobPath(pattern, uri) {
54-
var path = getUrlPath(uri);
55-
var matches = micromatch(path, pattern);
54+
var pathname = getUrlPathName(uri);
55+
var matches = micromatch(pathname, pattern);
5656
return matches && (matches.length > 0);
5757
}
5858

@@ -75,8 +75,14 @@ function matchMultiPath(contextList, uri) {
7575
return false;
7676
}
7777

78-
function getUrlPath(uri) {
79-
return uri && url.parse(uri).path;
78+
/**
79+
* Parses URI and returns RFC 3986 path
80+
*
81+
* @param {String} uri from req.url
82+
* @return {String} RFC 3986 path
83+
*/
84+
function getUrlPathName(uri) {
85+
return uri && url.parse(uri).pathname;
8086
}
8187

8288
function isStringPath(context) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http-proxy-middleware",
3-
"version": "0.16.0",
3+
"version": "0.17.0-beta",
44
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
55
"main": "index.js",
66
"scripts": {

recipes/context-matching.md

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# Context matching
22

3-
Determine which requests should be proxied. `http-proxy-middleware` offers several ways to do this:
3+
Determine which requests should be proxied.
4+
5+
Context matching is optional and is useful in cases where you are not able to use the regular [middleware mounting](http://expressjs.com/en/4x/api.html#app.use).
6+
7+
The [RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.
8+
9+
```
10+
foo://example.com:8042/over/there?name=ferret#nose
11+
\_/ \______________/\_________/ \_________/ \__/
12+
| | | | |
13+
scheme authority path query fragment
14+
```
15+
16+
17+
`http-proxy-middleware` offers several ways to do this:
418

519
<!-- MarkdownTOC autolink=true bracket=round -->
620

@@ -16,9 +30,7 @@ Determine which requests should be proxied. `http-proxy-middleware` offers sever
1630

1731
## Path
1832

19-
This example will create a basic proxy.
20-
21-
Requests with path `/api` will be proxied to `http://localhost:3000`
33+
This will match paths starting with `/api`
2234

2335
```javascript
2436
var proxy = require("http-proxy-middleware");
@@ -30,9 +42,7 @@ var apiProxy = proxy('/api', {target: 'http://localhost:3000'});
3042

3143
## Multi Path
3244

33-
This example will create a basic proxy
34-
35-
Requests with path `/api` and `/rest` will be proxied to `http://localhost:3000`
45+
This will match paths starting with `/api` or `/rest`
3646

3747
```javascript
3848
var proxy = require("http-proxy-middleware");
@@ -45,7 +55,7 @@ var apiProxy = proxy(['/api', '/rest'], {target: 'http://localhost:3000'});
4555

4656
## Wildcard
4757

48-
This example will create a proxy with wildcard context matching.
58+
This will match paths starting with `/api/` and should also end with `.json`
4959

5060
```javascript
5161
var proxy = require("http-proxy-middleware");
@@ -55,12 +65,12 @@ var apiProxy = proxy('/api/**/*.json', {target: 'http://localhost:3000'});
5565

5666
## Multi Wildcard
5767

58-
This example will create a proxy with wildcard context matching.
68+
Multiple wildcards can be used.
5969

6070
```javascript
6171
var proxy = require("http-proxy-middleware");
6272

63-
var apiProxy = proxy(['/api/**', '/ajax/**'], {target: 'http://localhost:3000'});
73+
var apiProxy = proxy(['/api/**/*.json', '/rest/**'], {target: 'http://localhost:3000'});
6474
```
6575

6676
## Wildcard / Exclusion
@@ -70,19 +80,19 @@ This example will create a proxy with wildcard context matching.
7080
```javascript
7181
var proxy = require("http-proxy-middleware");
7282

73-
var apiProxy = proxy(['/api/**', '!**/bad.json'], {target: 'http://localhost:3000'});
83+
var apiProxy = proxy(['foo/*.js', '!bar.js'], {target: 'http://localhost:3000'});
7484
```
7585

7686
## Custom filtering
7787

78-
This example will create a proxy with custom filtering.
79-
The request `path` and `req` object are provided to determine which requests should be proxied or not.
88+
Write your custom context matching function to have full control on the matching behavior.
89+
The request `pathname` and `req` object are provided to determine which requests should be proxied or not.
8090

8191
```javascript
8292
var proxy = require("http-proxy-middleware");
8393

84-
var filter = function (path, req) {
85-
return (path.match('^/api') && req.method === 'GET');
94+
var filter = function (pathname, req) {
95+
return (pathname.match('^/api') && req.method === 'GET');
8696
};
8797

8898
var apiProxy = proxy(filter, {target: 'http://localhost:3000'});

test/unit/context-matcher.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ describe('Context Matching', function() {
111111
expect(contextMatcher.match(pattern, 'http://localhost/some/path/index.html')).to.be.false;
112112
});
113113

114-
it('should only match .php files with query params', function() {
115-
expect(contextMatcher.match('/**/*.php', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.false;
116-
expect(contextMatcher.match('/**/*.php?*', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.true;
114+
it('should ignore query params', function() {
115+
expect(contextMatcher.match('/**/*.php', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.true;
116+
expect(contextMatcher.match('/**/*.php?*', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.false;
117117
});
118118

119119
it('should only match any file in root path', function() {

0 commit comments

Comments
 (0)