A common requirement in the era of Web 2.0 (and beyond) technologies is the ability to have “smart” client interfaces that are aware of changes that occur on the server (data, sessions, etc.). Most approaches include some manner of AJAX that regularly polls the server for changes. While this is easy enough to accomplish, it can be a taxing and somewhat annoying process. After all, wouldn’t it be much better if the server could communicate to the client when it has something share, rather than the client mindlessly asking over-and-over-again for the same thing?

In HTML5, this becomes a reality. Enter Server-Sent Events. In a nutshell, server-sent events “enable servers to push data to Web pages over HTTP or using dedicated server-push protocols.” This means, basically, that the client doesn’t have to keep asking for information: the server will notify the client when new information is available.

In it’s present state, server-sent events are only available for Opera and Chrome (6) dev releases. Additionally, they are currently implemented in two different ways. For Opera, the technology utilizes a DOM element (), while Chrome is entirely JS based. For this overview, I’ll be concentrating on the Chrome implementation.

For the example I worked up, I’ve created a simple chat interface. The basic idea is that two people share usernames, entering them into their respective client interfaces. When each chat message is sent, the record is saved to a database on the server, and an independent page continues to query the db for new data pertaining to the chat session. If new data exists, it pushes it to the clients’ interfaces.

The Code

So first things first. Let’s create our EventSource, the base object that we’ll utilize to make the magic happen:

serverevent = new EventSource("serversent_helper.cfm");
serverevent.addEventListener('message', onMessageHandler);

This is easy enough to see what’s happening. Basically, I define a new EventSource, and pass to it the server-side page that will manage the pushing of data. Finally, I add a listener to the “onmessage” event of the EventSource, which then in turn calls a user-defined function to do whatever with the data from the server (an event object is passed in the callback method).

That’s it. Seriously, pretty simple. From here, the rest is basically getting the data from the server to be in the proper format to work with the EventSource. And this is where it gets a little murky. Per the spec, the data pushed from the server has to be in a very specific format. To be honest, I spent more time than I’d care to admit on this. But it was a good lesson.

There’s a lot in the spec about this, so I’ll only cover the most important details. This is from serversent_helper.cfm, the source specified in the EventSource invocation:

           writeoutput("data:"&serializejson(message)&chr(10));
           writeoutput("id:"&qmessages.messageid&chr(10));

What I’m not showing you is the queries I used to create and retrieve data from the chat sessions. Assuming that a chat to be pushed exists, this is the code that generates the data pushed to the client.

First, notice the content declaration of “text/event-stream“. This is incredibly important. If your content is not in this format, the push from the server will not work.

Next, notice the output sections. Per the spec, data pushed should be in the following format: “data: [string]”. It is the combination of “data:” and an accompanying string that makes the push work. Without it, nothing will happen. Following the “data:” declaration, I’ve also set “id:”. This is important because you can optionally tag each push with an id which will then be subsequently passed as a header in future interactions between the client and server. Specifically, the header “Last-Event-ID” will be used, so if a connection is lost, the last-used id can be processed when the connection is restored, picking up where it left off.

Finally, notice the newline characters at the end of each line (“chr(10)” in ColdFusion, “\n” in other languages). This is EXTREMELY important because the “field names” of the response are processed on the basis of the newline characters. In this example, if I did not put the newline character after the “data” block, my response would look like {…jsonstring…}id:numeric, rather than just {…jsonstring…}. Additionally, the Last-Event-ID would not get set.

And yes, you can use JSON, so not problems at all pushing complex data objects 🙂

Additional Methods and Considerations

I didn’t use these in my demo, but thought I’d point them out anyway. Besides the “onmessage” event, you can also add listeners to the following exposed events from EventSource:

  1. onopen – fires when connection between server and client is created
  2. onerror – fires when there’s an error in the connection

And in addition to the url and Last-Event-ID, the readyState attribute is available from EventSource. As its name implies, readyState represents the current state of the connection, and can take the following values:

  1. CONNECTING = 0
  2. OPEN = 1
  3. CLOSED = 2

Wrapping Up

As mentioned earlier, because of the relative newness of EventSource, its implementation is severely limited. However, I think it is a pretty cool idea and I can think of lots of potential uses for this, specifically in relation to connection-less data pushing. I’m excited to see how this develops as the spec is more well-defined and more browsers begin to implement it.

A note about the demo. It definitely works, but there are a few things to keep in mind:

  1. It will ONLY work with the latest dev version of Chrome (v. 6). If you try it in any other browser, it will be quite disappointing 🙂
  2. Because it’s just a quick demo, you’ll have to arrange a chat with someone with the same dev version of Chrome, and you’ll have to coordinate the usernames that you’ll use to try it out, given that the chat sessions are based on user-entered names
  3. Sure, I could have made a static example…but I thought the chat would show off the tech a bit more…so sue me!!!