Thursday, March 8, 2012

A small Tutorial on HTML5 Server Sent Events(SSE) Part-I



When communicating using SSEs, a server can push data to your app whenever it wants, without the need to make an initial request. Updates can be streamed from server to client as they happen. SSEs open a single unidirectional HTTP channel between server and client.

1) To subscribe to an event stream, create an EventSource object and pass it the URL of your stream:

if(!window.EventSource){
 var source =newEventSource('stream.php');
}else{
 // Result to xhr polling :( xhttprequest
}

Note: If the URL passed to the EventSource constructor is an absolute URL, its origin (scheme, domain, port) must match that of the calling page.

2) Next, set up a handler for the message event. You can optionally listen for open and error:

source.addEventListener('message',function(event){
 console.log(event.data);
},false);


source.addEventListener('open',function(event){
 // Connection was opened.
},false);

source.addEventListener('error',function(event){
 if(e.readyState ==EventSource.CLOSED){
   // Connection was closed.
 }
},false);

3) When updates are pushed from the server, the onmessage handler fires and new data is be available in its event.data property. The magical part is that whenever the connection is closed, the browser will automatically reconnect to the source after ~3 seconds. Your server implementation can even have control over this reconnection timeout.
That's it. Your client is now ready to process events from stream.php.

Event Stream Format:

For sending an event stream from the source on server
  • Construct a plaintext response (HTTP 200 OK), served with Content-Type:text/event-stream.
  • The response should be of this format
data:My message\n\n
  • You can send a unique id with an stream event by including a line starting with "id:":
id:12345\n
data: GOOG\n
data:556\n\n
  • Setting an ID lets the browser keep track of the last event fired so that if, the connection to the server is dropped, a special HTTP header (Last-Event-ID) is set with the new request. This lets the browser determine which event is appropriate to fire. The message event contains a event.lastEventId property.
  • Set the timeout by including "retry:", followed by the number of milliseconds to wait before trying to reconnect (default is 3 secs)
retry:10000\n
data: hello world\n\n
  • If "event:" is present, followed by a unique name for the event, the event is associated with that name. On the client, an event listener can be setup to listen to that particular event. For example, the following server output sends three types of events, a generic 'message' event, 'userlogon', and 'update' event:
data:{"msg":"First message"}\n\n
event: userlogon\n
data:{"username":"John123"}\n\n
event: update\n
data:{"username":"John123","emotion":"happy"}\n\n


With event listeners setup on the client Browser :
source.addEventListener('message',function(e){
 var data = JSON.parse(e.data);
 console.log(data.msg);
},false);

source.addEventListener('userlogon',function(e){
 var data = JSON.parse(e.data);
 console.log('User login:'+ data.username);
},false);

Cancel an Event Stream (avoid reconnection):
  • To cancel a stream from the client simply call:
source.close();
  • To cancel a stream from the server, respond with a non "text/event-stream" Content-Type or return an HTTP status other than 200 OK (e.g. 404 Not Found).
SSE support in Desktop Browsers:
Web Browser
Support?
Information
Internet ExplorerNone
Mozilla FirefoxYesStarting with Firefox v6.0
Google ChromeYes
OperaYesStarting with Opera v11
SafariYesStarting with Safari v5.0

The above table was referred from http://en.wikipedia.org/wiki/Server-sent_events



SSE support in Hand Held devices (Browsers):



Test the functionality of SSE with Apache WAMP server:

Implementation:
The implementation has been done by using perl, html and java scripts.

Requirements:

  1. Apache Server which supports perl, php etc.,
  2. Browser that supports HTML5.
Installation:
  1. Apache WAMP Server 2.2 has been used here. Just download and install it from www.wampserver.com
  2. Active Perl software is needed. Just download and install it from www.activestate.com
Configuring Perl on WAMP:
  1. Click on the WAMP system tray apache>httpd.conf. The file will open in a text editor.
  2. Search for “Options Indexes FollowSymLinks ” and replace it with “Options Indexes FollowSymLinks Includes ExecCGI “
  3. Search for “#AddHandler cgi-script .cgi” and replace it with “AddHandler cgi-script .cgi
AddHandler cgi-script .pl “
  1. Search for “DirectoryIndex index.php index.php3 index.html index.htm” and replace it with “DirectoryIndex index.php index.php3 index.html index.htm index.cgi index.pl
  2. Now WAMP is configured to run perl scripts.
  3. Click on WAMP system tray and click on start all services. The system tray should glow green in color.
  4. Keep your .cgi or .pl files in the path “C:\wamp\bin\apache\Apache2.2.11\cgi-bin\” and to run the scripts open the browser and type this url :http://localhost/cgi-bin/  followed by your program name
For more information regarding installtion and configuring WAMP and Active Perl, Please refer the following blog post : 
http://shravanthimohan.wordpress.com/2011/11/26/configuring-perl-on-wamp/

Example 1: (using CGI script)

1. Consider the following perl script “ticker.cgi”
#!c:/perl/bin/perl
print "Content-Type: text/event-stream\n\n";
while(true){
  print "event: server-time\n";
  $time = localtime();
  print "data: $time\n\n";
}
Save it in “C:\wamp\bin\apache\Apache2.2.11\cgi-bin\”

2. Consider the following html with javascript. “testcgi.html”
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function invokeSSE(){

var source = new EventSource('http://localhost:80/cgi-bin/ticker.cgi');
source.addEventListener('server-time', function(e) {
document.getElementById('ticker').innerHTML = e.data + '<br>';
}, false);


source.addEventListener('open', function(e) {
//Connection Open
}, false);
source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
alert("Connection closed");
}
}, false);
}
</script>
</head>
<body onload="invokeSSE()">
<div id="ticker" name="ticker">
  [TIME]
</div>
</body>
</html>
Save the file in “C:\wamp\www”

3. Open the browser and type the URL as http://localhost/testcgi.html and you should be able to view the system time ticking.

Example 2: (With 2 events)

1. Consider a perl script “sendImage.cgi”
#!c:/perl/bin/perl
use CGI qw(:standard);
my $cgi = CGI;
my $apple = "apple.jpg";
my $android = "android.jpg";
my $image = $cgi->param("image");
print "Content-Type: text/event-stream\n\n";
print "event: image\n";
if($image == $apple){

$img = $android;
}
else

$img = $apple;
print "data: $img\n\n";
sleep(3);
print "event: refresh\n";
print "data: location.reload()\n\n";
sleep(2);

Save it in “C:\wamp\bin\apache\Apache2.2.11\cgi-bin\”

2. Consider a html code with javascript “client.html”. This page has many images. But one image changes due to server sent event.
<html>
<head>
<title>CLient Page</title>
<script type="text/javascript">

function invokeSSE(){
var img = document.getElementById('dynamic').getElementsByTagName('img')[0].getAttribute('src');
source.addEventListener('image', function(e) {
var data=e.data;
document.getElementById('dynamic').getElementsByTagName('img')[0].setAttribute('src',data);
}, false);
 
source.addEventListener('refresh', function(e) {
setTimeout(e.data,3000);
}, false);


source.addEventListener('open', function(e) {
//Connection Open
}, false);

source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
alert("Connection closed");
}
}, false);
}
</script>
</head>
<body onload="invokeSSE()">
<center>
<table>

<tr>
<td id="dynamic"><img src="apple.jpg" height=200 width=200/></td>
<td><img src="google.jpg"/></td>
<td><img src="linked.jpg"/></td>
</tr>
<tr>
<td><img src="micro.jpg"/></td>
<td><img src="sony.jpg"/></td>
<td><img src="xerox.jpg"/></td>
</tr>
</table>
</center>
</body>
</html>
Save the file in “C:\wamp\www\SSEClient”

3. Open the browser and type the URL as http://localhost/SSEClient/client.html and you should be able to view a page with few images. One image changes on server sent event.
Initially:

:
Later: (After few seconds)

2 comments: