2012年01月11日

PATH_INFOと%2Fの関係

 CGIを呼び出す際、昔はパラメータをhttp://example.com/hoge.cgi?user=tom&key=valueと、?と&で繋げるのが常識だったわけですが、SEO的にサーチエンジンにも好まれないということでhttp://example.com/hoge/tom/valueといったあたかもディレクトリ構造であるかの様な表記が出てきました。(たぶんAmazonが先駆者だと思う)

 さて、http://example.com/hoge/tom/valueだとちょっとスクリプトがどこかわかりづらいので、http://example.com/hoge.cgi/tom/valueを例にとりましょう。"hoge.cgi"以降の情報を取得するには、$ENV{'PATH_INFO'}を使います。
#Script1
print $ENV{'PATH_INFO'};
【結果】/tom/value

これをパラメータとして取得するには、頭の/を落として、/でsplitしてやればいいわけです。
#Script2
my $param = $ENV{'PATH_INFO'};
$param =~ s/^\///;
my @data = split('/',$param);
print join('&',@data);
【結果】tom&value




 ところで・・・実際に使用する際にはURLエンコード(Percent-Encoding)されたものがくる可能性もありますので、URI::Escape::uri_unescape()などでパラメータを元に戻してやる必要があります。(ちなみに、URI::Escapeはコアモジュールではなかったりします。まあ、たいていのレンタルサーバには入っているとは思うけれども)

 で、ここからがややこしいお話。PATH_INFOに/がURLエンコードされた"%2F"が入っている場合、Apacheの設定でAllowEncodedSlashes onにしてやらないと404エラーになります。Debian(squeeze)からapt-get installした限りでは、ONになっていませんでしたので設定ファイルをいじってやる必要があります。で、この項目は.htaccessでは設定できないんですよ(<VirtualHost>〜</VirtualHost>内に記述するのはOK)。自宅サーバや専用サーバ・VPSの人はいいけれども、共用レンタルサーバの人は管理者にお願いするかそういう設定をしているサービスを探す必要があります。

 そんでもって、さらにややこしいお話。$ENV{'PATH_INFO'}で取得するパラメータには、当然&2F(/)が含まれる可能性もあるわけで、そのためにApacheのAllowEncodedSlashesをonにしました。
http://example.com/hoge.cgi/tom/value%2Fmoneyのパラメータを上記#Script2で取得したら、"tom"と"value%2Fmoney"が得られるはずです。
 ところがどっこい、Apacheに問題あり。頼んでもいないのに"%2F"を勝手に"/"へデコードしてくれちゃいます
その結果、$ENV{'PATH_INFO'}に入る値は/tom/value/moneyであり、#Script2で取得される値は"tom","value","money"です。バグ発生!
 この辺のApacheの挙動については、【PATH_INFO中の%2Fがデコードされる - Practice of Programming】が大いに参考になりました。

 じゃあどうしましょうかというと、$ENV{'REQUEST_URI'}と$ENV{'SCRIPT_NAME'}を使いましょうということになります。http://example.com/hoge.cgi/tom/value%2Fmoneyを呼び出した例ですと、$ENV{'REQUEST_URI'}には"http://example.com/hoge.cgi/tom/value%2Fmoney"が、$ENV{'SCRIPT_NAME'}には"http://example.com/hoge.cgi"が入っています。
$ENV{'REQUEST_URI'}から$ENV{'SCRIPT_NAME'}を「引いて」やれば、$ENV{'PATH_INFO'}と同じことができます。
#Script2を手直しするとこんな感じ。
("http://example.com/hoge.cgi/tom/value%2Fmoney"で呼び出したと仮定)
#Script3
my $param = $ENV{'REQUEST_URI'};
my $pattern = "^$ENV{'SCRIPT_NAME'}/"; #スラッシュ忘れるなよ
$param =~ s/$pattern//;
my @data = split('/',$param);
print join('&',@data);
【結果】tom&value%2Fmoney

@dataの中身についてはこの後URI::Escape::uri_unescape()するなり、Encode::decode()するなり、よしなに。


と、いうことを新しいスクリプト(の共通ライブラリ)を書いていてわかったので記録しておきます。

なお、mod_rewriteを使ってURLの書き換えをしている場合、$ENV{'REQUEST_URI'}と$ENV{'SCRIPT_NAME'}の頭が一致しないという事象が発生するらしいので(cf.REQUEST_URIとSCRIPT_NAMEの違い - 蜘蛛の糸をつむぐには…)mod_rewriteを使う場合には素直にmod_rewriteで?&=と/の書き換えもやりましょう。
タグ:Perl Apache
posted by 鯖缶 at 20:48 | Comment(0) | TrackBack(0) | ただいま開発中 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/245588255

この記事へのトラックバック