1. 9.2 サーバー送信イベント
      1. 9.2.1 導入
      2. 9.2.2 EventSourceインターフェイス
      3. 9.2.3 `Last-Event-ID`ヘッダー
      4. 9.2.4 イベントストリームのフォーマット
      5. 9.2.5 オーサリングに関する注意事項

9.2 サーバー送信イベント

Server-sent_events

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+

9.2.1 導入

サーバーがHTTP経由で、または専用のサーバープッシュプロトコルを使用してウェブページにデータをプッシュできるようにするために、この仕様はEventSourceインターフェイスを導入する。

このAPIの使用は、EventSourceオブジェクトの作成とイベントリスナーの登録で構成される。

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

サーバー側では、スクリプト(この場合は"updates.cgi")は、text/event-streamのMIMEタイプで、次の形式でメッセージを送信する:

data: This is the first message.

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

data: This is the third message.

著者は、さまざまなイベントタイプを使用してイベントを分離できる。これは、"add"および"remove"の2つのイベントタイプを持つストリームである:

event: add
data: 73857293

event: remove
data: 2153

event: add
data: 113411

そのようなストリームを処理するためのスクリプトは次のようになる(ここでaddHandlerおよびremoveHandlerは、イベントという1つの引数を取る関数である):

var source = new EventSource('updates.cgi');
source.addEventListener('add', addHandler, false);
source.addEventListener('remove', removeHandler, false);

デフォルトのイベントタイプは "message"である。

イベントストリームは常にUTF-8としてデコードされる。別の文字エンコーディングを指定する方法は存在しない。


イベントストリームリクエストは、通常のHTTPリクエストと同様に、HTTP 301および307リダイレクトを使用してリダイレクトできる。接続が閉じられる場合にクライアントは再接続する。クライアントは、HTTP 204 No Contentレスポンスコードを使用して再接続を停止するように指示できる。

XMLHttpRequestまたはiframeを使用してエミュレートするよりもむしろこのAPIを使用することは、ユーザーエージェントの実装者およびネットワークオペレーターが事前に調整できる場合に、ユーザーエージェントがネットワークリソースをより有効に活用可能になる。他の利点の中でも、これは、ポータブルデバイスのバッテリー寿命を大幅に節約するという結果をもたらす。これについては、下記のコネクションレスプッシュに関するセクションで詳しく説明される。

9.2.2 EventSourceインターフェイス

EventSource

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android11+
source = new EventSource( url [, { withCredentials: true } ])

新しいEventSourceオブジェクトを作成する。

urlは、イベントストリームを提供するURLを示す文字列である。

withCredentialsをtrueに設定すると、urlへの接続リクエストの資格情報モードが"include"に設定される。

source.close()

このEventSourceオブジェクトに対して開始されたフェッチアルゴリズムのインスタンスをすべて中止し、readyState属性をCLOSEDに設定する。

source.url

イベントストリームを提供するURLを返す。

source.withCredentials

イベントストリームを提供するURLへの接続リクエストのクレデンシャルモードが"include"に設定される場合はtrueを返し、そうでなければfalseを返す。

source.readyState

このEventSourceオブジェクトの接続の状態を返す。以下に説明する値を持つことができる。

CONNECTING(数値0)
接続がまだ確立されていない、または接続が閉じられてユーザーエージェントが再接続している。
OPEN(数値1)
ユーザーエージェントは接続を開いており、イベントを受信するとイベントをディスパッチしている。
CLOSED(数値2)
接続が開いておらず、ユーザーエージェントが再接続を試みていない。致命的なエラーが発生したか、close()メソッドが呼び出されたかのいずれか。

以下は、EventSourceインターフェイスを実装するすべてのオブジェクトによって、イベントハンドラーIDL属性として、サポートされるイベントハンドラー(および対応するイベントハンドラーイベント型)である:

イベントハンドラーイベントハンドラーイベント型
onopen

EventSource/open_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
open
onmessage

EventSource/message_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
message
onerror

EventSource/error_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android?WebView Android?Samsung Internet?Opera Android12+
error

9.2.3 `Last-Event-ID`ヘッダー

Last-Event-ID` HTTPリクエスト ヘッダーは、ユーザーエージェントが接続を再確立するときに、EventSourceオブジェクトの最後のイベントID文字列をサーバーに報告する。

値空間をより適切に定義するには、whatwg/html issue #7363を参照。これは基本的には、U+0000 NULL、U+000A LF、またはU+000D CRを含まない、UTF-8でエンコードされた文字列である。

9.2.4 イベントストリームのフォーマット

このイベントストリームのフォーマットのMIMEタイプは、text/event-streamである。

イベントストリームのフォーマットは、次のABNFのstream生成で説明されているとおりである。文字セットはUnicodeである。[ABNF]

stream        = [ bom ] *event
event         = *( comment / field ) end-of-line
comment       = colon *any-char end-of-line
field         = 1*name-char [ colon [ space ] *any-char ] end-of-line
end-of-line   = ( cr lf / cr / lf )

; characters
lf            = %x000A ; U+000A LINE FEED (LF)
cr            = %x000D ; U+000D CARRIAGE RETURN (CR)
space         = %x0020 ; U+0020 SPACE
colon         = %x003A ; U+003A COLON (:)
bom           = %xFEFF ; U+FEFF BYTE ORDER MARK
name-char     = %x0000-0009 / %x000B-000C / %x000E-0039 / %x003B-10FFFF
                ; a scalar value other than U+000A LINE FEED (LF), U+000D CARRIAGE RETURN (CR), or U+003A COLON (:)
any-char      = %x0000-0009 / %x000B-000C / %x000E-10FFFF
                ; a scalar value other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)

このフォーマットのイベントストリームは、常にUTF-8としてエンコードしなければならない。[ENCODING]

行は、U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF)文字ペア、単一のU+000A LINE FEED (LF)文字、または単一のU+000D CARRIAGE RETURN (CR)文字のいずれかで区切らなければならない。

次のイベントストリームの後には、空白行が1行続く:

data: YHOO
data: +2
data: 10

インターフェイスMessageEventをもつイベントmessageEventSourceオブジェクトに送出される。イベントのdata属性には、文字列"YHOO\n+2\n10"が含まれる("\n"は改行を表す)。

これは、次のように使用できる:

var stocks = new EventSource("https://stocks.example.com/ticker.php");
stocks.onmessage = function (event) {
  var data = event.data.split('\n');
  updateStocks(data[0], data[1], data[2]);
};

ここで、updateStocks()は次のように定義された関数である:

function updateStocks(symbol, delta, value) { ... }

…など。

次のストリームには、4つのブロックが含まれている。最初のブロックにはコメントのみが含まれており、何も発火しない。2番目のブロックには、それぞれ"data"および"id"という名前の2つのフィールドがある。このブロックに対して、データ"first event"でイベントが発生し、最後のイベントIDを"1"に設定する。これにより、このブロックと次のブロックとの間の接続が切断された場合、サーバーには値`1`の`Last-Event-ID`ヘッダーが送信される。3番目のブロックは"second event"というデータでイベントを発火し、"id" フィールドも持っているが、今回は値が存在しない。これは、最後のイベントIDを空の文字列にリセットする(つまり、再接続が試行された場合に`Last-Event-ID`ヘッダーが送信されなくなる)。最後に、最終ブロックは、データ" third event"(先頭に1つのスペース文字)をもつイベントを発火するだけである。最後の行は空白行で終了する必要があることに注意する。ストリームの終わりは、最後のイベントの早出をトリガーするのに十分ではない。

: test stream

data: first event
id: 1

data:second event
id

data:  third event

次のストリームは、2つのイベントを発火させる:

data

data
data

data:

最初のブロックは、データが空の文字列に設定された状態でイベントを発火させる。最後のブロックの後に空白行が続く場合も同様である。中間のブロックは、データが単一の改行文字に設定されたイベントを発火させる。最後のブロックは、その後に空白行がないため破棄される。

次のストリームは、2つの同じイベントを発火させる:

data:test

data: test

これは、コロンの後のスペースが存在する場合、無視されるためである。

9.2.5 オーサリングに関する注意事項

レガシープロキシサーバーは、特定のケースにおいて、短いタイムアウトの後にHTTP接続を切断することが知られている。そのようなプロキシサーバーから保護するために、著者は約15秒ごとにコメント行(':'文字で始まる行)を含めることができる。

イベントソース接続を相互に、または以前に配信された特定の文書に関連付けたい著者は、IPアドレスに依存することが機能しないことに気づくかもしれない。これは、個々のクライアントが複数のIPアドレスを持つことができ(複数のプロキシサーバーがあるため)、そして個々のIPアドレスが複数のクライアントを持つことができる(プロキシサーバーを共有しているため)あるためである。文書が提供されるときにその文書に一意の識別子を含め、接続が確立されるときにその識別子をURLの一部として渡す方がよい。

著者はまた、特にタイミング要件を認識しない別のレイヤーでチャンクが行われる場合、HTTPチャンクがこのプロトコルの信頼性に予期しない悪影響を与える可能性があることに注意する。これが問題である場合、イベントストリームの配信時にチャンクを無効にすることができる。

HTTPのサーバーごとの接続制限をサポートするクライアントは、各ページが同じドメインへのEventSourceを持つ場合、あるサイトから複数のページを開くときに問題が発生する可能性がある。著者は、接続ごとに一意なドメイン名を使用するという比較的複雑なメカニズムを使用する、ユーザーがページごとにEventSource機能を有効もしくは無効にできるようにすること、または共有ワーカーを使用して単一のEventSourceオブジェクトを共有することによって、この問題を回避できる。