Thursday, 9 May 2013

HTML5 – Server Sent Events


What are Server Sent Events?

What is the best way to receive notifications from server? For example, friend’s status update or news feeds or a data-push from server to update data in indexed-db. If your client does not want to communicate back to server, then Server Side Event is your best friend.
As it says, Server Sent Events are message events sent from server. Will it be another contender for Websockets in real time communication? So what is the difference between these two? We will see the differences at the end of this article.
According to W3 eventsource specification, it is an API for opening an HTTP connection for receiving push notifications from a server in the form of DOM events. To enable servers to push data to Web pages over HTTP or using dedicated server-push protocols, this specification introduces the EventSource interface.

The EventSource Interface

[Constructor(DOMString url, optional EventSourceInit eventSourceInitDict)]
interface EventSource : EventTarget {
  readonly attribute DOMString 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
  [TreatNonCallableAsNull] attribute Function? onopen;
  [TreatNonCallableAsNull] attribute Function? onmessage;
  [TreatNonCallableAsNull] attribute Function? onerror;
  void close();
};

dictionary EventSourceInit {
  boolean withCredentials = false;
};

Attributes

  • onerror :  JavaScript function to call when an error occurs.
  • onmessage :  JavaScript function to call when an a message without an event field arrives.
  • onopen :  JavaScript function to call when the connection has opened.
  • readyState :  State of connection. It can be one of CONNECTING, OPEN, or CLOSED.
  • url :  URI of a script that generates the events

Lets get started..

As said before, using this API consists of creating an EventSource object and registering an event listener.

Client side implementation

<html>
<head>
<script type=’text/javascript’>
if (window.EventSource) {
      var message;
      var eventSource = new EventSource(“http://localhost:8080/HTML5Lab/sse”);
      eventSource.addEventListener(‘customevent’, function (e) {
        message  = document.getElementById(‘message’);
        message.innerHTML += “<br>[NEW Event] ” + e.data;
      });
      eventSource.addEventListener(‘open’, function(e) {
                console.log(“connection opened!”);
      }, false);
      eventSource.addEventListener(‘error’, function(e) {
          if (e.readyState == EventSource.CLOSED) {
              console.log(“connection closed!”);
           }
      }, false);
   } else {
      alert(“No Event source support.”)
    }
</script>
</head>
<body>
<h2>Server Sent Events – Example</h2>
<div id=”message”>
</div>
</body>
</html>
var eventSource = new EventSource("http://localhost:8080/HTML5Lab/sse");
Above statement initializes an eventsource. You can also see that, instead of listening to ‘message’, it listens for ‘customevent’. It is because, that is how it is named in the data object sent from sever. You will see the server code in a short while. In this code fragment, I am pointing the URL to a servlet. You can use any server side technologies like php or nodejs as given below.
var source=new EventSource("/html5lab/php/SSEServer.php");

Server side implementation

package personal.winster.technology.html5.sse;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(value = “/sse”, loadOnStartup = 1, asyncSupported = false)
public class SSEServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, 
  IOException {
    response.setContentType(“text/event-stream; charset=utf-8″);
    response.getWriter().append(“retry: 5000\n”).append(“event: customevent\n”).append(“data: Server Sent Message.\n\n”);
  }
}
Please note the content-type used. Make sure you use the same in php or node too as follows.
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$time = date('r');
echo "data: The server time is: {$time}\n\n";
flush();
?>
In above server side examples, you can see that whenever a message is sent a new connection is opened. If you do not want to do that, you can hold the ServletResponse object in an ‘AsyncContext’ and write message to it and flush the data. In that scenario, no new connection will be opened.
You might have noticed various attributes used in the message being constructed. Lets take a detailed look.

How to construct message?

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

  • 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. For example ‘customevent’ as given above.
  • data : Data part of 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.
  • id :  last event ID value of EventSource object.
  • retry : The reconnection time in milliseconds .
All other field names are ignored.

Websockets Vs Server Sent Events

  1. Both can be used to communicate in real time with browsers.
  2. SSE uses standard HTTP protocol for communication, but it is specifically designed for a one way communication, from server to client.
  3. Websockets uses complex ‘ws’ protocol for communication, but has the advantages of a duplex communication between client and server.
My recommendation to choose them efficiently based on your requirements. In my case, I use Websockets in Games. But in case of status updates in social networking sites and email-push, SSE is preferred.

References

  1. http://www.w3.org/TR/eventsource/
  2. https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events

No comments:

Post a Comment