PSGI Specificationが1.03に更新されています。新しく psgi.streaming が追加されました (Tatsumakiでも使ってる)。
psgi.streaming とは
PSGIアプリの戻り値は配列リファレンスですが、 psgi.streaming が有効になっているサーバではコールバック関数を返せます。
コールバック関数には返答用の関数が渡ってくるので、そこにいつもの配列リファレンスを返します。
sub { my $env = shift; $env->{'psgi.streaming'} or die; return sub { my $respond = shift; my $t; $t = AE::timer 5, 0, sub { $respond->([ '200', [ 'Content-Type' => 'text/plain' ], [ "Hello World" ], ]); undef $t; }; }; };
また、返答用の関数に3値ではなく2値の配列リファレンスを渡すと、書き込み用のオブジェクトが返ってきます。そいつにだらだらwriteを繰り返せば、ストリーミングができます。
psgi.streaming と Middleware
Middlewareは必ずしも psgi.streaming でのコールバック関数の戻り値に対応する必要はないですが、戻り値が理解できない場合は素通しすべきとのことです。
psgi.nonblocking とは
PSGIアプリケーションが、イベントループからコールバックとして呼ばれていることを示す値です。psgi.streamingと違い、戻り値等の変更ありません。
PSGI 1.02の時は、 psgi.nonblocking が有効なときに IO::Handle 的なオブジェクトを返すことでストリームを実現することが想定されてましたが、PSGI 1.03のFAQではpsgi.streamingの仕様を利用するように変更されています*1。IO::Writerへの言及もなくなりました。
psgi.nonblocking 時のイベントループの選択
psgi.nonblockingが有効になっているサーバではイベントループを利用して非同期処理を書くことが許されます*2が、どのイベントループを使うかの指針をtwitterでid:miyagawaさんに聞いてみました。
hiratara psgi.nonblocking が TRUEだった時に、アプリ側では何ができるんだろうか。AnyEvent だと思って組んでいいってことでもないだろうしなあ。Perlbalもあるし。
miyagawa @hiratara You can use Danga::Socket::AnyEvent to run AE modules on Perlbal
hiratara @miyagawa AEとかPerlbalの話ですが、PSGI的には、psgi.nonblocking が設定された環境下で application が何を使ってブロックしないようにするかは、デプロイしようとしているサーバに依存ですか? それとも、AnyEvent推奨ですか?
miyagawa @hiratara AnyEvent 推奨かつサーバ依存です :)
ということで、現状ではAnyEventで書くのが一番のようです。
まとめ
PSGI 1.03でのストリーミングの書き方のまとめです。