Phusion PassengerでAliasで設定できない理由

Redmineで最初Aliasでやろうとしてコケました。なんでだろ?と思ってソースコードを追ってみた記録。

Phusion Passengerの公式サイトを見てもRailsBaseURIで設定、シンボリックリンクを張れと書いてあるのでどうやらPassenger自体の仕様のようです。
どのサイト調べてもこうしろとはかいてあるけどなんで?というのは書いてない...
だれも気にならないんだろうか?それとも常識なんだろうか?...
いずれにせよわからないのはきもちわるいのでソースコードを追ってみることにしました。

Passengerフォルダ内のext/apache2/Configuration.cppに

	AP_INIT_TAKE1("RailsBaseURI",
		(Take1Func) cmd_passenger_base_uri,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Deprecated option.")

というRailsBaseURIを定義していそうな部分を見つけました。
このAP_INIT_TAKE1はマクロで第二引数にディレクティブを処理する関数のポインタが来るようです。
そこで次にcmd_passenger_base_uriの実装を探します。

cmd_passenger_base_uri(cmd_parms *cmd, void *pcfg, const char *arg) {
	DirConfig *config = (DirConfig *) pcfg;
	if (strlen(arg) == 0) {
		return "PassengerBaseURI may not be set to the empty string";
	} else if (arg[0] != '/') {
		return "PassengerBaseURI must start with a slash (/)";
	} else if (strlen(arg) > 1 && arg[strlen(arg) - 1] == '/') {
		return "PassengerBaseURI must not end with a slash (/)";
	} else {
		config->baseURIs.insert(arg);
		return NULL;
	}
}

と定義されていました。これを見てみるとDirConfig構造体のBaseURIsにパラメータを代入しています。
そこでDirConfig構造体の定義を探し見るとBaseURIsはstd::setとして定義されていました。
またコメントに「Per-directory configuration information.」とあるのでディレクトリごとの設定を保持する構造体のようです。
insertしているのでRailsBaseURIが複数回現れた場合は上書きされるのではなく追加されます。

次に実際にどう使われているのか調べてみます。
名前的にDirectoryMapper.hが怪しそうなので眺めているとDirectoryMapperというクラス内のautoDetectメソッド内に

/* Find the base URI for this web application, if any. */
const char *baseURI = findBaseURI();
if (baseURI != NULL) {
	/* We infer that the 'public' directory of the web application
	 * is document root + base URI.
	 */
	publicDir = docRoot + baseURI;
} else {
	/* No base URI directives are applicable for this request. So assume that
	 * the web application's public directory is the document root.
	 */
	publicDir = docRoot;
}

という部分がありました。Railsのpublicディレクトリを決定する動作のようです。
baseURIがnullではない場合

publicDir = docRoot + baseURI;

という処理が実行されます。
baseURIはなにが入っているか調べるためfindBaseURIメソッドの定義を見てみたところ、リクエストを解析してBaseURIの集合の中にマッチするものがあれば
それをchar*にして返すという処理をしていました。
ここでなぜAliasで設定できないかという理由がわかりました。
docRootはおそらくApacheのDocumentRootディレクティブで指定された文字列が入り、baseURIはRailsBaseURIで指定されたディレクトリがはいるのでAliasで設定されていてもpublicDirには影響を及ぼしません。
そのためRailsBaseURIで指定してあげる必要があります。
また、Aliasはリクエストが届いた際に振り分ける機能なためAliasを張ったディレクトリに対してRailsBaseURIを指定しても、
Passengerのほうはあくまで実際のファイルシステム上のフォルダを参照するためうまく働きません。
ということでシンボリックリンクをはってRailsBaseURIで指定してあげないといけません。
ということでした。