mod_perl向けのスクリプトを開発していて気付いた事の覚書ですが、公開する以上は解り易くするため実例も用意しました。
mod_perlはアパッチサーヴァの拡張機能ですので、当然ウィンドウズネイティヴとされるIISサーヴァでは利用出来ません。
そこで、例えプロフェッショナルエディションであってもアパッチサーヴァを用意する必要があります。
アパッチサーヴァ自体は簡単に入手出来るのでそれほど問題にはなりませんが、問題はmod_perlの入手です。
調べてみたところ、幾つかの方法があります。
可能であるなら、これが一番確実でしょう。
しかしながら、ウィンドウズでメイクするには、マイクロソフト・ヴィジュアル C++ コンパイラ 5.0以降が必要です。
他社のC++コンパイラでは、コマンドラインの書式が違うのか、メイク出来ない事があります。
参考までにmod_perlソースコードの入手先は以下の通りです(いずれもtar.gz圧縮アーカイヴです)。
また、具体的なメイク方法は以下の文書をご覧下さい(いずれも英語)
PPMとは、Perlのバイナリをインストールするシステムで、アクティヴステイト社のActivePerlの現行版に添付されております。
マイクロソフト社のC++コンパイラを持っていない場合にはこの方法が簡単でしょう。
以下のコマンドをMS-DOSプロンプト/コマンドプロンプトから入力するとインストール出来ます。
mod_perlには 1.x(アパッチ 1.x用)と 2.x(1.99以降・アパッチ 2.x用)の二つのヴァージョンがあり、双方間の互換性は余りありません。
設定一つ取っても、大きな違いが見られます。
アパッチ 1.x で動作する mod_perl 1.x の場合、以下のような設定をhttpd.confまたは.htaccessで行います。
AddHandler cgi-script
拡張子<Files ~ "\.
拡張子$">
Options +ExecCGI
SetHandler perl-script
PerlHandler Apache::Registry
# Apache::Registry または Apache::PerlRun のいずれかを指定します。PerlInitHandler Apache::StatINC
# スクリプトの差替えに即時対応したてもらいたい場合にのみ記述します。PerlSendHeader On
# コンテンツ配信時にヘッダを送出してもらう場合はOnに、不要ならOffにします。</Files>
アパッチ 2.x で動作する mod_perl 2.x(1.99以降)の場合、以下のような設定をhttpd.confまたは.htaccessで行います。
AddHandler cgi-script
拡張子<Files ~ "\.
拡張子$">
Options +ExecCGI
SetHandler perl-script
PerlHandler ModPerl::Registry
# ModPerl::ModPerl または ModPerl::PerlRun のいずれかを指定します。PerlInitHandler Apache::Reload
# スクリプトの差替えに即時対応したてもらいたい場合にのみ記述します。PerlSendHeader On
# コンテンツ配信時にヘッダを送出してもらう場合はOnに、不要ならOffにします。setenv PERL_SEND_HEADER On
# 上記の PerlSendHeader の値と同じ値を指定します(mod_perl 1.xと互換性を持つため)。</Files>
最後の一行は、mod_perl 1.xと互換性を維持するためのものです。
mod_perl 1.x では、PerlSendHeader
を設定した場合には、環境変数 $ENV{'PERL_SEND_HEADER'}
に設定した文字列が入りますが、mod_perl 2.x ではこのような環境変数はありません。
そこで、当該拡張子を持つスクリプトが実行された際に無理矢理この環境変数を設定してしまうと言う訳です。
mod_perlに於いては、
my
宣言している変数を除く)点に注意すべきと言うのは基本中の基本と言えますが、環境変数%ENV
も例外ではありません。
環境変数については、サーヴァがキーごとに新たな値を設定しているだけで、新たな値が与えられないキーに関してはサーヴァは何もしてくれないようです。
この結果、ユーザエージェント独自の環境変数が与えられた場合、それは次回実行時に削除されずにそのままの値が参照出来る事になります。
特に携帯電話には独自のキーの環境変数を利用する端末があるので、これらの環境変数を扱う場合には充分注意しましょう。
HTTP_X_JPHONE_
で始まるキーが、EZウェブのUPブラウザではHTTP_X_UP_DEVCAP_
で始まるキーが、独自の環境変数のキーとなります。これらのキーの環境変数の有無で機種判別を行った場合、mod_perl上では例えばEZウェブ端末のアクセス後にドコモがアクセスするとそれもEZウェブと誤認する危険が生じます。具体的な対策としては、環境変数の性質上実行時に初期化する事は不可能なので、スクリプト終了時に必ず
undef %ENV;
を実行して環境変数ハッシュも初期化するようにすると良いでしょう。
mod_perl固有の環境変数がいくつかあります。
$ENV{'MOD_PERL'}
mod_perl環境ではこの環境変数が設定されます。
内容は、mod_perlのヴァージョンなどが入ります。
$ENV{'PERL_SEND_HEADER'}
コンテンツ配信時にサーヴァにHTTP 応答ヘッダを調整してもらう設定なら On、そうでなければ Off が入ります。
mod_perlの場合、しばしば出力をバッファリングせずに行う(事前に $|=1;
を実行する)事が多いようです。
制作者も見よう見まねでそうしましたが、その際に幾つかのトラブルに見舞われました。
エラーやリダイレクトなど、200 OK 以外のHTTP ステータスコードを出すと、その際に配信したコンテンツの後ろに余計なものが憑いて来るようなのです。
例えば、指定されたリソースが見出せない場合にカスタムエラーメッセージを送り出すスクリプトの場合、当然HTTP ステータスコード 404 Not Found も出さなければならない訳ですが、そうするとサーヴァで設定しているエラー文書が勝手に憑いて来てしまうのです。
これはXMLなどを配信するスクリプトでは致命的な問題となります。
特にコンテンツを GZIP で圧縮してから配信する場合、圧縮されたコンテンツに無圧縮のエラー文書がそのまま後続すると、ユーザエージェントが展開した際に意味不明のバイナリが文書の後ろに憑く事になります。
当然、XML扱いのユーザエージェントでは配信した XHTML文書 や WML文書がパースエラーになりました。
リダイレクトでも余計なものが後ろに憑いてくるので、やはりトラブルの種になりました。
これらのトラブルは、バッファリングで配信前にコンテンツを適切に補正する事で何とか解決したのです。
結局、HTTP ステータスコードが 200 OK 以外になる場合には必ずバファリングを行うようにすると(配信直前に $|=0;
を実行すると)良いようです。
PerlSendHeader On
にしないと拙いと思われます。CGIでは、しばしば転送量の節約などのためにコンテンツをGZIPで圧縮する事があります。
それを最も簡単に行うには、.htaccessなどでコンテンツ配信時に自動的に圧縮するように指定する事ですが、そのような設定が可能かどうかを想定しないスクリプトではコンテンツ出力時にパイプ処理でOSが持っているGZIPコマンドに引渡すようにしている場合が多いようです。
つまり、出力処理で
print "content-encoding: gzip\n";
open(STDOUT,"|
GZIPへのOS内のパス-9 -c");
print $content;
等とする訳です。
ところが、mod_perlを利用する場合、通常のやり方は使えないようです。
結局、mod_perlでコンテンツを配信する場合には、出力時にGZIPにて圧縮する必要があります。
すなわち、出力に先立って以下のような処理を行う訳です。
この処理を実現するには以下のようなコードになるでしょう。
print "content-encoding: gzip\n";
require Compress::Zlib;
$content=Compress::Zlib::memGzip($content);
print $content;
ただ、この方法では圧縮率を変える事が出来ないため、圧縮効果が若干落ちてしまいます。
GETメソッドとPOSTメソッドの両方を一つのスクリプトで扱う場合には注意が必要です。
GETメソッドの場合に標準入力からデータを受け取ろうとすると、意味不明のクエリを受取る事があります。
このため、GETメソッドの場合、すなわち環境変数 $ENV{'REQUEST_METHOD'} の値が 'POST' でない場合には、そのクエリを無効にする必要があるでしょう。
通常、CGI上でPerlスクリプトを動作させる場合、実行時に自動的にsrand(time())
が実行され、その時点の時刻に合わせた乱数の初期化が行われます(Perlのヴァージョンが旧い場合を除く)。
これに依り、rand()は実行時刻に依り異なった値が得られます。
しかし、mod_perl上でPerlスクリプトを動作させた場合、自動的に実行されるsrand(time())
でのtime()函数の値は始めて実行された際のものとなり、この結果二度目以降rand()の値は何度実行させても同じ結果になってしまいます。
このため、rand()函数を利用する場合には、必ずスクリプトの初期設定でsrand(time())
を書いてやる必要があります。
srand(time())
が実行されないようですので、その意味でもやは明記すべきと言えます。アパッチにmod_perlをインストールして設定する方法や、実際にmod_perl対応スクリプトを書くための注意点が書かれております。
mod_perlで使えるApacheモジュールのリファレンスです。
但し、制作者は非mod_perlとの互換性を維持するため、敢えてこれらのモジュールは使いませんでしたが(その所為でいろいろ徒労したのでしょう)、mod_perlのもとでのみ使うスクリプトであれば、これらを活用する方がいいでしょう。