Merge remote-tracking branch 'origin/caronte' into caronte-merge

This commit is contained in:
yawnt 2014-01-08 10:19:11 +01:00
commit 0b642d4cf7
51 changed files with 3721 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules
*.swp
cov
atest.js
notes
primus-proxy.js
tes.js

12
.travis.yml Normal file
View File

@ -0,0 +1,12 @@
language: node_js
node_js:
- "0.10"
- "0.11"
notifications:
email:
- travis@nodejitsu.com
irc: "irc.freenode.org#nodejitsu"
script:
npm test

4
CHANGELOG.md Normal file
View File

@ -0,0 +1,4 @@
### v 1.0.0-alpha
- Complete refactor with new API

23
LICENSE Normal file
View File

@ -0,0 +1,23 @@
node-http-proxy
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

322
README.md Normal file
View File

@ -0,0 +1,322 @@
<p align="center">
<img src="doc/logo.png?raw=true"/>
</p>
node-http-proxy
=======
`node-http-proxy` is an HTTP programmable proxying library that supports
websockets. It is suitable for implementing components such as
proxies and load balancers.
### Build Status
<p align="center">
<a href="https://travis-ci.org/nodejitsu/node-http-proxy" target="_blank">
<img src="https://travis-ci.org/nodejitsu/node-http-proxy.png?branch=caronte"/></a>&nbsp;&nbsp;
<a href="https://coveralls.io/r/nodejitsu/node-http-proxy" target="_blank">
<img src="https://coveralls.io/repos/nodejitsu/node-http-proxy/badge.png?branch=caronte"/></a>
</p>
### Looking to Upgrade from 0.8.x ? Click [here](UPGRADING.md)
### Core Concept
A new proxy is created by calling `createProxyServer` and passing
an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L26-L39))
```javascript
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer(options);
```
An object will be returned with four values:
* web `req, res, [options]` (used for proxying regular HTTP(S) requests)
* ws `req, socket, head, [options]` (used for proxying WS(S) requests)
* listen `port` (a function that wraps the object in a webserver, for your convenience)
Is it then possible to proxy requests by calling these functions
```javascript
require('http').createServer(function(req, res) {
proxy.web(req, res, { target: 'http://mytarget.com:8080' });
});
```
Errors can be listened on either using the Event Emitter API
```javascript
proxy.on('error', function(e) {
...
});
```
or using the callback API
```javascript
proxy.web(req, res, { target: 'http://mytarget.com:8080' }, function(e) { ... });
```
When a request is proxied it follows two different pipelines ([available here](lib/http-proxy/passes))
which apply transformations to both the `req` and `res` object.
The first pipeline (ingoing) is responsible for the creation and manipulation of the stream that connects your client to the target.
The second pipeline (outgoing) is responsible for the creation and manipulation of the stream that, from your target, returns data
to the client.
#### Setup a basic stand-alone proxy server
```js
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create your proxy server and set the target in the options.
//
httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000);
//
// Create your target server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
```
#### Setup a stand-alone proxy server with custom server logic
This example show how you can proxy a request using your own HTTP server
and also you can put your own logic to handle the request.
```js
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create a proxy server with custom application logic
//
var proxy = httpProxy.createProxyServer({});
//
// Create your custom server and just call `proxy.web()` to proxy
// a web request to the target passed in the options
// also you can use `proxy.ws()` to proxy a websockets request
//
var server = require('http').createServer(function(req, res) {
// You can define here your custom logic to handle the request
// and then proxy the request.
proxy.web(req, res, { target: 'http://127.0.0.1:5060' });
});
console.log("listening on port 5050")
server.listen(5050);
```
#### Setup a stand-alone proxy server with latency
```js
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create a proxy server with latency
//
var proxy = httpProxy.createProxyServer();
//
// Create your server that make an operation that take a while
// and then proxy de request
//
http.createServer(function (req, res) {
// This simulate an operation that take 500ms in execute
setTimeout(function () {
proxy.web(req, res, {
target: 'http://localhost:9008'
});
}, 500);
}).listen(8008);
//
// Create your target server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9008);
```
#### Listening for proxy events
* `error`: The error event is emitted if the request to the target fail.
* `proxyRes`: This event is emitted if the request to the target got a response.
```js
var httpProxy = require('http-proxy');
// Error example
//
// Http Proxy Server with bad target
//
var proxy = httpProxy.createServer({
target:'http://localhost:9005'
});
proxy.listen(8005);
//
// Listen for the `error` event on `proxy`.
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
//
// Listen for the `proxyRes` event on `proxy`.
//
proxy.on('proxyRes', function (res) {
console.log('RAW Response from the target', JSON.stringify(res.headers, true, 2));
});
```
#### Using HTTPS
You can activate the validation of a secure SSL certificate to the target connection (avoid self signed certs), just set `secure: true` in the options.
##### HTTPS -> HTTP
```js
//
// Create the HTTPS proxy server in front of a HTTP server
//
httpProxy.createServer({
target: {
host: 'localhost',
port: 9009
},
ssl: {
key: fs.readFileSync('valid-ssl-key.pem'), 'utf8'),
cert: fs.readFileSync('valid-ssl-cert.pem'), 'utf8')
}
}).listen(8009);
```
##### HTTPS -> HTTPS
```js
//
// Create the proxy server listening on port 443
//
httpProxy.createServer({
ssl: {
key: fs.readFileSync('valid-ssl-key.pem'), 'utf8'),
cert: fs.readFileSync('valid-ssl-cert.pem'), 'utf8')
},
target: 'https://localhost:9010',
secure: true // Depends on your needs, could be false.
}).listen(443);
```
#### Proxying WebSockets
You can activate the websocket support for the proxy using `ws:true` in the options.
```js
//
// Create a proxy server for websockets
//
httpProxy.createServer({
target: 'ws://localhost:9014',
ws: true
}).listen(8014);
```
Also you can proxy the websocket requests just calling the `ws(req, socket, head)` method.
```js
//
// Setup our server to proxy standard HTTP requests
//
var proxy = new httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 9015
}
});
var proxyServer = http.createServer(function (req, res) {
proxy.web(req, res);
});
//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
proxyServer.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
proxyServer.listen(8015);
```
### Contributing and Issues
* Search on Google/Github
* If you can't find anything, open an issue
* If you feel comfortable about fixing the issue, fork the repo
* Commit to your local branch (which must be different from `master`)
* Submit your Pull Request (be sure to include tests and update documentation)
### Options
`httpProxy.createProxyServer` supports the following options:
* **target**: url string to be parsed with the url module
* **forward**: url string to be parsed with the url module
* **agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects)
* **secure**: true/false, if you want to verify the SSL Certs
If you are using the `proxyServer.listen` method, the following options are also applicable:
* **ssl**: object to be passed to https.createServer()
* **ws**: true/false, if you want to proxy websockets
* **xfwd**: true/false, adds x-forward headers
### Test
```
$ npm test
```
### Logo
Logo created by [Diego Pasquali](http://dribbble.com/diegopq)
### License
>The MIT License (MIT)
>
>Copyright (c) 2010 - 2013 Nodejitsu Inc.
>
>Permission is hereby granted, free of charge, to any person obtaining a copy
>of this software and associated documentation files (the "Software"), to deal
>in the Software without restriction, including without limitation the rights
>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>copies of the Software, and to permit persons to whom the Software is
>furnished to do so, subject to the following conditions:
>
>The above copyright notice and this permission notice shall be included in
>all copies or substantial portions of the Software.
>
>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>THE SOFTWARE.

96
UPGRADING.md Normal file
View File

@ -0,0 +1,96 @@
Looking to upgrade from `http-proxy@0.x.x` to `http-proxy@1.0`? You've come to the right place!
`http-proxy@1.0` is a from-scratch implementation of `http-proxy` and, as such
brings some breaking changes to APIs.
## Server creation
Available through `.createServer()` or `.createProxyServer()`.
```javascript
httpProxy.createServer({
target:'http://localhost:9003'
}).listen(8003);
```
Check the [README.md](https://github.com/nodejitsu/node-http-proxy/blob/caronte/README.md) for a more detailed explanation of the parameters.
## Proxying
Web proying is done by calling the `.web()` method on a Proxy instance. You can check among some use cases in the [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/http)
```javascript
//
// Create a HTTP Proxy server with a HTTPS target
//
httpProxy.createProxyServer({
target: 'https://google.com',
agent : https.globalAgent,
headers: {
host: 'google.com'
}
}).listen(8011);
```
Websockets are proxied by the `.ws()` method. The [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/websocket) again provides a lot of useful snippets!
```javascript
var proxy = new httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 9015
}
});
var proxyServer = http.createServer(function (req, res) {
proxy.web(req, res);
});
//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
proxyServer.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
```
## Error Handling
It is possible to listen globally on the `error` event on the server. In alternative, a
callback passed to `.web()` or `.ws()` as last parameter is also accepted.
```javascript
var proxy = httpProxy.createServer({
target:'http://localhost:9005'
});
//
// Tell the proxy to listen on port 8000
//
proxy.listen(8005);
//
// Listen for the `error` event on `proxy`.
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
```
## Dropped
Since the API was rewritten to be extremely flexible we decided to drop some features
which were in the core and delegate them to eventual "userland" modules.
- Middleware API
- ProxyTable API
### Middleware API
The new API makes it really easy to implement code that behaves like the old Middleware API. You can check some examples [here](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/middleware)

33
benchmark/README.md Normal file
View File

@ -0,0 +1,33 @@
# Benchmarking `node-http-proxy`
The long-term goal of these scripts and documentation is to provide a consistent and well understood benchmarking process for `node-http-proxy` so that performance does not degrade over time. They were initially created to compare the performance of `v0.10.3` and `v1.0.0` (which was a significant rewrite).
## Pre-requisites
All benchmarking shall be done with [wrk](https://github.com/wg/wrk) which _is the same tool used for performance testing by the node.js core team._ **Make sure you have `wrk` installed before continuing**.
```
$ wrk
Usage: wrk <options> <url>
Options:
-c, --connections <n> Connections to keep open
-r, --requests <n> Total requests to make
-t, --threads <n> Number of threads to use
-H, --header <h> Add header to request
-v, --version Print version details
Numeric arguments may include a SI unit (2k, 2M, 2G)
```
## Benchmarks
1. [Simple HTTP benchmark](#simple-http)
### Simple HTTP
_This benchmark requires three terminals running:_
1. **A proxy server:** `node benchmark/scripts/proxy.js`
2. **A target server:** `node benchmark/scripts/hello.js`
3. **A wrk process:** `wrk -c 20 -r 10000 -t 2 http://127.0.0.1:8000`

View File

@ -0,0 +1,3 @@
require('http').createServer(function(req, res) {
res.end('Hello world!');
}).listen(9000);

View File

@ -0,0 +1,6 @@
var http = require('http'),
httpProxy = require('../../');
//
// Create your proxy server
//
httpProxy.createProxyServer({ target: 'http://localhost:9000' }).listen(8000);

View File

@ -0,0 +1,88 @@
var crypto = require('crypto'),
WebSocket = require('ws'),
async = require('async'),
httpProxy = require('../../');
var SERVER_PORT = 8415,
PROXY_PORT = 8514;
var testSets = [
{
size: 1024 * 1024, // 1 MB
count: 128 // 128 MB
},
{
size: 1024, // 1 KB,
count: 1024 // 1 MB
},
{
size: 128, // 128 B
count: 1024 * 8 // 1 MB
}
];
testSets.forEach(function (set) {
set.buffer = new Buffer(crypto.randomBytes(set.size));
set.buffers = [];
for (var i = 0; i < set.count; i++) {
set.buffers.push(set.buffer);
}
});
function runSet(set, callback) {
function runAgainst(port, callback) {
function send(sock) {
sock.send(set.buffers[got++]);
if (got === set.count) {
t = new Date() - t;
server.close();
proxy.close();
callback(null, t);
}
}
var server = new WebSocket.Server({ port: SERVER_PORT }),
proxy = httpProxy.createServer(SERVER_PORT, 'localhost').listen(PROXY_PORT),
client = new WebSocket('ws://localhost:' + port),
got = 0,
t = new Date();
server.on('connection', function (ws) {
send(ws);
ws.on('message', function (msg) {
send(ws);
});
});
client.on('message', function () {
send(client);
});
}
async.series({
server: async.apply(runAgainst, SERVER_PORT),
proxy: async.apply(runAgainst, PROXY_PORT)
}, function (err, results) {
if (err) {
throw err;
}
var mb = (set.size * set.count) / (1024 * 1024);
console.log(set.size / (1024) + ' KB * ' + set.count + ' (' + mb + ' MB)');
Object.keys(results).forEach(function (key) {
var t = results[key],
throughput = mb / (t / 1000);
console.log(' ' + key + ' took ' + t + ' ms (' + throughput + ' MB/s)');
});
callback();
});
}
async.forEachLimit(testSets, 1, runSet);

BIN
doc/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,84 @@
/*
simple-balancer.js: Example of a simple round robin balancer for websockets
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// A simple round-robin load balancing strategy.
//
// First, list the servers you want to use in your rotation.
//
var addresses = [
{
host: 'ws1.0.0.0',
port: 80
},
{
host: 'ws2.0.0.0',
port: 80
}
];
//
// Create a HttpProxy object for each target
//
var proxies = addresses.map(function (target) {
return new httpProxy.createProxyServer({
target: target
});
});
//
// Get the proxy at the front of the array, put it at the end and return it
// If you want a fancier balancer, put your code here
//
function nextProxy() {
var proxy = proxies.shift();
proxies.push(proxy);
return proxy;
}
//
// Get the 'next' proxy and send the http request
//
var server = http.createServer(function (req, res) {
nextProxy().web(req, res);
});
//
// Get the 'next' proxy and send the upgrade request
//
server.on('upgrade', function (req, socket, head) {
nextProxy().ws(req, socket, head);
});
server.listen(8001);

View File

@ -0,0 +1,64 @@
/*
simple-balancer.js: Example of a simple round robin balancer
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// A simple round-robin load balancing strategy.
//
// First, list the servers you want to use in your rotation.
//
var addresses = [
{
host: 'ws1.0.0.0',
port: 80
},
{
host: 'ws2.0.0.0',
port: 80
}
];
var proxy = httpProxy.createServer();
http.createServer(function (req, res) {
//
// On each request, get the first location from the list...
//
var target = { target: addresses.shift() };
//
// ...then proxy to the server whose 'turn' it is...
//
console.log('balancing request to: ', target);
proxy.web(req, res, target);
//
// ...and then the server you just used becomes the last item in the list.
//
addresses.push(target);
}).listen(8021);
// Rinse; repeat; enjoy.

64
examples/helpers/store.js Normal file
View File

@ -0,0 +1,64 @@
//
// just to make these example a little bit interesting,
// make a little key value store with an http interface
// (see couchbd for a grown-up version of this)
//
// API:
// GET /
// retrive list of keys
//
// GET /[url]
// retrive object stored at [url]
// will respond with 404 if there is nothing stored at [url]
//
// POST /[url]
//
// JSON.parse the body and store it under [url]
// will respond 400 (bad request) if body is not valid json.
//
// TODO: cached map-reduce views and auto-magic sharding.
//
var Store = module.exports = function Store () {
this.store = {};
};
Store.prototype = {
get: function (key) {
return this.store[key]
},
set: function (key, value) {
return this.store[key] = value
},
handler:function () {
var store = this
return function (req, res) {
function send (obj, status) {
res.writeHead(200 || status,{'Content-Type': 'application/json'})
res.write(JSON.stringify(obj) + '\n')
res.end()
}
var url = req.url.split('?').shift()
if (url === '/') {
console.log('get index')
return send(Object.keys(store.store))
} else if (req.method == 'GET') {
var obj = store.get (url)
send(obj || {error: 'not_found', url: url}, obj ? 200 : 404)
} else {
//post: buffer body, and parse.
var body = '', obj
req.on('data', function (c) { body += c})
req.on('end', function (c) {
try {
obj = JSON.parse(body)
} catch (err) {
return send (err, 400)
}
store.set(url, obj)
send({ok: true})
})
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
basic-proxy.js: Basic example of proxying over HTTP
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
var welcome = [
'# # ##### ##### ##### ##### ##### #### # # # #',
'# # # # # # # # # # # # # # # # ',
'###### # # # # ##### # # # # # # ## # ',
'# # # # ##### ##### ##### # # ## # ',
'# # # # # # # # # # # # # ',
'# # # # # # # # #### # # # '
].join('\n');
util.puts(welcome.rainbow.bold);
//
// Basic Http Proxy Server
//
httpProxy.createServer({
target:'http://localhost:9003'
}).listen(8003);
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9003);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8003'.yellow);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9003 '.yellow);

View File

@ -0,0 +1,68 @@
/*
concurrent-proxy.js: check levelof concurrency through proxy.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Basic Http Proxy Server
//
httpProxy.createServer({
target:'http://localhost:9004'
}).listen(8004);
//
// Target Http Server
//
// to check apparent problems with concurrent connections
// make a server which only responds when there is a given nubmer on connections
//
var connections = [],
go;
http.createServer(function (req, res) {
connections.push(function () {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
});
process.stdout.write(connections.length + ', ');
if (connections.length > 110 || go) {
go = true;
while (connections.length) {
connections.shift()();
}
}
}).listen(9004);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8004'.yellow);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9004 '.yellow);

View File

@ -0,0 +1,55 @@
/*
custom-proxy-error.js: Example of using the custom `proxyError` event.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Http Proxy Server with bad target
//
var proxy = httpProxy.createServer({
target:'http://localhost:9005'
});
//
// Tell the proxy to listen on port 8000
//
proxy.listen(8005);
//
// Listen for the `error` event on `proxy`.
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.');
});
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8005 '.yellow + 'with custom error message'.magenta.underline);

View File

@ -0,0 +1,63 @@
/*
error-handling.js: Example of handle erros for HTTP and WebSockets
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// HTTP Proxy Server
//
var proxy = httpProxy.createProxyServer({target:'http://localhost:9000', ws:true});
//
// Example of error handling
//
function requestHandler(req, res) {
// Pass a callback to the web proxy method
// and catch the error there.
proxy.web(req, res, function (err) {
// Now you can get the err
// and handle it by your self
// if (err) throw err;
res.writeHead(502);
res.end("There was an error proxying your request");
});
// In a websocket request case
req.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head, function (err) {
// Now you can get the err
// and handle it by your self
// if (err) throw err;
socket.close();
})
})
}
http.createServer(requestHandler).listen(8000);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);

View File

@ -0,0 +1,67 @@
/*
forward-and-target-proxy.js: Example of proxying over HTTP with additional forward proxy
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Setup proxy server with forwarding
//
httpProxy.createServer({
target: {
port: 9006,
host: 'localhost'
},
forward: {
port: 9007,
host: 'localhost'
}
}).listen(8006);
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9006);
//
// Target Http Forwarding Server
//
http.createServer(function (req, res) {
util.puts('Receiving forward for: ' + req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9007);
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8006 '.yellow + 'with forward proxy'.magenta.underline);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9006 '.yellow);
util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9007 '.yellow);

View File

@ -0,0 +1,53 @@
/*
forward-proxy.js: Example of proxying over HTTP with additional forward proxy
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Setup proxy server with forwarding
//
httpProxy.createServer({
forward: {
port: 9019,
host: 'localhost'
}
}).listen(8019);
//
// Target Http Forwarding Server
//
http.createServer(function (req, res) {
util.puts('Receiving forward for: ' + req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9019);
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8019 '.yellow + 'with forward proxy'.magenta.underline);
util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9019 '.yellow);

View File

@ -0,0 +1,54 @@
/*
latent-proxy.js: Example of proxying over HTTP with latency
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Http Proxy Server with Latency
//
var proxy = httpProxy.createProxyServer();
http.createServer(function (req, res) {
setTimeout(function () {
proxy.web(req, res, {
target: 'http://localhost:9008'
});
}, 500);
}).listen(8008);
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9008);
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8008 '.yellow + 'with latency'.magenta.underline);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9008 '.yellow);

View File

@ -0,0 +1,46 @@
/*
proxy-http-to-https.js: Basic example of proxying over HTTP to a target HTTPS server
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var https = require('https'),
http = require('http'),
util = require('util'),
path = require('path'),
fs = require('fs'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy');
//
// Create a HTTP Proxy server with a HTTPS target
//
httpProxy.createProxyServer({
target: 'https://google.com',
agent : https.globalAgent,
headers: {
host: 'google.com'
}
}).listen(8011);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8011'.yellow);

View File

@ -0,0 +1,60 @@
/*
proxy-https-to-http.js: Basic example of proxying over HTTPS to a target HTTP server
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var https = require('https'),
http = require('http'),
util = require('util'),
path = require('path'),
fs = require('fs'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy'),
fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures');
//
// Create the target HTTP server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('hello http over https\n');
res.end();
}).listen(9009);
//
// Create the HTTPS proxy server listening on port 8000
//
httpProxy.createServer({
target: {
host: 'localhost',
port: 9009
},
ssl: {
key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'),
cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8')
}
}).listen(8009);
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8009'.yellow);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9009 '.yellow);

View File

@ -0,0 +1,59 @@
/*
proxy-https-to-https.js: Basic example of proxying over HTTPS to a target HTTPS server
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var https = require('https'),
http = require('http'),
util = require('util'),
fs = require('fs'),
path = require('path'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy'),
fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures'),
httpsOpts = {
key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'),
cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8')
};
//
// Create the target HTTPS server
//
https.createServer(httpsOpts, function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('hello https\n');
res.end();
}).listen(9010);
//
// Create the proxy server listening on port 443
//
httpProxy.createServer({
ssl: httpsOpts,
target: 'https://localhost:9010',
secure: false
}).listen(8010);
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8010'.yellow);
util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '9010 '.yellow);

View File

@ -0,0 +1,54 @@
/*
standalone-proxy.js: Example of proxying over HTTP with a standalone HTTP server.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
httpProxy = require('../../lib/http-proxy');
//
// Http Server with proxyRequest Handler and Latency
//
var proxy = new httpProxy.createProxyServer();
http.createServer(function (req, res) {
setTimeout(function () {
proxy.web(req, res, {
target: 'http://localhost:9002'
});
}, 200);
}).listen(8002);
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9002);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxy.web() handler'.cyan.underline + ' and latency'.magenta);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow);

View File

@ -0,0 +1,119 @@
/*
bodyDecoder-middleware.js: Basic example of `connect.bodyParser()` middleware in node-http-proxy
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var http = require('http'),
connect = require('connect'),
request = require('request'),
colors = require('colors'),
util = require('util'),
Store = require('../helpers/store'),
httpProxy = require('../../lib/http-proxy'),
proxy = httpProxy.createProxyServer({});
http.createServer(new Store().handler()).listen(7531, function () {
util.puts('http '.blue + 'greetings '.green + 'server'.blue + ' started '.green.bold + 'on port '.blue + '7531'.yellow);
//try these commands:
// get index:
// curl localhost:7531
// []
//
// get a doc:
// curl localhost:7531/foo
// {"error":"not_found"}
//
// post an doc:
// curl -X POST localhost:7531/foo -d '{"content": "hello", "type": "greeting"}'
// {"ok":true}
//
// get index (now, not empty)
// curl localhost:7531
// ["/foo"]
//
// get doc
// curl localhost:7531/foo
// {"content": "hello", "type": "greeting"}
//
// now, suppose we wanted to direct all objects where type == "greeting" to a different store
// than where type == "insult"
//
// we can use connect connect-bodyDecoder and some custom logic to send insults to another Store.
//insult server:
http.createServer(new Store().handler()).listen(2600, function () {
util.puts('http '.blue + 'insults '.red + 'server'.blue + ' started '.green.bold + 'on port '.blue + '2600'.yellow);
//greetings -> 7531, insults-> 2600
// now, start a proxy server.
//don't worry about incoming contont type
//bodyParser.parse[''] = JSON.parse
connect.createServer(
//refactor the body parser and re-streamer into a separate package
connect.bodyParser(),
//body parser absorbs the data and end events before passing control to the next
// middleware. if we want to proxy it, we'll need to re-emit these events after
//passing control to the middleware.
require('connect-restreamer')(),
function (req, res) {
//if your posting an obect which contains type: "insult"
//it will get redirected to port 2600.
//normal get requests will go to 7531 nad will not return insults.
var port = (req.body && req.body.type === 'insult' ? 2600 : 7531)
proxy.web(req, res, { target: { host: 'localhost', port: port }});
}
).listen(1337, function () {
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '1337'.yellow);
//bodyParser needs content-type set to application/json
//if we use request, it will set automatically if we use the 'json:' field.
function post (greeting, type) {
request.post({
url: 'http://localhost:1337/' + greeting,
json: {content: greeting, type: type || "greeting"}
})
}
post("hello")
post("g'day")
post("kiora")
post("houdy")
post("java", "insult")
//now, the insult should have been proxied to 2600
//curl localhost:2600
//["/java"]
//but the greetings will be sent to 7531
//curl localhost:7531
//["/hello","/g%27day","/kiora","/houdy"]
})
})
});

View File

@ -0,0 +1,65 @@
/*
gzip-middleware.js: Basic example of `connect-gzip` middleware in node-http-proxy
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
connect = require('connect')
httpProxy = require('../../lib/http-proxy');
//
// Basic Connect App
//
connect.createServer(
connect.compress({
// Pass to connect.compress() the options
// that you need, just for show the example
// we use threshold to 1
threshold: 1
}),
function (req, res) {
proxy.web(req, res);
}
).listen(8012);
//
// Basic Http Proxy Server
//
var proxy = httpProxy.createProxyServer({
target: 'http://localhost:9012'
});
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9012);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8012'.yellow);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9012 '.yellow);

View File

@ -0,0 +1,67 @@
/*
modifyBody-middleware.js: Example of middleware which modifies response
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
colors = require('colors'),
http = require('http'),
connect = require('connect'),
httpProxy = require('../../lib/http-proxy');
//
// Basic Connect App
//
connect.createServer(
function (req, res, next) {
var _write = res.write;
res.write = function (data) {
_write.call(res, data.toString().replace("Ruby", "nodejitsu"));
}
next();
},
function (req, res) {
proxy.web(req, res);
}
).listen(8013);
//
// Basic Http Proxy Server
//
var proxy = httpProxy.createProxyServer({
target: 'http://localhost:9013'
});
//
// Target Http Server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, I know Ruby\n');
}).listen(9013);
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8013'.yellow);
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9013 '.yellow);

13
examples/package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "http-proxy-examples",
"description": "packages required to run the examples",
"version": "0.0.0",
"dependencies": {
"colors": "~0.6.2",
"socket.io": "~0.9.16",
"socket.io-client": "~0.9.16",
"connect": "~2.11.0",
"request": "~2.27.0",
"connect-restreamer": "~1.0.0"
}
}

View File

@ -0,0 +1,91 @@
/*
standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
http = require('http'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy');
try {
var io = require('socket.io'),
client = require('socket.io-client');
}
catch (ex) {
console.error('Socket.io is required for this example:');
console.error('npm ' + 'install'.green);
process.exit(1);
}
//
// Create the target HTTP server and setup
// socket.io on it.
//
var server = io.listen(9016);
server.sockets.on('connection', function (client) {
util.debug('Got websocket connection');
client.on('message', function (msg) {
util.debug('Got message from client: ' + msg);
});
client.send('from server');
});
//
// Setup our server to proxy standard HTTP requests
//
var proxy = new httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 9016
}
});
var proxyServer = http.createServer(function (req, res) {
proxy.web(req, res);
});
//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
proxyServer.on('upgrade', function (req, socket, head) {
setTimeout(function () {
proxy.ws(req, socket, head);
}, 1000);
});
proxyServer.listen(8016);
//
// Setup the socket.io client against our proxy
//
var ws = client.connect('ws://localhost:8016');
ws.on('message', function (msg) {
util.debug('Got message: ' + msg);
ws.send('I am the client');
});

View File

@ -0,0 +1,88 @@
/*
standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
http = require('http'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy');
try {
var io = require('socket.io'),
client = require('socket.io-client');
}
catch (ex) {
console.error('Socket.io is required for this example:');
console.error('npm ' + 'install'.green);
process.exit(1);
}
//
// Create the target HTTP server and setup
// socket.io on it.
//
var server = io.listen(9015);
server.sockets.on('connection', function (client) {
util.debug('Got websocket connection');
client.on('message', function (msg) {
util.debug('Got message from client: ' + msg);
});
client.send('from server');
});
//
// Setup our server to proxy standard HTTP requests
//
var proxy = new httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 9015
}
});
var proxyServer = http.createServer(function (req, res) {
proxy.web(req, res);
});
//
// Listen to the `upgrade` event and proxy the
// WebSocket requests as well.
//
proxyServer.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head);
});
proxyServer.listen(8015);
//
// Setup the socket.io client against our proxy
//
var ws = client.connect('ws://localhost:8015');
ws.on('message', function (msg) {
util.debug('Got message: ' + msg);
ws.send('I am the client');
});

View File

@ -0,0 +1,70 @@
/*
web-socket-proxy.js: Example of proxying over HTTP and WebSockets.
Copyright (c) Nodejitsu 2013
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var util = require('util'),
http = require('http'),
colors = require('colors'),
httpProxy = require('../../lib/http-proxy');
try {
var io = require('socket.io'),
client = require('socket.io-client');
}
catch (ex) {
console.error('Socket.io is required for this example:');
console.error('npm ' + 'install'.green);
process.exit(1);
}
//
// Create the target HTTP server and setup
// socket.io on it.
//
var server = io.listen(9014);
server.sockets.on('connection', function (client) {
util.debug('Got websocket connection');
client.on('message', function (msg) {
util.debug('Got message from client: ' + msg);
});
client.send('from server');
});
//
// Create a proxy server with node-http-proxy
//
httpProxy.createServer({ target: 'ws://localhost:9014', ws: true }).listen(8014);
//
// Setup the socket.io client against our proxy
//
var ws = client.connect('ws://localhost:8014');
ws.on('message', function (msg) {
util.debug('Got message: ' + msg);
ws.send('I am the client');
});

13
index.js Normal file
View File

@ -0,0 +1,13 @@
/*!
* Caron dimonio, con occhi di bragia
* loro accennando, tutte le raccoglie;
* batte col remo qualunque sadagia
*
* Charon the demon, with the eyes of glede,
* Beckoning to them, collects them all together,
* Beats with his oar whoever lags behind
*
* Dante - The Divine Comedy (Canto III)
*/
module.exports = require('./lib/http-proxy');

48
lib/http-proxy.js Normal file
View File

@ -0,0 +1,48 @@
var http = require('http'),
https = require('https'),
url = require('url'),
httpProxy = require('./http-proxy/');
/**
* Export the the proxy "Server" as the main export
*/
module.exports = httpProxy.Server;
/**
* Creates the proxy server.
*
* Examples:
*
* httpProxy.createProxyServer({ .. }, 8000)
* // => '{ web: [Function], ws: [Function] ... }'
*
* @param {Object} Options Config object passed to the proxy
*
* @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests
*
* @api public
*/
module.exports.createProxyServer = module.exports.createServer = function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new httpProxy.Server(options);
};

82
lib/http-proxy/common.js Normal file
View File

@ -0,0 +1,82 @@
var common = exports,
url = require('url'),
extend = require('util')._extend;
/**
* Copies the right headers from `options` and `req` to
* `outgoing` which is then used to fire the proxied
* request.
*
* Examples:
*
* common.setupOutgoing(outgoing, options, req)
* // => { host: ..., hostname: ...}
*
* @param {Object} Outgoing Base object to be filled with required properties
* @param {Object} Options Config object passed to the proxy
* @param {ClientRequest} Req Request Object
* @param {String} Forward String to select forward or target
* 
* @return {Object} Outgoing Object with all required properties set
*
* @api private
*/
common.setupOutgoing = function(outgoing, options, req, forward) {
outgoing.port = options[forward || 'target'].port ||
(~['https:', 'wss:'].indexOf(options[forward || 'target'].protocol) ? 443 : 80);
['host', 'hostname', 'socketPath'].forEach(
function(e) { outgoing[e] = options[forward || 'target'][e]; }
);
['method', 'headers'].forEach(
function(e) { outgoing[e] = req[e]; }
);
if (options.headers){
extend(outgoing.headers, options.headers);
}
if (options[forward || 'target'].protocol == 'https:') {
outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure;
}
outgoing.agent = options.agent || false;
outgoing.path = url.parse(req.url).path;
return outgoing;
};
/**
* Set the proper configuration for sockets,
* set no delay and set keep alive, also set
* the timeout to 0.
*
* Examples:
*
* common.setupSocket(socket)
* // => Socket
*
* @param {Socket} Socket instance to setup
* 
* @return {Socket} Return the configured socket.
*
* @api private
*/
common.setupSocket = function(socket) {
socket.setTimeout(0);
socket.setNoDelay(true);
socket.setKeepAlive(true, 0);
return socket;
};
common.getPort = function(req) {
var res = req.headers.host.match(/:(\d+)/);
return res ?
res[1] :
req.connection.pair ? '443' : '80' ;
}

149
lib/http-proxy/index.js Normal file
View File

@ -0,0 +1,149 @@
var httpProxy = exports,
extend = require('util')._extend,
parse_url = require('url').parse,
EE3 = require('eventemitter3').EventEmitter,
http = require('http'),
https = require('https'),
web = require('./passes/web-incoming'),
ws = require('./passes/ws-incoming');
httpProxy.Server = ProxyServer;
/**
* Returns a function that creates the loader for
* either `ws` or `web`'s passes.
*
* Examples:
*
* httpProxy.createRightProxy('ws')
* // => [Function]
*
* @param {String} Type Either 'ws' or 'web'
* 
* @return {Function} Loader Function that when called returns an iterator for the right passes
*
* @api private
*/
function createRightProxy(type) {
return function(options) {
return function(req, res /*, [head], [opts] */) {
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
args = [].slice.call(arguments),
cntr = args.length - 1,
head, cbl;
/* optional args parse begin */
if(typeof args[cntr] === 'function') {
cbl = args[cntr];
cntr--;
}
if(
!(args[cntr] instanceof Buffer) &&
args[cntr] !== res
) {
//Copy global options
options = extend({}, options);
//Overwrite with request options
extend(options, args[cntr]);
cntr--;
}
if(args[cntr] instanceof Buffer) {
head = args[cntr];
}
/* optional args parse end */
['target', 'forward'].forEach(function(e) {
if (typeof options[e] === 'string')
options[e] = parse_url(options[e]);
});
for(var i=0; i < passes.length; i++) {
/**
* Call of passes functions
* pass(req, res, options, head)
*
* In WebSockets case the `res` variable
* refer to the connection socket
* pass(req, socket, options, head)
*/
if(passes[i](req, res, options, head, cbl ? false : this, cbl)) { // passes can return a truthy value to halt the loop
break;
}
}
};
};
}
function ProxyServer(options) {
EE3.call(this);
this.web = this.proxyRequest = createRightProxy('web')(options);
this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options);
this.options = options;
this.webPasses = Object.keys(web).map(function(pass) {
return web[pass];
});
this.wsPasses = Object.keys(ws).map(function(pass) {
return ws[pass];
});
}
require('util').inherits(ProxyServer, EE3);
ProxyServer.prototype.listen = function(port) {
var self = this,
closure = function(req, res) { self.web(req, res); };
this._server = this.options.ssl ?
https.createServer(this.options.ssl, closure) :
http.createServer(closure);
if(this.options.ws) {
this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); });
}
this._server.listen(port);
return this;
};
ProxyServer.prototype.before = function(type, passName, callback) {
if (type !== 'ws' || type !== 'web') {
throw new Error('type must be `web` or `ws`');
}
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
i = false;
passes.forEach(function(v, idx) {
if(v.name === passName) i = idx;
})
if(!i) throw new Error('No such pass');
passes.splice(i, 0, callback);
};
ProxyServer.prototype.after = function(type, passName, callback) {
if (type !== 'ws' || type !== 'web') {
throw new Error('type must be `web` or `ws`');
}
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
i = false;
passes.forEach(function(v, idx) {
if(v.name === passName) i = idx;
})
if(!i) throw new Error('No such pass');
passes.splice(i++, 0, callback);
};

View File

@ -0,0 +1,134 @@
var http = require('http'),
https = require('https'),
web_o = require('./web-outgoing'),
common = require('../common'),
passes = exports;
web_o = Object.keys(web_o).map(function(pass) {
return web_o[pass];
});
/*!
* Array of passes.
*
* A `pass` is just a function that is executed on `req, res, options`
* so that you can easily add new checks while still keeping the base
* flexible.
*/
[ // <--
/**
* Sets `content-length` to '0' if request is of DELETE type.
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function deleteLength(req, res, options) {
if(req.method === 'DELETE' && !req.headers['content-length']) {
req.headers['content-length'] = '0';
}
},
/**
* Sets timeout in request socket if it was specified in options.
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function timeout(req, res, options) {
if(options.timeout) {
req.socket.setTimeout(options.timeout);
}
},
/**
* Sets `x-forwarded-*` headers if specified in config.
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function XHeaders(req, res, options) {
if(!options.xfwd) return;
var values = {
for : req.connection.remoteAddress || req.socket.remoteAddress,
port : common.getPort(req),
proto: req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http')
};
['for', 'port', 'proto'].forEach(function(header) {
req.headers['x-forwarded-' + header] =
(req.headers['x-forwarded-' + header] || '') +
(req.headers['x-forwarded-' + header] ? ',' : '') +
values[header];
});
},
/**
* Does the actual proxying. If `forward` is enabled fires up
* a ForwardStream, same happens for ProxyStream. The request
* just dies otherwise.
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function stream(req, res, options, _, server, clb) {
if(options.forward) {
// If forward enable, so just pipe the request
var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req, 'forward')
);
(options.buffer || req).pipe(forwardReq);
if(!options.target) { return res.end(); }
}
// Request initalization
var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req)
);
// Error Handler
proxyReq.on('error', function(err){
if(options.buffer) { options.buffer.destroy(); }
if (clb) {
clb(err);
} else {
server.emit('error', err, req, res);
}
});
(options.buffer || req).pipe(proxyReq);
proxyReq.on('response', function(proxyRes) {
server.emit('proxyRes', proxyRes);
for(var i=0; i < web_o.length; i++) {
if(web_o[i](req, res, proxyRes)) { break; }
}
proxyRes.pipe(res);
});
//proxyReq.end();
}
] // <--
.forEach(function(func) {
passes[func.name] = func;
});

View File

@ -0,0 +1,78 @@
var passes = exports;
/*!
* Array of passes.
*
* A `pass` is just a function that is executed on `req, res, options`
* so that you can easily add new checks while still keeping the base
* flexible.
*/
[ // <--
/**
* If is a HTTP 1.0 request, remove chunk headers
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {proxyResponse} Res Response object from the proxy request
*
* @api private
*/
function removeChunked(req, res, proxyRes) {
if(req.httpVersion === '1.0') {
delete proxyRes.headers['transfer-encoding'];
}
},
/**
* If is a HTTP 1.0 request, set the correct connection header
* or if connection header not present, then use `keep-alive`
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {proxyResponse} Res Response object from the proxy request
*
* @api private
*/
function setConnection(req, res, proxyRes) {
if (req.httpVersion === '1.0') {
proxyRes.headers.connection = req.headers.connection || 'close';
} else if (!proxyRes.headers.connection) {
proxyRes.headers.connection = req.headers.connection || 'keep-alive';
}
},
/**
* Copy headers from proxyResponse to response
* set each header in response object.
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {proxyResponse} Res Response object from the proxy request
*
* @api private
*/
function writeHeaders(req, res, proxyRes) {
Object.keys(proxyRes.headers).forEach(function(key) {
res.setHeader(key, proxyRes.headers[key]);
});
},
/**
* Set the statusCode from the proxyResponse
*
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {proxyResponse} Res Response object from the proxy request
*
* @api private
*/
function writeStatusCode(req, res, proxyRes) {
res.writeHead(proxyRes.statusCode);
}
] // <--
.forEach(function(func) {
passes[func.name] = func;
});

View File

@ -0,0 +1,121 @@
var http = require('http'),
https = require('https'),
common = require('../common'),
passes = exports;
/*!
* Array of passes.
*
* A `pass` is just a function that is executed on `req, socket, options`
* so that you can easily add new checks while still keeping the base
* flexible.
*/
/*
* Websockets Passes
*
*/
var passes = exports;
[
/**
* WebSocket requests must have the `GET` method and
* the `upgrade:websocket` header
*
* @param {ClientRequest} Req Request object
* @param {Socket} Websocket
*
* @api private
*/
function checkMethodAndHeader (req, socket) {
if (req.method !== 'GET' || !req.headers.upgrade) {
socket.destroy();
return true;
}
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
socket.destroy();
return true;
}
},
/**
* Sets `x-forwarded-*` headers if specified in config.
*
* @param {ClientRequest} Req Request object
* @param {Socket} Websocket
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function XHeaders(req, socket, options) {
if(!options.xfwd) return;
var values = {
for : req.connection.remoteAddress || req.socket.remoteAddress,
port : common.getPort(req),
proto: req.connection.pair ? 'wss' : 'ws'
};
['for', 'port', 'proto'].forEach(function(header) {
req.headers['x-forwarded-' + header] =
(req.headers['x-forwarded-' + header] || '') +
(req.headers['x-forwarded-' + header] ? ',' : '') +
values[header];
});
},
/**
* Does the actual proxying. Make the request and upgrade it
* send the Switching Protocols request and pipe the sockets.
*
* @param {ClientRequest} Req Request object
* @param {Socket} Websocket
* @param {Object} Options Config object passed to the proxy
*
* @api private
*/
function stream(req, socket, options, server, head, clb) {
common.setupSocket(socket);
if (head && head.length) socket.unshift(head);
var proxyReq = (~['https:', 'wss:'].indexOf(options.target.protocol) ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req)
);
// Error Handler
proxyReq.on('error', onError);
proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
proxySocket.on('error', onError);
common.setupSocket(proxySocket);
if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead);
socket.write('HTTP/1.1 101 Switching Protocols\r\n');
socket.write(Object.keys(proxyRes.headers).map(function(i) {
return i + ": " + proxyRes.headers[i];
}).join('\r\n') + '\r\n\r\n');
proxySocket.pipe(socket).pipe(proxySocket);
});
return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
function onError(err) {
if (clb) {
clb(err);
} else {
server.emit('error', err, req, socket);
}
}
}
] // <--
.forEach(function(func) {
passes[func.name] = func;
});

40
package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name" : "http-proxy",
"version" : "1.0.0",
"description" : "HTTP proxying for the masses",
"author": "Nodejitsu Inc. <info@nodejitsu.com>",
"maintainers" : [
"yawnt <yawnt@nodejitsu.com>",
"indexzero <charlie@nodejitsu.com>"
],
"main" : "index.js",
"dependencies" : {
"eventemitter3" : "*"
},
"devDependencies": {
"mocha" : "*",
"expect.js" : "*",
"dox" : "*",
"coveralls" : "*",
"mocha-lcov-reporter": "*",
"blanket" : "*",
"ws" : "*",
"socket.io" : "*",
"socket.io-client" : "*",
"async" : "*"
},
"scripts" : {
"coveralls" : "mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js",
"blanket" : { "pattern": "lib/http-proxy" },
"test" : "mocha -R landing test/*-test.js",
"test-cov" : "mocha --require blanket -R html-cov > cov/coverage.html"
},
"engines" : {
"node" : ">=0.10.0"
},
"license" : "MIT"
}

71
test/examples-test.js Normal file
View File

@ -0,0 +1,71 @@
/*
examples-test.js: Test to run all the examples
Copyright (c) Nodejitsu 2013
*/
var path = require('path'),
fs = require('fs'),
spawn = require('child_process').spawn,
expect = require('expect.js'),
async = require('async');
var rootDir = path.join(__dirname, '..'),
examplesDir = path.join(rootDir, 'examples');
describe.skip('http-proxy examples', function () {
describe('Before testing examples', function () {
// Set a timeout to avoid this error
this.timeout(30 * 1000);
it('should have installed dependencies', function (done) {
async.waterfall([
//
// 1. Read files in examples dir
//
async.apply(fs.readdir, examplesDir),
//
// 2. If node_modules exists, continue. Otherwise
// exec `npm` to install them
//
function checkNodeModules(files, next) {
if (files.indexOf('node_modules') !== -1) {
return next();
}
console.log('Warning: installing dependencies, this operation could take a while');
var child = spawn('npm', ['install', '-f'], {
cwd: examplesDir
});
child.on('exit', function (code) {
return code
? next(new Error('npm install exited with non-zero exit code'))
: next();
});
},
//
// 3. Read files in examples dir again to ensure the install
// worked as expected.
//
async.apply(fs.readdir, examplesDir),
], done);
})
});
describe('Requiring all the examples', function () {
it('should have no errors', function (done) {
async.each(['balancer', 'http', 'middleware', 'websocket'], function (dir, cb) {
var name = 'examples/' + dir,
files = fs.readdirSync(path.join(rootDir, 'examples', dir));
async.each(files, function (file, callback) {
var example;
expect(function () { example = require(path.join(examplesDir, dir, file)); }).to.not.throwException();
expect(example).to.be.an('object');
callback();
}, cb);
}, done);
})
})
})

13
test/fixtures/agent2-cert.pem vendored Normal file
View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV
UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR
cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy
WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD
VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg
MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF
AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC
WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA
C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9
1LHwrmh29rK8kBPEjmymCQ==
-----END CERTIFICATE-----

10
test/fixtures/agent2-csr.pem vendored Normal file
View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH
EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD
EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ
KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf
+6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG
SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB
AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm
U3J9q9MDUf0+Y2+EGkssFfk=
-----END CERTIFICATE REQUEST-----

9
test/fixtures/agent2-key.pem vendored Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5
QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH
9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p
OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf
WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb
AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa
cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I
-----END RSA PRIVATE KEY-----

19
test/fixtures/agent2.cnf vendored Normal file
View File

@ -0,0 +1,19 @@
[ req ]
default_bits = 1024
days = 999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = US
ST = CA
L = SF
O = Joyent
OU = Node.js
CN = agent2
emailAddress = ry@tinyclouds.org
[ req_attributes ]
challengePassword = A challenge password

View File

@ -0,0 +1,103 @@
var common = require('../lib/http-proxy/common'),
expect = require('expect.js');
describe('lib/http-proxy/common.js', function () {
describe('#setupOutgoing', function () {
it('should setup the correct headers', function () {
var outgoing = {};
common.setupOutgoing(outgoing,
{
agent : '?',
target: {
host : 'hey',
hostname : 'how',
socketPath: 'are',
port : 'you',
},
headers: {'fizz': 'bang', 'overwritten':true},
},
{
method : 'i',
url : 'am',
headers : {'pro':'xy','overwritten':false}
});
expect(outgoing.host).to.eql('hey');
expect(outgoing.hostname).to.eql('how');
expect(outgoing.socketPath).to.eql('are');
expect(outgoing.port).to.eql('you');
expect(outgoing.agent).to.eql('?');
expect(outgoing.method).to.eql('i');
expect(outgoing.path).to.eql('am');
expect(outgoing.headers.pro).to.eql('xy');
expect(outgoing.headers.fizz).to.eql('bang');
expect(outgoing.headers.overwritten).to.eql(true);
});
it('should set the agent to false if none is given', function () {
var outgoing = {};
common.setupOutgoing(outgoing, {target:
'http://localhost'
}, { url: '/' });
expect(outgoing.agent).to.eql(false);
});
it('set the port according to the protocol', function () {
var outgoing = {};
common.setupOutgoing(outgoing,
{
agent : '?',
target: {
host : 'how',
hostname : 'are',
socketPath: 'you',
protocol: 'https:'
}
},
{
method : 'i',
url : 'am',
headers : 'proxy'
});
expect(outgoing.host).to.eql('how');
expect(outgoing.hostname).to.eql('are');
expect(outgoing.socketPath).to.eql('you');
expect(outgoing.agent).to.eql('?');
expect(outgoing.method).to.eql('i');
expect(outgoing.path).to.eql('am');
expect(outgoing.headers).to.eql('proxy')
expect(outgoing.port).to.eql(443);
});
});
describe('#setupSocket', function () {
it('should setup a socket', function () {
var socketConfig = {
timeout: null,
nodelay: false,
keepalive: false
},
stubSocket = {
setTimeout: function (num) {
socketConfig.timeout = num;
},
setNoDelay: function (bol) {
socketConfig.nodelay = bol;
},
setKeepAlive: function (bol) {
socketConfig.keepalive = bol;
}
}
returnValue = common.setupSocket(stubSocket);
expect(socketConfig.timeout).to.eql(0);
expect(socketConfig.nodelay).to.eql(true);
expect(socketConfig.keepalive).to.eql(true);
});
});
});

View File

@ -0,0 +1,130 @@
var webPasses = require('../lib/http-proxy/passes/web-incoming'),
httpProxy = require('../lib/http-proxy'),
expect = require('expect.js'),
http = require('http');
describe('lib/http-proxy/passes/web.js', function() {
describe('#deleteLength', function() {
it('should change `content-length`', function() {
var stubRequest = {
method: 'DELETE',
headers: {}
};
webPasses.deleteLength(stubRequest, {}, {});
expect(stubRequest.headers['content-length']).to.eql('0');
})
});
describe('#timeout', function() {
it('should set timeout on the socket', function() {
var done = false, stubRequest = {
socket: {
setTimeout: function(value) { done = value; }
}
}
webPasses.timeout(stubRequest, {}, { timeout: 5000});
expect(done).to.eql(5000);
});
});
describe('#XHeaders', function () {
var stubRequest = {
connection: {
remoteAddress: '192.168.1.2',
remotePort: '8080'
},
headers: {
host: '192.168.1.2:8080'
}
}
it('set the correct x-forwarded-* headers', function () {
webPasses.XHeaders(stubRequest, {}, { xfwd: true });
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2');
expect(stubRequest.headers['x-forwarded-port']).to.be('8080');
expect(stubRequest.headers['x-forwarded-proto']).to.be('http');
});
});
});
describe('#createProxyServer.web() using own http server', function () {
it('should proxy the request using the web proxy handler', function (done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:8080'
});
function requestHandler(req, res) {
proxy.web(req, res);
}
var proxyServer = http.createServer(requestHandler);
var source = http.createServer(function(req, res) {
source.close();
proxyServer.close();
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql('8081');
done();
});
proxyServer.listen('8081');
source.listen('8080');
http.request('http://127.0.0.1:8081', function() {}).end();
});
it('should proxy the request and handle error via callback', function(done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:8080'
});
var proxyServer = http.createServer(requestHandler);
function requestHandler(req, res) {
proxy.web(req, res, function (err) {
proxyServer.close();
expect(err).to.be.an(Error);
expect(err.code).to.be('ECONNREFUSED');
done();
});
}
proxyServer.listen('8082');
http.request({
hostname: '127.0.0.1',
port: '8082',
method: 'GET',
}, function() {}).end();
});
it('should proxy the request and handle error via event listener', function(done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:8080'
});
var proxyServer = http.createServer(requestHandler);
function requestHandler(req, res) {
proxy.once('error', function (err, errReq, errRes) {
proxyServer.close();
expect(err).to.be.an(Error);
expect(errReq).to.be.equal(req);
expect(errRes).to.be.equal(res);
expect(err.code).to.be('ECONNREFUSED');
done();
});
proxy.web(req, res);
}
proxyServer.listen('8083');
http.request({
hostname: '127.0.0.1',
port: '8083',
method: 'GET',
}, function() {}).end();
});
});

View File

@ -0,0 +1,104 @@
var httpProxy = require('../lib/http-proxy/passes/web-outgoing'),
expect = require('expect.js');
describe('lib/http-proxy/passes/web-outgoing.js', function () {
describe('#setConnection', function () {
it('set the right connection with 1.0 - `close`', function() {
var proxyRes = { headers: {} };
httpProxy.setConnection({
httpVersion: '1.0',
headers: {
connection: null
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('close');
});
it('set the right connection with 1.0 - req.connection', function() {
var proxyRes = { headers: {} };
httpProxy.setConnection({
httpVersion: '1.0',
headers: {
connection: 'hey'
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('hey');
});
it('set the right connection - req.connection', function() {
var proxyRes = { headers: {} };
httpProxy.setConnection({
httpVersion: null,
headers: {
connection: 'hola'
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('hola');
});
it('set the right connection - `keep-alive`', function() {
var proxyRes = { headers: {} };
httpProxy.setConnection({
httpVersion: null,
headers: {
connection: null
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('keep-alive');
});
});
describe('#writeStatusCode', function () {
it('should write status code', function() {
var res = {
writeHead: function(n) {
expect(n).to.eql(200);
}
}
httpProxy.writeStatusCode({}, res, { statusCode: 200 });
});
});
describe('#writeHeaders', function() {
var proxyRes = {
headers: {
hey: 'hello',
how: 'are you?'
}
};
var res = {
setHeader: function(k, v) {
this.headers[k] = v;
},
headers: {}
};
httpProxy.writeHeaders({}, res, proxyRes);
expect(res.headers.hey).to.eql('hello');
expect(res.headers.how).to.eql('are you?');
});
describe('#removeChunked', function() {
var proxyRes = {
headers: {
'transfer-encoding': 'hello'
}
};
httpProxy.removeChunked({ httpVersion: '1.0' }, {}, proxyRes);
expect(proxyRes.headers['transfer-encoding']).to.eql(undefined);
});
});

View File

@ -0,0 +1,120 @@
var httpProxy = require('../lib/http-proxy/passes/ws-incoming'),
expect = require('expect.js');
describe('lib/http-proxy/passes/ws-incoming.js', function () {
describe('#checkMethodAndHeader', function () {
it('should drop non-GET connections', function () {
var destroyCalled = false,
stubRequest = {
method: 'DELETE',
headers: {}
},
stubSocket = {
destroy: function () {
// Simulate Socket.destroy() method when call
destroyCalled = true;
}
}
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
expect(returnValue).to.be(true);
expect(destroyCalled).to.be(true);
})
it('should drop connections when no upgrade header', function () {
var destroyCalled = false,
stubRequest = {
method: 'GET',
headers: {}
},
stubSocket = {
destroy: function () {
// Simulate Socket.destroy() method when call
destroyCalled = true;
}
}
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
expect(returnValue).to.be(true);
expect(destroyCalled).to.be(true);
})
it('should drop connections when upgrade header is different of `websocket`', function () {
var destroyCalled = false,
stubRequest = {
method: 'GET',
headers: {
upgrade: 'anotherprotocol'
}
},
stubSocket = {
destroy: function () {
// Simulate Socket.destroy() method when call
destroyCalled = true;
}
}
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
expect(returnValue).to.be(true);
expect(destroyCalled).to.be(true);
})
it('should return nothing when all is ok', function () {
var destroyCalled = false,
stubRequest = {
method: 'GET',
headers: {
upgrade: 'websocket'
}
},
stubSocket = {
destroy: function () {
// Simulate Socket.destroy() method when call
destroyCalled = true;
}
}
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
expect(returnValue).to.be(undefined);
expect(destroyCalled).to.be(false);
})
});
describe('#XHeaders', function () {
it('return if no forward request', function () {
var returnValue = httpProxy.XHeaders({}, {}, {});
expect(returnValue).to.be(undefined);
});
it('set the correct x-forwarded-* headers from req.connection', function () {
var stubRequest = {
connection: {
remoteAddress: '192.168.1.2',
remotePort: '8080'
},
headers: {
host: '192.168.1.2:8080'
}
}
httpProxy.XHeaders(stubRequest, {}, { xfwd: true });
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2');
expect(stubRequest.headers['x-forwarded-port']).to.be('8080');
expect(stubRequest.headers['x-forwarded-proto']).to.be('ws');
});
it('set the correct x-forwarded-* headers from req.socket', function () {
var stubRequest = {
socket: {
remoteAddress: '192.168.1.3',
remotePort: '8181'
},
connection: {
pair: true
},
headers: {
host: '192.168.1.3:8181'
}
};
httpProxy.XHeaders(stubRequest, {}, { xfwd: true });
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3');
expect(stubRequest.headers['x-forwarded-port']).to.be('8181');
expect(stubRequest.headers['x-forwarded-proto']).to.be('wss');
});
});
});

297
test/lib-http-proxy-test.js Normal file
View File

@ -0,0 +1,297 @@
var httpProxy = require('../lib/http-proxy'),
expect = require('expect.js'),
http = require('http'),
ws = require('ws')
io = require('socket.io'),
ioClient = require('socket.io-client');
//
// Expose a port number generator.
// thanks to @3rd-Eden
//
var initialPort = 1024, gen = {};
Object.defineProperty(gen, 'port', {
get: function get() {
return initialPort++;
}
});
describe('lib/http-proxy.js', function() {
describe('#createProxyServer', function() {
it.skip('should throw without options', function() {
var error;
try {
httpProxy.createProxyServer();
} catch(e) {
error = e;
}
expect(error).to.be.an(Error);
})
it('should return an object otherwise', function() {
var obj = httpProxy.createProxyServer({
target: 'http://www.google.com:80'
});
expect(obj.web).to.be.a(Function);
expect(obj.ws).to.be.a(Function);
expect(obj.listen).to.be.a(Function);
});
});
describe('#createProxyServer with forward options and using web-incoming passes', function () {
it('should pipe the request using web-incoming#stream method', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
forward: 'http://127.0.0.1:' + ports.source
}).listen(ports.proxy);
var source = http.createServer(function(req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
source.close();
proxy._server.close();
done();
});
source.listen(ports.source);
http.request('http://127.0.0.1:' + ports.proxy, function() {}).end();
})
});
describe('#createProxyServer using the web-incoming passes', function () {
it('should make the request on pipe and finish it', function(done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:' + ports.source
}).listen(ports.proxy);
var source = http.createServer(function(req, res) {
expect(req.method).to.eql('POST');
expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
source.close();
proxy._server.close();
done();
});
source.listen(ports.source);
http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'POST',
headers: {
'x-forwarded-for': '127.0.0.1'
}
}, function() {}).end();
});
});
describe('#createProxyServer using the web-incoming passes', function () {
it('should make the request, handle response and finish it', function(done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:' + ports.source
}).listen(ports.proxy);
var source = http.createServer(function(req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end('Hello from ' + source.address().port);
});
source.listen(ports.source);
http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'GET'
}, function(res) {
expect(res.statusCode).to.eql(200);
res.on('data', function (data) {
expect(data.toString()).to.eql('Hello from ' + ports.source);
});
res.on('end', function () {
source.close();
proxy._server.close();
done();
});
}).end();
});
});
describe('#createProxyServer() method with error response', function () {
it('should make the request and emit the error event', function(done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:' + ports.source
});
proxy.on('error', function (err) {
expect(err).to.be.an(Error);
expect(err.code).to.be('ECONNREFUSED');
proxy._server.close();
done();
})
proxy.listen(ports.proxy);
http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'GET',
}, function() {}).end();
});
});
describe('#createProxyServer setting the correct timeout value', function () {
it('should hang up the socket at the timeout', function (done) {
this.timeout(30);
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:' + ports.source,
timeout: 3
}).listen(ports.proxy);
var source = http.createServer(function(req, res) {
setTimeout(function () {
res.end('At this point the socket should be closed');
}, 5)
});
source.listen(ports.source);
var testReq = http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'GET',
}, function() {});
testReq.on('error', function (e) {
expect(e).to.be.an(Error);
expect(e.code).to.be.eql('ECONNRESET');
proxy._server.close();
source.close();
done();
});
testReq.end();
})
})
// describe('#createProxyServer using the web-incoming passes', function () {
// it('should emit events correclty', function(done) {
// var proxy = httpProxy.createProxyServer({
// target: 'http://127.0.0.1:8080'
// }),
// proxyServer = proxy.listen('8081'),
// source = http.createServer(function(req, res) {
// expect(req.method).to.eql('GET');
// expect(req.headers.host.split(':')[1]).to.eql('8081');
// res.writeHead(200, {'Content-Type': 'text/plain'})
// res.end('Hello from ' + source.address().port);
// }),
// events = [];
// source.listen('8080');
// proxy.ee.on('http-proxy:**', function (uno, dos, tres) {
// events.push(this.event);
// })
// http.request({
// hostname: '127.0.0.1',
// port: '8081',
// method: 'GET',
// }, function(res) {
// expect(res.statusCode).to.eql(200);
// res.on('data', function (data) {
// expect(data.toString()).to.eql('Hello from 8080');
// });
// res.on('end', function () {
// expect(events).to.contain('http-proxy:outgoing:web:begin');
// expect(events).to.contain('http-proxy:outgoing:web:end');
// source.close();
// proxyServer._server.close();
// done();
// });
// }).end();
// });
// });
describe('#createProxyServer using the ws-incoming passes', function () {
it('should proxy the websockets stream', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'ws://127.0.0.1:' + ports.source,
ws: true
}),
proxyServer = proxy.listen(ports.proxy),
destiny = new ws.Server({ port: ports.source }, function () {
var client = new ws('ws://127.0.0.1:' + ports.proxy);
client.on('open', function () {
client.send('hello there');
});
client.on('message', function (msg) {
expect(msg).to.be('Hello over websockets');
client.close();
proxyServer._server.close();
destiny.close();
done();
});
});
destiny.on('connection', function (socket) {
socket.on('message', function (msg) {
expect(msg).to.be('hello there');
socket.send('Hello over websockets');
});
});
});
});
describe('#createProxyServer using the ws-incoming passes', function () {
it('should proxy a socket.io stream', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var proxy = httpProxy.createProxyServer({
target: 'ws://127.0.0.1:' + ports.source,
ws: true
}),
proxyServer = proxy.listen(ports.proxy),
destiny = io.listen(ports.source, function () {
var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy);
client.on('connect', function () {
client.emit('incoming', 'hello there');
});
client.on('outgoing', function (data) {
expect(data).to.be('Hello over websockets');
proxyServer._server.close();
destiny.server.close();
done();
});
});
destiny.sockets.on('connection', function (socket) {
socket.on('incoming', function (msg) {
expect(msg).to.be('hello there');
socket.emit('outgoing', 'Hello over websockets');
});
})
});
})
});

View File

@ -0,0 +1,222 @@
var httpProxy = require('../lib/http-proxy'),
expect = require('expect.js'),
http = require('http')
https = require('https'),
path = require('path'),
fs = require('fs');
//
// Expose a port number generator.
// thanks to @3rd-Eden
//
var initialPort = 1024, gen = {};
Object.defineProperty(gen, 'port', {
get: function get() {
return initialPort++;
}
});
describe('lib/http-proxy.js', function() {
describe('HTTPS #createProxyServer', function() {
describe('HTTPS to HTTP', function () {
it('should proxy the request en send back the response', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var source = http.createServer(function(req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from ' + ports.source);
});
source.listen(ports.source);
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:' + ports.source,
ssl: {
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
}
}).listen(ports.proxy);
https.request({
host: 'localhost',
port: ports.proxy,
path: '/',
method: 'GET',
rejectUnauthorized: false
}, function(res) {
expect(res.statusCode).to.eql(200);
res.on('data', function (data) {
expect(data.toString()).to.eql('Hello from ' + ports.source);
});
res.on('end', function () {
source.close();
proxy._server.close();
done();
})
}).end();
})
});
describe('HTTP to HTTPS', function () {
it('should proxy the request en send back the response', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var source = https.createServer({
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
}, function (req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from ' + ports.source);
});
source.listen(ports.source);
var proxy = httpProxy.createProxyServer({
target: 'https://127.0.0.1:' + ports.source,
// Allow to use SSL self signed
secure: false
}).listen(ports.proxy);
http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'GET'
}, function(res) {
expect(res.statusCode).to.eql(200);
res.on('data', function (data) {
expect(data.toString()).to.eql('Hello from ' + ports.source);
});
res.on('end', function () {
source.close();
proxy._server.close();
done();
});
}).end();
})
})
describe('HTTPS to HTTPS', function () {
it('should proxy the request en send back the response', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var source = https.createServer({
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
}, function(req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from ' + ports.source);
});
source.listen(ports.source);
var proxy = httpProxy.createProxyServer({
target: 'https://127.0.0.1:' + ports.source,
ssl: {
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
},
secure: false
}).listen(ports.proxy);
https.request({
host: 'localhost',
port: ports.proxy,
path: '/',
method: 'GET',
rejectUnauthorized: false
}, function(res) {
expect(res.statusCode).to.eql(200);
res.on('data', function (data) {
expect(data.toString()).to.eql('Hello from ' + ports.source);
});
res.on('end', function () {
source.close();
proxy._server.close();
done();
})
}).end();
})
});
describe('HTTPS not allow SSL self signed', function () {
it('should fail with error', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var source = https.createServer({
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
}).listen(ports.source);
var proxy = httpProxy.createProxyServer({
target: 'https://127.0.0.1:' + ports.source,
secure: true
});
proxy.listen(ports.proxy);
proxy.on('error', function (err, req, res) {
expect(err).to.be.an(Error);
expect(err.toString()).to.be('Error: DEPTH_ZERO_SELF_SIGNED_CERT')
done();
})
http.request({
hostname: '127.0.0.1',
port: ports.proxy,
method: 'GET'
}).end();
})
})
describe('HTTPS to HTTP using own server', function () {
it('should proxy the request en send back the response', function (done) {
var ports = { source: gen.port, proxy: gen.port };
var source = http.createServer(function(req, res) {
expect(req.method).to.eql('GET');
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from ' + ports.source);
});
source.listen(ports.source);
var proxy = httpProxy.createServer({
agent: new http.Agent({ maxSockets: 2 })
});
var ownServer = https.createServer({
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
}, function (req, res) {
proxy.web(req, res, {
target: 'http://127.0.0.1:' + ports.source
})
}).listen(ports.proxy);
https.request({
host: 'localhost',
port: ports.proxy,
path: '/',
method: 'GET',
rejectUnauthorized: false
}, function(res) {
expect(res.statusCode).to.eql(200);
res.on('data', function (data) {
expect(data.toString()).to.eql('Hello from ' + ports.source);
});
res.on('end', function () {
source.close();
ownServer.close();
done();
})
}).end();
})
})
});
});