PSGI::FAQ - Frequently Asked Questions and answers / 頻出問答集
私達はこれを単純にピー・エス・ジー・アイ(P-S-G-I)と読んでいます。
PSGIとは、ウェブサーバーと、perlに基づくウェブアプリケーションとの間のインターフェースであり、CGIがウェブサーバーとCGIスクリプトで行っていることと同種のものです。
PerlにはCGIやmod_perlやFastCGIの差異を幾分か抽象化するコアモジュールとしてCGI があります。しかし、多くのウェブアプリケーションフレームワーク(例えばCatalystやJifty)の開発者は、性能を最大化したり、低水準のAPIにアクセスするために、このモジュールを使うことを避けています。結局その人達は、それらの異なる環境の全てに対応させるためのアダプターを書いていますが、あるものは十分にテストされている一方で、そうでないものもあります。
PSGIによって、ウェブアプリケーションフレームワークの開発者は、PSGIのためのアダプターを書くだけで済むようになります。エンドユーザーは、PSGIインターフェースに対応する全てのバックエンドを選ぶことができます。
PSGIはとCGIと同類だそうですね。PSGIインターフェースはCGIとどのように違うのですか?
PSGIインターフェースは、CGIがとても簡単になることに加え、PSGIに対応するために、CGIにとてもよく似るように意図的に設計されています。CGIとPSGIの主な違いの見所は、以下の通りです:
CGIにおけるサーバーとは、何かの言語(ただし大抵はC)によって記述された実際のウェブサーバーであり、スクリプトとは、CやPerlやシェルスクリプトやRubyやPythonのような何かの言語で記述されたものです。
PSGIにおけるサーバーもウェブサーバーではありますが、それらはperlのプロセスであり、普通は(mod_perlのように)ウェブサーバーに組み込まれているものか、(FastCGIのように)ウェブサーバーから呼ばれるperlのデーモンプロセスであるか、完全にperlに基づくウェブサーバーです。そして、PSGIアプリケーションとは、perlのコードリファレンスです。
CGIにおいては、パラメーターとHTTPリクエストボディーを読み取り、アプリケーションからのエラーを送信するために、標準出力と標準エラー出力と環境変数を使います。
PSGIにおいては、サーバーとアプリケーションとの間でデータを受け渡すために、$env
ハッシュリファレンスとpsgi.input ストリームとpsgi.error ストリームを使います。
CGIにおけるアプリケーションとは、HTTPヘッダーとボディーを標準出力へ印字し、それをウェブサーバーに対して受け戻すものだとされています。
PSGIにおけるアプリケーションとは、HTTPステータスコードと、ヘッダーと、(配列リファレンスまたはファイルハンドルのようなオブジェクトとしての)ボディーを、配列リファレンスとしてアプリケーションへ返すものだとされています。
私のフレームワークは既にCGIとFSGIとmod_perlで動かせます。どうしてPSGIへ対応させたがるのでしょうか?
もしあなたのウェブアプリケーションフレームワークが、既に多くのサーバー環境に対応していて、性能が良くて、バックエンドがよくテストされているならば、PSGIバックエンドと重複している何かのコードを削除することによってPSGIに直ちに対応しても、直接的な利益はないでしょう。しかし現在CGI環境のみ対応しているならば、とても簡単にPSGIに対応することで、アプリケーションをPSGI互換サーバーで動かせるようになり、plackup やPlack::Test や多くのPlack::Middleware コンポーネントのようなユーティリティーを使えるようになるので、あなたとあなたのフレームワークのユーザーのためになるでしょう。
私はウェブアプリケーションを書いています。PSGIは私にとってどのような利益がありますか?
あなたがお使いのフレームワークがPSGIに対応しているということは、あなたのアプリケーションが既存およびこれから生まれるPSGI実装のどこでも動かせることを意味しています。あなたは、PSGIアプリケーションを返す.psgi
ファイルを提供することができ、それによって、あなたのアプリケーションのエンドユーザーは、あなたのアプリケーションを色々な別の方法によって構成・実行できるはずです。
でも、私はCGIで動くウェブアプリケーションを書いていますが、それは満足に動いていますよ。PSGIに切り替える必要があるのですか?
もしあなたが生のCGI.pmを使って、いずれのウェブフレームワークも使わずにウェブアプリケーションを書いているならば、あなたのアプリケーションは生のCGI環境や、mod_perlや、FastCGIなどでの稼働に制限されます。あなた一人があなたのアプリケーションの開発者でありユーザーであるならば、たぶんそれでもいいでしょう。
しかしいつか、あなたのアプリケーションを顧客のために共有ホスティング環境に配備したいと思ったとき、またはCGIスクリプトとしてではなくスタンドアローンモードのサーバーで動かしたいと思ったとき、またはオープンソースソフトウェアとして配布したいと思うことがあるかも知れません。CGI.pmを使っているあなたのアプリケーションがCGI環境のみに制限されていると、痛い目を見るでしょう。
あなたはPSGIを意識した将来性のあるアプリケーションを作るために、PSGI互換のフレームワーク(フルスタックのものでも、小規模のものでも)の一つか、フレームワークがお嫌いであればPlack::Request を使い始めることができます。
たとえ現在PSGIを知らなくて、生のCGIで動くアプリケーションを書いていたとしても、CGI::PSGI ラッパーを使うことで、今後いつでもPSGIに切り替えることができます。
PSGIに対応するためにはどうすればいいのですか?
あなたがウェブサーバーの開発者ならば、PSGIアプリケーションを呼ぶPSGI実装を書いてください。または、PSGIツールキットおよびユーティリティーであるPlackの開発に参画し、あなたのウェブサーバーのためのサーバーアダプターを加えてください。
あなたがウェブアプリケーションフレームワークの開発者ならば、PSGI用のアダプターを書いてください。これで、サーバー環境の全ての差異に対応させる必要がなくなります。
あなたがウェブアプリケーションの開発者(またはウェブアプリケーションフレームワークのユーザー)であれば、PSGIに対応しているフレームワークを選ぶか、またはPSGIへ対応してくれないかとフレームワークの作者に訊ねてください :) あなたのアプリケーションが大規模なアプリケーション(例えばWebGUIやMovable Type)であるならば、あなたはPSGIの観点ではフレームワーク開発者であるものとして考えられるので、既存のフレームワークを使わないでください。つまり、あなたのアプリケーションでPSGI向けのアダプターを書く方が理に適っています。
PSGIは(私のフレームワークよりも)高速ですか?
繰り返しますが、PSGIは実装ではありません。ただ、とても高速なPSGI実装が生まれる可能性があります。全てを事前ロードしていて、XSパーザーによって事前フォークされていて、sendfile(2)カーネルコールを使うような、十分に最適化されたコードを実行するスタンドアローン。Cで書かれ、PSGIに対応するperlが組み込まれた、イベント駆動型の極小ウェブサーバー。どのモジュールも全くロードせず、多量のメモリーを消費せずに、CGI環境下で相当早く動く、旧来のCGI.pmに基づくバックエンドなどです。
HTTP::Server::PSGI::PreforkやCoroに基づく、とても高速なPSGIサーバーが既に存在していますし、それらにはPlack用のアダプターが含まれているので、plackup ユーティリティーを使って実行することもできます。
あなたのフレームワークのユーザーは、必要に応じて最適なバックエンドを選べます。ウェブアプリケーションフレームワーク開発者としてのあなたは、それぞれ異なる要求を持っている様々なユーザーのことを考える必要がありません。
Plackとは何ですか?PSGIとPlackの違いは何ですか?
PSGIは仕様であり、ソフトウェアやモジュールからPSGIが呼ばれるわけではありません。エンドユーザーは、PSGIアプリケーションを動かすために、PSGIサーバー実装のどれかを選ぶでしょう。Plackとは、PSGIのユーティリティーの集合体であり、PSGIのリファレンスサーバー実装であるHTTP::Server::PSGI に加え、CGIとFastCGIとmod_perl向けのウェブサーバーアダプターも含んでいます。
Plackには、PSGI上の便利なAPIとヘルパーもあります。リクエストオブジェクトにおける行儀のよいオブジェクト指向APIを提供するためのPlack::Request のようなものや、app.psgi を使ってコマンドラインからPSGIアプリケーションを実行したり環境設定するplackup (RackのRackupのようなものです)や、標準的なHTTP::Request とHTTP::Response の組を使ってモックのHTTPまたはHTTPサーバーによるアプリケーションをテストできるPlack::Test などです。詳細はPlack を参照してください。
どのような種類のサーバーバックエンドがありますか?
Plackの場合、私達はApache2などの多くのウェブサーバーへ既に対応していて、標準的なCGIやFastCGIに対応しているものもありますが、さらにPerlbalやnginxのようなperlを組み込める特別なウェブサーバーへの対応も試みているところです。Apacheモジュールであるmod_perliteやGoogle AppEngineまでもがPSGIに対応してくれて、PSGI/Plackに基づくアプリケーションをクラウドで動かせるようになれば、とてもいいと思っています。
RubyにはRackがあり、JavaScriptにはJackがあります。これをPackと呼ばないのはなぜですか?
Packは確かに申し分なく魅力的な名前ですが、Perlには組み込み関数としてpackがあるので、とりわけ書き物ではなく会話の際に若干の混乱を引き起こします。
PSGIに対応する実装の名前空間は何にすべきですか?
PSGIバックエンドやアダプターを実装するために、PSGI::名前空間を使わないでください 。
PSGI名前空間は、PSGI仕様と、実装者が合格するためのリファレンス単体テストのために予約されています。それは特定の実装によって使われるべきではありません。
もしCamper
という(架空の)ウェブアプリケーションフレームワークのためにPSGIへ対応させるためのプラグインか拡張を書く場合、そのコードの名前はCamper::Engine::PSGI
のようになるでしょう。
もしPSGIインターフェースに対応するウェブサーバーを書く場合、あなたが望む名前を何でも付けてください。あなたはPlack::Handler の抽象インターフェースへ任意に対応するか、それ用のアダプターを以下のように書くこともできます:
my $server = Plack::Handler::FooBar->new(%opt);
$server->run($app);
あなたのサーバーでこのnew
とrun
のメソッドに対応すると、それはplackup互換になり、ユーザーがあなたのアプリケーションを<plackup>コマンドで実行できるようになります。このAPIに従うことは推奨されますが、PSGIアプリケーションのローンチャーを自分自身で提供する場合には、必須ではありません。
PSGI/Plackで動かしたい、CGIやmod_perl向けのアプリケーションがあります。どうすればいいですか?
いくつかの方法があります:
CGI::PSGI
クエリーパラメーターをハンドルするためにCGI.pmを使っているウェブアプリケーション(またはフレームワーク)であれば、CGI::PSGI がPSGIへの移行を手助けできます。あなたは、CGIオブジェクトの作成方法や、レスポンスヘッダーおよびボディーの返却方法を変更しなければなりませんが、残りのコードは変更なしに動くでしょう。
CGI::Emulate::PSGIとCGI::Compile
必要最小限の変更にとどめたい(または全く変更したくない)枯れたCGIスクリプトであれば、CGI::Emulate::PSGI とCGI::Compile がそれをコンパイルし、PSGIアプリケーションとして包み込んでくれます。
CGI::PSGI と比較すると、標準出力と標準エラー出力をキャプチャーして環境変数を切り刻んでいるために効率が悪いかも知れませんが、CGI.pmのみならずどのようなCGI実装を使っても動くはずですし、CGI::Compile はCGIスクリプトをmod_perlのレジストリーと同様にコードリファレンスに変換する役目を果たしてくれるはずです。
Plack::RequestとPlack::Response
HTTP::Engine に基づくアプリケーション(フレームワーク)である場合や、アプリケーションを一から書いてCGI より優れたインターフェースを必要とする場合や、Apache::Request を使っている場合には、Plack::Request とPlack::Response がお望みのものかも知れません。それは、PSGI環境ハッシュとレスポンス配列を巧く処理する、行儀のよいリクエストおよびレスポンスのオブジェクトAPIをもたらしてくれます。
注意: 一度だけ実行されて終了するというCGIスクリプトを持っていて、それを永続化プロセスに切り替えるときは、再設定される必要がある変数や、閉じられるか削除される必要があるファイルなどの、リクエスト後に都度起こす必要のあるものが一掃されるかもしれないということを忘れないでください。PSGIは、ここで親切な注意を行うことを除いて、この問題については手の打ちようがありません(あなたがそれを修正する必要があります)。
なぜHTTP::EngineではなくてPSGI/Plackなのですか?
HTTP::Engineは偉大な実験でしたが、アプリケーションインターフェース(request_handler
インターフェース)と実装が混ざっていましたし、一枚岩のクラス階層と役割に基づくインターフェースは、新たなバックエンドを記述するのには大きな困難を生じさせました。私達はHTTP::Engineの存在は保ったままで、これを3つの部分に割りました: インターフェース仕様(PSGI)と、リファレンスのサーバー実装(Plack::Server)と、標準的なAPIおよびツール(Plack)です。
HTTP::Engineは使われなくなるのですか?
使われなくなることはないでしょう。HTTP::Engineは現在の状態のままでいるでしょうし、フレームワークではなく極小のウェブサーバーアプリケーションを書きたい場合には、有用なままでもいるでしょう。
PSGIインターフェースへ適合させるために、私のHTTP::Engineアプリケーションを書き直す必要がありますか?
いいえ、既存のHTTP::Engineアプリケーションを書き直す必要はありません。HTTP::Engine::Interface::PSGI を使うことで、PSGIアプリケーションへ簡単に切り替えることができます。
あるいは、HTTP::Engine::Request とHTTP::Engine::Response のAPIと互換性のあるPlack::Request とPlack::Response を使うことができます:
use Plack::Request;
use Plack::Response;
sub request_handler {
my $req = Plack::Request->new(shift);
my $res = Plack::Response->new;
# ...
return $res->finalize;
}
これで、request_handler
がPSGIアプリケーションになります。
私のHTTP::EngineアプリケーションをPSGIで動くように改造することの利点は何ですか?
今日では、Plackによる多くのウェブサーバー実装とミドルウェア実装が、HTTP::Engineと同様に大抵は使えますので、PSGIへ切り替えることの直接的な利益をご案内できないかも知れません。しかし、PSGIはさらに将来が保証されています。そして、とても高速なサーバー実装(RubyのRackのためのPassengerを思い起こしてください)や、今日HTTP::Enginが備えていない多くの便利なミドルウェアを、私達が近い将来にも手に入れられるであろうと、大いに期待されています。
詳細は「私のフレームワークは既にCGIとFCGIとmod_perlで動かせます。どうしてPSGIに対応させたいのですか?」 の質問を参照してください。
PSGI仕様において選ばれた多くの設計は、バックエンドの必要条件を最小化するためのものであり、それによって色々な物事を最適化できていることに留意してください。PSGIレイヤーにおいて凝ったインターフェースを追加したり、柔軟性を許容すると、エンドユーザーに対しての口当たりはよくなるでしょうけれども、バックエンドがそれに対応する必要があるので、それは結局最適化の妨げになったり、さらなるバグをもたらしてしまうでしょう。凝ったAPIを作ってウェブアプリケーション開発者を魅惑するのはあなたのフレームワークであって、PSGIではありません。
APIがオブジェクトではなく巨大な環境ハッシュであるのはどうしてですか?
インターフェースの簡潔性は、WSGIとRackを成功させる鍵となっています。PSGIは、バックエンドとウェブアプリケーション開発者との間にある低水準のインターフェースです。もし私達が、どのオブジェクトが認められるか、どんなメソッドが実装されなければならないか、などとAPIを定義したら、バックエンドでは多くの重複したコードが生まれてしまうでしょうし、そのうちいくつかはバグが多くなってしまうでしょう。
例えば、PSGIは$env->{REMOTE_ADDR}
を文字列として定義しています。それでは、PSGI仕様をNet::IPのインスタンスにするためには何が必要なのでしょうか。バックエンドのコードは、通常ならNet::IPモジュールに依存させるところですが、Net::IPの全てのメソッドを実装するモックオブジェクトを書きます。特定のモジュールへバックエンドが依存していることや、たくさんのものを再発明することは有害であると考えられますので、インターフェースは必要最小限のものになっています。
エンドユーザーのために行儀のよいAPIを作成することはウェブアプリケーションフレームワーク(アダプター開発者)の仕事であるべきで、PSGIの仕事ではありません。
アプリケーションが->callメソッドを持つオブジェクトではなくコードリファレンスであるのは何故ですか?
コードリファレンスに加えて オブジェクトを要求することは、いくらかのより冗長な行数を「すべての」バックエンドのコードへ作らせてしまいますし、さらに、コードリファレンスに加えて オブジェクトを要求することは、もう一つのクラスと、オブジェクトをインスタンス化するコードを、アプリケーション開発者に書かせてしまいます。
言い換えれば、call
メソッドが動くオブジェクトはよいでしょうけれども、繰り返しますが、PSGIは可能な限り簡素になるように設計されています。そして、クラスやオブジェクトの中からコードリファレンスをこしらえることは簡単ですが、逆に、コードのいくらかの行数と、ことによると新たなファイルも常に要求してしまうのです。
ヘッダーがハッシュリファレンスではなく配列リファレンスを返すのはなぜですか?
端的には: 複合的なヘッダー(例えばSet-Cookie
)をサポートするためです。
具体的には: PythonのWSGIにおいて、レスポンスヘッダーは(header_name
とheader_value
から成る)タプル(tuples) 、すなわちtype(response_headers) is ListType
であり、これによって同一のヘッダーキーに複数のエントリーを持たせることができています。RackとJSGIにおいては、ヘッダー値は"\n
"で区切られた行から成る文字列です。
私達はここではPythonの仕様を好んでいて、Perlのハッシュが(それがtie
されているときを除いては)同一のキーによる複数のエントリーを認めていないために、[ key => value, key => value ]
を格納するために配列リファレンスを使うことで、フレームワークのアダプターとバックエンドを簡素なままに保つための最も単純な解決策としています。配列リファレンスに加えて生のスカラー値を許容すると、(フレームワークのアダプターとバックエンドの)両側のコードに不必要な冗長さを生じさせる、というような意見もあります。
$bodyはイテレーターに対応していないのですか?
WSGIとRackが、Pythonのイテレート可能オブジェクトや、Rubyのイテレーターという、PythonとRubyの言語が持つ長所を実によく活用していることを、私達は学びました。
例えばRackでは、each
メソッドに応答してバッファーをもたらすというオブジェクトとして、ボディーを以下のように予期しています:
body.each { |buf| request.write(buf) }
このコードは、ボディーが配列であろうと、FileIOオブジェクトであるろうと、イテレーターを実装しているオブジェクトであろうと、正に魔法のように動きます。Perlには、autobox がロードされていない限り、このような素晴らしいものは言語に備わっていません。PSGIはautoboxを前提モジュールにすべきでないので、私達は単純な配列リファレンスかファイルハンドルのみに対応させました。
IO::Handleのようなオブジェクトを書くことは、getline
とclose
を実装するだけなので、とても簡単です。少し不安定だとみなされるかも知れませんが、Considerファイルハンドルのように振る舞うオブジェクトを書くために、PerlIOを使うこともできます。
イテレーターのような何かをIO::Handleのようなものに変える方法については、IO::Handle::Util も参照してください。
sendfile(2)に基づくサービスへ切り替えることを、サーバーはどのように判断したらよいでしょうか?
まず第一に、アプリケーションは常に、getline
とclose
に応答するIO::Handleのようなオブジェクト(または大量の配列)をボディーへセットする「べき」です。そうすれば、どのサーバーでも確実に働きます。
もしサーバーがperlで書かれているか、ファイル供給用としてファイルデスクリプター番号(※訳註: UNIX系ファイルシステムにおけるi-node番号などのこと)を「C-land」(※訳註: 先生、わかりません! キリッ 「C(言語)の世界(つまりCで書かれたウェブサーバ然りOSのこと)」?)へ知らせることができるならば、ボディーが真のファイルハンドルであるかを、サーバーは任意に検査「できます」(ひょっとしたらPlack::Util のis_real_fh
関数を使えるかも知れません)し、そうすれば、その検査結果を使って、fileno
とsendfile(2)の呼び出しによるか、ゼロコピーデータ通信(※訳註: ユーザーメモリーからカーネルメモリーへのコピーを行わない、高速な通信方法のこと)相当のものによって、ファイルデスクリプターを取得できるでしょう。
そうでない場合で、サーバーがファイルデスクリプターを使ったファイルを送信できないのに、ローカルファイルのパスを必要としている場合(mod_perlやnginxのような場合)は、アプリケーションはpath
メソッドに応答するIO::Handleのようなオブジェクトを返せます。この種のIO的オブジェクトは、IO::File::WithPath や、IO::Handle::Util や、Plack::Util のset_io_path
関数を使って、簡単に作れます。
ミドルウェアは、ボディーがpath
メソッドを備えていて、かつ、何かができるか(X-Sendfile
ヘッダーが設定されているか否かなど)を、確認することもできます。
要約すると:
静的ファイルをサービスするとき、アプリケーションは常に真のファイルハンドルかIO::Handleオブジェクトを返すべきです。これで、どこでも動くようになりますし、いくつかの環境では最適化できるでしょう。
アプリケーションは、path
メソッドを加えたIO::Handleのようなオブジェクトを設定することもでき、それによって、どんな場所でもアプリケーションを実行し直せるはずですし、さらに多くの環境で最適化できます。
long-poll実装によるComet送信をするか、コンテントをストリーミングしたい場合は、どうすればいいですか?
サーバープッシュを実装するための最も簡単な方法は、プッシュ送信されるコンテントを返すために、あなたのアプリケーションに、getline
を実装するIO::Handleのようなオブジェクトをコンテントボディーとして返し続けさせることです。そうすればどこでも動きますけれども、それはプッシュ(push) というよりはプル(pull) であるのに過ぎず、Coroに見られるようなものを除いては、非ブロッキングIOをすることが難しいです。
アプリケーションをイベントループ内で実行し、クライアントの準備が整い次第コンテントボディーをプッシュするというサーバープッシュを行いたいならば、レスポンスを遅延させるためにコールバックを返すべきです。
# long-poll Cometのようなチャットアプリケーション
my $app = sub {
my $env = shift;
unless ($env->{'psgi.streaming'}) {
die "This application needs psgi.streaming support";
}
return sub {
my $respond = shift;
wait_for_new_message(sub {
my $message = shift;
my $body = [ $message->to_json ];
$respond->([200, ['Content-Type', 'application/json'], $body]);
});
};
};
wait_for_new_message
は、ブロッキング通信であることも、非ブロッキング通信であることも可能です: どうするかはあなた次第です。非ブロッキング通信で実行しようと望む多くの場合、AnyEvent のようなイベントループを使うべきです。非ブロッキング通信できるか、そうでなければブロッキングコールにフォールバックすることができるかを調べるため、psgi.nonblocking
の値を検査することもできます。
さらに、(FlashソケットやマルチパートXMLHTTPRequestを介してメッセージをストリーミングするように)コンテントボディーをストリーミングする場合は、以下のようにしてください:
my $app = sub {
my $env = shift;
unless ($env->{'psgi.streaming'}) {
die "This application needs psgi.streaming support";
}
return sub {
my $respond = shift;
my $writer = $respond->([200, ['Content-Type', 'text/plain']]);
wait_for_new_message(sub {
my $message = shift;
if ($message) {
$writer->write($message->to_json);
} else {
$writer->close;
}
});
};
};
ストリーミングするために、どのフレームワークを使うべきですか?
非ブロッキング通信に対応するサーバー(psgi.nonblocking
が真に設定されているもの)がありますが、フレームワーク側では非同期イベントループ(asynchronous event loop)に対応している必要がないことが問題です。例えばCatalystにはレスポンスオブジェクトにwrite
メソッドがあります:
while ($cond) {
$c->res->write($some_stuff);
}
これは、たとえブロッキング通信されているものであっても、マルチプロセス(psgi.multiprocess
が真である)のものであっても、psgi.streaming
に対応していれば全てのサーバーで動くでしょう。
getline
に対応するIO::Handleのようなオブジェクトにも、Catalyst::Engine::PSGI は対応していますので、IO::Handle::Util を使うこともできます:
my $io = io_from_getline sub {
return $data; # または、done()の場合にはundef
};
$c->res->body($io);
これでストリーミングとして満足に動かすことができますが、これは非同期サーバープッシュ(asynchronous server push)ではなくブロッキング通信(プル(pull) )であるので、あなたはこのアプリケーションを非ブロッキング(および非マルチプロセス)サーバー環境で実行しないように、再度気を付けなければなりません。
非同期および非ブロッキングストリーミングインターフェースに焦点を当てた多くのウェブフレームワークが現れる可能性や、既存のフレームワークがこれに対応する可能性があります。
psgi.streamingインターフェースはサーバーにとって必須ですか?
このインターフェースを実装しない強い理由がない限りは、それは記される「べき」 で、全てのサーバーはこのインターフェースを実装することが推奨されます。
しかし、究極の性能を追求するためにPerl XSインターフェースを使ったたり、Apacheやnginxのようなウェブサーバーと一体化させたりするというPSGIサーバーを実装する場合や、あるいは(Google AppEngineやHerokuのような)サンドボックス的な環境(※訳註: 実行権限が制約されている環境のこと)や、Gearmanのようなツールを使ったを分散プラットフォームを実装する場合には、このインターフェースを実装すべきではありません。
それでいいのです。そうした事例では、ストリーミングインターフェースに頼っているアプリケーションは、バッファリングした出力を非対応のサーバーに対して行う処理へフォールバックするために、Plack::Middleware::BufferedStreaming を相変わらず使えるのですから。
ハッシュとしてのHTTPヘッダーではなくCGIスタイルの環境変数を使うのはなぜですか?
多くの既存のウェブアプリケーションフレームワークには、CGI環境下で実行するためのコードやハンドラーが既に備わっています。HTTPヘッダーの代わりにCGIスタイルのハッシュキーを使うことで、PSGIをサポートするためのアダプターをフレームワークの開発者がより簡単に実装することができます。例えば、Catalyst::Engine::PSGI はCatalyst::Engine::CGI と数十行だけしか違わないですし、それは1時間未満で書かれたものです。
どうしてPATH_INFOのURIはデコードされないのですか?
CGI仕様(RFC 3875)および多くの(Apacheやlighttpdのような)ウェブサーバーの実装との互換性を保つためです。
トレイリングパス(trailing path)でfoo%2fbar
とfoo/bar
を見分けることが不便であることは理解していますが、CGI仕様では「PATH_INFO
はサーバーによってデコードされるべきであり、ウェブサーバーは%2f
を含んでいるリクエストなどを拒絶できる(そのようなリクエストはPATH_INFOで情報が失われるであろうため)」と明確に規定されています。これらのデコードされていない(または部分的にデコードされている)予約文字をそのままにしておくと、foo%252fbar
とfoo%2fbar
を区別できず、二重エンコードまたは二重デコードによるセキュリティーホールになりえますので、事態が悪化してしまうでしょう。
(Catalyst のような)ウェブアプリケーションフレームワークには、実際の生URIのさらなる制御が必要とされますので、私達はREQUEST_URI
環境ハッシュキーを必須としています。サーバーはデコードされていない(解析されていない)本来の(クエリー文字列を含む)URIをこのキーにセットすべきです。REQUEST_URI
は、たとえエンコードされた実体がURIとして使えるものであっても、完全に生の状態であることに注意してください。
ちなみに、WSGI (PEP-333)はSCRIPT_NAME
とPATH_INFO
の両方がデコードされていると定義していて、Rackはその定義を実装依存のものとしています。
http://www.python.org/dev/peps/pep-0333/#url-reconstruction http://groups.google.com/group/rack-devel/browse_thread/thread/ddf4622e69bea53f
WSGIのFAQは、APIをどのように設計するかということについての、たくさんの質問へ明快に答えていて、その内のいくつかはPSGIに直接適用することができます。
http://www.python.org/dev/peps/pep-0333/#questions-and-answers
MORE QUESTIONS? / 他に質問がありますか?
ここで回答されていない質問がありましたら、または、全く同意できない回答がありましたら、#plackというirc.perl.org上のIRCチャネルか、http://groups.google.com/group/psgi-plack というメーリングリストに参加してください。その際には、あなたがどちらの肩書きを持っているのかを明らかにしてください: アプリケーション開発者か、サーバー実装者か、ミドルウェア開発者か。そして、仕様に対して「ためにする批判」をしないでください: 仕様の制約によって動かない、または複雑になっている正確なコードを開示してください。私達は粗探しや「自転車置き場の議論」(※訳註: 些末な対象ほど紛糾してしまう議論のこと)には配慮しないでしょう。
Tatsuhiko Miyagawa <miyagawa@bulknews.net>
COPYRIGHT AND LICENSE / 著作権とライセンス
※訳註: 原文にはこのセクションの内容が(見出しを除いて)記述されていません( http://twitter.com/gardejo/statuses/13853425119 )。ただし、PSGI.podと同様のものが適用されるという見解を宮川さんから頂戴しています( http://twitter.com/miyagawa/statuses/13865409851 )。
I, gardejo, had executed a
git remote add http://git.coderepos.org/share/lang/perl/PSGI-Doc-JA.git
command.