EventSourceについて発表してきた
昨日、partake.inで、10分ぐらいでEventSourceの話をしてきました。
発表資料は一応あるのですが、それだけだと全く意味が分からないので、当日話したことをここにまとめておきます。
EventSource とは
HTTP の仕組みを使って、クライアント側でリアルタイムにデータを受信するための仕組みです。
html5 の一部になっていて、 Server-sent Events に仕様が載っています。
EventSource は、技術的には単なるロングポーリングです。
つまりクライアントは HTTP を繋ぎっぱなしにすることで、レスポンスデータをリアルタイムにずっと受信し続けるという仕組みです。
普通の HTTP 通信だと、クライアントがリクエストを送信すると、サーバがレスポンスを返して終わりなのですが、EventSource はレスポンスのデータを途中までしか送信しないので、接続が終わらず、ずっと繋ぎっぱなしになります。
ずっと HTTP を繋ぎっぱなしにしていると、当然いつかタイムアウトで接続が切られてしまうわけですが、EventSource は『切られたら即繋ぎ直す』という仕様があるので、何も考えなくても勝手に再接続してくれます。
どのブラウザでサポートしているのかちゃんと試してないので知りませんが、とりあえず Chrome はちゃんと動きます。
まあでも単なる HTTP 通信なので、やろうと思えば多分 jQuery.ajax あたりを使って実装できますね。
なぜ EventSource を使ったのか
こんなサイトを作るためです。Yesod 製です。
ideone みたいな Web 上でコンパイル/実行する仕組みですが、その結果がリアルタイムで出力されるという点が違います。
WebSocket を使うという手もあったのですが、wai-websockets の使い方が本気で意味分からなかったので、より簡単そうな wai-eventsource を使うことにしました。
Mighty の EventSource 問題
自分は↑のサイトとは別に、もうひとつ、別のサイトを作っています。こっちも当然 Yesod 製です。
こちらは普通のサイトで、EventSource を使ったりといったことはしてません。
で、2つのサイトを別々に作ったのですが、これを両方公開する場合、お互いがHTTPの80番ポートを使ってしまって起動できないことに気が付きました。
これはどうすればいいんだろうと悩んでいたのですが、どうやらこれはリバースプロキシなるものを使うのが一般的らしく、Haskell のリバースプロキシ可能なアプリとして Mighty があるので、これを使えばいいようです。
2つのサイトをそれぞれ 3000 番、4000 番ポートで起動して、 /wandbox に対するアクセスは全部 3000 番に流して、/yesodbookjp に対するアクセスは全部 4000 番ポートに流すようにしました。
これで無事2つのサイトが1つのドメインに入ってくれました。
が、こうすると、なぜか EventSource のデータがちゃんと受信できなくなったのです。
そもそも EventSource のデータは、リアルタイムで細切れに送信するために、ソケットを flush するタイミングというのが明確に決まっています。
つまり EventSource は、空行がある(つまり改行コードが2連続で続く)場合に、ソケットを flush します。
しかし Mighty は当然 EventSource のそんな特殊な仕様なんて知らないので、普通にソケットのバッファが一杯になるか、ソケットが閉じられるまで待ちます。
で、そうなるとリアルタイムで受信することができなくなり、EventSource が動かなくなってしまいます。
ということで、これを解決するため、Mighty のリバースプロキシのコード*2を自分のところに持ってきて、改行コードが2連続で続いた場合は即 flush するというコードを入れました。*3
これでちゃんと EventSource が動きました。めでたしめでたし。
という話を、昨日のYesod勉強会でしました。
この修正、EventSource 専用の修正だから全く別コードにしたのですが、Mighty 作者の kazu-yamamoto さんに Pull Request していいよと言われたので、近々そうしようと思います。*4
*1:まだ開発中なのでイジメコードを送信しないで下さい。イジメカッコワルイ
*2: https://github.com/kazu-yamamoto/wai-app-file-cgi/blob/f3bf3033f679189e26ef497027dda92a059f1e49/Network/Wai/Application/Classic/RevProxy.hs
*3: https://github.com/melpon/melpon.org/blob/95fc0d68f17d8a77af88a0fa1bdca45b9cc235e8/RevProxy.hs#L96
*4:実は今の実装だと問題があって、EventSource は CR, LF, CRLF の改行コードを認めているのだけど、今の実装は LF しか見ていないので、そこら辺をちゃんと直さないといけなかったり