Skip to content

Commit

Permalink
[test doc api] Added forward proxy functionality with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Nov 22, 2010
1 parent bedc7a3 commit c06f4bf
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 142 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- reverse-proxies incoming http.Server requests
- can be used as a CommonJS module in node.js
- uses event buffering to support application latency in proxied requests
- can proxy based on simple JSON-based configuration
- forward proxying based on simple JSON-based configuration
- minimal request overhead and latency
- fully-tested
- battled-hardened through production usage @ [nodejitsu.com][0]
Expand All @@ -35,8 +37,9 @@ There are several ways to use node-http-proxy; the library is designed to be fle

1. Standalone HTTP Proxy server
2. Inside of another HTTP server (like Connect)
3. From the command-line as a proxy daemon
4. In conjunction with a Proxy Routing Table
3. In conjunction with a Proxy Routing Table
4. As a forward-proxy with a reverse proxy
5. From the command-line as a proxy daemon

### Setup a basic stand-alone proxy server
<pre>
Expand Down Expand Up @@ -132,8 +135,23 @@ The above route table will take incoming requests to 'foo.com' and forward them
<pre>
var proxyServer = httpProxy.createServer(options);
proxyServer.listen(80);
</pre>

### Proxy requests with an additional forward proxy
Sometimes in addition to a reverse proxy, you may want your front-facing server to forward traffic to another location. For example, if you wanted to load test your staging environment. This is possible when using node-http-proxy using similar JSON-based configuration to a proxy table:
<pre>
var proxyServerWithForwarding = httpProxy.createServer(9000, 'localhost', {
forward: {
port: 9000,
host: 'staging.com'
}
});
proxyServerWithForwarding.listen(80);
</pre>

The forwarding option can be used in conjunction with the proxy table options by simply including both the 'forward' and 'router' properties in the options passed to 'createServer'.

<br/>
### Why doesn't node-http-proxy have more advanced features like x, y, or z?

If you have a suggestion for a feature currently not supported, feel free to open a [support issue](http://github.com/nodejitsu/node-http-proxy/issues). node-http-proxy is designed to just proxy http requests from one server to another, but we will be soon releasing many other complimentary projects that can be used in conjunction with node-http-proxy.
Expand Down
26 changes: 24 additions & 2 deletions demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ httpProxy.createServer(function (req, res, proxy) {
}).listen(8002);
sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with latency'.magenta.underline);

//
//
//
httpProxy.createServer(9000, 'localhost', {
forward: {
port: 9001,
host: 'localhost'
}
}).listen(8003);
sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8003 '.yellow + 'with forward proxy'.magenta.underline)

//
// Http Server with proxyRequest Handler and Latency
//
Expand All @@ -75,8 +86,8 @@ http.createServer(function (req, res) {
setTimeout(function() {
proxy.proxyRequest(9000, 'localhost');
}, 200);
}).listen(8003);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8003 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);
}).listen(8004);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8004 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);

//
// Target Http Server
Expand All @@ -87,3 +98,14 @@ http.createServer(function (req, res) {
res.end();
}).listen(9000);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);

//
// Target Http Forwarding Server
//
http.createServer(function (req, res) {
sys.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(9001);
sys.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow);
49 changes: 38 additions & 11 deletions lib/node-http-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ exports.createServer = function () {
var server = http.createServer(function (req, res) {
var proxy = new HttpProxy(req, res);

if (options && options.forward) {
var forward = new HttpProxy(req, res);
forward.forwardRequest(options.forward.port, options.forward.host);
}

// If we were passed a callback to process the request
// or response in some way, then call it.
if (callback) {
Expand Down Expand Up @@ -154,10 +159,8 @@ HttpProxy.prototype = {
},

proxyRequest: function (port, server) {
// Remark: nodeProxy.body exists solely for testability
var self = this, req = this.req, res = this.res;
self.body = '';


// Open new HTTP request to internal resource with will act as a reverse proxy pass
var p = manager.getPool(port, server);

Expand All @@ -167,7 +170,6 @@ HttpProxy.prototype = {
// should be emitting this event.
});

var client = http.createClient(port, server);
p.request(req.method, req.url, req.headers, function (reverse_proxy) {
// Create an error handler so we can use it temporarily
function error (obj) {
Expand All @@ -187,11 +189,8 @@ HttpProxy.prototype = {
};

// Add a listener for the connection timeout event
var reverseProxyError = error(reverse_proxy),
clientError = error(client);

var reverseProxyError = error(reverse_proxy);
reverse_proxy.addListener('error', reverseProxyError);
client.addListener('error', clientError);

// Add a listener for the reverse_proxy response event
reverse_proxy.addListener('response', function (response) {
Expand Down Expand Up @@ -235,9 +234,37 @@ HttpProxy.prototype = {
reverse_proxy.end();
});

// On 'close' event remove 'error' listener
client.addListener('close', function() {
client.removeListener('error', clientError);
self.unwatch(req);
});
},

forwardRequest: function (port, server) {
var self = this, req = this.req;

// Open new HTTP request to internal resource with will act as a reverse proxy pass
var p = manager.getPool(port, server);

p.on('error', function (err) {
// Remark: We should probably do something here
// but this is a hot-fix because I don't think 'pool'
// should be emitting this event.
});

p.request(req.method, req.url, req.headers, function (forward_proxy) {
// Add a listener for the connection timeout event
forward_proxy.addListener('error', function (err) {
// Remark: Ignoring this error in the event
// forward target doesn't exist.
});

// Chunk the client request body as chunks from the proxied request come in
req.addListener('data', function (chunk) {
forward_proxy.write(chunk, 'binary');
})

// At the end of the client request, we are going to stop the proxied request
req.addListener('end', function () {
forward_proxy.end();
});

self.unwatch(req);
Expand Down
61 changes: 61 additions & 0 deletions test/forward-proxy-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* forward-proxy-test.js: Tests for node-http-proxy forwarding functionality.
*
* (C) 2010, Charlie Robbins
*
*/

var fs = require('fs'),
vows = require('vows'),
sys = require('sys'),
path = require('path'),
request = require('request'),
assert = require('assert'),
helpers = require('./helpers'),
TestRunner = helpers.TestRunner;

var runner = new TestRunner(),
assertProxiedWithTarget = helpers.assertProxiedWithTarget,
assertProxiedWithNoTarget = helpers.assertProxiedWithNoTarget;

var forwardOptions = {
forward: {
port: 8300,
host: 'localhost'
}
};

var badForwardOptions = {
forward: {
port: 9000,
host: 'localhost'
}
};

vows.describe('node-http-proxy').addBatch({
"When using server created by httpProxy.createServer()": {
"with forwarding enabled": {
topic: function () {
runner.startTargetServer(8300, 'forward proxy');
return null;
},
"with no latency" : {
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8120, 8121, function () {
runner.startProxyServerWithForwarding(8120, 8121, 'localhost', forwardOptions);
}),
"and without a valid forward server": assertProxiedWithTarget(runner, 'localhost', 8122, 8123, function () {
runner.startProxyServerWithForwarding(8122, 8123, 'localhost', badForwardOptions);
})
}
}
}
}).addBatch({
"When the tests are over": {
topic: function () {
return runner.closeServers();
},
"the servers should clean up": function () {
assert.isTrue(true);
}
}
}).export(module);
65 changes: 65 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,63 @@
*/

var http = require('http'),
vows = require('vows'),
assert = require('assert'),
request = require('request'),
httpProxy = require('./../lib/node-http-proxy');

exports.assertProxiedWithTarget = function (runner, host, proxyPort, port, createProxy) {
var assertion = "should receive 'hello " + host + "'",
output = 'hello ' + host;

var test = {
topic: function () {
var options = {
method: 'GET',
uri: 'http://localhost:' + proxyPort,
headers: {
host: host
}
};

if (createProxy) createProxy();
if (port) runner.startTargetServer(port, output);
request(options, this.callback);
}
};

test[assertion] = function (err, res, body) {
assert.equal(body, output);
};

return test;
};

exports.assertProxiedWithNoTarget = function (runner, proxyPort, statusCode, createProxy) {
var assertion = "should receive " + statusCode + " responseCode";

var test = {
topic: function () {
var options = {
method: 'GET',
uri: 'http://localhost:' + proxyPort,
headers: {
host: 'unknown.com'
}
};

if (createProxy) createProxy();
request(options, this.callback);
}
};

test[assertion] = function (err, res, body) {
assert.equal(res.statusCode, statusCode);
};

return test;
}

var TestRunner = function () {
this.testServers = [];
}
Expand Down Expand Up @@ -70,6 +125,16 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
return proxyServer;
};

//
// Creates proxy server forwarding to the specified options
//
TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort, host, options) {
var proxyServer = httpProxy.createServer(targetPort, host, options);
proxyServer.listen(port);
this.testServers.push(proxyServer);
return proxyServer;
};

//
// Creates the 'hellonode' server
//
Expand Down
Loading

0 comments on commit c06f4bf

Please sign in to comment.