FLAGS

筆者おかあつ 大きな区分 記事の区分 記事の一覧 検索 ツイート

2014年9月29日月曜日

Apache Rewriteで無限ループから脱出する方法 (oka01-czzfjhphghzhupmb)

HTTPサーバー・Apache の Rewrite は非常に癖が強い。Apache Rewrite が何なのか、その癖がどの様なものなのかは、次のサイトを参考にするとよいだろう。

    杉浦とホームページ製作『URLを書き換えろ! Apache Rewrite 機能の応用』
    WEB ARCH LABO『Apache Rewrite 正規表現を用いて転送する』
    技林『mod_rewrite URLを書き換えるApacheモジュール』

ここでは筆者がネット上の海外サイトで小耳にはさんだEXIT NOW ルール について説明する。これは知らなければ絶対にルールを書くことが出来ない筈なのにアンドキュメントだ。


元ネタは次の通りである。

Rewrite ルールの L フラグが動作しない (StackOverflow)
http://stackoverflow.com/questions/6797998/rewriterule-last-l-flag-not-working

ここにこの様なことが記述されていた。
 

  • 記述されたRewriteルールは繰り返し実行される。
  • Rewriteルールの繰り返し処理は特定の条件が満たされた時、終了する。
  • [L] フラグは、JavaScript で言うところの continue に該当し、次のループ処理に進むだけでループを中断しない。

筆者は、Rewriteを利用し始めてすぐに、ルールは繰り返し実行されることや、Lフラグを使ってもその繰り返し処理は止まらないことなどに気付いた。だが肝心の繰り返し処理の止め方がわからない。注意深く書かなければ処理が無限ループに突入してしまい、予期しないディレクトリに転送され 500 Internal Server Error が発生する。注意深く書くにせよ、繰り返しが終了する条件がわからないのだから、何に注意すればよいのかわからない。

この記事中にさり気なく小さく書かれていた次の話が筆者にとっては大変に驚異的だった。

繰り返し処理は次の条件を満たした時、終了する:
  1. 条件を満たすルールがひとつもなかった時。
  2. EXIT NOW ルールが実行された時。
  3. Rewriteルールが条件を満たして実行され、処理前と処理後で結果が同じだった時。
  4. Rewrite繰り返し処理数の最大(デフォルトで10)に到達した時。

ここでいう EXIT NOW ルール というのは次のようなルールらしい。

RewriteRule .* - [L]


上記の発言者によると、このルールが実行されるとループ処理は中断するという。

大抵の単純な書き換えの場合、処理は一度だけ実行すれば充分だ。だが、作成したRewriteルールは、自動的にルールの条件がマッチしなくなるまで繰り返し実行される。よって明示的にここでいう EXIT NOWルールを指定しない限り、予期せず無限ループに突入する可能性が残る。明示的にEXIT NOW ルールを指定しない場合、暗黙の内に無限ループ処理が開始されてしまい、意味なく繰り返し処理の最大数10になるまでマッチング処理が行われ、サーバーのレスポンスが悪化してしまう可能性がある。

ファイル名を書き換える時に無限ループに突入しない為の定型句として「ファイルが存在したら書き換えない」という方法があるという。同記事のなかである方が小さく紹介していたが。これは ApacheでRewriteを利用する際、とても重要なことだと思われる。

        # do not do anything for already existing files
        RewriteCond %{REQUEST_FILENAME} -f [OR]
        RewriteCond %{REQUEST_FILENAME} -d
        RewriteRule .+ - [L]

このルールは『リクエストが来た時、そのリクエストが指し示す内部ディレクトリにファイルが存在したら、ループ処理を中断し書き換えない』という意味だ。URL書き換えを利用する場合は、大抵、存在しないファイルを存在する様に見せかける処理である筈なので、この常套句は大抵の場合、便利に使える筈である。


また繰り返し処理を行わず一度だけ処理を加えたい、つまり処理の結果がマッチ・アンマッチに関わらずループを中断したいケースがある。これを実現することが思いの他難しい。何故ならばループ処理中に処理状態を保存するための変数機能がRewriteには用意されていない為だ。

 [L]パラメーターはループ処理を続行するだけであることは既に述べたが、[L]パラメータは飽く迄も『ループ処理続行』を行うフラグで『ループ処理の中断』を実現する為の機能ではない。環境変数を流用することも可能だが、ループ処理中に環境変数の存在が持続しないケースがあるらしい。どうすればよいだろうか。

この様な場合、ダミーのパラメーターを付け加え、パラメータがある時は処理を中断する様に RewriteCondを指定すると良い。

       # URLQueryString(パラメータ)の最後が rwrite=done だったらループ処理を中断する。
       RewriteCond %{QUERY_STRING} rewrite=done$
       RewriteRule .+ - [L] # 処理の中断
      
       # 条件に一致した場合、URLの書き換えを行う。
       # 書き換えを行った事を示す為、rewrite=done というダミーパラメータを追加する。
       RewriteRule ^(.*html)$ /cgi-bin/applying-template.py?path=$1&rewrite=done [L]



上記のstackoverflowにて回答をした方はひょっとするとApache Rewriteの開発に関わった事がある方なのではないか。発言者は、今のApacheのドキュメントは完璧ではないが、上記の仕様を推測するだけの必要最低限の情報は提供している、と言っている。筆者は同意できないが、言われてみて改めてドキュメントを読み返してみたら、この繰り返し処理のことは説明されていた。

Apache Rewrite Internal Ruleset
http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#InternalRuleset


だが正直、筆者には理解不能である。この乱雑な説明では、図を書いて全てのパターンを列挙し、最終的にどういう結果を招くのかをまとめない限り、実際に何を気をつけなければならないのか不明瞭だ。たかだかURLを書き換えるだけの為にこんなヘヴィーウェイトな作業はしたくない、というのが正直な気持ちである。




(Wed, 01 Oct 2014 13:39:39 +0700)
rewrite=doneの件を追記した。