l'essentiel est invisible pour les yeux

Wednesday, May 24, 2006

[Rails] lighttpdとWEBRickでのRequestオブジェクトの挙動

開発環境はWEBRick、プロダクション環境はlighttpd+fcgiで運用しているという方も多いと思います。ここで一つ注意が必要なのは、requestを扱う際の注意です。

requestにはユーザからのHTTPリクエストをサーバが解析しRailsがrequestオブジェクトにマッピングします。そのため、WEBサーバの実装によって一部値が変わってきます。
requestオブジェクトのプロパティの値で処理を分岐させるなどしている場合は、うまく動作しません。

同一のソースをlighttpd + fcgiとWEBRickで動作させた場合のrequestオブジェクトを比較してみます。(どちらもRAILS_ENV => development)

http://rakuto/testにアクセスしたとして話を進めます。
lighttpdは80番、WEBRickでは3000番ポートで動作させます。


lighttpd + fcgi


{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "SERVER_ADDR"=>"222.230.136.50",
"SCRIPT_NAME"=>"/dispatch.fcgi", "REDIRECT_URI"=>"/dispatch.fcgi",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_HOST"=>"rakuto",
"HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"SERVER_SOFTWARE"=>"lighttpd/1.4.11", "REMOTE_ADDR"=>"222.230.136.61",
"HTTP_KEEP_ALIVE"=>"300", "HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"HTTP_COOKIE"=>"__utma=146761718.1802360063.1148377808.1148377808.1148439243.2;
_session_id=1b956cee50de1e32e9c1762b1c9df19e;
__utmz=146761718.1148377808.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto; __utmc=146761718",
"DOCUMENT_ROOT"=>"/var/www/rakuto/current/public", "REQUEST_URI"=>"/test/",
"SERVER_PORT"=>"80", "GATEWAY_INTERFACE"=>"CGI/1.1", "REMOTE_PORT"=>"38429",
"QUERY_STRING"=>"",
"SCRIPT_FILENAME"=>"/var/www/rakuto/current/public/dispatch.fcgi",
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"REQUEST_METHOD"=>"GET", "REDIRECT_STATUS"=>"200", "HTTP_CONNECTION"=>"keep-alive"}


WEBRick

{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"/test/", "REMOTE_HOST"=>"222.230.136.61",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U;
Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"HTTP_HOST"=>"rakuto:3000", "REMOTE_ADDR"=>"222.230.136.61",
"SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.8.4/2006-04-28)", "HTTP_KEEP_ALIVE"=>"300",
"HTTP_COOKIE"=>"__utma=146761718.1802360063.1148377808.1148377808.1148439243.2;
_session_id=1b956cee50de1e32e9c1762b1c9df19e;
__utmz=146761718.1148377808.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto; __utmc=146761718",
"HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"REQUEST_URI"=>"http://rakuto:3000/test/", "SERVER_PORT"=>"3000",
"GATEWAY_INTERFACE"=>"CGI/1.1", "QUERY_STRING"=>nil, "REMOTE_USER"=>nil,
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"REQUEST_METHOD"=>"GET", "HTTP_CONNECTION"=>"keep-alive"}


おまけで、Mongrelも。
Mongrelの場合は、自分の環境ではWEBRickで動作させたときと挙動は同じでした。

{"SERVER_NAME"=>"rakuto", "PATH_INFO"=>"/test",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3", "SCRIPT_NAME"=>"/",
"SERVER_PROTOCOL"=>"HTTP/1.1", "HTTP_ACCEPT_LANGUAGE"=>"ja,en-us;q=0.7,en;q=0.3",
"HTTP_HOST"=>"rakuto:3000", "REMOTE_ADDR"=>"192.168.255.1",
"SERVER_SOFTWARE"=>"Mongrel 0.3.12.4", "HTTP_KEEP_ALIVE"=>"300",
"HTTP_COOKIE"=>"__utma=124112458.1769474473.1147068425.1147768112.1147937337.13;
_session_id=24d7dc5809d572486716edac18f59a33;
__utmz=124112458.1147068425.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);
username=rakuto", "HTTP_ACCEPT_CHARSET"=>"Shift_JIS,utf-8;q=0.7,*;q=0.7",
"HTTP_VERSION"=>"HTTP/1.1", "REQUEST_URI"=>"/test/",
"SERVER_PORT"=>"3000", "GATEWAY_INTERFACE"=>"CGI/1.2",
"HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"HTTP_CONNECTION"=>"keep-alive", "REQUEST_METHOD"=>"GET"}


まず一つ目の違い。
request.env["QUERY_STRING"]の値が異なります。

http://rakuto/testでアクセスしてみます。

"QUERY_STRING"=>"" # lighttpd + fcgi
"QUERY_STRING"=>nil # webcik
# => Mongrel プロパティが存在しない


あれ、lighttpdではQUERY_STRINGが空のときはnilではなくて空文字列になるんだ。

次に、htttp://rakuto/test?q=hogeでアクセスしてみます。

"QUERY_STRING"=>"" # lighttpd + fcgi
"QUERY_STRING"=>"q=hoge" # webcik


・・・lighttpd + fcgiの場合は、request.evn["QUERY_STRING"]は使えないようですね。

controller内でQUERY_STRINGの値でその後の処理を期待した、コードを書いていたらダメポと言うことです。WEBRickからlighttpd環境に移したときに正常に動作しません。

class IndexController < ApplicationController
def index
query_string =request.env["QUERY_STRING"].nil?? "" : request.env['QUERY_STRING']}
・・・
end
end



二つ目の違い
request.env["PATH_INFO"]の値もlighttpd+fcgi環境下では正常に設定されません。


"PATH_INFO"=>"" # lighttpd + fcgi
"PATH_INFO"=>"/test" # WEBRick


結論
request.env内の値は、WEBサーバの実装依存。この値を元にロジックを実装するのは危険!ということを学びました。