Last night we got Cometd working across domains, without plugins.
“Cross domain Ajax” (see JSONP) hasn’t taken off the way that I’d hoped, perhaps in part because of a lack of necessity. It might just be easier to set up a permissive proxy to do requests to services hosted on other domains. Moore’s Law discounts distributed coordination in favor of localized transformation. But whatever the reason, there hasn’t been huge uptake, even with beautiful abstractions like Dojo’s JSON-RPC facilities (see the Yahoo services example). Intensely useful, but not everyone is on board just yet. Hopefully Joseph Smarr‘s talk on the topic at OSCON will help turn some heads.
But there’s one scenario where it’s a total no-brainer: cross-domain Comet. Normally when you set up a Comet server, it either has to sit in front of the application server or have requests passed off to it from the main web server. In the case where the app server dishes off the requests, it’s potentially still “involved” in the process if it acts like a proxy. Unless your front-end web server is also built with event-based-IO in it’s core, this can really really hurt. Luckily, there’s hope on the horizon. Sun even announced that they’re baking Comet support directly into their app server. But until the web tier collectively upgrades, we need some other way to simplify the configuration of apps+Comet servers. Until now, help has come in the form of iframes plus
document.domain setting. This lets you put the Comet server on a sub-domain and still pass event payloads back-and-forth between parent document and the iframe. This is how the tried-and-true mod_pubsub client operates, but it’s brittle. Browsers get picky when you try to set document.domain and things don’t line up exactly the right way. Add Safari’s non-deterministic iframe behaviors to the mix and it’s a recipe for sleepless nights and begging forgiveness from the ops folks when you need just one more DNS change and server restart.
We can do better.
The solution to this that Juggernaut employs is to use the built-in cross-domain capabilities of the Flash player and it’s proprietary crossdomain.xml scheme. This isn’t a bad solution, but having Flash turned off isn’t unheard of. The performance-across-the-flash-boundary problems are mostly licked these days (thanks dojo.flash!), but the complexity with getting it all set up correctly puts it in the position of “Plan B” if something better comes along. And we just might have something better in the form of
By building on top of the Dojo ScriptSrcIO infrastructure it was trivial to make a JSONP transport for Cometd. We just adapt the long-poll style of Comet but make the initial handshake messages JSONP-clued. That in place, the rest works just like “normal” long-polling, except that the client can connect from *any domain*. The configuration problem just dropped from “make sure everything cooperates at the systems and network level” to “make sure that the client and server speak the same protocol”…and I like solutions that put the problem in software and not external configuration or human effort. The checked-in server-side code shows how to handle it.
So what’s the downside? The first downside is that the latency characteristics for cross-domain Comet using this technique are the same as long-polling. Not bad, mind you, but not as optimal as iframe, multipart-mime, or Flash-based solutions when you have large numbers of messages being thrown at the client. Additionally, it’s more difficult to know when a connection “times out” or in some other way fails, but I think these are tractable. Having a Plan A and a Plan B for cross-domain Comet and a server that can speak both styles via pluggable transport wrappers is exciting to me. By lowering the configuration barrier, I think we’ll start to see a significant uptick in Comet adoption.
Next time: Why we need a standard protocol for this stuff, and what we’re doing about that in Cometd.
PS: as usual, big thanks go out to my employer SitePen who is funding this research and making it available under liberal Open Source licenses.