Skip to content
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
38 changes: 33 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The one-liner proxy middleware for [connect](https://github.com/senchalabs/conne

## Install
```javascript
npm install --save-dev http-proxy-middleware
$ npm install --save-dev http-proxy-middleware
```

## Core concept
Expand All @@ -26,13 +26,14 @@ var proxy = proxyMiddleware('/api', {target: 'http://www.example.org'});
```
* **context**: matches provided context against request-urls' path.
Matching requests will be proxied to the target host.
Example: `'/api'` or `['/api', '/ajax']`
Example: `'/api'` or `['/api', '/ajax']`. (more about [context matching](#context-matching))
* **options.target**: target host to proxy to.
Check out available [proxy options](#options).



## Example
A simple example with express server.
```javascript
// include dependencies
var express = require('express');
Expand All @@ -54,6 +55,8 @@ var app = express();
app.listen(3000);
```

See [more examples](#more-examples).

**Tip:** For [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based), you'll need to use the option `changeOrigin` and set it to `true`.

## Compatible servers:
Expand Down Expand Up @@ -95,10 +98,31 @@ Undocumented options are provided by the underlying [http-proxy](https://github.
* **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.


## Context matching
Request URL's [ _path-absolute_ and _query_](https://tools.ietf.org/html/rfc3986#section-3) will be used for context matching .

* URL: `http://example.com:8042/over/there?name=ferret#nose`
* context: `/over/there?name=ferret`

http-proxy-middleware offers several ways to decide which requests should be proxied:
* path matching
* `'/'` - matches any path, all requests will be proxied.
* `'/api'` - matches paths starting with `/api`
* multiple path matching
* `['/api','/ajax','/someotherpath']`
* wildcard path matching

For fine-grained control you can use wildcard matching. Glob pattern matching is done by _micromatch_. Visit [micromatch](https://www.npmjs.com/package/micromatch) or [glob](https://www.npmjs.com/package/glob) for more globbing examples.
* `**` matches any path, all requests will be proxied.
* `**.html` matches any path which ends with `.html`
* `/*.html` matches paths directly under path-absolute
* `/api/**.html` matches requests ending with `.html` in the path of `/api`
* `['/api/**', '/ajax/**']` combine multiple patterns
* `['/api/**', '!**/bad.json']` exclusion

## More Examples

To [view the examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples), clone the http-proxy-middleware repo and install the dependencies:
To run and view the [proxy examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples), clone the http-proxy-middleware repo and install the dependencies:

```bash
$ git clone https://github.com/chimurai/http-proxy-middleware.git
Expand All @@ -112,17 +136,21 @@ $ npm install
$ node examples/connect
```

Or just explore the [proxy examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples) sources:
Or just explore the proxy examples' sources:
* `examples/connect` - [connect proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect)
* `examples/express` - [express proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express)
* `examples/browser-sync` - [browser-sync proxy middleware example](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync)

## Tests

To run the test suite, first install the dependencies, then run `npm test`:
To run the test suite, first install the dependencies, then run:

```bash
# unit tests
$ npm test

# code coverage
$ npm run cover
```


Expand Down
63 changes: 53 additions & 10 deletions lib/context-matcher.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,78 @@
var url = require('url');
var isGlob = require('is-glob');
var micromatch = require('micromatch');

module.exports = {
match : matchContext,
matchSinglePath : matchSinglePath,
matchMultiPath : matchMultiPath
match : matchContext
}

function matchContext (context, uri) {
// single path
if (typeof context === 'string') {
return matchSinglePath(context, uri);
if (isStringPath(context)) {
return matchSingleStringPath(context, uri);
}
// single glob path
if (isGlobPath(context)) {
return matchSingleGlobPath(context, uri);
}
// multi path
if (Array.isArray(context)) {
return matchMultiPath(context, uri);
if (context.every(isStringPath)) {
return matchMultiPath(context, uri);
}
if (context.every(isGlobPath)) {
return matchMultiGlobPath(context, uri);
}

throw new Error('[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]');
}

throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]');
}

function matchSinglePath (context, uri) {
var urlPath = url.parse(uri).path;
return urlPath.indexOf(context) === 0;
/**
* @param {String} context '/api'
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchSingleStringPath (context, uri) {
var path = getUrlPath(uri);
return path.indexOf(context) === 0;
}

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

function matchMultiGlobPath (patternList, uri) {
return matchSingleGlobPath(patternList, uri);
}

/**
* @param {String} context ['/api', '/ajax']
* @param {String} uri 'http://example.org/api/b/c/d.html'
* @return {Boolean}
*/
function matchMultiPath (contextList, uri) {
for (var i = 0; i < contextList.length; i++) {
var context = contextList[i];
if (matchSinglePath(context, uri)) {
if (matchSingleStringPath(context, uri)) {
return true;
}
}
return false;
}

function getUrlPath (uri) {
return uri && url.parse(uri).path;
}

function isStringPath (context) {
return typeof context === 'string' && !isGlob(context);
}

function isGlobPath (context) {
return isGlob(context);
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
},
"dependencies": {
"http-proxy": "^1.11.1",
"is-glob": "^2.0.0",
"micromatch": "^2.1.6",
"url": "^0.10.3"
}
}
Loading