nginx の組み込み Perl で独自の認証をかける

独自の認証機能付き HTTP ダウンローダを提供するために、nginx の組み込み Perl を使ってみました。
公式のドキュメントはこちら。EmbeddedPerlModule - Nginx Community

自前の handler でリクエストをみて処理を行い、許可するなら DECLINED を返して後続の処理に任せる。そうでなければ Fobidden を返しておしまい、という流れです。

package MyAuth;
use strict;
use warnings;
use nginx;
sub handler {
    my $r = shift;
    if ( $r->header_in("MyAuth") ) { # なにか独自の認証をする(ここではリクエストヘッダを見るだけ
        return DECLINED;             # 処理を継続させるために DECLINED
    }
    $r->status( HTTP_FORBIDDEN );    # 失敗したら FORBIDDEN
    $r->send_http_header;
    $r->print("403 Forbidden");
    return OK;
}
1;

nginx.conf はこんな感じで。

events {
    use epoll;
}
http {
    sendfile     on;
    perl_modules /home/fujiwara/lib;
    perl_require MyAuth.pm;
    server {
        listen 80;
        location /download/ {
            perl  MyAuth::handler;
            alias /var/www/download/;
        }
    }
}

もちろん DECLINED を返さないで、自前でファイル配信まで処理することもできます。
$r->sendfile($path); でファイル内容を返すことは簡単にできるのですが、真面目にやろうとすると条件付き GET (If-Modified-Since) とか Range ヘッダ付きのリクエストに Partial Content を返すとか、なにかと面倒なので DECLINED で通常の nginx のハンドラに全部任せました。

ab でベンチマークを取ってみたところ、例のように $r->header_in を参照するだけの処理ならばオーバーヘッドはほぼ無視できるぐらいでした。測定誤差程度です。

ただし注意点があって、組み込み Perl は worker_processes の数 (default: 1) しか同時に動けません。そのため、途中でブロックするような処理 (外部へ HTTP リクエストを飛ばしたり) をすると極端に並列性が落ちるので、そこは要注意ですね。