Commit 9f0aeaca authored by indexzero's avatar indexzero

[api] Object creation is cheap for HttpProxy, so lets take advantage

parent 66afb2a2
...@@ -45,15 +45,15 @@ httpProxy.createServer(9000, 'localhost').listen(8000); ...@@ -45,15 +45,15 @@ 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, proxy){
setTimeout(function(){ setTimeout(function(){
proxy.proxyRequest(9000, 'localhost', req, res); proxy.proxyRequest(9000, 'localhost', req, res);
}, 200) }, 200)
}).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 server with proxyRequest handler and latency******/
http.createServer(function (req, res){ /*http.createServer(function (req, res){
var proxy = new httpProxy.HttpProxy; var proxy = new httpProxy.HttpProxy;
proxy.watch(req, res); proxy.watch(req, res);
...@@ -61,12 +61,12 @@ http.createServer(function (req, res){ ...@@ -61,12 +61,12 @@ http.createServer(function (req, res){
proxy.proxyRequest(9000, 'localhost', req, res); proxy.proxyRequest(9000, 'localhost', req, res);
}, 200); }, 200);
}).listen(8002); }).listen(8002);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta); 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'});
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2)); res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
res.end(); res.end();
}).listen(9000); }).listen(9000);
sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);*/ sys.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
var sys = require('sys'), var sys = require('sys'),
http = require('http'), http = require('http'),
eyes = require('eyes'), eyes = require('eyes'),
pool = require('pool'),
events = require('events'), events = require('events'),
pool = require('pool'), pool = require('pool'),
min = 0, min = 0,
...@@ -38,9 +39,26 @@ manager.setMinClients(min); ...@@ -38,9 +39,26 @@ manager.setMinClients(min);
manager.setMaxClients(max); manager.setMaxClients(max);
exports.createServer = function () { exports.createServer = function () {
// Initialize the nodeProxy to start proxying requests var args, callback, port, host;
var proxy = new (exports.HttpProxy); args = Array.prototype.slice.call(arguments);
return proxy.createServer.apply(proxy, arguments); callback = typeof args[args.length - 1] === 'function' && args.pop();
if (args[0]) port = args[0];
if (args[1]) host = args[1];
var server = http.createServer(function (req, res){
var proxy = new HttpProxy(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, proxy);
}
else {
proxy.proxyRequest(port, server);
}
});
return server;
}; };
exports.setMin = function (value) { exports.setMin = function (value) {
...@@ -53,14 +71,15 @@ exports.setMax = function (value) { ...@@ -53,14 +71,15 @@ exports.setMax = function (value) {
manager.setMaxClients(max); manager.setMaxClients(max);
}; };
exports.HttpProxy = function () { var HttpProxy = function (req, res) {
this.emitter = new(events.EventEmitter); this.emitter = new(events.EventEmitter);
this.events = {}; this.events = {};
this.listeners = {}; this.req = req;
this.collisions = {}; this.res = res;
this.watch(req);
}; };
exports.HttpProxy.prototype = { HttpProxy.prototype = {
toArray: function (obj){ toArray: function (obj){
var len = obj.length, var len = obj.length,
arr = new Array(len); arr = new Array(len);
...@@ -70,102 +89,42 @@ exports.HttpProxy.prototype = { ...@@ -70,102 +89,42 @@ exports.HttpProxy.prototype = {
return arr; return arr;
}, },
createServer: function () { watch: function (req) {
var self = this, this.events = [];
server,
port,
callback;
if (typeof(arguments[0]) === "function") {
callback = arguments[0];
}
else {
port = arguments[0];
server = arguments[1];
}
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 proxyServer;
},
watch: function (req, res) {
var self = this; var self = this;
// Create a unique id for this request so this.onData = function () {
// we can reference it later. self.events.push(['data'].concat(self.toArray(arguments)));
var id = new Date().getTime().toString(); };
this.onEnd = function () {
// If we get a request in the same tick, we need to self.events.push(['end'].concat(self.toArray(arguments)));
// append to the id so it stays unique.
if(typeof this.collisions[id] === 'undefined') {
this.collisions[id] = 0;
}
else {
this.collisions[id]++;
id += this.collisions[id];
}
req.id = id;
this.events[req.id] = [];
this.listeners[req.id] = {
onData: function () {
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); req.addListener('data', this.onData);
req.addListener('end', this.listeners[req.id].onEnd); req.addListener('end', this.onEnd);
}, },
unwatch: function (req, res) { unwatch: function (req) {
req.removeListener('data', this.listeners[req.id].onData); req.removeListener('data', this.onData);
req.removeListener('end', this.listeners[req.id].onEnd); req.removeListener('end', this.onEnd);
// Rebroadcast any events that have been buffered // Rebroadcast any events that have been buffered
while(this.events[req.id].length > 0) { for (var i = 0, len = this.events.length; i < len; ++i) {
var args = this.events[req.id].shift(); req.emit.apply(req, this.events[i]);
req.emit.apply(req, args);
}
// Remove the data from the event and listeners hashes
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) { proxyRequest: function (port, server) {
// Remark: nodeProxy.body exists solely for testability // Remark: nodeProxy.body exists solely for testability
this.body = ''; var self = this, req = this.req, res = this.res;
var self = this; self.body = '';
// Open new HTTP request to internal resource with will act as a reverse proxy pass // Open new HTTP request to internal resource with will act as a reverse proxy pass
var p = manager.getPool(port, server); var p = manager.getPool(port, server);
eyes.inspect(req.headers);
// Make request to internal server, passing along the method and headers
p.request(req.method, req.url, req.headers, function (reverse_proxy) { p.request(req.method, req.url, req.headers, function (reverse_proxy) {
// Add a listener for the connection timeout event // Create an error handler so we can use it temporarily
reverse_proxy.connection.addListener('error', function (err) { var error = function (err) {
res.writeHead(200, {'Content-Type': 'text/plain'}); res.writeHead(200, {'Content-Type': 'text/plain'});
if(req.method !== 'HEAD') { if(req.method !== 'HEAD') {
...@@ -173,8 +132,9 @@ exports.HttpProxy.prototype = { ...@@ -173,8 +132,9 @@ exports.HttpProxy.prototype = {
} }
res.end(); res.end();
}); };
// Add a listener for the connection timeout event
reverse_proxy.connection.addListener('error', error);
// Add a listener for the reverse_proxy response event // Add a listener for the reverse_proxy response event
reverse_proxy.addListener('response', function (response) { reverse_proxy.addListener('response', function (response) {
...@@ -182,7 +142,7 @@ exports.HttpProxy.prototype = { ...@@ -182,7 +142,7 @@ exports.HttpProxy.prototype = {
if (req.headers.connection) response.headers.connection = req.headers.connection; if (req.headers.connection) response.headers.connection = req.headers.connection;
else response.headers.connection = 'close'; else response.headers.connection = 'close';
} }
// 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);
...@@ -211,9 +171,65 @@ exports.HttpProxy.prototype = { ...@@ -211,9 +171,65 @@ exports.HttpProxy.prototype = {
// At the end of the client request, we are going to stop the proxied request // At the end of the client request, we are going to stop the proxied request
req.addListener('end', function () { req.addListener('end', function () {
reverse_proxy.end(); reverse_proxy.end();
reverse_proxy.connection.removeListener('error', error);
}); });
self.unwatch(req, res); self.unwatch(req);
}); });
} }
}; };
exports.HttpProxy = HttpProxy;
/*// Create an error handler so we can use it temporarily
var 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();
};
// Add a listener for the connection timeout event
reverse_proxy.connection.addListener('error', error);
// Add a listener for the reverse_proxy response event
reverse_proxy.addListener('response', function (response) {
if (response.headers.connection) {
if (req.headers.connection) response.headers.connection = req.headers.connection;
else response.headers.connection = 'close';
}
// Set the response headers of the client response
res.writeHead(response.statusCode, response.headers);
// Add event handler for the proxied response in chunks
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');
})
// At the end of the client request, we are going to stop the proxied request
req.addListener('end', function () {
reverse_proxy.end();
//reverse_proxy.connection.removeListener('error', error);
});
self.unwatch(req);*/
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