Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

Plack::App::Proxyについてだらだらと

去年の年末、FlashのXMLSocket通信を横取りしてダンプするアプリを書こうとしたのですが、その時に見つけたのがLee AylwardさんのPlack::App::Proxyです。HTMLを置換してFlashの接続先を横取りしつつ、自前のXMLSocketサーバを立ち上げるってのが1プロセスでできるってのは、非同期なWEBサーバならではの技ですね!

ただ、Plack::App::Proxyを触り始めてみると、そのままではなかなか難しい部分が色々あったりしたので、コメンテナにしてもらってちょくちょくいじったりしています。(ちなみに、miyagawaさんもコラボレータで、私よりバリバリいじってますw)

一言で言えば、 Plack::App::Proxy がカバーしているのは Apache の mod_proxy の機能です。 使い方はpod見てもらうと全部載ってますが、例えば /backend 以下を別のサーバに処理を振りたい場合は以下のように書けます。

use Plack::App::Proxy;
use Plack::Builder;
 
builder {
    enable 'Proxy::RewriteLocation';
    mount "/backend" => Plack::App::Proxy->new(
        remote => 'http://localhost:8080',
        preserve_host_header => 1,
    )->to_app;
};

RewriteLocation は、Locationヘッダを適切に書き換えてくれるミドルウェア*1です。バックエンドがそのままLocationヘッダを吐くとブラウザがバックエンドを直接見に行こうとしてしまうので、これは有効にしておいた方が無難です。後、preserve_host_header は Apache の mod_proxy の ProxyPreserveHost と同じ働きですんで、バックエンドが名前ベースのバーチャルホストになってる場合は入れた方がいいです。

後、 0.12 からは、miyagawa さんが Middleware で App::Proxy の動きを制御できるように変更してくれてます。Middleware の中で $envにplack.proxy.remoteやplack.proxy.urlを設定し、App::Proxyに対してリクエストを転送すべきホストやURLを指示できます。また、$envのHTTP_*の値や戻り値に含まれるヘッダを変更して*2、バックエンドのリクエストやレスポンスを変更することも可能です。

例えば、バックエンドが2台あってランダムに振り分けたい時はこんな感じでしょうか。

use Plack::App::Proxy;
use Plack::Builder;
 
builder {
    mount "/backend" => builder {
        enable 'Proxy::RewriteLocation';
        enable sub {
            my $app = shift;
            sub {
                my $env = shift;
                $env->{ 'plack.proxy.remote' } = rand > .5
                                                     ? 'http://localhost:8080'
                                                     : 'http://localhost:8081';
                $app->( $env );
            };
        };
        Plack::App::Proxy->new->to_app;
    };
};

後、Server::Standalone や Server::AnyEvent のように全てのリクエストを受けられるサーバなら*3、オープンプロクシとしても動きます。(動くってだけで現状は積極的にサポートされてませんが。)

今後のTODOは以下のような感じかなあと朧げに思ってます。

  • mod_proxyのProxyPassReverseCookie* の実装
  • keep-aliveの対応 (AE::HTTPが対応されたら)
  • AllowCONNECT (psgi.io みたいなハンドルが生で公開されるようになれば)
  • Middleware::Reproxy?*4 (PlackでMiddleware::Recursiveが実装されたら?)
  • 他、paste.proxy とかでサポートしてる機能があれば

*1:mod_proxyのProxyPassReverse

*2:App::Proxyのレスポンスは、psgi.streaming 形式なので注意。

*3:1.0までに Plack::Handler::* にrenameされます。

*4:Plack::App::Reproxy は typester さんがすでに実装してます