Commit fd618289 authored by indexzero's avatar indexzero

[api dist] Merge of branch 0.2.0

parents 6661753f eb39018f
# node-http-proxy - v0.1.5 # node-http-proxy - v0.2.0
<img src = "http://i.imgur.com/dSSUX.png"/> <img src = "http://i.imgur.com/dSSUX.png"/>
...@@ -52,9 +52,9 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js) ...@@ -52,9 +52,9 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
httpProxy = require('http-proxy'); httpProxy = require('http-proxy');
// create a proxy server with custom application logic // create a proxy server with custom application logic
httpProxy.createServer(function (req, res, proxy) { httpProxy.createServer(function (req, res, proxyRequest) {
// Put your custom server logic here // Put your custom server logic here
proxy.proxyRequest(9000, 'localhost', req, res); proxyRequest(9000, 'localhost');
}).listen(8000); }).listen(8000);
http.createServer(function (req, res){ http.createServer(function (req, res){
...@@ -65,37 +65,28 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js) ...@@ -65,37 +65,28 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
</pre> </pre>
### How to proxy requests with a regular http server ### How to proxy requests with latent operations (IO, etc.)
<pre>
var http = require('http'),
httpProxy = require('http-proxy');
// create a regular http server and proxy its handler
http.createServer(function (req, res){
var proxy = new httpProxy.HttpProxy;
proxy.watch(req, res);
// Put your custom server logic here
proxy.proxyRequest(9000, 'localhost', req, res);
}).listen(8001);
http.createServer(function (req, res){ node-http-proxy supports event buffering, that means if an event (like 'data', or 'end') is raised by the incoming request before you have a chance to perform your custom server logic, those events will be captured and re-raised when you later proxy the request. Here's a simple example:
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('request successfully proxied: ' + req.url +'\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
<pre>
httpProxy.createServer(function (req, res, proxyRequest) {
setTimeout(function () {
proxyRequest(port, server);
}, latency);
}).listen(8081);
</pre> </pre>
### Why doesn't node-http-proxy have more advanced features like x, y, or z? ### 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. 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.
<br/><hr/> <br/>
### License ### License
(The MIT License) (The MIT License)
Copyright (c) 2010 Charlie Robbins & Marak Squires http://github.com/nodejitsu/ Copyright (c) 2010 Mikeal Rogers, Charlie Robbins & Marak Squires
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
var sys = require('sys'), var sys = require('sys'),
colors = require('colors') colors = require('colors')
http = require('http'), http = require('http'),
httpProxy = require('http-proxy'); httpProxy = require('./lib/node-http-proxy');
// ascii art from http://github.com/marak/asciimo // ascii art from http://github.com/marak/asciimo
var welcome = '\ var welcome = '\
...@@ -45,24 +45,13 @@ httpProxy.createServer(9000, 'localhost').listen(8000); ...@@ -45,24 +45,13 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
sys.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow); sys.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
/****** http proxy server with latency******/ /****** http proxy server with latency******/
httpProxy.createServer(function (req, res, proxy){ httpProxy.createServer(function (req, res, proxyRequest){
setTimeout(function(){ setTimeout(function(){
proxy.proxyRequest(9000, 'localhost', req, res); proxyRequest(9000, 'localhost', req, res);
}, 200) }, 2000)
}).listen(8001); }).listen(8001);
sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with latency'.magenta.underline ); sys.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with latency'.magenta.underline );
/****** http server with proxyRequest handler and latency******/
http.createServer(function (req, res){
var proxy = new httpProxy.HttpProxy;
proxy.watch(req, res);
setTimeout(function(){
proxy.proxyRequest(9000, 'localhost', req, res);
}, 200);
}).listen(8002);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);
/****** regular http server ******/ /****** regular http server ******/
http.createServer(function (req, res){ http.createServer(function (req, res){
res.writeHead(200, {'Content-Type': 'text/plain'}); res.writeHead(200, {'Content-Type': 'text/plain'});
......
/* /*
node-http-proxy.js: http proxy for node.js node-http-proxy.js: http proxy for node.js with pooling and event buffering
Copyright (c) 2010 Charlie Robbins & Marak Squires http://github.com/nodejitsu/node-http-proxy Copyright (c) 2010 Mikeal Rogers, Charlie Robbins
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
...@@ -26,170 +26,114 @@ ...@@ -26,170 +26,114 @@
var sys = require('sys'), var sys = require('sys'),
http = require('http'), http = require('http'),
events = require('events'); pool = require('pool'),
url = require('url'),
events = require('events'),
min = 0,
max = 100;
exports.HttpProxy = function () { // Setup the PoolManager
this.emitter = new(events.EventEmitter); var manager = pool.createPoolManager();
this.events = {}; manager.setMinClients(min);
this.listeners = {}; manager.setMaxClients(max);
this.collisions = {};
};
exports.createServer = function () { exports.createServer = function () {
// Initialize the nodeProxy to start proxying requests var args, action, port, host;
var proxy = new (exports.HttpProxy); args = Array.prototype.slice.call(arguments);
return proxy.createServer.apply(proxy, arguments); action = typeof args[args.length - 1] === 'function' && args.pop();
}; if (args[0]) port = args[0];
if (args[1]) host = args[1];
exports.HttpProxy.prototype = {
toArray: function (obj){ var proxy = createProxy();
var len = obj.length, proxy.on('route', function (req, res, callback) {
arr = new Array(len); var uri = url.parse(req.url);
for (var i = 0; i < len; ++i) { if (action) {
arr[i] = obj[i]; action(req, res, callback);
}
return arr;
},
createServer: function () {
var self = this,
server,
port,
callback;
if (typeof(arguments[0]) === "function") {
callback = arguments[0];
} }
else { else {
port = arguments[0]; port = port ? port : uri.port ? uri.port : 80;
server = arguments[1]; host = host ? host : uri.hostname;
} callback(port, host);
var proxyServer = http.createServer(function (req, res){
self.watch(req, res);
// If we were passed a callback to process the request
// or response in some way, then call it.
if(callback) {
callback(req, res, self);
}
else {
self.proxyRequest(port, server, req, res);
} }
}); });
return proxy;
};
exports.setMin = function (value) {
min = value;
manager.setMinClients(min);
};
return proxyServer; exports.setMax = function (value) {
}, max = value;
manager.setMaxClients(max);
}
watch: function (req, res) { var createProxy = function () {
var self = this; var server = http.createServer(function (req, res) {
var buffers = [],
b = function (chunk) { buffers.push(chunk) },
e = function () { e = false };
// Create a unique id for this request so req.on('data', b);
// we can reference it later. req.on('end', e);
var id = new Date().getTime().toString();
// If we get a request in the same tick, we need to server.emit('route', req, res, function (port, hostname) {
// append to the id so it stays unique. var p = manager.getPool(port, hostname);
if(typeof this.collisions[id] === 'undefined') {
this.collisions[id] = 0;
}
else {
this.collisions[id]++;
id += this.collisions[id];
}
req.id = id; p.request(req.method, req.url, req.headers, function (reverse_proxy) {
this.events[req.id] = []; var data = '';
reverse_proxy.on('error', function (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
this.listeners[req.id] = { if(req.method !== 'HEAD') {
onData: function () { res.write('An error has occurred: ' + sys.puts(JSON.stringify(err)));
self.events[req.id].push(['data'].concat(self.toArray(arguments)));
},
onEnd: function () {
self.events[req.id].push(['end'].concat(self.toArray(arguments)));
} }
};
req.addListener('data', this.listeners[req.id].onData); res.end();
req.addListener('end', this.listeners[req.id].onEnd); });
}, buffers.forEach(function (c) {
data += c;
reverse_proxy.write(c);
});
unwatch: function (req, res) { buffers = null;
req.removeListener('data', this.listeners[req.id].onData); req.removeListener('data', b);
req.removeListener('end', this.listeners[req.id].onEnd); sys.pump(req, reverse_proxy);
// Rebroadcast any events that have been buffered if (e) {
while(this.events[req.id].length > 0) { req.removeListener('end', e);
var args = this.events[req.id].shift(); req.addListener('end', function () { reverse_proxy.end() });
req.emit.apply(req, args);
} }
else {
// Remove the data from the event and listeners hashes reverse_proxy.end();
delete this.listeners[req.id];
delete this.events[req.id];
// If this request id is a base time, delete it
if (typeof this.collisions[req.id] !== 'undefined') {
delete this.collisions[req.id];
} }
},
proxyRequest: function (port, server, req, res) { // Add a listener for the reverse_proxy response event
// Remark: nodeProxy.body exists solely for testability reverse_proxy.addListener('response', function (response) {
this.body = ''; if (response.headers.connection) {
var self = this; if (req.headers.connection) response.headers.connection = req.headers.connection;
else response.headers.connection = 'close';
// Open new HTTP request to internal resource with will act as a reverse proxy pass
var c = http.createClient(port, server);
// Make request to internal server, passing along the method and headers
var reverse_proxy = c.request(req.method, req.url, req.headers);
// Add a listener for the connection timeout event
reverse_proxy.connection.addListener('error', function (err) {
res.writeHead(200, {'Content-Type': 'text/plain'});
if(req.method !== 'HEAD') {
res.write('An error has occurred: ' + sys.puts(JSON.stringify(err)));
} }
res.end(); // These two listeners are for testability and observation
// of what's passed back from the target server
response.addListener('data', function (chunk) {
data += chunk;
}); });
response.addListener('end', function() {
server.emit('proxy', null, data);
});
// Add a listener for the reverse_proxy response event
reverse_proxy.addListener('response', function (response) {
// Set the response headers of the client response // Set the response headers of the client response
res.writeHead(response.statusCode, response.headers); res.writeHead(response.statusCode, response.headers);
// Add event handler for the proxied response in chunks sys.pump(response, res);
response.addListener('data', function (chunk) {
if(req.method !== 'HEAD') {
res.write(chunk, 'binary');
self.body += chunk;
}
}); });
// Add event listener for end of proxied response
response.addListener('end', function () {
// Remark: Emit the end event for testability
self.emitter.emit('end', null, self.body);
res.end();
}); });
}); });
// Chunk the client request body as chunks from the proxied request come in
req.addListener('data', function (chunk) {
reverse_proxy.write(chunk, 'binary');
}) })
return server;
// At the end of the client request, we are going to stop the proxied request
req.addListener('end', function () {
reverse_proxy.end();
});
this.unwatch(req, res);
}
}; };
\ No newline at end of file
...@@ -29,14 +29,14 @@ var vows = require('vows'), ...@@ -29,14 +29,14 @@ var vows = require('vows'),
assert = require('assert'), assert = require('assert'),
http = require('http'); http = require('http');
var httpProxy = require('http-proxy'); var httpProxy = require('./../lib/node-http-proxy');
var testServers = {}; var testServers = {};
// //
// Creates the reverse proxy server // Creates the reverse proxy server
// //
var startProxyServer = function (port, server, proxy) { var startProxyServer = function (port, server) {
var proxyServer = proxy.createServer(port, server); var proxyServer = httpProxy.createServer(port, server);
proxyServer.listen(8080); proxyServer.listen(8080);
return proxyServer; return proxyServer;
}; };
...@@ -44,11 +44,11 @@ var startProxyServer = function (port, server, proxy) { ...@@ -44,11 +44,11 @@ var startProxyServer = function (port, server, proxy) {
// //
// Creates the reverse proxy server with a specified latency // Creates the reverse proxy server with a specified latency
// //
var startLatentProxyServer = function (port, server, proxy, latency) { var startLatentProxyServer = function (port, server, latency) {
// Initialize the nodeProxy and start proxying the request // Initialize the nodeProxy and start proxying the request
var proxyServer = proxy.createServer(function (req, res, proxy) { var proxyServer = httpProxy.createServer(function (req, res, proxy) {
setTimeout(function () { setTimeout(function () {
proxy.proxyRequest(port, server, req, res); proxy(port, server);
}, latency); }, latency);
}); });
...@@ -73,57 +73,72 @@ var startTargetServer = function (port) { ...@@ -73,57 +73,72 @@ var startTargetServer = function (port) {
// //
// The default test bootstrapper with no latency // The default test bootstrapper with no latency
// //
var startTest = function (proxy, port) { var startTest = function (port) {
var proxyServer = startProxyServer(port, 'localhost'),
targetServer = startTargetServer(port);
testServers.noLatency = []; testServers.noLatency = [];
testServers.noLatency.push(startProxyServer(port, 'localhost', proxy)); testServers.noLatency.push(proxyServer);
testServers.noLatency.push(startTargetServer(port)); testServers.noLatency.push(targetServer);
return proxyServer;
}; };
// //
// The test bootstrapper with some latency // The test bootstrapper with some latency
// //
var startTestWithLatency = function (proxy, port) { var startTestWithLatency = function (port) {
var proxyServer = startLatentProxyServer(port, 'localhost', 2000),
targetServer = startTargetServer(port);
testServers.latency = []; testServers.latency = [];
testServers.latency.push(startLatentProxyServer(port, 'localhost', proxy, 2000)); testServers.latency.push(proxyServer);
testServers.latency.push(startTargetServer(port)); testServers.latency.push(targetServer);
return proxyServer;
}; };
//var proxy = startTest(8082);
//var latent = startTestWithLatency(8083);
vows.describe('node-http-proxy').addBatch({ vows.describe('node-http-proxy').addBatch({
"A node-http-proxy": { "A node-http-proxy": {
"when instantiated directly": { "when instantiated directly": {
"and an incoming request is proxied to the helloNode server" : { "and an incoming request is proxied to the helloNode server" : {
"with no latency" : { "with no latency" : {
topic: function () { topic: function () {
var proxy = new (httpProxy.HttpProxy); var proxyServer = startTest(8082);
startTest(proxy, 8082); proxyServer.on('proxy', this.callback);
proxy.emitter.addListener('end', this.callback);
var client = http.createClient(8080, 'localhost'); var client = http.createClient(8080, 'localhost');
var request = client.request('GET', '/'); var request = client.request('GET', '/');
request.end(); request.end();
},
teardown: function () {
}, },
"it should received 'hello world'": function (err, body) { "it should received 'hello world'": function (err, body) {
assert.equal(body, 'hello world'); assert.equal(body, 'hello world');
testServers.noLatency.forEach(function (server) { testServers.noLatency.forEach(function (server) {
server.close(); server.close();
}) });
} }
}, },
"with latency": { "with latency": {
topic: function () { topic: function () {
var proxy = new (httpProxy.HttpProxy); var proxyServer = startTestWithLatency(8083);
startTestWithLatency(proxy, 8083); proxyServer.on('proxy', this.callback);
proxy.emitter.addListener('end', this.callback);
var client = http.createClient(8081, 'localhost'); var client = http.createClient(8081, 'localhost');
var request = client.request('GET', '/'); var request = client.request('GET', '/');
request.end(); request.end();
},
teardown: function () {
}, },
"it should receive 'hello world'": function (err, body) { "it should receive 'hello world'": function (err, body) {
assert.equal(body, 'hello world'); assert.equal(body, 'hello world');
testServers.latency.forEach(function (server) { testServers.latency.forEach(function (server) {
server.close(); server.close();
}) });
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment