- 複数アプリ対応
- local-server, external-server両方の例
- $c->baseや$c->uri_forも正しく動く
- app/root/static以下のファイルはlighttpdが処理する
server.modules += ( "mod_alias", "mod_redirect", "mod_fastcgi")
server.document-root = "/var/www/localhost/htdocs"
url.redirect = ("")
alias.url = ("")
# app1
url.redirect += ( "^/app1(\?.*)?$" => "/app1/$1"
alias.url += ( "/app1/static/" => "/path/to/App1/root/static/" )
$HTTP["url"] =~ "^/app1/(?!static/)" {
fastcgi.server = (
"/app1" => ( "localhost-ext" => (
"check-local" => "disable",
# -- external --
"host" => "127.0.0.1",
"port" => 10021,
# -- local --
# "bin-path" => "/path/to/App1/script/app1_fastcgi.pl",
# "socket" => "/tmp/app.sock",
))
)
}
#app2
url.redirect += ( "^/app2(\?.*)?$" => "/app2/$1" )
alias.url += ( "/app2/static/" => "/another/path/to/App2/root/static/" )
$HTTP["url"] =~ "^/app2/(?!static/)" {
fastcgi.server = (
"/app2" => ( "localhost-int" => (
"check-local" => "disable",
# -- external --
# "host" => "127.0.0.1",
# "port" => 10022,
# -- local --
"bin-path" => "/other/path/to/App2/script/app2_fastcgi.pl",
"socket" => "/tmp/app.sock",
))
)
}
FastCGIの動作を変える重要なオプションは"bin-path", "check-local"と,fastcgi.server配列のキー("/app1"など,ドキュメントでは<extension>)の三つです。
まずは"bin-path"です。このオプションがあるとlighttpdは子プロセスとしてFastCGIプロセスを起動します(local-server)。そのとき,"socket"オプションで指定したファイルを開いてFastCGIプロセスの標準入力とし,このソケット経由で通信します。今回は省略していますが,{min,max}-procsやbin-environなどのオプションはこのモードのときだけ有効です。"bin-path"が無いときはlighttpdはFastCGIプロセスの起動に関与せず(external-server),host:portまたはsocket経由でFastCGIプロセスと通信します。
続いて"check-local"です。最初,このオプションの意味が良く分からなかったのですが,「これはPHP向けなんだな」と気づいてようやく意味が分かりました。"check-local" => "enable"のとき(こっちがデフォルトです),lighttpdは"document-root/path"を探してファイルがあるかどうかをチェックします(例えば/index.php → /var/www/localhost/htdocs/index.php)。ファイルが存在しているときに限りFastCGIに処理を渡します。存在しないときは404となります(Railsではこの404-handlerを使って処理を渡しています)。"check-local" => "disable"のときはこのチェックが省略され,document-root以下にファイルがあろうがなかろうがFastCGIに処理を回してくれます。
最後に"<extension>"です。".ext"と拡張子で指定するか,"/app1"のように/から始まるprefixで指定するかの,二通りの方法があります。ドキュメントには書いていないですが,ここの指定により,FastCGIに渡すSCRIPT_NAME, PATH_INFOが決定されます。
prefixで指定するときには"/app1"とするか,"/app1/"とするかで違いがあります。ドキュメントではExamplesの中でこっそりと書いているので分かりにくいですが,最後に"/"を付けるか付けないかで,SCRIPT_NAMEとPATH_INFOの値が変わります。"/app1/"としたときにはその後に続くファイル名(と推定される部分?)をSCRIPT_NAMEとします。"/app1"としたときには"/app1"がSCRIPT_NAMEになります。また,$HTTP["url"]で条件分けしているからといってこの部分を省略したりすると,SCRIPT_NAMEとPATH_INFOが予期しない値になってしまいます。
以上の3点が設定できれば後は細かな調整だけです。"/app1"→"/app1/"としたりするなら,301 Moved Permanentlyを返してブラウザ側にURLを変えさせた方が良いので,url.redirectを使うようにします。(本当はパスではなく絶対URLで返した方が良いようなのですが,$HTTP["host"] + "/app1/$1"などとやってもダメだったので妥協)
"/app1/static/"以下をCatalyst + C::P::Static::Simple経由ではなく,lighttpdに直接処理させるには,document-rootと関係なく処理できるurl.aliasを使っています。同時にFastCGIハンドラに処理が渡らないよう,条件文でfastcgi.serverの設定を切り分けています。
url.redirect, alias.urlの設定を「最初は = ("..." => "...")で,後は += ("..." => "...")で」といった非対称な設定にするより,全部+=で書けるようにするために,ダミーを放り込んでいます。alias.url = ()とは書けなかったので = ("")と空エントリを入れています。(何でこれで良いのかは未調査)
SCRIPT_NAMEやPATH_INFOが正しく渡っていることを確認してみましょう。App1のdefaultで色々表示させてみます。
sub default : Private {
my ( $self, $c, @args ) = @_;
my $str = "App1::default\n"
. "uri: " . $c->request->uri . "\n"
. "base: " . $c->request->base . "\n"
. "arg: " . join(':',@args) . "\n"
. "req->arg: " . join(':',@{$c->request->arguments}) . "\n"
. "req->parameters: " . join(', ',map{"$_:".$c->request->parameters->{$_}} keys %{$c->request->parameters}) . "\n"
. "engine->env->{REQUEST_URI}: " . $c->engine->env->{REQUEST_URI} . "\n"
. "engine->env->{SCRIPT_NAME}: " . $c->engine->env->{SCRIPT_NAME} . "\n"
. "engine->env->{PATH_INFO}: " . $c->engine->env->{PATH_INFO} . "\n"
. "engine->env->{QUERY_STRING}: " . $c->engine->env->{QUERY_STRING} . "\n"
. "uri_for: " . $c->uri_for('test') . "\n";
$c->response->body( $str );
}
$ script/envtest_fastcgi.pl -n 1 -l :10021
として,:10021で待たせます。テストなのでデーモン化はしていません。
$ GET -e 'http://localhost:8080/app1/foo/hoge?a=1&b=2'
Connection: close
Date: Sun, 26 Feb 2006 14:33:36 GMT
Server: lighttpd/1.4.10
Content-Length: 355
Client-Date: Sun, 26 Feb 2006 14:33:36 GMT
Client-Peer: 127.0.0.1:8080
Client-Response-Num: 1
X-Catalyst: 5.64
App1::default
uri: http://localhost:8080/app1/foo/hoge?a=1&b=2
base: http://localhost:8080/app1/
arg: foo:hoge
req->arg: foo:hoge
req->parameters: a:1, b:2
engine->env->{REQUEST_URI}: /app1/foo/hoge?a=1&b=2
engine->env->{SCRIPT_NAME}: /app1
engine->env->{PATH_INFO}: /foo/hoge
engine->env->{QUERY_STRING}: a=1&b=2
uri_for: http://localhost:8080/app1/test
app1/root/static以下がlighttpd経由で送信されていることも確認しておきましょう。X-Catalystヘッダが無いですし,FastCGIプロセスを落としているときでも送信されています。
$ wget -S http://localhost:8080/app1/static/images/catalyst_logo.png
--23:38:10-- http://localhost:8080/app1/static/images/catalyst_logo.png
=> `catalyst_logo.png'
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response...
HTTP/1.0 200 OK
Connection: keep-alive
Content-Type: image/png
ETag: "-514715257"
Accept-Ranges: bytes
Last-Modified: Sun, 26 Feb 2006 08:53:16 GMT
Content-Length: 13710
Date: Sun, 26 Feb 2006 14:38:10 GMT
Server: lighttpd/1.4.10
Length: 13,710 (13K) [image/png]
100%[====================================>] 13,710 --.--K/s
23:38:10 (64.41 MB/s) - `catalyst_logo.png' saved [13710/13710]
追記:2006/02/27 09:35
(バーチャル)ホスト全体のハンドラとしてFastCGIにする場合のSCRIPT_NAME, PATH_INFOについても同じことが起こる様子。fastcgi.server => ( "/" => ... )とすると「"/"で終わるprefix」として認識され続くURLをスクリプトファイル名とみなす。fastcgi.server => ( "" => ... )とすると「空のprefix = 空文字列のスクリプトファイル」として認識される。
Trackback URL for this post:
http://old.typemiss.net/trackback/73
