Plack::Builder+Plack::Middleware::Staticで Plack::App::Directoryみたいな事をやってみる。
そんなわけでとりとめもない記事です。
ディレクトリ構成
こんな感じ
static.psgi root/ root/index.html root/xxx.html root/hoge/index.html root/hoge/xxx.html
psgi
% cat static.psgi use Plack::Builder;builder { enable "Plack::Middleware::Static", path => sub { s!(.*/$)!${1}/index.html! or return qr{^/.+} }, root => './root/'; };
結果
- http://localhost:5000/ でアクセスすると http://localhost:5000/index.htmlと同じコンテンツが表示されます。
- http://localhost:5000/hoge/ でアクセスすると http://localhost:5000/hoge/index.htmlと同じコンテンツが表示されます。
- ただし、http://localhost:5000 と http://localhost:5000/hoge はnot found.
解説
Plack::Middleware::Staticの_handle_static()を見てみます。
package Plack::Middleware::Static; use strict; use warnings; use parent qw/Plack::Middleware/; use Plack::App::File; use Plack::Util::Accessor qw( path root encoding pass_through ); ... sub _handle_static { my($self, $env) = @_; my $path_match = $self->path or return; my $path = $env->{PATH_INFO}; for ($path) { my $matched = 'CODE' eq ref $path_match ? $path_match->($_) : $_ =~ $path_match; return unless $matched; } $self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); local $env->{PATH_INFO} = $path; # rewrite PATH return $self->{file}->call($env); }
以下の行でAccessorとしてpath, rootと他2つ定義されています。
use Plack::Util::Accessor qw( path root encoding pass_through );
そのpathですが、以下の記述からコードリファレンス、もしくは正規表現のリファレンスを期待している事がわかります。
my $matched = 'CODE' eq ref $path_match ? $path_match->($_) : $_ =~ $path_match;
ここで自前で実装したstatic.psgiでpathに対して次のようなコードリファレンスを渡します。これはこの無名関数に渡された文字列の最後が/で終わる場合、その部分を/index.htmlと書き換えます。
sub { s!(.*/$)!${1}/index.html! or return qr{^/.+} }
もし、書き換えられた場合は次の行でrewriteされます。localはそのスコープ内でのみ値を一時的に保存します。
local $env->{PATH_INFO} = $path; # rewrite PATH
localで一時的に保存された値はスコープ内でのみ有効。で、そのスコープ内でPlack::App::File->new->call($env)が実行されます。
$self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); local $env->{PATH_INFO} = $path; # rewrite PATH return $self->{file}->call($env);
余談
ちなみになんとなくこの処理はこの順番のほうが(処理の流れを説明する場合は)いい気がする。次のような感じ。
local $env->{PATH_INFO} = $path; # rewrite PATH $self->{file} ||= Plack::App::File->new({ root => $self->root || '.', encoding => $self->encoding }); return $self->{file}->call($env);
でもlocalで変化している期間を最小にしたいなら元の書き方が正しい気もする。うーん。
独り言
はてなぶろぐつかってみたいなー