Skip to content

Commit

Permalink
fix(context matching): Use RFC 3986 path in context matching. (excl…
Browse files Browse the repository at this point in the history
…udes query parameters) (#87)
  • Loading branch information
chimurai authored Jun 21, 2016
1 parent 09926cd commit 8fe6008
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 31 deletions.
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

0 comments on commit 8fe6008

Please sign in to comment.