Skip to content

Commit

Permalink
[api dist] Merge of branch 0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Sep 7, 2010
2 parents 6661753 + eb39018 commit fd61828
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 216 deletions.
37 changes: 14 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# node-http-proxy - v0.1.5
# node-http-proxy - v0.2.0

<img src = "http://i.imgur.com/dSSUX.png"/>

Expand Down Expand Up @@ -52,9 +52,9 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)
httpProxy = require('http-proxy');

// 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
proxy.proxyRequest(9000, 'localhost', req, res);
proxyRequest(9000, 'localhost');
}).listen(8000);

http.createServer(function (req, res){
Expand All @@ -65,37 +65,28 @@ see the [demo](http://github.com/nodejitsu/node-http-proxy/blob/master/demo.js)

</pre>

### How to proxy requests with a regular http server
<pre>
var http = require('http'),
httpProxy = require('http-proxy');
### How to proxy requests with latent operations (IO, etc.)

// 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);
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:

http.createServer(function (req, res){
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>

### 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.

<br/><hr/>
<br/>
### 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
a copy of this software and associated documentation files (the
Expand All @@ -116,4 +107,4 @@ 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.

[0]:http://nodejitsu.com "nodejitsu.com"
[0]:http://nodejitsu.com "nodejitsu.com"
19 changes: 4 additions & 15 deletions demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
var sys = require('sys'),
colors = require('colors')
http = require('http'),
httpProxy = require('http-proxy');
httpProxy = require('./lib/node-http-proxy');

// ascii art from http://github.com/marak/asciimo
var welcome = '\
Expand All @@ -45,24 +45,13 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
sys.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);

/****** http proxy server with latency******/
httpProxy.createServer(function (req, res, proxy){
httpProxy.createServer(function (req, res, proxyRequest){
setTimeout(function(){
proxy.proxyRequest(9000, 'localhost', req, res);
}, 200)
proxyRequest(9000, 'localhost', req, res);
}, 2000)
}).listen(8001);
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 ******/
http.createServer(function (req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
Expand Down
260 changes: 102 additions & 158 deletions lib/node-http-proxy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
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
a copy of this software and associated documentation files (the
Expand All @@ -24,172 +24,116 @@
*/

var sys = require('sys'),
http = require('http'),
events = require('events');

exports.HttpProxy = function () {
this.emitter = new(events.EventEmitter);
this.events = {};
this.listeners = {};
this.collisions = {};
};
var sys = require('sys'),
http = require('http'),
pool = require('pool'),
url = require('url'),
events = require('events'),
min = 0,
max = 100;

// Setup the PoolManager
var manager = pool.createPoolManager();
manager.setMinClients(min);
manager.setMaxClients(max);

exports.createServer = function () {
// Initialize the nodeProxy to start proxying requests
var proxy = new (exports.HttpProxy);
return proxy.createServer.apply(proxy, arguments);
};

exports.HttpProxy.prototype = {
toArray: function (obj){
var len = obj.length,
arr = new Array(len);
for (var i = 0; i < len; ++i) {
arr[i] = obj[i];
}
return arr;
},
var args, action, port, host;
args = Array.prototype.slice.call(arguments);
action = typeof args[args.length - 1] === 'function' && args.pop();
if (args[0]) port = args[0];
if (args[1]) host = args[1];

createServer: function () {
var self = this,
server,
port,
callback;

if (typeof(arguments[0]) === "function") {
callback = arguments[0];
}
var proxy = createProxy();
proxy.on('route', function (req, res, callback) {
var uri = url.parse(req.url);
if (action) {
action(req, res, callback);
}
else {
port = arguments[0];
server = arguments[1];
port = port ? port : uri.port ? uri.port : 80;
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 proxyServer;
},

watch: function (req, res) {
var self = this;

// Create a unique id for this request so
// we can reference it later.
var id = new Date().getTime().toString();

// If we get a request in the same tick, we need to
// 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('end', this.listeners[req.id].onEnd);

},

unwatch: function (req, res) {
req.removeListener('data', this.listeners[req.id].onData);
req.removeListener('end', this.listeners[req.id].onEnd);

// Rebroadcast any events that have been buffered
while(this.events[req.id].length > 0) {
var args = this.events[req.id].shift();
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) {
// Remark: nodeProxy.body exists solely for testability
this.body = '';
var self = this;

// 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)));
}
});
return proxy;
};

res.end();
});
exports.setMin = function (value) {
min = value;
manager.setMinClients(min);
};

exports.setMax = function (value) {
max = value;
manager.setMaxClients(max);
}

// Add a listener for the reverse_proxy response event
reverse_proxy.addListener('response', function (response) {
// Set the response headers of the client response
res.writeHead(response.statusCode, response.headers);
var createProxy = function () {
var server = http.createServer(function (req, res) {
var buffers = [],
b = function (chunk) { buffers.push(chunk) },
e = function () { e = false };

req.on('data', b);
req.on('end', e);

server.emit('route', req, res, function (port, hostname) {
var p = manager.getPool(port, hostname);

// 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;
p.request(req.method, req.url, req.headers, function (reverse_proxy) {
var data = '';
reverse_proxy.on('error', function (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});

if(req.method !== 'HEAD') {
res.write('An error has occurred: ' + sys.puts(JSON.stringify(err)));
}

res.end();
});

buffers.forEach(function (c) {
data += c;
reverse_proxy.write(c);
});

buffers = null;
req.removeListener('data', b);
sys.pump(req, reverse_proxy);

if (e) {
req.removeListener('end', e);
req.addListener('end', function () { reverse_proxy.end() });
}
else {
reverse_proxy.end();
}
});

// 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();
// 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';
}

// 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);
});

// Set the response headers of the client response
res.writeHead(response.statusCode, response.headers);

sys.pump(response, res);
});
});
});

// 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();
});

this.unwatch(req, res);
}
};
})
return server;
};
Loading

0 comments on commit fd61828

Please sign in to comment.