1. 9.2 服务器发送的事件
      1. 9.2.1 概述
      2. 9.2.2 The EventSource interface
      3. 9.2.3 Processing model
      4. 9.2.4 Parsing an event stream
      5. 9.2.5 Interpreting an event stream
      6. 9.2.6 Authoring notes
      7. 9.2.7 无连接推送和其他特性
      8. 9.2.8 垃圾回收
      9. 9.2.9 Implementation advice

9.2 服务器发送的事件

Server-sent_events

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android11+

9.2.1 概述

This section is non-normative.

为了让服务器能够使用 HTTP 或专用的服务器推送协议推送数据给 Web 页面, 本规范引入了 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")的流:

event: add
data: 73857293

event: remove
data: 2153

event: add
data: 113411

处理这样的流的脚本可能看起来像这样(其中 addHandlerremoveHandler 函数接受一个参数,就是事件):

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

默认的事件类型是 "message"。

事件流总是使用 UTF-8 解码。没有办法声明另外一种字符编码。


事件流请求可以使用 HTTP 301 或 307 重定向,就像普通 HTTP 请求一样。 如果连接关闭客户端会重新连接;可以使用 HTTP 204 No Content 响应码来告诉客户端不要重连了。

使用这个 API 而不是用 XMLHttpRequestiframe 模拟, 可以让用户代理更好地利用网络资源,尤其是用户代理实现者和网络维护者可以事先协调。 其他的好处包括,能显著节约移动设备的电量。 这在下面的一章 无连接推送 中有更详细的讨论。

9.2.2 The EventSource interface

EventSource

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android11+
[Exposed=(Window,Worker)]
interface EventSource : EventTarget {
  constructor(USVString url, optional EventSourceInit eventSourceInitDict = {});

  readonly attribute USVString url;
  readonly attribute boolean withCredentials;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSED = 2;
  readonly attribute unsigned short readyState;

  // networking
  attribute EventHandler onopen;
  attribute EventHandler onmessage;
  attribute EventHandler onerror;
  undefined close();
};

dictionary EventSourceInit {
  boolean withCredentials = false;
};

Each EventSource object has the following associated with it:

Apart from url these are not currently exposed on the EventSource object.

source = new EventSource( url [, { withCredentials: true } ])

EventSource/EventSource

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera11+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+

Creates a new EventSource object.

url is a string giving the URL that will provide the event stream.

Setting withCredentials to true will set the credentials mode for connection requests to url to "include".

source . close()

EventSource/close

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+

Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED.

source . url

EventSource/url

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+

Returns the URL providing the event stream.

source . withCredentials

EventSource/withCredentials

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+

Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise.

source . readyState

EventSource/readyState

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+

Returns the state of this EventSource object's connection. It can have the values described below.

The EventSource(url, eventSourceInitDict) constructor, when invoked, must run these steps:

  1. Let ev be a new EventSource object.

  2. Let settings be ev's relevant settings object.

  3. Let urlRecord be the result of parsing url with settings's API base URL and settings's API URL character encoding.

  4. If urlRecord is failure, then throw a "SyntaxError" DOMException.

  5. Set ev's url to urlRecord.

  6. Let corsAttributeState be Anonymous.

  7. If the value of eventSourceInitDict's withCredentials member is true, then set corsAttributeState to Use Credentials and set ev's withCredentials attribute to true.

  8. Let request be the result of creating a potential-CORS request given urlRecord, the empty string, and corsAttributeState.

  9. Set request's client to settings.

  10. User agents may set `Accept`/`text/event-stream` in request's header list.

  11. Set request's cache mode to "no-store".

  12. Set ev's request to request.

  13. Run this step in parallel:

    1. Fetch request.

  14. Return ev.


The url attribute's getter must return the serialization of this EventSource object's url.

The withCredentials attribute must return the value to which it was last initialized. When the object is created, it must be initialized to false.

The readyState attribute represents the state of the connection. It can have the following values:

CONNECTING (numeric value 0)
The connection has not yet been established, or it was closed and the user agent is reconnecting.
OPEN (numeric value 1)
The user agent has an open connection and is dispatching events as it receives them.
CLOSED (numeric value 2)
The connection is not open, and the user agent is not trying to reconnect. Either there was a fatal error or the close() method was invoked.

When the object is created its readyState must be set to CONNECTING (0). The rules given below for handling the connection define when the value changes.

The close() method must abort any instances of the fetch algorithm started for this EventSource object, and must set the readyState attribute to CLOSED.

The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the EventSource interface:

Event handler Event handler event type
onopen

EventSource/open_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+
open
onmessage

EventSource/message_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+
message
onerror

EventSource/error_event

Support in all current engines.

Firefox6+Safari5+Chrome6+
Opera12+Edge79+
Edge (Legacy)NoInternet ExplorerNo
Firefox Android45+Safari iOS5+Chrome Android18+WebView Android4.4+Samsung Internet1.0+Opera Android12+
error

9.2.3 Processing model

The resource indicated in the argument to the EventSource constructor is fetched when the constructor is run.

As data is received, the tasks queued by the networking task source to handle the data must act as follows.

HTTP 200 OK responses with a `Content-Type` header specifying the type `text/event-stream`, ignoring any MIME type parameters, must be processed line by line as described below.

When a successful response with a supported MIME type is received, such that the user agent begins parsing the contents of the stream, the user agent must announce the connection.

The task that the networking task source places on the task queue once fetching for such a resource (with the correct MIME type) has completed must cause the user agent to reestablish the connection in parallel. This applies whether the connection is closed gracefully or unexpectedly (but does not apply when fetching is canceled by the user agent, e.g., in response to window.stop(), since in those cases the final task is actually discarded). It doesn't apply for the error conditions listed below except where explicitly specified.

HTTP 200 OK responses that have a Content-Type specifying an unsupported type, or that have no Content-Type at all, must cause the user agent to fail the connection.

Network errors that prevents the connection from being established in the first place (e.g. DNS errors), should cause the user agent to reestablish the connection in parallel, unless the user agent knows that to be futile, in which case the user agent may fail the connection.

Any other HTTP response code not listed here, as well as the cancelation of the fetch algorithm by the user agent (e.g. in response to window.stop() or the user canceling the network connection manually) must cause the user agent to fail the connection.


When a user agent is to announce the connection, the user agent must queue a task which, if the readyState attribute is set to a value other than CLOSED, sets the readyState attribute to OPEN and fires an event named open at the EventSource object.

When a user agent is to reestablish the connection, the user agent must run the following steps. These steps are run in parallel, not as part of a task. (The tasks that it queues, of course, are run like normal tasks and not themselves in parallel.)

  1. Queue a task to run the following steps:

    1. If the readyState attribute is set to CLOSED, abort the task.

    2. Set the readyState attribute to CONNECTING.

    3. Fire an event named error at the EventSource object.

  2. Wait a delay equal to the reconnection time of the event source.

  3. Optionally, wait some more. In particular, if the previous attempt failed, then user agents might introduce an exponential backoff delay to avoid overloading a potentially already overloaded server. Alternatively, if the operating system has reported that there is no network connectivity, user agents might wait for the operating system to announce that the network connection has returned before retrying.

  4. Wait until the aforementioned task has run, if it has not yet run.

  5. Queue a task to run the following steps:

    1. If the EventSource object's readyState attribute is not set to CONNECTING, return.

    2. Let request be the EventSource object's request.

    3. If the EventSource object's last event ID string is not the empty string, set `Last-Event-ID`/last event ID string, encoded as UTF-8, in request's header list.

    4. Fetch request and process the response obtained in this fashion, if any, as described earlier in this section.

When a user agent is to fail the connection, the user agent must queue a task which, if the readyState attribute is set to a value other than CLOSED, sets the readyState attribute to CLOSED and fires an event named error at the EventSource object. Once the user agent has failed the connection, it does not attempt to reconnect!


The task source for any tasks that are queued by EventSource objects is the remote event task source.

9.2.4 Parsing an event stream

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.

Since connections established to remote servers for such resources are expected to be long-lived, UAs should ensure that appropriate buffering is used. In particular, while line buffering with lines are defined to end with a single U+000A LINE FEED (LF) character is safe, block buffering or line buffering with different expected line endings can cause delays in event dispatch.

9.2.5 Interpreting an event stream

Streams must be decoded using the UTF-8 decode algorithm.

The UTF-8 decode algorithm strips one leading UTF-8 Byte Order Mark (BOM), if any.

The stream must then be parsed by reading everything line by line, with a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character not preceded by a U+000D CARRIAGE RETURN (CR) character, and a single U+000D CARRIAGE RETURN (CR) character not followed by a U+000A LINE FEED (LF) character being the ways in which a line can end.

When a stream is parsed, a data buffer, an event type buffer, and a last event ID buffer must be associated with it. They must be initialized to the empty string

Lines must be processed, in the order they are received, as follows:

If the line is empty (a blank line)

Dispatch the event, as defined below.

If the line starts with a U+003A COLON character (:)

Ignore the line.

If the line contains a U+003A COLON character (:)

Collect the characters on the line before the first U+003A COLON character (:), and let field be that string.

Collect the characters on the line after the first U+003A COLON character (:), and let value be that string. If value starts with a U+0020 SPACE character, remove it from value.

Process the field using the steps described below, using field as the field name and value as the field value.

Otherwise, the string is not empty but does not contain a U+003A COLON character (:)

Process the field using the steps described below, using the whole line as the field name, and the empty string as the field value.

Once the end of the file is reached, any pending data must be discarded. (If the file ends in the middle of an event, before the final empty line, the incomplete event is not dispatched.)


The steps to process the field given a field name and a field value depend on the field name, as given in the following list. Field names must be compared literally, with no case folding performed.

If the field name is "event"

Set the event type buffer to field value.

If the field name is "data"

Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to the data buffer.

If the field name is "id"

If the field value does not contain U+0000 NULL, then set the last event ID buffer to the field value. Otherwise, ignore the field.

If the field name is "retry"

If the field value consists of only ASCII digits, then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer. Otherwise, ignore the field.

Otherwise

The field is ignored.

When the user agent is required to dispatch the event, the user agent must process the data buffer, the event type buffer, and the last event ID buffer using steps appropriate for the user agent.

For web browsers, the appropriate steps to dispatch the event are as follows:

  1. Set the last event ID string of the event source to the value of the last event ID buffer. The buffer does not get reset, so the last event ID string of the event source remains set to this value until the next time it is set by the server.

  2. If the data buffer is an empty string, set the data buffer and the event type buffer to the empty string and return.

  3. If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.

  4. Let event be the result of creating an event using MessageEvent, in the relevant Realm of the EventSource object.

  5. Initialize event's type attribute to message, its data attribute to data, its origin attribute to the serialization of the origin of the event stream's final URL (i.e., the URL after redirects), and its lastEventId attribute to the last event ID string of the event source.

  6. If the event type buffer has a value other than the empty string, change the type of the newly created event to equal the value of the event type buffer.

  7. Set the data buffer and the event type buffer to the empty string.

  8. Queue a task which, if the readyState attribute is set to a value other than CLOSED, dispatches the newly created event at the EventSource object.

If an event doesn't have an "id" field, but an earlier event did set the event source's last event ID string, then the event's lastEventId field will be set to the value of whatever the last seen "id" field was.

For other user agents, the appropriate steps to dispatch the event are implementation dependent, but at a minimum they must set the data and event type buffers to the empty string before returning.

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.6 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.

9.2.7 无连接推送和其他特性

在受控环境中运行的用户代理(例如与特定运营商绑定的手机上的浏览器), 可能会将连接管理工作卸载到网络上的代理服务器上。 在这种情况下,出于符合性目的的用户代理被认为包括手机软件和网络代理。

例如,移动设备上的一个浏览器在已经建立连接后,可能检测到这是一个支持的网络, 因此请求这个网络上的一个代理服务器来接管连接的管理工作。 这一情况下的时间线可能是这样的:

  1. 浏览器连接到远程 HTTP 服务器并请求 EventSource 构造器中的作者指定的资源。
  2. 服务器发送了一些消息。
  3. 在两个消息之间,浏览器检测到除了有一个 TCP 连接外整个网络活动是空闲的, 于是决定切换到睡眠模式来保存电量。
  4. 浏览器断开服务器连接。
  5. 浏览器联系网络上的一个服务,并请求它(一个 "推送代理")来代替维护连接。
  6. 这个 "推送代理" 服务联系远程 HTTP 服务器并请求 EventSource 构造器 (可能包含一个 `Last-Event-ID` HTTP 头) 中的作者指定的资源。
  7. 浏览器允许移动设备进入睡眠。
  8. 服务器发送了另一条消息。
  9. "推送代理" 服务使用类似 OMA 的技术将事件传送给移动设备, 该事件仅唤醒足以处理该事件的程序,然后返回到睡眠状态。

这可以减少总的数据用量,因此会节省可观的电量。

除了实现既有 API 和本规范定义的 text/event-stream 连线格式, 以及上述更分散的实现方式之外, 也可以支持由其他 适用规范 定义的事件帧的格式。 本规范中没有定义如何解析和处理它们。

9.2.8 垃圾回收

EventSource 对象的 readyStateCONNECTING 且该对象有一个或更多事件监听器 在监听 open, messageerror 事件时, 调用 EventSource 对象的构造器的 WindowWorkerGlobalScope 对象上 必须有一个指向 EventSource 对象自己的强引用。

EventSource 对象的 readyStateOPEN 且该对象有一个或更多事件监听器 在监听 messageerror 事件时, 调用 EventSource 对象的构造器的 WindowWorkerGlobalScope 对象上 必须有一个指向 EventSource 对象自己的强引用。

远程事件任务源 中存在由 EventSource 对象入队的事件时, 调用 EventSource 对象的构造器的 WindowWorkerGlobalScope 对象上 必须有一个指向那个 EventSource 对象的强引用。

当用户代理要 强制关闭 一个 EventSource 对象时(这在 Document 对象永远消失时就会发生), 用户代理必须终止为这个 EventSource 对象启动的任何 fetch 算法的实例, 同时必须设置 readyState 属性为 CLOSED

如果 EventSource 对象在连接仍然打开的情况下被垃圾回收, 用户代理必须终止为这个 EventSource 对象启动的任何 fetch 算法的实例。

9.2.9 Implementation advice

This section is non-normative.

User agents are strongly urged to provide detailed diagnostic information about EventSource objects and their related network connections in their development consoles, to aid authors in debugging code using this API.

For example, a user agent could have a panel displaying all the EventSource objects a page has created, each listing the constructor's arguments, whether there was a network error, what the CORS status of the connection is and what headers were sent by the client and received from the server to lead to that status, the messages that were received and how they were parsed, and so forth.

Implementations are especially encouraged to report detailed information to their development consoles whenever an error event is fired, since little to no information can be made available in the events themselves.