CloudForecastや先日のNoPasteではPlack::Builderを使って、デフォルトの機能としてPlack::Middleware::ReverseProxyを有効にしている。
my @frontproxy = map { s/\s//g } split(/,/, $ENV{FRONT_PROXY} || "");
foreach my $ip ( @frontproxy ) {
    my $netip = Net::IP->new($ip) or die;
    push @frontproxies, $netip;
}
buider {
    enable_if {
        my $addr = $_[0]->{REMOTE_ADDR};
        my $netip;
        if ( defined $addr && ($netip = Net::IP->new($addr)) ) {
            for my $proxy ( @frontproxies ) {
                my $overlaps = $proxy->overlaps($netip);
                return 1 if ( $overlaps == $IP_B_IN_A_OVERLAP || $overlaps == $IP_IDENTICAL );
            }
        }
        return;
    } "Plack::Middleware::ReverseProxy";
    $app;
}
                                        このように環境変数FRONTPROXYにReverse ProxyのIP帯域(アドレス)をいれ、Plack::Builderのenableifを使って、REMOTE_ADDRがその帯域にあれば、Middlewareを有効にするような方法をとっている。
このようなenable_ifはなんども書きそうなので、モジュールにしておいた方がいいんじゃないかと思い、書いてみた。
github: http://github.com/kazeburo/Plack-Builder-Conditionals
使い方は
use Plack::Builder;
use Plack::Builder::Conditionals;
# exports "match_if, addr, path, method, header, any, all"
builder {
    enable match_if addr(['192.168.0.0/24','127.0.0.1']),
        "Plack::Middleware::ReverseProxy";
    $app
 }
                                        な、感じ。コードの見通しが良くなりました。
Plack::Builder::Conditionalsを利用するには、enableのあとに、matchif をおきます。matchifは内部的にPlack::Middleware::Conditionalを使っていてあとに続く条件によってMiddlewareを有効にするかの判断をします。
条件でとして、REMOTEADDRの検証を行う「addr」、PATHINFOをみる「path」、REQUEST_METHODの「method」、そして任意のヘッダを確認するための「header」のメソッドが用意されています。これに加えて複合的な条件の実現するための「all」と「any」もあります。
# REMOTE_ADDRの帯域を調べる
addr([qw!192.168.0.0/24 127.0.0.1!]);
# PATH_INFO
path('/')
path(qr!^/(\w+)/!)
#REQUEST_METHOD
method('GET')
method(qr!/^(get|head)$/!)
# 任意のヘッダ
header('User-Agent',qr/iphone/i)
header('If-Modified-Since') #ヘッダがあれば真
# REQUEST_METHODがGETかつ、PATH_INFOが/static以下
all( method('GET'), path(qr!^/static!) )
# PATH_INFOが/static以下もしくは、/favicon.ico
any( path(qr!^/static!), path('/favicon.ico') )
                                        このように任意の条件を組み合わせても使えます。また、否定の条件の場合は
addr('!','127.0.0.1'); #localhostではない
path('!', qr!^/private($|/)!) #/private以下ではない
method('!','GET')
header('!', 'User-Agent',qr/MSIE/)
                                        最初の引数として、「!」を渡します。
非常に簡単なメソッド名を利用しているため、アプリケーションによっては重なってしまうことも考えられます。その際はimport時に メソッド名にprefixを追加することができます。
use Plack::Builder::Conditionals -prefx => 'c'; #prefixに「c」追加 # exports "c_match_if, c_addr, c_path, c_method, c_header, c_any, c_all"
 
 