Using server-sent events

  • Revision slug: Server-sent_events/Using_server-sent_events
  • Revision title: Using server-sent events
  • Revision id: 342849
  • Created:
  • Creator: teoli
  • Is current revision? No
  • Comment Revert to revision of 2012-12-26 09:52:09 by EvanProdromou

Revision Content

Developing a web application that uses server-sent events is quite easy. You'll need a bit of code on the server to stream the events to the web application, but the web application side of things works nearly identically to handling any other type of events.

You can see the example used here in action.

Receiving events from the server

The server-sent event API is contained in the EventSource interface; to open a connection to the server to begin receiving events from it, you create a new EventSource object, specifying the URI of a script that generates the events. For example:

var evtSource = new EventSource("ssedemo.php");
Note: Although it's not yet part of the standard, EventSource supports CORS in Firefox 11 and later. This is expected to become standard soon.

Once you've instantiated your event source, you can begin listening for messages:

evtSource.onmessage = function(e) {
  var newElement = document.createElement("li");
  
  newElement.innerHTML = "message: " + e.data;
  eventList.appendChild(newElement);
}

This code listens for incoming messages (that is, notices from the server that do not have an event field on them) and appends the message text to a list in the document's HTML.

You can also listen for events, using addEventListener():

evtSource.addEventListener("ping", function(e) {
  var newElement = document.createElement("li");
  
  var obj = JSON.parse(e.data);
  newElement.innerHTML = "ping at " + obj.time;
  eventList.appendChild(newElement);
}, false);

This code is similar, except that it will be called automatically whenever the server sends a message with the event field set to "ping"; it then parses the JSON in the data field and outputs that information.

Sending events from the server

The server-side script that sends events needs to respond using the MIME type text/event-stream. Each notification is sent as a block of text terminated by a pair of newlines. For details on the format of the event stream, see {{ anch("Event stream format") }}.

The PHP code for the example we're using here follows:

date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");

$counter = rand(1, 10);
while (1) {
  // Every second, sent a "ping" event.
  
  echo "event: ping\n";
  $curDate = date(DATE_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  echo "\n\n";
  
  // Send a simple message at random intervals.
  
  $counter--;
  
  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }
  
  ob_flush();
  flush();
  sleep(1);
}

It generates an event every second, with the event type "ping". Each event's data is a JSON object containing, in this case, just the ISO 8601 timestamp corresponding to the time at which the event was generated. At random intervals, a simple message (with no event type) is sent.

Event stream format

The event stream is a simple stream of text data, which must be encoded using UTF-8. Each message is separated by a pair of newline characters. A colon as the first character of a line is, in essence, a comment, and is ignored.

Note: The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.

Each message consists of one or more lines of text listing the fields for that message. Each field is represented by the field name, followed by a colon, followed by the text data for that field's value.

Fields

The following field names are defined by the specification:

event
The event's type. If this is specified, an event will be dispatched on the browser to the listener for the specified event name; the web site would use addEventListener() to listen for named events. the onmessage handler is called if no event name is specified for a message.
data
The data field for the message. You may specify multiple lines of data by including more than one line starting with "data:" in the message; the server will concatenate the lines' data with newline ('\n') characters separating them before sending the message. [Which server will do that?] When sending the message, any terminating newline will be removed from the value before transmission. [It will?]
id
The event ID to set the EventSource object's last event ID value to.
retry
The reconnection time to use when attempting to send the event. [What code handles this?] This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.

All other field names are ignored.

Note: If a line doesn't contain a colon, the entire line is treated as the field name, with an empty value string.

Examples

Data-only messages

In the following example, there are three messages sent. The first is just a comment, since it starts with a colon character. As mentioned previously, this can be useful as a keep-alive if messages may not be sent regularly.

The second message contains a data field with the value "some text". The third message contains a data field with the value "another message\nwith two lines". Note the newline in the value.

: this is a test stream

data: some text

data: another message
data: with two lines 

Named events

This example sends some named events. Each has an event name specified by the event field, and a data field whose value is an appropriate JSON string with the data needed for the client to act on the event. The data field could, of course, have any string data; it doesn't have to be JSON.

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}

event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

Mixing and matching

You don't have to use just unnamed messages or typed events; you can mix them together in a single event stream.

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

data: Here's a system message of some kind that will get used
data: to accomplish some task.

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

Browser compatibility

{{ CompatibilityTable() }}

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
EventSource support 9 {{ CompatGeckoDesktop("6.0") }} {{ CompatNo() }} 11 5
Feature Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
EventSource support {{ CompatNo() }} {{ CompatUnknown() }} {{ CompatUnknown() }} 11.1 4

 

Revision Source

<p>Developing a web application that uses server-sent events is quite easy. You'll need a bit of code on the server to stream the events to the web application, but the web application side of things works nearly identically to handling any other type of events.</p>
<p>You can <a class="external" href="/samples/sse" title="https://developer.mozilla.org/samples/sse/">see the example used here in action</a>.</p>
<h2 id="Receiving_events_from_the_server">Receiving events from the server</h2>
<p>The server-sent event API is contained in the <a href="/en/Server-sent_events/EventSource" title="en/Server-sent events/EventSource"><code>EventSource</code></a> interface; to open a connection to the server to begin receiving events from it, you create a new <a href="/en/Server-sent_events/EventSource" title="en/Server-sent events/EventSource"><code>EventSource</code></a> object, specifying the URI&nbsp;of a script that generates the events. For example:</p>
<pre>
var evtSource = new EventSource("ssedemo.php");
</pre>
<div class="note">
  <strong>Note:</strong> Although it's not yet part of the standard, <code>EventSource</code> supports <a href="/En/HTTP_access_control" title="En/HTTP_access_control">CORS</a> in Firefox 11 and later. This is expected to become standard soon.</div>
<p>Once you've instantiated your event source, you can begin listening for messages:</p>
<pre class="brush: js">
evtSource.onmessage = function(e) {
  var newElement = document.createElement("li");
  
  newElement.innerHTML = "message: " + e.data;
  eventList.appendChild(newElement);
}
</pre>
<p>This code listens for incoming messages (that is, notices from the server that do not have an <code>event</code> field on them) and appends the message text to a list in the document's HTML.</p>
<p>You can also listen for events, using <code>addEventListener()</code>:</p>
<pre class="brush: js">
evtSource.addEventListener("ping", function(e) {
  var newElement = document.createElement("li");
  
  var obj = JSON.parse(e.data);
  newElement.innerHTML = "ping at " + obj.time;
  eventList.appendChild(newElement);
}, false);
</pre>
<p>This code is similar, except that it will be called automatically whenever the server sends a message with the <code>event</code> field set to "ping"; it then parses the JSON&nbsp;in the <code>data</code> field and outputs that information.</p>
<h2 id="Sending_events_from_the_server">Sending events from the server</h2>
<p>The server-side script that sends events needs to respond using the MIME&nbsp;type text/event-stream. Each notification is sent as a block of text terminated by a pair of newlines. For details on the format of the event stream, see {{ anch("Event stream format") }}.</p>
<p>The PHP code for the example we're using here follows:</p>
<pre class="brush: php">
date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");

$counter = rand(1, 10);
while (1) {
  // Every second, sent a "ping" event.
  
  echo "event: ping\n";
  $curDate = date(DATE_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  echo "\n\n";
  
  // Send a simple message at random intervals.
  
  $counter--;
  
  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }
  
  ob_flush();
  flush();
  sleep(1);
}
</pre>
<p>It generates an event every second, with the event type "ping". Each event's data is a JSON&nbsp;object containing, in this case, just the ISO 8601 timestamp corresponding to the time at which the event was generated. At random intervals, a simple message (with no event type) is sent.</p>
<h2 id="Event_stream_format">Event stream format</h2>
<p>The event stream is a simple stream of text data, which must be encoded using UTF-8. Each message is separated by a pair of newline characters. A&nbsp;colon as the first character of a line is, in essence, a comment, and is ignored.</p>
<div class="note">
  <strong>Note:</strong> The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.</div>
<p>Each message consists of one or more lines of text listing the fields for that message. Each field is represented by the field name, followed by a colon, followed by the text data for that field's value.</p>
<h3 id="Fields">Fields</h3>
<p>The following field names are defined by the specification:</p>
<dl>
  <dt>
    <code>event</code></dt>
  <dd>
    The event's type. If this is specified, an event will be dispatched on the browser to the listener for the specified event name; the web site would use <code>addEventListener()</code>&nbsp;to listen for named events. the <code>onmessage</code> handler is called if no event name is specified for a message.</dd>
  <dt>
    <code>data</code></dt>
  <dd>
    The data field for the message. You may specify multiple lines of data by including more than one line starting with "data:"&nbsp;in the message; the server will concatenate the lines' data with newline ('\n') characters separating them before sending the message. [<em>Which server will do that?</em>] When sending the message, any terminating newline will be removed from the value before transmission. [<em>It will?</em>]</dd>
  <dt>
    <code>id</code></dt>
  <dd>
    The event ID&nbsp;to set the <a href="/en/Server-sent_events/EventSource" title="en/Server-sent events/EventSource"><code>EventSource</code></a> object's last event ID&nbsp;value to.</dd>
  <dt>
    <code>retry</code></dt>
  <dd>
    The reconnection time to use when attempting to send the event. [<em>What code handles this?</em>] This must be an integer, specifying the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.</dd>
</dl>
<p>All other field names are ignored.</p>
<div class="note">
  <strong>Note:</strong> If a line doesn't contain a colon, the entire line is treated as the field name, with an empty value string.</div>
<h3 id="Examples">Examples</h3>
<h4 id="Data-only_messages">Data-only messages</h4>
<p>In the following example, there are three messages sent. The first is just a comment, since it starts with a colon character. As mentioned previously, this can be useful as a keep-alive if messages may not be sent regularly.</p>
<p>The second message contains a data field with the value "some text". The third message contains a data field with the value "another message\nwith two lines". Note the newline in the value.</p>
<pre>
: this is a test stream

data: some text

data: another message
data:&nbsp;with two lines 
</pre>
<h4 id="Named_events">Named events</h4>
<p>This example sends some named events. Each has an event name specified by the <code>event</code> field, and a <code>data</code> field whose value is an appropriate JSON string with the data needed for the client to act on the event. The <code>data</code> field could, of course, have any string data; it doesn't have to be JSON.</p>
<pre>
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

event: usermessage
data:&nbsp;{"username": "bobby", "time":&nbsp;"02:34:11", "text":&nbsp;"Hi everyone."}

event:&nbsp;userdisconnect
data: {"username": "bobby", "time": "02:34:23"}

event: usermessage
data:&nbsp;{"username": "sean", "time":&nbsp;"02:34:36", "text":&nbsp;"Bye, bobby."}
</pre>
<h4 id="Mixing_and_matching">Mixing and matching</h4>
<p>You don't have to use just unnamed messages or typed events; you can mix them together in a single event stream.</p>
<pre>
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

data: Here's a system message of some kind that will get used
data:&nbsp;to accomplish some task.

event: usermessage
data:&nbsp;{"username": "bobby", "time":&nbsp;"02:34:11", "text":&nbsp;"Hi everyone."}
</pre>
<h2 id="Browser_compatibility">Browser compatibility</h2>
<p>{{ CompatibilityTable() }}</p>
<div id="compat-desktop">
  <table class="compat-table">
    <tbody>
      <tr>
        <th>Feature</th>
        <th>Chrome</th>
        <th>Firefox (Gecko)</th>
        <th>Internet Explorer</th>
        <th>Opera</th>
        <th>Safari</th>
      </tr>
      <tr>
        <td>EventSource support</td>
        <td>9</td>
        <td>{{ CompatGeckoDesktop("6.0") }}</td>
        <td>{{ CompatNo() }}</td>
        <td>11</td>
        <td>5</td>
      </tr>
    </tbody>
  </table>
</div>
<div id="compat-mobile">
  <table class="compat-table">
    <tbody>
      <tr>
        <th>Feature</th>
        <th>Android</th>
        <th>Firefox Mobile (Gecko)</th>
        <th>IE Mobile</th>
        <th>Opera Mobile</th>
        <th>Safari Mobile</th>
      </tr>
      <tr>
        <td>EventSource support</td>
        <td>{{ CompatNo() }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>{{ CompatUnknown() }}</td>
        <td>11.1</td>
        <td>4</td>
      </tr>
    </tbody>
  </table>
</div>
<p>&nbsp;</p>
Revert to this revision