1. 9.2 サーバー送信イベント
      1. 9.2.1 導入
      2. 9.2.2 EventSourceインターフェイス
      3. 9.2.3 The `Last-Event-ID` header
      4. 9.2.4 The event stream format
      5. 9.2.5 Authoring notes

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 The `Last-Event-ID` header

The Last-Event-ID` HTTP request header reports an EventSource object's last event ID string to the server when the user agent is to reestablish the connection.

See whatwg/html issue #7363 to define the value space better. It is essentially any UTF-8 encoded string, that does not contain U+0000 NULL, U+000A LF, or U+000D CR.

9.2.4 The event stream format

This event stream format's MIME type is text/event-stream.

The event stream format is as described by the stream production of the following ABNF, the character set for which is 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)

Event streams in this format must always be encoded as UTF-8. [ENCODING]

Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character, or a single U+000D CARRIAGE RETURN (CR) character.

The following event stream, once followed by a blank line:

data: YHOO
data: +2
data: 10

...would cause an event message with the interface MessageEvent to be dispatched on the EventSource object. The event's data attribute would contain the string "YHOO\n+2\n10" (where "\n" represents a newline).

This could be used as follows:

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]);
};

...where updateStocks() is a function defined as:

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

...or some such.

The following stream contains four blocks. The first block has just a comment, and will fire nothing. The second block has two fields with names "data" and "id" respectively; an event will be fired for this block, with the data "first event", and will then set the last event ID to "1" so that if the connection died between this block and the next, the server would be sent a `Last-Event-ID` header with the value `1`. The third block fires an event with data "second event", and also has an "id" field, this time with no value, which resets the last event ID to the empty string (meaning no `Last-Event-ID` header will now be sent in the event of a reconnection being attempted). Finally, the last block just fires an event with the data " third event" (with a single leading space character). Note that the last still has to end with a blank line, the end of the stream is not enough to trigger the dispatch of the last event.

: test stream

data: first event
id: 1

data:second event
id

data:  third event

The following stream fires two events:

data

data
data

data:

The first block fires events with the data set to the empty string, as would the last block if it was followed by a blank line. The middle block fires an event with the data set to a single newline character. The last block is discarded because it is not followed by a blank line.

The following stream fires two identical events:

data:test

data: test

This is because the space after the colon is ignored if present.

9.2.5 Authoring notes

Legacy proxy servers are known to, in certain cases, drop HTTP connections after a short timeout. To protect against such proxy servers, authors can include a comment line (one starting with a ':' character) every 15 seconds or so.

Authors wishing to relate event source connections to each other or to specific documents previously served might find that relying on IP addresses doesn't work, as individual clients can have multiple IP addresses (due to having multiple proxy servers) and individual IP addresses can have multiple clients (due to sharing a proxy server). It is better to include a unique identifier in the document when it is served and then pass that identifier as part of the URL when the connection is established.

Authors are also cautioned that HTTP chunking can have unexpected negative effects on the reliability of this protocol, in particular if the chunking is done by a different layer unaware of the timing requirements. If this is a problem, chunking can be disabled for serving event streams.

Clients that support HTTP's per-server connection limitation might run into trouble when opening multiple pages from a site if each page has an EventSource to the same domain. Authors can avoid this using the relatively complex mechanism of using unique domain names per connection, or by allowing the user to enable or disable the EventSource functionality on a per-page basis, or by sharing a single EventSource object using a shared worker.