Created
May 25, 2010 20:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
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
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?