Server-sent events

Gabor Paller's picture

The post about the off-line HTML5 applications mentioned 4 different traits that can be identified in mobile applications. These are: context-awareness, reflection, off-line access and asynchronous communication. This time we will see, how HTML5 supports asynchronous communication.
 
The classic web networking model was built on the HTTP protocol. While HTTP proved to be extremely scalable thus enabling the huge services of the web economy, its transactional nature made it hard to implement asynchronous behaviour. Asynchronous behaviour in this sense means that the requests and responses are not tightly coupled like in a transactional protocol but can have more complex relationship. For example an asynchronous request may take some time to execute and may result in more than one responses. Also, the time difference between the request and the responses may be significant.
 
A transactional protocol is not a good choice for a communication scenario like this. Still, as the web model depended so strongly on HTTP, it was necessary to implement asynchronous communication on top of HTTP and a number of strategies have been devised, most popular of them being the long polling. Common characteristic of these methods is that they abuse transactional HTTP so that it can be turned into an asynchronous protocol. Long polling, for example, blocks the response generation of the client's HTTP requests so that it can return data immediately using the open response stream. While these methods provide acceptable reliability in the fixed-line networking world where communication is relatively fast and cheap, they cause power consumption and network load problems on mobile.
 
The answer of HTML5 was to incorporate a standard way of asynchronous communication which is called server-sent events. Server-sent events API comprises client-side JavaScript API and server-side data format to deliver push messages. The server sees HTTP long polling requests: the client sends an HTTP GET request and if that request is terminated (because the underlying TCP stream times out or the server closes the response stream), the request is repeated by the client. The role of the server is to send its push messages in a predefined format over the HTTP response stream.
 
The beauty of this mechanism is that it does not define the protocol used by the client. By default, it is the same protocol seen by the server. Nothing prevents the implementator, however, to implement an efficient push protocol (e.g. based on SMS) between the mobile browser client and a network proxy and let the proxy convert this proprietary protocol into the HTTP long polling data format seen by the application server. This allows the same push application to be be deployed with a default HTTP long polling protocol which is not efficient for the device's battery and may deliver push messages with considerable delay (see more about the delivery speed/power consumption trade-off here) for free access and  deploy a proxy that exploits efficient radio protocols for wireless push with QoS guarantees for paying access. This can be a cool business case for operators.
 
The example program is attached to this post. You have to be logged into the Sfonge website to access it.
 
The example is a client-server pair. The client is an HTML5 page that displays two continuously changing "stock tickers". The server generates these ticker values in a randomly increasing manner and sends them to the client asynchronously.
 
Install first Glassfish Open Source Edition (I tested with version 3.1.2.2). Download events.war from under this post and issue the following command:
 

asadmin deploy --force events.war
 
Now direct your browser to
 

http://127.0.0.1/events
 
and observe the two, continuously updated "ticker" values.
 
The JavaScript machinery relies on the EventSource class that creates the channel to the server.
 

var source = new EventSource('EventExample');
source.addEventListener('tckr1', tckr1Handler, false);
source.addEventListener('tckr2', tckr2Handler, false);
 
Here we create the event source with the URI that points to the event source generator (implemented by the EventExample servlet) and add two event handlers for "tckr1" and "tckr2" events. These event handlers eventually extract the event data from the event object and update the HTML page accordingly.
 
Compared to regular servlets, EventExample does a strange thing. It does not try to serve the request as quickly as it could but actually blocks the response stream. When it has data (simulated by a timer here), it sends the data back to the client and flushes the stream. An important thing has not been implemented here: the server has to be prepared that the response stream disconnects, due to a timeout. In this case the client will repeat the request automatically. The server has to be able to store its state when the response stream is interrupted and has to be able to identify the new request from the client when that request comes in. Some sort of session ID is necessary to achieve that.
 
The server-sent event specification describes the data format the server should generate. Without going into too many details, let's observe the format that our example program generates.
 

event: tckr1
data: <ticker1 value>
<two empty lines>
event: tckr2
data: <ticker2 value>
<two empty lines>
 
Eventually these data packets are turned into event objects by the client and are delivered to our JavaScript handlers.
 
Server-sent events work well in Firefox and Chrome desktop browsers and under iOS. Unfortunately Android built-in browser does not support server-sent events but both Firefox and Chrome on Android do. These implementations all use HTTP GET so they are not more efficient with regards to battery consumption and delivery time guarantees than normal HTTP long polling. The important point, however, is the option of server-sent event proxy for mobiles.

Attached file: 
Taxonomy upgrade extras: