PSGIミドルウェアを利用する
PSGIミドルウェアはアプリケーションと同様にコードリファレンスとして実装されますが、いちいち.psgiファイルに記述するのはあまり効率が良くありませんし、再利用性もありません。そこでPlackにはミドルウェアをモジュールとして利用するためのベースクラスPlack::Middlewareや、それを利用して実装された各種ミドルウェアが同梱されています[2] 。
ここでは、これらのミドルウェアを利用して先ほどのTatsumakiアプリケーションを拡張してみましょう。
Auth::Basic
PSGIアプリケーションに簡単なHTTP認証機能を付けるミドルウェアがAuth::Basicです。容易に拡張できるコールバックインタフェースを持っているため、データベースやLDAP(Lightweight Directory Access Protocol )などとの連携もできます。
まずは、先ほどのhello.psgiを開き、ミドルウェアを利用するコードを追加します。リスト5 のように、Plack::Middleware::Auth::Basicをロードし、wrapメソッドを使ってPSGIアプリケーションのコードリファレンス($app->psgi_app
)に認証機能を追加します((2) ) 。authenticatorはユーザ名、パスワードを受け取り、それが正しいかどうかを返すコールバック関数になります。また、Helloハンドラ内でも、認証されたユーザを$self->request->userから取得して画面に表示((1) )するようにしてみます。
リスト5 Auth::Basicの追加
use strict;
package Hello;
use parent 'Tatsumaki::Handler';
sub get {
my $self = shift;
$self->write("Hello ", $self->request->user);
}
use Tatsumaki::Application;
my $app = Tatsumaki::Application->new([
'/hello' => 'Hello',
]);
use Plack::Middleware::Auth::Basic;
$app = Plack::Middleware::Auth::Basic->wrap(
$app->psgi_app,
authenticator => sub {
my($user, $pass) = @_;
return $user eq 'admin' && $pass eq 'gihyo';
},
);
ブラウザでhttp://localhost:5000/hello
をもう一度開いてみましょう。図6 のように認証ダイアログが表示されるはずです。ここではコードに記述したように、ユーザ名「admin」 、パスワード「gihyo」だけが正しい入力ペアとなっています。それ以外の値を適当に入力しても、再度ダイアログが表示されるだけでしょう。正しいパスワードを入力すれば、「 Hello admin」と認証によって取得したユーザ名が出力されます。
Auth::BasicミドルウェアはCPANモジュールのAuthen::Simpleのインタフェースにも対応しているため、LDAPや.htpasswdファイルなどによる認証と簡単に連携できます。詳しくはオンラインドキュメントを参照してください。
図6 Auth::Basicによる認証機能の追加
Plack::Builder
先ほどの例では、ミドルウェアを通常のPerlモジュールのようにuseし、wrapメソッドを呼ぶことでミドルウェアを追加しました。この方法では利用するミドルウェアを増やしていく場合などに記述が長くなりがちで、ミドルウェアの実行順序などが直感的でないという問題があります。そこで用意されているのがPlack::BuilderモジュールによるDSL(Domain Specific Language 、ドメイン特化言語)です。
Auth::Basicを利用したミドルウェアの例は、Plack::Builderを利用するとリスト6 のように記述できます。ラップされるアプリケーションをbuilder { }
で囲み、その上にenableミドルウェア名,オプション...;
のようにスタックしていくイメージです。ミドルウェア名の「Plack::Middleware::」部分は省略できますし、モジュールはBuilderによって自動的にロード(require)されますので、記述を大幅に簡潔化できます。
リスト6 Builderによるミドルウェアの記述
use Plack::Builder;
builder {
enable "Auth::Basic", authenticator => sub { ... };
$app->psgi_app;
};
たとえば、ここでは詳細を省きますが、スタティックファイルを配信するミドルウェアStaticやアプリケーションの実行時間をX-Runtimeヘッダに挿入するRuntimeミドルウェアと組み合わせるとリスト7のように書くことができます。図4で見たようにアプリケーションが真ん中にあるとすれば、builderの中で下の行にありアプリケーションに近いほうが、真ん中に近い位置にあることになります。よって、この.psgiファイルを実行すると、Auth::Basic⇒Static⇒Runtime⇒Tatsumakiアプリケーション⇒Runtime⇒Static⇒Auth::Basic
の順番で処理が実行されます。ミドルウェアごとにアプリケーションの前に実行されるもの(認証など) 、後に実行されるもの(出力ヘッダの挿入など)に種類が分かれていますので、ドキュメントなどを参照して、どの順番が最適かを検討してください。
リスト7 ミドルウェアの追加
use Plack::Builder;
builder {
enable "Auth::Basic", authenticator => sub { ... };
enable "Static", path => qr/^\/images/, root => "/images";
enable "Runtime";
$app->psgi_app;
};
そのほかのミドルウェア
紙幅の都合ですべては紹介できませんが、PlackコアやCPANには便利な各種ミドルウェアがアップロードされています。
Session
Sessionミドルウェアはフレームワークにセッション管理機能を追加します。多くのフレームワークが自前でセッション管理を実装している場合がほとんどのため、執筆時点ではあまりメリットがありませんが、フレームワークからこのPlack::Middleware::Sessionを利用するためのアダプタを記述すれば、各種フレームワークでセッションを共有する、といったことが容易にできるようになります。SessionミドルウェアはCookieによりID管理を行い、ストレージには各種ファイルやmemcachedといったキャッシュバックエンドが利用可能です。
Debug
PythonのDjangoプラグインであるdjango-debugtoolbarやRackのミドルウェアrack-bugにインスパイアされて作成したミドルウェアがDebugです。このミドルウェアを有効にすると、HTMLページの右側にパネル(図7 )が表示され、リクエストヘッダやレスポンス、各種フレームワークのログなどをクリックして見ることができます。開発時にはとても有用なミドルウェアでしょう。
図7 Debugパネル
PSGI非対応Webアプリケーションを移行する
利用しているWebフレームワークがPSGIに対応している場合、PSGIへ移行してPlackやミドルウェアを利用することは簡単でした。基本的にはコードの変更は必要なく、.psgiファイルなどを作成して起動方法を変更するだけです。
では、使っているフレームワークがPSGIに対応していない場合や、自前のフレームワークを作成している場合はどうでしょうか。
CGI::PSGI
先述したように、PSGIの環境はCGIに似せた仕様となっており、CGI環境で動くアプリケーションであればPSGIへの移行が簡単に行えるようにデザインされています。
お使いのフレームワークでCGI.pmを利用している場合、CPANモジュールのCGI::PSGIを利用することでPSGIへの対応が容易になるかもしれません。実装の詳細は省略しますが、リスト8 のようにCGI.pmをリクエストのパラメータやパスなどの処理のみに使用しているフレームワークであれば、リスト9 のようにPSGI環境変数を取得し((1) ) 、CGI::PSGIを利用するように書き換え、あとはヘッダを出力しレスポンスを返す部分を配列リファレンスに変更する((2) )だけでよいでしょう。
リスト8 CGI.pmを利用したフレームワークのコード
package MyFramework;
use CGI;
sub new {
my $class = shift;
bless { cgi => CGI->new }, $class;
}
sub dispatch {
my $self = shift;
my $path = $self->{cgi}->path_info;
# $pathからディスパッチするアクションを決定
my $action = $self->get_action($path);
my($headers, $body) = $action->dispatch($self);
# $headers is HTTP::Headers
print $headers->as_string, $body;
}
1;
リスト9 CGI::PSGIの利用
package MyFramework;
use CGI::PSGI;
sub new {
my $class = shift;
my $env = shift;
bless { cgi => CGI::PSGI->new($env) }, $class;
}
sub dispatch {
my $self = shift;
my $path = $self->{cgi}->path_info;
# $pathからディスパッチするアクションを決定
my $action = $self->get_action($path);
my($headers, $body) = $action->dispatch($self);
# $headers is HTTP::Headers
my @headers;
$headers->scan(sub { push @headers, @_ });
return [ 200, \@headers, [ $body ] ];
}
1;
CGI::Emulate::PSGI+CGI::Compile
前項では簡単に言いましたが、言うは易く行うは難し。CGI環境でしか利用できない%ENVを利用していたり、CGI.pmの関数をメソッドでない利用方法で呼んでいたりと、うまくPSGIで動かない例もあるかもしれません。
その際に使えるのがCGI::Emulate::PSGIとCGI::Compileで、どちらもCPANから入手できます。CGI::Compileは、CGIスクリプトを何度も呼べる形のサブルーチンリファレンスにコンパイルします。CGI::Emulate::PSGIは、それをPSGIアプリケーションとして実行できるよう、環境変数%ENVや標準入出力をラップしてCGI環境をエミュレートします。CGI::PSGIを利用する場合に比較してエミュレートする分のオーバーヘッドはありますが、各種PSGIサーバで実行でき、Plackミドルウェアが利用できるなど、手に入るメリットはとても大きいはずです。
Plack::Request
すでに自前のRequestクラスなどを持っている既存のフレームワークや、mod_perlのApache::Requestを利用したコードをPlack対応させるには、Plack::Requestが便利です。Plack::Requestは執筆時点で鋭意開発中のPlackバージョン1.0からコアモジュールとなり、CatalystやApacheに似たリクエスト/レスポンスAPIを提供します。単純にパラメータを処理するだけならば、CGI.pmの置き換えとして利用することも可能でしょう。
PSGIサーバ
ここでは、どのようなWebサーバ環境でPSGIがサポートされているかを簡単に紹介します。
CGI、FastCGI
いわゆる一般的なWebサーバであるApache、lighttpdやnginxなどからPSGIを実行するには、CGIやFastCGIなどを利用するのが一般的になります。Plackにはこれらの環境で実行できるアダプタが含まれています。
mod_perl
Perlアプリケーションを永続環境で実行するスタンダードな方法としてmod_perlがあります。Plackにはmod_perl 1.xと2.xに対応したハンドラが含まれていますので、どのようなPSGIアプリケーションでもmod_perl上で実行できます。
HTTP::Server::PSGI
先述したように、Plackに標準でバンドルされているスタンドアロンのサーバがHTTP::Server::PSGIです。Pure Perlで書かれており、Cモジュールなどへの依存もありませんので、Windowsなどを含むどのような環境でもインストールが可能です。また、プロセス数を複数指定した場合には、preforkの形で起動させて同時リクエストを並列処理することもできます。
Starman
筆者も開発に関わっているUNIXベースのWebサーバがStarman です。StarmanはNet::Server::PreForkをベースにしたHTTPサーバで、preforkによる並列動作やHTTP 1.1対応、UNIXソケットへのバインド、Server::Starter などのホットデプロイツールに自動対応するなど、プロダクション環境での実行に最適化することを念頭において開発されています。
AnyEvent、POE、Coro
Perlの非同期フレームワークやイベントループライブラリとしてよく利用されているAnyEvent、POEやCoroを利用したPSGIサーバも、それぞれCPANに登録されています。Plackからこれらのサーバを利用するアダプタがPlack::Handler::AnyEven(t POE、Coro)の名前で登録されていますので、これらをキーにして検索してみてください。
まとめ
Plackを利用することで、PSGIに対応したどのようなアプリケーション、フレームワークも、さまざまなWebサーバ環境で動かすことが可能になります。また、ミドルウェアを利用してさまざまな拡張機能を実装し、その実装をフレームワーク間で共有することが可能になります。今回は紹介しきれませんでしたが、Plackを利用してWebのテストを完全にサーバから切り離すためのツール(Plack::TestやTest::WWW::Mechanize::PSGI)なども出てきています。各種ミドルウェアの充実もあり、PSGIに対応するメリットは日々増えています。ぜひ、Plackで新しいWebアプリケーション開発を体験してください。
そのほかの最新情報などについては、PlackのWebサイトやCPANのドキュメント などを参照してください。
次回は、本文最後でも紹介した非同期のイベントループを簡潔に記述するためのライブラリAnyEventについて、牧大輔さんが担当します。
では、Happy Hacking!