Chapter 1. Introducing CGI and mod_perl を読んで

Practical Mod-Perlという本があるのだけれど、その内容はオープンソースになっている。
mod_perlはいつかちゃんと勉強しなきゃと思っていたので読みはじめました。
んで、やっぱり書かないと覚えないし身に付かないし。

    • -

CGIの歴史

まあ、これはね。問題ないでしょう。
HTTPを使ってデータをやりとりします。ヘッダとデータは空行で区切られます。リクエストを受け取ってレスポンスを返します。レスポンスではヘッダーでMIMEタイプとか文字コードとかを指定します。Apacheはmod_cgiを使ってスクリプトを実行します。環境変数を使っていくつかのパラメータを受け取れます。HTTP_USER_AGENTは簡単に偽装できます。HTTP自体にはURIの長さ制限がないけどサーバーとクライアントにはあるので注意しましょう。ユーザーから受け取った値を使ってレスポンスを作成する場合はクロスサイトスクリプティングに注意しましょう。

Apache 1.3のサーバーモデル

forking modelです(Apache2には他のモデルもあるとのこと)。

サーバーを起動するとparent processと呼ばれるプロセスが起動します。それ以降は必要に応じて子プロセスを生成(fork)します。ひとつのプロセスがいくつのリクエストを処理するかはMaxRequestsPerChildの値で決まります。これでHTMLとか画像とかのスタティックなファイルを処理できます。CGIを処理する場合は子プロセスからさらにプロセスをforkします。これと同時にPerlインタープリタもロードします。両方ともリクエストが終わると破棄されます。このモデルの利点の1つは、あるプロセスが死んでも周りに影響を及ぼさないことです。欠点は、

  1. forkのオーバーヘッド(でも最近のUNIX系システムではあまり影響ない)
  2. Perlインタープリタのロードによるオーバーヘッド
  3. コードのロードとコンパイルによるオーバーヘッド
  4. 永続的な変数とかDBへのコネクションとかが作れない

などがあります。さらにmod_cgiではコンテンツハンドラのフェーズで実行するCGIスクリプトしか書くことができず、他のフェーズで何かをしたい場合にはApacheモジュールを別にインストールしないといけません。

mod_perl 1.0での開発

mod_perlではApacheの子プロセスにPerlインタープリタを組み込むことでmod_cgiのforkを回避します。PerlインタープリタはApaccheの子プロセスが起動するときに1回だけロードされます。コードも最初に1回だけロードされてコンパイルされます。あとは単にコードを実行するだけなのでレスポンスタイムが短くなります。でも、mod_perlの本当の功績はApache coreへのAPIを提供することです。これによってApacheモジュールをPerlで書くことができ、すべてのリクエストフェーズをハンドルすることができるようになります。でもmod_perlを組み込むことでプログラムが大きくなるのでメモリ消費量は増えます。ただし実際にはレスポンスタイムが短くなって1つのプロセスで処理できるリクエスト数が増えるのでそんなに深刻な問題ではありません。
後方互換性のためにmod_perlCGIスクリプトを動かすことができます。そのためにはApache::PerlRunかApache::Registryというモジュールを使います。両者の違いはApache::Registryがすべてのスクリプトをキャッシュするのに対してApache::PerlRunはそうしないことです。その理由の1つは無防備に使用されたグローバル変数にあります。グローバル変数が1回目のリクエスト時だけに初期化され、以降のリクエスト時に初期化されないとするとバグの要因となります。use strictを使いましょう。正規表現のoオプションには気をつけましょう。
Apache::Registryは高速ですが、CGIスクリプトmod_perlハンドラを使うようにすればもうちょっと早くなります。
Apache::Registryはスクリプトを独自のネームスペースに突っ込んでからevalしてコンパイルします。
コンパイル後にCGIスクリプトが変更されたら?Apache::Registryは最終変更日をチェックして変更されていたらリロードしてコンパイルし直します。

Apache 1.3のリクエスト処理フェーズ

Apacheはリクエストを11のフェーズに分けて処理します。それぞれのフェーズにはデフォルトのハンドラがあるけどそれをオーバーライドしたり拡張したりできます。

Post-read-request
リクエストデータを読み込んでHTTPヘッダを解析してから呼ばれます。プロセスごとに必要なデータを初期化するのに使ったりします。Apache2::UploadProgressはこのフェーズで動きます。
URI translation
URIを実際のファイルなどに解決します。ScriptAliasなどのディレクティブを参照します。mod_rewriteはこのフェーズで呼び出され、URIの解決を完全にカスタマイズできます。
Header parsing
ヘッダーを調べてなんかできます。
Access control
特定のリソースへのアクセスをIPアドレスや曜日などで制限できます。
Authentication
ユーザー名/パスワードなどでユーザーの認証を行えます。
Authorization
ある領域へのアクセスを特定のユーザーだけに許可するといったことができます。
MIME type checking
ここでリクエストの処理方法が決まります。スタティックなファイルは直接返したり、CGIスクリプトならmod_cgiだったり。
Fixup
最後の調整とリクエストの記録を行います。
Response
もっともいろんなことをするところ。ヘッダーとコンテンツを返します。mod_cgiApache::Registryなどのコンテンツハンドラはこのフェーズで動きます。
Logging
デフォルトではファイルに書き込みます。
Cleanup
いろんな後片付けをします。各モジュールはクリーンアップコードを登録できます。

各フェーズでエラーが発生した場合は、デフォルトのエラーハンドラを使ってApacheがエラーコードを返します。
リクエストフェーズを細分化するメリットは各フェーズでプログラマにフックを提供できることです。
フックしたとき、モジュールではhandlerというメソッドが呼ばれます。
他のApacheモジュール同様、mod_perlもCで書かれていて、リクエストフェーズにハンドラを登録してApacheAPIを使いますが、異なる点はリクエストを処理しないでPerlで書かれた(あなたが書いた)ハンドラにディスパッチするってことです。PerlハンドラはCGIとの互換性がない代わりに、Responseフェーズに縛られることなく、他のフェーズでいろんな処理ができます。

    • -

http://modperlbook.org/html/ch01_01.html
Practical mod_perlクリエイティブコモンズです。
ってあまりよくわかってないんだけどリンク提供するだけでいいのかな。