Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2010-10-11

EventSource 覚え書き

12:24

ようやく Opera が新しい仕様に対応したので Server-Sent Events を読んでた。忘れないうちに簡単な覚え書き。昨日出た Opera 10.7 の Snapshot と WebKit では既に使えるみたい。

まず、ブラウザ側は、こんなふうに使う。

var source = new EventSource('updates.cgi');
source.onmessage = function (event) {
  alert(event.data);
};

もちろん same-origin policy は適応される。CORS が使えるのかは書いてないけど、CORS 仕様への参照だけはあるので、使えることになるんだろう。

サーバー側は、必ず text/event-stream という MIME type で返さなければならない。(又は UA が他にサポートするストリーミング形式)

また、Cache-Control: no-cache を付けるべきである。

ステータスコードが200だったらストリーミング開始。他の200番台だったら再接続。また、レスポンスが切れても再接続。300番台はリダイレクト。それ以外だったら、接続失敗とする。(305,401,407 について何か書いてあるがよくわからない)

重要なのは、一旦ストリーミングを開始すると、サーバーから明示的に接続が切られたとしても、EventSource オブジェクトがある限りは再接続し続けるということ。そうされたくない場合は、400番台を返すか、クライアント側で close() を呼ぶ。

くれぐれも onerror だけで終了と判断してはいけない。

var source = new EventSource('updates.cgi');
source.onerror = function (event) {
  if (source.readyState === EventSource.CONNECTING) { // === 0 
    // 再接続中
  } else if (source.readyState === EventSource.CLOSED) { // === 2
    // 終了
  }
};

readyState が2の時でもステータスコードを知ることができないので、もし切断する理由も通知したい場合は通常のメッセージに終了理由を書いてクライアント側で close を呼んだほうがいいかもしれない。

ブラウザで同じ URL のタブを2つ開くということもあり得るので、何本も接続を貼りつづけたくない場合は SharedWorker を使って EventSource を一本化するのがいい。


レスポンスの文字コードは必ず UTF-8 でなければならない。本文はこのような形になる。

data: This is the first message.

data: This is the second message, it
data: has two lines.

data: This is the third message.

message イベントは空行が現れた時点、またはファイルの終端で発火される。つまり上の例では3回。2回めの it と has の間には改行コードとして LF が付加されることになっている。

data: の後のスペースは1つまでは取り除かれることになってるので、

data:foo
data: foo
data:  foo

だったら、上の2つはまったく同じ意味になるが、一番下は " foo" (スペース2つだったのが1つになった)という意味になる。

セミコロンが無くてもいいらしい。その場合はちょっと面倒で、

data

だったら内容が空の message イベントを発火することになると思いきや、空の場合はイベントは発火しないらしい

data
data

この場合は間に挿入される LF が message イベントとして出ると例示されている。(ただし草案のアルゴリズムをきちんと追うと上の場合でも LF が出ることになるのだが…あとで文句言おう文句言った


data 以外に許されているものは、event、id、retry となっている。

例をいくつか。

event: hoge
data: fuga

この場合は、type が hoge で data が fuga な MessageEvent が発火する。これは source.onhoge で捕まえられるような気もするが、よくわからないので source.addEventLitener('hoge',...) で監視したほうがいいかな。まあ、正直 event: は使うシーンは無いと思う。

id: foo

EventSource オブジェクトの内部プロパティである lastEventId を付加する。これはサーバーとの再接続のときに Last-Event-ID リクエストヘッダーとして送信される。「ここまで送った」に使えるというわけ。

retry: 1000

retry: の後には十進数の数字で、接続が切れたときの再接続までの時間をミリ秒で指定する。この場合だと1秒後に再接続。

セミコロンで始まる行はコメントとなる。仕様の備考には、プロキシによっては長い間通信が無いと接続を切るものがあるので、15秒ぐらいおきにコメント行を送るのがいいと書いてある。

それ以外の行は無視される。


以下感想。

バイナリを送るのには向いてない。\x0A (LF) が来たら改行になってしまうし。UTF-8 でデコードされてしまうし。まあ base64 でいいよね。

世の中は WebSocket が熱いみたいだけど、用途を考えれば EventSource で十分なことのほうが多いと思う。Twitter Streaming とか。Hixie さんもそんなことを書いてたことがある。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20101011
 |