Adventures In Comet and Multipart Mime

The Dojo Bayeux client implements a bunch of different “transports” and tries to pick the right one based on what the browser can support, the cross-domain requirements, and so forth. When we started down this path, most of the reason for doing this was to implement both the forever-frame and long-polling styles of Comet as well as providing a platform to experiment with alternate transport types (e.g., Flash sockets). One of the most promising of these experiments took advantage of the multipart mime support that’s been tucked away in the Mozilla codebase for quite a while. What follows is one of those stories that makes people assume that I’m crazy to do what I do for a living. They might be right.

Multipart is attractive because it provides a way of avoiding TCP set-up and tear-down for each and every event across the channel. While it’s not significant overhead (comparatively), being able to also reduce the number of HTTP header blocks sent can also help out when it comes to wringing latency out of the system. The code indicates that multipart is supported on Safari and Mozilla, but while events are indeed delivered at the right times on Safari, you can’t get at the payload until the connection closes completely. Not useful.

Things were looking better on Firefox and it was the preferred transport type in the Dojo client, but I think that’s going to have to change. Sadly, it seems we can’t actually tell when a multipart connection has failed. In “normal” XHR requests, the 200 HTTP status code plus a “finished” readystate indicates that the contents of the request can be read and control handed back. In the multipart case, each successful block fires of a load handler and resets the readystate. That means that the combination of readystate and and status can’t be used to differentiate between block success and connection success. Making matters worse, server-side connection failure doesn’t fire any kind of readystatechange handler, and even if it did, it doesn’t appear to be possible to determine if the connection is closed from any of the public properties on the object.

So, OK, what about falling back to a timer that restarts the connection every N seconds for good measure? This might work in cases like failover where a lag of 10 to 30 seconds might be acceptable but not for normal operation. Should events be flow regularly, it might never be necessary to hit this “backstop”. Not great, but I gave it a try, only to discover that Firefox won’t give you responseText of an XHR request if the connection is marked as multipart but the response isn’t a 200 and wrapped in a multipart block. Since we’re trying to use HTTP status codes correctly and keep the server internals from needing to fork significantly for each pluggable transport, it’s something of a step backward to need this kind of hand-holding.

I’d still like to support the multipart transport type, but until at least one of the implementations becomes rational for use from the XHR object, I think I’m going to just be commenting this transport out in cometd.js. Like XHR itself, it’s one to mark down for resurrection a year or two from now. At least we still have good enough options in the mean time.

4 Comments

  1. Posted December 21, 2006 at 4:53 pm | Permalink

    Dude, you are a freakin guru on the edge.  Awesome to see!  Thanks for the killer info, and work.  (I still owe you and Owen some Dojo-by-examples… haven’t forgotten, just swamped with work before the Holiday shutdown).
    Thanks again and Happy Holidays!

  2. Posted December 22, 2006 at 11:32 pm | Permalink

    Crap! I ran into the issue of not being able to use multipart documents a while ago (before I came out to San Francisco) – I had hoped that someone would take the time to investigate if there was a way to get around the status problem but it looks like it broken for this use case…

    I had built a chat client that assumed a “ping” from the server every 10 seconds, so I knew something bad happened when I didn’t get a “hello” (a slight modification to the twisted example chat server)

  3. Posted December 22, 2006 at 11:36 pm | Permalink

    also another potential issue is the number of active connections you can make (I think it defaults to 2 or maybe 8) hence if your app uses multipart docs and users have multiple instances of it in tabs you might use up all your connections)

    If I remember right there is both a limit per server and total

  4. Posted March 4, 2007 at 8:35 am | Permalink

    Alex, thanks I’ve been looking for a way to do this!

    Check out: <a title=”Force Apache to output any HTTP Status Code with ErrorDocument” href=”http://www.askapache.com/2007/htaccess/apache-status-code-headers-errordocument.html”>Force Apache to output any HTTP Status Code with ErrorDocument</a>

    <blockquote cite=”http://www.askapache.com/2007/htaccess/apache-status-code-headers-errordocument.html”>I setup an automated system to view all 57 Apache Response codes and ErrorDocuments, saving the headers and returned content for future reference. Use this page as a reference when designing scripts that use headers. Ex: 404 Not Found, 200 OK, 304 Not-Modified, 506 Service Temporarily Unavailable, etc.

    When a Status code is encountered, Apache serves the header and the ErrorDocument for the error code. So if you can see any Header and ErrorDocument by causing that error on Apache.

    For instance, if you request a file that doesn’t exist, a 404 Not Found is issued and the corresponding ErrorDocument is served with the 404 Not Found Header. So we can see what Apache 404 Errors and Response Codes look like, but how do we cause errors for the 56 other Apache Response Codes?</blockquote>