_ 障害連絡を受ける携帯に深夜にメールが飛んできて、すわ何事と起きてみたら、それが spam だったときのやるせなさといったら。
_ で、起こされた。さっき。
_ あした受信拒否設定しとこ。とりあえず、寝る。
_ えーと、サーバが Connection: close を返しておきながらサーバの方からは TCP を切断しない場合、クライアントの IE はタイムアウトするまで仕事をサボるから遅い(≠重い)、ということでいいのかな。へー、こんな問題あるんだねぇ。知らなかった。今まで聞いたことなかったけど、よほどレアなケースなんだろう。
_ でも、うーん、これって IE のバグなのかなぁ?
_ HTTP ってアプリケーション層のプロトコルであって下位の層は関知しないし、実際に RFC にはトランスポート層に TCP を前提としないと明記されている。だから、Connection: close を送ったら HTTP としては終了だけど、TCP 的にどう扱うべきかは HTTP の仕様上は何の規定もない。そういうときは TCP/IP のいろんなプロトコルの一般的な常識にしたがってよしなに取り扱う、つまりサーバの方から TCP セッションも終了する、というのが適切なんじゃないかなぁ。サーバの方はその TCP セッション上でやりとりされるデータはもうないよ、ということを知ってるんだから、とっとと自分の方から切断すべきであって、クライアントの方から切断されるのを待つべきじゃないと思う。だってクライアントの方の終了処理がもたついている場合、サーバ側にずっと無駄なコネクションが残っちゃうわけでしょ。今回の件はまさにそれにひっかかったわけで。
_ もちろん、クライアントの方でも、もうデータが送られてこないことがわかったら、その時点でとっとと TCP セッションを切るべきだろう。その判断材料となるのがレスポンスヘッダの Connection: close なんだけど、でも、そもそも HTTP/1.0 では Connection ヘッダなんてものは定義されていないんだよね。というか、1.0 には persistent connection の概念自体がないわけで、keep-alive だろうが close だろうが、そんなヘッダの存在自体無視したとしてもそれをバグと責めるのは酷だろう。つーか無視するのがむしろ正しい動作なんじゃ? ちゃんと定義されている HTTP/1.1 の上でば話はまったく別だけど。
_ このケースでは IE のバグとみなす人が多いようだけど、個人的にはどっちかというと、そういうおかしなレスポンスを返すサーバの方がクロに近いグレー、だと思うなぁ。IE の方は HTTP/1.0 ならばシロに近いグレー、HTTP/1.1 でもグレー。どっちが一方的に悪いと断言するのはためらわれるけど、責任割合としてはサーバの方が大きいと思う。
_ つーか、どう設定したらこんなヘンテコなレスポンスができるの? wikipedia のケースではオリジンサーバは lighttpd らしいけど、 こっちの otaba.jp の方は apache のようだし、 ここにあるように、途中にあるプロクシが妙な動きをしてるってこと? 長大なヘッダつきのレスポンスを squid に食わせてやればいい? よくわからんなぁ。
_ X という人が a、b、c の3人にメールを送る。ところが a、b、c の3人は自分でメールを受け取るだけでなく、それぞれ他の2人にもメールを転送するようにしている。さてこの場合、3人が受けとるメールは全部で何通になるか。
_ a → b → a のような転送ループを許す場合。ひとりが複数に転送しているので、無限級数的に増殖する。南無。実際は Received: ヘッダ数の上限に達した時点で止まるが、ふつーその前に死ぬ。ちなみに上限に達するまでに20周したとすれば 3^20 = 3486784401。
_ 転送ループを許さない場合は以下の15通り。
a a-b a-c a-b-c a-c-b b b-c b-a b-c-a b-a-c c c-a c-b c-a-b c-b-a_ 最近の MTA(あるいは MDA)は Delivered-To のヘッダを使って転送ループの発生を検知する仕組みを備えているが、Delivered-To を使ったとしても、たった3人が相互に転送しただけで、X から送られたメールが15通に増殖する。これでもループ検知がない場合の34億通に比べればずっとずっとマシだが、それでもかなりヒドい。
_ 3人の相互転送ではなく、4人になるとどうなるか。列挙はしないけど、64通。5人だと325通。パターンを列挙してみたら、S(n) = n * ( S(n-1) + 1 ), S(1) = 1 という漸化式になった(これを解こうとしてみたけど、わしの錆びた頭じゃ無理だった。誰か解いて)。
_ ましてや、これを20人でやられた日には……。
% awk 'BEGIN{print s(20)} function s(n){return n==1 ? 1 : n*(s(n-1)+1)}' 6613313319248078848_ 実話(泣)。みなさん、おかしな転送ループは作らないでね。おねがい。
_ 最初見たときは gmail でやってたけど、しばらくしたら gmail じゃない例に記事が差し替えられてた。gmail の迷惑メールフォルダって1ヶ月で自動的に消えるわりには、わしのアカウントには現在1万3000通の spam が溜まってるので、とてもじゃないけどこんな使い方はできない。まあ、バックアップとしてしか使ってないのでどうでもいいけど。
_ で、spam メールを処理するのにベイジアンフィルタがこんなにはやってるのに、spam 以外のメールを学習型フィルタに通してフォルダに振り分ける、ということをやるツールはなんでないんだろうね。そういうのがあれば、迷惑メール判定で代用なんかしなくていいのに。
_ いや、ないわけじゃないんだ。はやってないだけで。だいぶ前から開発が止まってるけど、POPFile のご先祖さまである ifileというツールがそれをやる。spam よけにベイジアンフィルタを使う手法を一気に有名にした Paul Graham の A Plan for Spamより以前から naive Bayes で仕分けしてた。
_ それから、spam ではなくウィルスを naive Bayes で検知しようという試みもあった。 ここに論文があったんだけど、なんか今つながらないな。UNIX Magazine にも記事が載ったことがある(2001年の夏ごろ?)。これも Paul Graham 以前。つーか、Paul Graham 以前はベイジアンフィルタじゃなくてちゃんとナイーブベイズって呼んでたんだな。
_ ま、そういうわけで、spam 判定に使われている手法を spam 以外のものを判定するのに使うってのは、今では忘れ去られてるけど、復活の機会をあげたらもしかしたら便利に使えるかもね。大昔 Emacs の RMAIL で使われていたラベル機能が Gmail で復活したように。
_ 目的の動作をする最小サイズのコードを書く Code Golfという遊びがあるんだけど、たまたま見つけた これもそのひとつなのかな。
_ が、そのお題のうちの echoの sed の最小サイズが今のところ 1byte だけど、これ違うよね。この問題だと、0byte のコードで題意を満たす。コマンドが与えられない場合の sed の挙動と同じなので。解答を受けつけるサーバの問題なのかしらん。
_ かんたんそうなものを自分でもいくつかやってみた。以下ぜんぶ awk で。\
以上はすでに出ている最小の解と同じ。以下はまだ短くなるらしい。
- hello world - 25bytes。awk の仕様上これより短くはできないはず。
- echo - 1byte。これも下限。ただし old awk(Solaris の /usr/bin/awk など)では 3bytes。
- Smileys Triangle - 55bytes
- delete blank lines - 2bytes。old awk では 3bytes。
- ultimate problem - 5bytes。old awk では 10bytes。
- Square root - GNU awk では 25bytes だが、old awk や new awk(/usr/xpg4/bin/awk や FreeBSD の /usr/bin/awk)では 23bytes。
- exit status - 19bytes
- even lines - 6bytes。5 でできるらしいがどうしても思いうかばん……。
- Fibonacci Numbers - 43bytes。これよりまだ短くできるのか?
- invert case - GNU awk 限定で 48bytes。gawk 以外でやるのはかなりめんどくさそう。
- delete last line - 19bytes。ほかに思いつかない……。
- swap lines - 25bytes。ほかに何かうまい方法がありそう。
- rotate lines - 27bytes。正攻法で考えたらダメな気がする。
_ ひさしぶりに頭を使った。しかも awk なんて……。