mod_perl2

日付どころか月まで変わっているけれども、引き続きの話題なので。。。
ちょっと経緯をすっ飛ばしたので、説明しますと、うちのwebサイト(不思議な話リンク集)の更新をすると、一気に十数ページのHTMLが増えるんですよ。
で、これまでは「メモ帳」に手打ちでHTMLのタグを書いていたのだけれども、perlを使えば、もっと簡単に更新できるのかな?って考えて。
で、perlCGIモジュールにたどり着いたのですが。
これまでも完全には手入力ではなく、SEDコマンドを使っていたのですが、そのため、sedとかのUNIX系コマンドを使うために、cygwinを入れていました。
で、perlcygwinperl使ってます。
やっと本題。
で、perlCGI.pmって遅いらしい。
手入力していた私からすると、別に遅いとか思わないけれども。
で、偶然見つけたのがCGI::Fast

cpan CGI::Fast

 だけで手に入った。

use CGI::Fast qw/:standard :no_xhtml/;

my $q = CGI::Fast->new;
print $q->start_html(-lang => '',
                         -head=>[meta({-http_equiv => 'Content-Type',
                                        -content    => 'text/html; charset=Shift_JIS'})
                         ],
                         -title=>'タイトル');

 で

<!DOCTYPE html	PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"	 "http://www.w3.org/TR/html4/loose.dtd">
<html><head><title>タイトル</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
</head>

 普通のCGIモジュールと同じになる。で、実際、速い。
で、さっき「perlでHTMLを編集」と書いたので、「perl自体では編集は出来ないだろ」と思った方もおられると思うのですが、「自分のPCでperlCGIを使ってajaxにHTMLを編集」という、斜め上な目標でやってます。
ので、cygwinでApache2サーバを立ち上げました。 ApacheサーバーのMakeは==>こちら
httpd.confに

Listen localhost:80

 って入れて、安全を確保したつもり。
で、タイトルの話になるのですが、Apache2にmod_perl2ってゆうモジュールを追加すると、さらにperlCGIが速くなるという話を見つけた。

cpan Apache2::MPM

 でやって、最初につまずいたのは、"apxs"が無いというエラー。
cygwinのsetup.exeを使って"apache2-devel"をインストールすると、

/usr/sbin/apxs2

 というのが出来る。
これで、やっと安心かと思いきや、次は"apr-config"が無いというエラー。
これは、またcgywinのsetup.exeで、今度は"libapr1-devel"をインストールすると

/bin/apr-1-config

 というのが出来る。
今度こそ、と思ったら"gcc-4"が無いというエラー。
またcgywinのsetup.exeで、今度は"gcc4"をインストールする。
しかし、makeでエラーが出る。
エラーをパッと見た感じでは、「今度はbase64を入れないといけないのか?」と思ったが、"libapr1-devel"をインストールした時点でapr-base64.hはインストールされている。
エラーをよく読むと、

/.cpan/build/mod_perl-2.0.4-tvjBdJ/xs/modperl_xs_typedefs.h:67行

 にエラーがあると言っている。ググると、[Perl][Apache]mod_perl2.0.4のビルド中、modperl_xs_typedefs.hでエラーが出る場合の対処法を発見。
その通りに2個のファイルを書き換えて、

cd /.cpan/build/mod_perl-2.0.4-dMqEoV
perl Makefile.PL
make install

 で、無事インストール出来ました。
で、httpd.confに

LoadModule perl_module lib/apache2/mod_perl.so

 を追加。Apache2を起動。すると、error_logに。

Apache/2.2.6 (Unix) mod_ssl/2.2.6 OpenSSL/0.9.8o DAV/2 mod_perl/2.0.4 Perl/v5.10.1 configured -- resuming normal operations

をを、ちゃんと読み込まれている。で、さらにhttpd.confをいじって

<IfModule mod_perl.c>
  <Files  ~ "\.pl$">
    SetHandler perl-script
    AddHandler perl-script .pl
    PerlHandler ModPerl::Registry
  </Files>
</IfModule>

 と書いて、起動して、*.plなCGIファイルをブラウザで開くと、
をを、動いた。
で、さらにhttpd.confを

<IfModule mod_perl.c>
  <Files  ~ "\.pl$">
    SetHandler modperl
    AddHandler modperl .pl
    PerlOptions +GlobalRequest
    PerlHandler ModPerl::Registry
  </Files>
</IfModule>

 と書き換えて、同じ*.plなCGIファイルをブラウザで開くと、"500 Internal Server Error"
error_logを見ると、

[error] Can't call method "header" on an undefined value

 *.plなCGIスクリプトCGIモジュールの"$q->header();が解釈できずに、エラーに。
しかたがないので、前のコンフィグに戻してみた。
が、同じエラーが出現。
で、ふと、*.plなCGIスクリプト

use CGI::Fast qw(:standard :no_xhtml);

 に書き換えると、1回目は動いた。が、2度目に同じエラーが出現。
"CGI header error"でググると、外人が、日本人ではちょっと見ない書き方でCGIモジュールを使っているのに遭遇。
まねして、書いた

use CGI::Fast qw(:standard :no_xhtml);

print start_html(-lang => '',
                         -head=>[meta({-http_equiv => 'Content-Type',
                                        -content    => 'text/html; charset=Shift_JIS'})
                         ],
                         -title=>'タイトル');

 そう、"my $q = new CGI::Fast;"ってやってない。でも動いた。これだとmod_perlでのエラーもなくなった。

10/11追記

 自分みたいに、ローカルな開発環境でmod_perlを動かすと、あるperlスクリプトをテストして、駄目だった場合、スクリプトを書き直してテストしようとしても、前のスクリプトが読み込まれたままで、新しいスクリプトは実行されない。
何度もトライアンドエラー出来るのがインタプリタ言語の利点だと思うので、これはちょっと嫌な感じ。
で、この問題を解決するには、Apache2::Reloadを使えば良いらしい。
ただし、安易に使うとパフォーマンスが落ちてmod_Perlの意味がなくなるので注意。
Apache2::Reloadはmod_Perlのパッケージ自体には入っていないので、

cpan Apache2::Reload

 で入手。
httpd.confを書き換える。

<IfModule mod_perl.c>
PerlModule Apache2::Reload
PerlInitHandler Apache2::Reload
PerlSetVar ReloadDirectories "(perlスクリプトのある場所)"
  <Files  ~ "\.pl$">
    SetHandler perl-script
    AddHandler perl-script .pl
    PerlOptions +GlobalRequest
    PerlHandler ModPerl::Registry
    Order allow,deny
    Allow from all
  </Files>
</IfModule>

 これで、"/usr/sbin/apachectl2 -t"で"Syntax OK"だったので、Apache2起動。
うむ、書き直すと即座に反映される。
でも、mod_perlは初回実行時と2回目以降で挙動が変わるので、同じスクリプトのテストを数回やってみないとバグが判らない罠。。。

以下、10/1追記分

 話が変わるけれども、mod_perlにしてからglob();の動きがおかしい。
ググるmod_perl時の注意点というページに、perl実行時のカレントディレクトリがperlスクリプトのある場所じゃないと書かれている
そのページにある確認用スクリプトを借用して

#!/usr/bin/perl

my $r =shift;
$r->content_type('text/html');
$r->headers_out->add('charset'=>'Shift_JIS');
print "カレントディレクトリ : " . `pwd` . "\n";

 というCGIを書いて、ブラウザで開いたところ、

カレントディレクトリ : /cygdrive/c/WINDOWS/system32

 これにはずっこけた。
なので、全ての*.plなCGIスクリプトの最初の行に
chdir $(ファイルあるパス);
を追加した。のだが、ここで問題が。
さっきのまま手を加えていない確認用スクリプトを、何気なく実行すると、

カレントディレクトリ : /cygdrive/c/(perlファイルのあるパス)

 に変わっていた。1つのスクリプト内でchdirを実行するだけで、mod_perlシステム全体のカレントディレクトリが変わってしまい、保持されるようだ。
これはヒドイ
ので、perlCGIは同じディレクトリにまとめておかないといけない。

10/9追記

 MOD_Perlを再ビルドした時に「ソースはそのまま残してある」とサラッと書いたのですが、これは実はとても重要。
libapreq2をビルドしようといろいろやっていた時に見つけた情報。

/lib/perl5/site_perl/5.10/i686-cygwin/Apache2/BuildConfig.pm

 の中に、MAKEしたパスがフルパスで記録されているのです。
なので、MOD_Perlのライブラリ"libaprext.a"は、デフォルトでは

/usr/include/apache2/libaprext.a

 にインストールされる。なんでこんな場所なのか。
で、"/usr/lib"にコピーした場合、必ずBuildConfig.pmの中も書き換えないといけないらしい。