Skip to content

Instantly share code, notes, and snippets.

@progrium
Created May 25, 2010 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save progrium/413607 to your computer and use it in GitHub Desktop.
Save progrium/413607 to your computer and use it in GitHub Desktop.
// This is a design doc for adding two methods to node via monkey patching
// that I find useful in all the little bits of HTTP glue I make. The point
// is to create a useful abstraction for forwarding requests and linking
// responses. In the most trivial case for building a reverse proxy, but
// see below for other use cases.
// API
http.ServerRequest.prototype.forward(port, host, dataFilter=null)
http.ServerResponse.prototype.attach(requestOrResponse, responseFilter=null, dataFilter=null)
// Simple reverse proxy
var server = http.createServer(function(req, resp)) {
resp.attach(req.forward(8080, 'localhost'));
});
server.listen(80);
// Simple SSL frontend
var server = http.createServer(function(req, resp)) {
req.headers['X-Forwarded-Proto'] = 'https';
req.headers['X-Forwarded-For'] = req.connection.remoteAddress;
resp.attach(req.forward(80, 'localhost'));
});
server.setSecure(crypto.createCredentials({key: 'key', cert: 'cert'}));
server.listen(843);
// Simple load balancer
var hosts = ['hostA', 'hostB', 'hostC'];
var cursor = 0;
var server = http.createServer(function(req, resp)) {
resp.attach(req.forward(80, hosts[cursor]));
cursor += 1;
if (cursor == hosts.length) cursor = 0;
});
server.listen(80);
// Fire and forget request fanout
var hosts = ['hostA', 'hostB', 'hostC'];
var server = http.createServer(function(req, resp)) {
for (var host in hosts) {
req.forward(80, host);
}
resp.writeHead(201);
resp.end();
});
server.listen(80);
// Webhook gate
// (only proxy if hook request succeeds, else return hook response)
var webhook = url.parse("http://example.com/continue-or-cancel-script");
var server = http.createServer(function(req, resp)) {
var hookRequest = http.createClient(webhook.port, webhook.host)
.request(webhook.pathname);
hookRequest.addListener('response', function(hookResponse) {
if (hookResponse.statusCode == 200) {
resp.attach(req.forward(8080, 'localhost'));
} else {
resp.attach(hookResponse);
}
}
});
server.listen(80);
// Simple page caching
var cache = {};
var server = http.createServer(function(req, resp)) {
if (req.method == 'GET') {
if (cache.hasOwnProperty(req.url)) {
resp.writeHead(200);
resp.write(cache[req.url]);
resp.end();
} else {
resp.attach(req.forward(8080, 'localhost'), null, function(data) {
cache[req.url] += data;
return data;
});
}
} else {
resp.attach(req.forward(8080, 'localhost'));
}
});
server.listen(80);
@ericfong
Copy link

Great. I think it will be really useful if all stream in node can be forward and insert a data filter into it.
But I don't quite understand the attach. It is concat diff inputs from proxy client responses?

@progrium
Copy link
Author

The attach method takes either a request (when you don't have a response yet) or response object and then hooks up the data and end events to the original response. This effectively "attaches" or ties one response to another. So you can see the reverse proxy is simply "take this request and forward it, then attach the response to the original response".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment