Skip to content

fix(context matching): Use RFC 3986 path (excludes query parameters) #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [develop](https://github.com/chimurai/http-proxy-middleware/releases/tag/develop)
- fix(context matching): Use [RFC 3986 path](https://tools.ietf.org/html/rfc3986#section-3.3) in context matching. (excludes query parameters)

## [v0.16.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.16.0)
- deprecated(proxyTable): renamed `proxyTable` to `router`.
- feat(router): support for custom `router` function.
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,20 @@ var app = express();

## Context matching

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

The [RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is be used for context matching.

```
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
```

* **path matching**
- `proxy({...})` or `proxy('/', {...})` - matches any path, all requests will be proxied.
- `proxy({...})` - matches any path, all requests will be proxied.
- `proxy('/', {...})` - matches any path, all requests will be proxied.
- `proxy('/api', {...})` - matches paths starting with `/api`

* **multiple path matching**
Expand All @@ -153,8 +163,8 @@ Providing an alternative way to decide which requests should be proxied; In case
/**
* @return {Boolean}
*/
var filter = function (path, req) {
return (path.match('^/api') && req.method === 'GET');
var filter = function (pathname, req) {
return (pathname.match('^/api') && req.method === 'GET');
};

var apiProxy = proxy(filter, {target: 'http://www.example.org'})
Expand Down
22 changes: 14 additions & 8 deletions lib/context-matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ function matchContext(context, uri, req) {

// custom matching
if (_.isFunction(context)) {
var path = getUrlPath(uri);
return context(path, req);
var pathname = getUrlPathName(uri);
return context(pathname, req);
}

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

function matchSingleGlobPath(pattern, uri) {
var path = getUrlPath(uri);
var matches = micromatch(path, pattern);
var pathname = getUrlPathName(uri);
var matches = micromatch(pathname, pattern);
return matches && (matches.length > 0);
}

Expand All @@ -75,8 +75,14 @@ function matchMultiPath(contextList, uri) {
return false;
}

function getUrlPath(uri) {
return uri && url.parse(uri).path;
/**
* Parses URI and returns RFC 3986 path
*
* @param {String} uri from req.url
* @return {String} RFC 3986 path
*/
function getUrlPathName(uri) {
return uri && url.parse(uri).pathname;
}

function isStringPath(context) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "http-proxy-middleware",
"version": "0.16.0",
"version": "0.17.0-beta",
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
"main": "index.js",
"scripts": {
Expand Down
40 changes: 25 additions & 15 deletions recipes/context-matching.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# Context matching

Determine which requests should be proxied. `http-proxy-middleware` offers several ways to do this:
Determine which requests should be proxied.

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).

The [RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.

```
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
```


`http-proxy-middleware` offers several ways to do this:

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

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

## Path

This example will create a basic proxy.

Requests with path `/api` will be proxied to `http://localhost:3000`
This will match paths starting with `/api`

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

## Multi Path

This example will create a basic proxy

Requests with path `/api` and `/rest` will be proxied to `http://localhost:3000`
This will match paths starting with `/api` or `/rest`

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

## Wildcard

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

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

## Multi Wildcard

This example will create a proxy with wildcard context matching.
Multiple wildcards can be used.

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

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

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

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

## Custom filtering

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

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

var filter = function (path, req) {
return (path.match('^/api') && req.method === 'GET');
var filter = function (pathname, req) {
return (pathname.match('^/api') && req.method === 'GET');
};

var apiProxy = proxy(filter, {target: 'http://localhost:3000'});
Expand Down
6 changes: 3 additions & 3 deletions test/unit/context-matcher.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ describe('Context Matching', function() {
expect(contextMatcher.match(pattern, 'http://localhost/some/path/index.html')).to.be.false;
});

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

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