SimpleComet, HTTP streaming and toilet scrubbing

Why use SimpleComet?

I don't know about you, but I just hate it when something I need is lost in the middle of some steam factory. I mean, personnally I really don't want to install the entire internet and the kitchen sink just so I can use a little feature it happens to provide. Well, that's the main problem with about every existing Comet implementation. Most of them come bundled with a special Comet server and while that's great for large-scale projects, if you just want to use basic Comet features in your application you probably won't want to install an entire server for it. Then there's also a few "standalone" libraries, except they're part of, and depend on to work, larger frameworks. So that's where SimpleComet fits in: it doesn't depend on anything and allows you to develop Javascript/PHP Comet-based applications without any third-party software or significant overhead.

How does SimpleComet work?

To this day there's only a handful of techniques that can be used to provide Comet features, all of which rely on obscure mechanisms and browser loopholes. And because of the whole thing's hackish nature, no technique is at the same time cross-browser compatible and without side effects. That's the reason why SimpleComet transparently uses different ones (all true push implementations, polling is lame) depending on the client's browser to get the best of all worlds:

I verified the library's compatibility with various recent browsers by running a test stream for 15 minutes during which it pushed more than 100 events to the client. All of them passed nicely, here's the list: Firefox 3.0.5, Internet Explorer 7, Safari 3.2, Opera 9.63, Chrome 1 and iPhone 2.2. I could have made my tests more intense to stress the browsers but truth be told, after 15 minutes and a hundred events it starts to be a good idea to reset the stream anyway (HTTP servers and browsers really aren't made to keep a connection open that long).

Demonstration

In order to illustrate how SimpleComet is used and what can be done with it, I made a short example application where every 5 seconds, the server pushes a random BOFH excuse as to why the script isn't working that the client then shows. Here it is in action:

Now lets see what it looks like from the inside. First, we have a server-side PHP component that generates the stream of excuses:

<?php// First we load the SimpleComet PHP class and the list of excuses.require('inc/simplecomet.class.php');
$comet = new SimpleComet();
$excuses = file('inc/excuses.txt');
// This is an infinite loop, which makes the stream endless.while(true){// We fetch an excuse at random.$excuse = trim($excuses[rand(0, count($excuses))]);
// If the excuse is too long, we get another one.if(strlen($excuse) > 60){continue; }// Finally, we push our excuse to the client.$comet->push($excuse);
// 5 seconds delay before the next excuse.sleep(5);
}?>

It's pretty straightforward; the push() method is used every time we want to push an event to the client (here every 5 seconds to send an excuse). One thing to remember though: you absolutely need to keep SimpleComet's instantiation before any other output is generated because it needs to disable output buffering and send headers which is impossible once data has already been sent to the client.

Next we have the client-side Javascript component that recieves the data pushed by the server and shows it on the page:

<script type="text/javascript">
// This function will be called every time the server pushes a new event.function push(event){// For this example, we simply show the excuse on the page.
document.getElementById('excuse').innerHTML = event;
}// This function will be called when/if the stream closes.function disconnected(){// For this example we'll just show a nice message.
document.getElementById('excuse').innerHTML = '<img src="img/arrow.png" alt="" title="" />'+
' Click to see MORE reasons why this souldn\'t work!';
document.getElementById('control').value = 'start';
}// This function is executed when the button is clicked.function toggle(){// First we check if the stream is open.if(!comet.active){// Lets start streaming!
comet.open('excuses.php', push, disconnected);
document.getElementById('control').value = 'stop';
}else{// Streaming is active, means the user wants to stop it.
comet.close();
}}
</script>

As you can see, I'm using different things from the SimpleComet library.

URL : The stream's address. It has to be on the same server due to cross-site browser restrictions.

push : Function that will be called each time a new event is pushed by the server. Optional.

disconnect : Function that will be called when/if the stream closes. Optional.

comet.close ( void ) : Closes the stream. Of course it can still be picked up again by comet.open() !

comet.active : Set to "true" when a stream is currently open, "false" otherwise.

Finally, the last piece we need to put everything together is a small HTML file with a button and a div for our Javascript to write into. This article is already way too long so I'm not going to paste the example's HTML code in here since you can just check it out in the source code of the live demonstration. At some point I'll probably rewrite MSNSpy so it uses Comet instead of a bunch of AJAX calls, which will be a much better design and a more interesting demonstration, but christmas is coming and I don't want to get behind on my drinking so time to wrap up the geek stuff!