HTML5 - Server-Sent Events

Monday 07 November 2011

There are a lot of cool new features and one of these is Server-Sent Events. According to Server-Sent Events are usable on almost half of the modern browsers. And as is often the case Internet Explorer is the big missing piece again. But fortunately there are some nice polyfills to take care of IE. More about that some other time.


So what are Server-Sent Events?

Server-Sent Events are a way of sending data from the server to the client. There is actually another part of HTML5 that will let you do so and that is the WebSockets standard. The difference between Server-Sent Events and WebSockets is that WebSockets is a 2 directional protocol making it more complex where Server-Sent Events just allow the server to push to the client and it uses HTTP as the underlying transport.

Starting listening for push events on the client is simple, just create a new EventSource object specifying the URL to check for events. Whenever an event is pushed the EventSource.onmessage callback will be called.


   1: var source = new EventSource('home/events');
   3: source.onmessage = function (e) {
   4:     $('<li>').text('#messages');
   5: };


Alternatively you can use the source.addEventListener('message', function (e) {}, false); way of binding an event listener.

Pushing data from the client is also quite easy. In this case I am using an ASP.NET MCV controller to check for messages in a BlockingCollection<string> and is anything is found I return it.


   1: public ActionResult Events()
   2: {
   3:     var data = "";
   4:     var sb = new StringBuilder();
   5:     if (_data.TryTake(out data, TimeSpan.FromSeconds(15)))
   6:     {
   7:         sb.AppendFormat("data: {0}: {1}\n\n", DateTime.Now, data);
   8:     }
   9:     return this.Content(sb.ToString(), "text/event-stream");
  10: }


The important parts here is that the data is pushed using a MIME type of text/event-stream and the actual data send is prefixed with data:. You can add multiple lines to the data if you want, the message ends with \n\n and if you like you can actually include multiple messages in one go. The data is always send as a string but it you want to provide a richer data type all you need to do is send it as JSON and in your JavaScript code use JSON.parse( to recreate the original object.


Custom events

By default all events are send to the onmessage callback at the client. But if you want to differentiate between different event types you can include the event: eventName syntax in the message send and bind an event listener to the eventName to receive the data. In the following example I am sending actual data using the standard message but if there is no data I send a ping message.

   1: public ActionResult Events()
   2: {
   3:     var data = "";
   4:     var sb = new StringBuilder();
   5:     if (_data.TryTake(out data, TimeSpan.FromSeconds(15)))
   6:     {
   7:         sb.AppendFormat("data: {0}: {1}\n\n", DateTime.Now, data);
   8:     }
   9:     else
  10:     {
  11:         sb.AppendLine("event: ping");
  12:         sb.AppendFormat("data: {0}\n\n", DateTime.Now, data);
  13:     }
  14:     return this.Content(sb.ToString(), "text/event-stream");
  15: }

The client page looks like this:



   1: <!DOCTYPE html>
   2: <html>
   3: <head>
   4:     <meta charset="utf-8" />
   5:     <title>Server-Sent Events</title>
   6:     <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
   7:     <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
   2: </head>
   3: <body>
   4:     <div class="page">
   5:         <header>
   6:             <div id="title">
   7:                 <h1>My MVC Application</h1>
   8:             </div>
   9:             <div id="logindisplay">
  10:                     [ <a href="/Account/LogOn">Log On</a> ]
  11:             </div>
  12:             <nav>
  13:                 <ul id="menu">
  14:                     <li><a href="/">Home</a></li>
  15:                     <li><a href="/Home/About">About</a></li>
  16:                 </ul>
  17:             </nav>
  18:         </header>
  19:         <section id="main">
  20:             <ol id='messages'>
  21:             </ol>
  22:         </section>
  23:     </div>
  24:     <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript">
   1: </script>
   2:     <script>
   3:         var source = new EventSource('home/events');
   4:         source.onmessage = function (e) {
   5:             $('<li>').text('#messages');
   6:         };
   8:         source.addEventListener('ping', function (e) {
   9:             $('<li>').text('#messages');
  10:         }, false);
   8: </body>
   9: </html>