SORRY専用Webサーバ

とにかく何が来ても「御免!」と応答するWebサーバを考える(以下、単にSorryサーバと呼ぶ)。

Sorryサーバの実装にはいくつか考えられる。

  • ErrorDocumentを使う
  • AliasMatchを使う
  • mod_rewriteを使う

これ以外にもありえると思うが、ErrorDocumentより簡単でmod_rewriteより何でも出来る方法はちょっと思いつかない。

ErrorDocumentを使う場合

一番最初に思いつくのはコレじゃあるまいか。

ErrorDocument 403 /SORRY/index.html
ErrorDocument 404 /SORRY/index.html

DocumentRootに/SORRY/ディレクトリのみを設置し、/SORRY/以外のどんなURLでアクセスされても 403 または 404 になる事を使って、このエラードキュメントをSORRYコンテンツにしてしまう方法。最もお手軽だが、/SORRY/以外のファイルを置いてはいけないため、DocumentRootそのものを専用にしないといけない。
また、IEの場合エラー表示を簡易表示されてしまう事があるから、これを回避しなければならない。/SORRY/index.htmlが512バイト以下の時に IE は「簡易表示」するから、空行足すでも良いのでとにかく512バイト以上のファイルにしよう。
さらに、クライアントへの応答コードが 404やら403になる点は見逃しがちだが、大事なポイントだ。Sorryサーバがこの応答をすると、クロールに来た検索エンジンは「なくなった」と判断するかもしれないから、サービス再開した後にクローラが取りに来てくれないかもしれないリスクがある。SEO(という単語は嫌いだが)を気にする人にはオススメしない。Sorryサーバとしては万能ではなくある程度の注意が必要で、要件によっては適さない場合がある。
そんな理由で、「ちょっと今だけ」とかいうレベルでなければこの方法はオススメしない。Sorryサーバとして常設する環境を作るならこの選択をするべきではない。

AliasMatchを使う

全く一般的な方法でないと思うのだが、二番目に思いついたのがこれなのだ。

AliasMatch ^(.*)$ /path/to/SORRY/index.html

きっかけは若かりし頃、ErrorDocumentでSorryサーバを実装した時にIEに簡易表示されてみたり、特殊なクライアントが200応答を期待していたがために別な不具合になった時のこと。とにかくエラーでない返答(200とか304とかね)でSorryサーバを実装しなおす必要が出た。
上の例では、全てのリクエストを/path/to/SORRY/index.htmlにAliasしている。これで何が来ても200ないしは304応答でSorryサーバが実現できた。
この時はこれで仕様を満たしたのだが、この方法には重大な欠陥がある。/SORRY/index.htmlファイル内で外部ファイルを読み込むように記述した場合、読み込む画像すらAliasMatchにヒットしてしまう。つまり、画像やCSSやJSを使いたければ別なWebサーバに配置してsrc=http://〜〜などと書く必要があるわけだ。

<body>
<p>SORRY!</p>
<img src="/logo.png">  ←この書き方ではいけない
</body>
<body>
<p>SORRY!</p>
<img src="http://another.site.example.jp/logo.png">  ←こう書かないといけない
</body>

この点の解決として、実用的には絶対にオススメしないが、技術的には面白い方法として以下を挙げる。

AliasMatch ^(.*)$ /path/to/SORRY/sorry.cgi

全てのリクエストを実行可能なコマンドに渡す事が可能だから、リクエストによる内容をsorry.cgiがハンドリングする事ができる。あとはperlでも何でも良いから好き勝手に書けば良いのだ。面倒だしばかばかしいけど、やるとちょっとだけ面白い。勉強にはなるかもな。

#!/usr/bin/perl
&init();
&main();
exit;

sub main() {
  $uri = $ENV{'REQUEST_URI'};
  $host = $ENV{'HTTP_HOST'};
  # $host, $uriに応じた処理をここに書くと良い。
  # 例えばファイルを開いて返すとか。
  if ($uri =~ m|^/SORRY/|) {
    $file = $uri;
  } else {
    $file = "/SORRY/index.html";
  }
  if ( ! -f $file ) {
    &error();
  }
  open(FILE,"$file) || die 'there is a serious security hole in this example code.';
  binmode(FILE);
  print "Content-type: text/html\n\n";
  while(read FILE, $buf, 4093) {
    print $buf;
  }
}
sub init() {
}
sub error() {
}

当たり前だけど、この方法はオススメしない。Content-typeはどうやって決めるんだ?とかね。問題多すぎ。技術的にトリッキーで面白いだけだよ。ユーザ入力をopenに突っ込むのも嫌だしね。(このサンプルコードの入力チェックはきっと甘いから真似しないでプリーズ)

さて、脱線しちゃったので戻す。
AliasMatchでやるならば、画像やCSSのようなファイルを読み込まないSorryコンテンツを用意するか、外部のWebサーバに置いて参照するのどちらかしかない。

mod_rewriteを使う

これが大本命。mod_rewriteは何でも出来るが故の諸刃の剣と良く言われるが、この件ではこれが大本命。Apacheグループの中の人、Brian Behlendorfさんがmod_rewriteについて面白い事を言っている。sendmailの設定の複雑さを知らないと笑えない話だけどな。

`` mod_rewrite のすばらしいところは、 Sendmailのような設定性と柔軟性を与えてくれるところだろう。 また、mod_rewrite のよくないところは、 Sendmailのような設定性と柔軟性を与えてくれるところだろう。''

http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html

さて、mod_rewriteを使ったSorryサーバの実装について、さっそく設定内容から書く。

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/%{HTTP_HOST}/SORRY/index.html -f
RewriteRule ^/SORRY/(.*) /%{HTTP_HOST}/SORRY/$1  [L]
RewriteCond %{DOCUMENT_ROOT}/%{HTTP_HOST}/SORRY/index.html -f
RewriteRule ^/(.*)$ /%{HTTP_HOST}/SORRY/index.html [L]
RewriteRule ^/SORRY/(.*) /SORRY/$1  [L]
RewriteRule ^/(.*)$ /SORRY/index.html [L]

Sorryサーバとしては、本当にこの7行で十分な、(俺なりに)緻密に考えた7行なのだ。

  • コンテンツは、ドキュメントルート直下に/SORRY/として配置する。
  • 1行目、mod_rewriteを使うためのおまじない
  • 2行目、ドメイン独自のSorryコンテンツがあるかどうかを調べて(-f)
  • 3行目、/SORRY/以下のリクエストをドメイン独自の内容を表すパスへ変換して終了
  • 4行目、またドメイン独自のSorryコンテンツの存在を調べて(-f)
  • 5行目、/SORRY/以外のアクセスを全てドメイン独自のSorryコンテンツへ変換して終了
  • 6行目、独自に設定しないドメイン用に/SORRY/宛のリクエストを全員共通/SORRY/へ変換して終了
  • 7行目、/SORRY/以外のアクセスを全て共通/SORRY/へ変換して終了


のっけからあれれ?と思われる事かと思う。2〜5行目はそもそも不要なはずなのだ。いきなりドメイン名毎って何だ?

ここまできて、まさかの後出しじゃんけん仕様なんだが、複数ドメインが同居しているようなサービス環境でも問題なく使えるように、という要件を考えた。ホスティングサービスみたいな環境で統一的なSorryコンテンツを表示させるのはサービス的によろしくない場合ってあるじゃん。「障害発生時のSorryコンテンツを独自に設定できる共有ホスティング」これは今までなかった新機能じゃない?差別化?差異化?知らないけど、これはきっと新しい!

さて、この設定では

  • どの顧客でも /SORRY/ に自分の好きなコンテンツを置けば良く
  • かつ、顧客が増えてもサーバの再起動はおろか、設定の追加削除も不要で(!)
  • 顧客ごとに異なるメッセージを、一切関知することなく好き勝手に設定させる事が出来る
  • 置かれなかった場合や削除された場合でも403や404を返すことがない
  • 実は/SORRY/以下のパスへありもしないURIを問い合わせた場合には404が返るんだが、そもそもそんなアクセスがおかしい。それこそ404のErrorDocumentを設定し、「そんなファイルはない」とでも返せばよろしい。

というわけで、今まで作った中で一番ステキなSorryサーバが出来上がったというわけだ。自画自賛。イエイ。