Webtide

HTTP Streaming and Internet Explorer

by Michael CarterOctober 25th, 2007

In early 2006, Alex Russell posted about a neat hack that the Google Talk team in Gmail use to support Comet in Internet Explorer, a trick which works as far back as IE 5.01. What great news! A reliable way to stream Comet messages to Microsoft’s browsers. If only it were that easy.

I have not been alone in the following findings: after connecting the htmlfile ActiveX object as a streaming Comet transport to my Comet server, everything works perfectly for a few messages, but then abruptly fails. The connection is closed by the browser with the server-side error “Connection reset by peer.” Surprisingly, however, no one seems to have looked too deeply into this problem.

On careful inspection, every open implementation or example of this transport out there fails in the same way; indeed, many sites purport to use this transport in their example code, but actually run live demos using some other mechanism. At first this left me feeling stupid: “Why does it work for all these other guys, but not for me?” Then I got smart, sniffed some packets, and looked through their actual JavaScript source. As of this writing, I could find no examples or explanations of how to correctly implement the htmlfile transport.

Finding a Pattern

Comet is complicated. The technical nature of Comet implementations make it very difficult to isolate the server-side code from the browser code. There are so many interactions between so many moving parts that the first step to debugging a new Comet app or transport is to specifically isolate the problem. So when my original htmlfile code failed, I followed these steps:

  1. Time it. I figured that it might be a set timeout, perhaps an idling timeout or a limit on the total time for an open connection. I tried waiting 10 seconds between each event, 1 minute, 3 minutes and 10 minutes. For all but the 10 minute interval I had got the same number of messages. So on to my second plan of attack.
  2. Count messages. I was getting 7 messages before failure.
  3. Increase message payload. Having ascertained that the problem is not a set time, but rather the number of messages, I wondered if it depended on the size of those messages. I switched my app to send “Yo” * 50 instead of just “Yo”. Nothing changed: I could send the same number of messages (7) even with a payload roughly 50 times larger.
  4. Change browser callbacks. Quickly running out of ideas, I tried altering the message payload in other ways. I created a new app which would simply ‘alert’ the data instead of adding it to a styled ‘div’. Behold! The eighth message was successfully received. As was the ninth, and every message up to the 42nd. Strange—and still broken—but encouraging.

Even more mysteriously, I was able to successfully send 50 messages to the browser, each of which contained a JavaScript ‘alert’. These alerts triggered alert dialogs, as expected, but after clicking “ok” 42 times, the “connection reset by peer” error arrived on-cue to my server—even though it had already successfully sent 8 more events than the browser seemed to receive.

I set aside this revelation for the time being. Changing the browser callbacks seemed to affect the behavior, so I tried another simple change—alerting twice. This time, only 25 messages were processed. Strange… I would understand 21 messages of two alerts each being allowed, as I was allowed 42 alerts sent one at a time. But now I’m allowed 50 if I send them two at a time?

I remain puzzled by the exact nature of the limitations of IE’s htmlfile object, but a rough estimate is as follows: You are allowed to make approximately fifty DOM manipulations/accesses before all JavaScript execution is terminated immediately and the streaming connection is closed. These functions aren’t limited to DOM-related activities though. For instance, an alert will count against you. I don’t know the list of functions that don’t count against you and those that do, but I suspect there is probably a simple explanation of how it works. And I very much doubt we’ll ever receive that explanation.

The Fix

We happen to be in luck. Changing JavaScript variables, including Array functions, seems okay as far as the gods of htmlfile streaming are concerned. So our solution is to simply append event payloads to an array from within the iframe, have the parent window use use a timer loop (’setInterval’) to periodically check the array for new messages, and then pass them to the callback. It’s not as elegant as I’d like…but it beats all the other techniques I’ve tried.

Why not just call a function attached the parent window, you wonder? It turns out htmlfile’s iframe doesn’t care where the function object lives; instead, it cares which thread is used to execute the code. The htmlfile thread is a capricious beast, and will rebel when employed to do too much DOM work. The effect of setInterval is to move the actual DOM manipulations to a thread that is perfectly safe for that sort of scripting. This fix works for IE 5.01+

Gotchas

  • If you recall my first debugging technique of changing the time between each event, it failed when I attempted to wait 10 minutes between events. To be specific, if the connection remains open for 300 seconds (5 minutes) without receiving any data, then Internet Explorer will consider the request to have timed out. In IE 5.01 and 5.5 an error dialog is displayed when the connection is broken, but no such error occurs in IE6/7.
  • When I attempted to use alerts in the htmlfile I noted that I was able to send 50 events but the alert only occurred 42 times. The reason is that an alert would block further execution of JavaScript. So if you use a blocking operation inside of the htmlfile, then the server will be able to keep pushing events to the browser, but they may never be executed and there will be no way of knowing server-side. The fix I’ve outlined above solves this problem by never doing any JavaScript besides appending events to an array. The parent window can do as many blocking operations as it wants and its never a problem.

Conclusion

We are left with a robust streaming transport for Internet Explorer 5.01+ which I believe to be production ready. I’d love to know how the Gmail team got around this problem. Perhaps it is as easy as an additional argument when instantiating the htmlfile object. I have no idea because I cannot find proper documentation. I am satisfied with this solution though. It is good enough. It gets the job done and the user sees none of the ugliness under the hood.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
Comet Support by SitePen

14 Responses to “HTTP Streaming and Internet Explorer”

  1. HTTP Streaming and Internet Explorer at ~/my-other-beans Says:

    [...] Read the whole story here… [...]

  2. Alex Says:

    Your post is very informative, I’m sure it will help me a lot.

    My comment is about Firefox. Supposedly, this ActiveX technique is important only because it improves the user experience in IE by eliminating the active throbber.

    But when using hidden iframe and streaming, I also can see the throbber working, along with the hourglass cursor and the message on the status bar on Firefox 2, at least. And we do not have ActiveX help here.

    What do you think about this?

    Thanks,
    Alex

  3. Michael Carter Says:

    You can use a technique called xhr streaming for Firefox 2 and Safari. We use this approach in Orbited. This is my recommendation for webkit or gecko based browsers.

    Additionally, there is a simple hack that I’ve discovered for Firefox 2 that we also use in Orbited. Create a new iframe object, x = document.createElement(”iframe”) and hold on to it. Then, every time an event is received, use document.body.addChild(x); document.body.removeChild(x); This will have the effect of eliminating the loading bar and hour glass. I suspect x can actually be any dom element, i just first tried it with an iframe and then never used anything else.

    The downside to the latter is that in some Firefox versions on a mac the favicon.ico flickers. Also, the it doesn’t do well with streaming large numbers of events per second. Adding and removing an object for each event can be a lot of overhead.

  4. Alex Says:

    Michael, thanks for the info… Do you have any URL or pointer to the “xhr streaming” technique? I know one technique using xhr and multipart/x-mixed-replace, but sadly has its own problems: for instance, it is difficult to recover from errors, since you do not get proper notifications…

    I will take a look at the add/remove of DOM nodes, sounds interesting, but yes: it won’t scale for large number of events.

  5. Jacob Rus Says:

    Alex:

    Yeah, to do XHR streaming (not using multipart), you want to create an XHR, which you feed some custom data format, with events separated by some known separator. Then you can have a callback in the browser for onreadystatechange, which parses out the response text, grabbing events from between separators, and does what it likes with them. It takes a bit of care, because for Safari, you’ll need to use some uncommon MIME type (we use ‘application/x-orbited-event-stream’), and then pad your stream with 256 bytes of dummy data. XHR streaming works great in Safari 1.3+ and Firefox 1.5+ (and I think earlier FF as well, but I’m not completely sure how far back), but doesn’t work in Opera or Internet Explorer, for which you need to use, respectively, server-sent events, and the htmlfile technique described here.

  6. Jacob Rus Says:

    Take a look at how Orbited does it, in these two bits of code.

    The meteor guys also have a reasonable explanation (they call this “XHR Interactive State”): http://meteorserver.org/browser-techniques/

  7. Jacob Rus Says:

    Make that link: Orbited. Heh.

  8. David Négrier Says:

    Actually, the solution described by Michael in this post is not completely exact.
    The strange behaviour observed by Michael was coming from the garbage collector collecting the HtmlFile object. The garbage collectors happens to trigger every X Javascript operations, which explains the strange behaviour.
    Hopefully, a more complete solution was found and described by Michael in a post on the Orbited mailing list:
    http://groups.google.com/group/orbited-users/browse_thread/thread/e337ac03d0c9f13f?hl=en

    So you might want to check this link.
    Oh, and by the way Michael, really really nice work! I’d never have figured the garbage collection problem.

  9. Michael Carter Says:

    Yes, since publishing this article I’ve discovered the root of these problems and a better solution. As David said, you can see my thoughts by following the above link to the Orbited mailing list. I’ll organize them into a coherent article and publish it soon as a second part to this article.

  10. Comet Daily » Blog Archive » IE ActiveX(”htmlfile”) Transport, Part II Says:

    [...] my last post I discussed using the ActiveX(”htmlfile”) technique to provide a usable streaming transport in Internet Explorer. The solution I provided will work, [...]

  11. Mark Says:

    Michael said: “Create a new iframe object, x = document.createElement(”iframe”) and hold on to it. Then, every time an event is received, use document.body.addChild(x); document.body.removeChild(x); This will have the effect of eliminating the loading bar and hour glass.”

    The additional iframe makes the hourglass go away but I still can see the cursor flickers from arrow to hourglass and back to arrow again. Is this the best that can be done or am I doing something wrong?

  12. Henry Says:

    I tried the add/remove iframe to avoid the hourglass in FF 2.0 and as Mark says it does create flockering of the cursor.

    The other possible way to is to set the body cursor to your custom cursor other than “default” so it will not change to the hourglass cursor.

  13. Justin Says:

    I’m having a similar hourglass problem with Opera. Have you found any solutions to it? Thanks!

  14. Adnan Siddiqi Says:

    wht about for FireFox? any alternative?

Leave a Reply



Copyright 2014 Comet Daily, LLC. All Rights Reserved