ちょっと書いてみたくなったので書いた。実用性とか気にしちゃいけない。
$ node reverse_proxy.js d.hatena.ne.jp reverse proxy to d.hatena.ne.jp http://localhost:8000
これで localhost:8000 → d.hatena.ne.jp の reverse proxy になります。
やってることは単純で、
- http server を起動
- client から来た request の Host を差し替えて upstream に投げる
- upstream から受け取った response を client に返す
だけです。例によってイベント駆動なので、http body を扱うには addListener する形ですね。
本来は body の中身 (HTML内の URL とか) を書き換えるようなものが欲しかったのですが、utf8 以外の文字コードを扱うのが面倒なのでここまで。
# utf8 以外は chunk が文字列じゃなくて、byte の入った Object として扱う必要があります
var sys = require('sys'), http = require('http'), port = 8000, upstreamHost = process.argv[2]; var main = function() { http.createServer(handle_request).listen(port); sys.puts("reverse proxy to " + upstreamHost + " http://localhost:" + port ); }; var handle_request = function (client_request, client_response) { var upstream = http.createClient(80, upstreamHost); var localhost = client_request.headers.host; client_request.headers.host = upstreamHost; var upstream_request = upstream.request( client_request.method, client_request.url, client_request.headers ); client_request.addListener("data", function(chunk) { upstream_request.write(chunk) }); client_request.addListener("end", function() { upstream_request.end(); }); upstream_request.addListener("response", function (upstream_response) { proxy_pass_reverse(upstream_response, localhost); client_response.writeHead( upstream_response.statusCode, upstream_response.headers ); var size = 0; upstream_response.addListener("data", function(chunk) { client_response.write(chunk); size += chunk.length; }); upstream_response.addListener("end", function() { sys.puts(client_request.method + " " + client_request.url + " " + upstream_response.statusCode + " " + size); client_response.end(); }); }); }; // Location ヘッダの書き換え var proxy_pass_reverse = function(upstream_response, localhost) { var location = upstream_response.headers["location"]; if (location) { upstream_response.headers.location = location.replace( upstreamHost, localhost ); sys.puts("new location: " + upstream_response.headers.location); } }; main();