Please read my blog's comment policy here.
The request/response nature of HTTP works very well for traditional web pages, but to build dynamic AJAX applications, it’s often desirable for the server to be able to send data to the client on its own schedule. You could imagine, for instance, scenarios like an online game, or an event viewer, where the server may want to notify the client of something on an irregular schedule.
The COMET model allows a web application to push data to the browser without the browser explicitly requesting it—the Wikipedia page is fairly comprehensive, but my favorite reference is the “Scaling with COMET” chapter by Dylan Schiemann in Steve Souders’ second book on web performance, Even Faster Websites. While improved alternatives (like Server-Sent Events and Web Sockets) will likely eventually take over, workable COMET implementations can run in any browser built in the last few years.
We had planned on fixing this for IE8 but unfortunately found that we couldn’t do so easily. IE’s “native” XMLHTTPRequest is simply a wrapper around the existing MSXML implementation, and the MSXML implementation performs this buffering internally. Since IE doesn’t install MSXML, we couldn’t simply change that implementation ourselves.
Update: IE10's newly enhanced CORS-capable XmlHttpRequest implementation can now stream responses as they are read from the server.
I put together a simple Streaming Test page where you can examine the behavior of your browser. The test uses AJAX to download a stream that contains ten small blocks of data, each sent 1 second apart. An event handler attached to the onreadystatechange event displays what data has been received. It’s interesting to see how different browsers behave when you click the “Try XMLHTTPRequest” button:
One point to keep in mind about streaming content in this way with XHR—even in the browsers where XHR behavior works as you might hope—is that the responseText property will grow and grow, increasing the amount of memory consumed by your page. In past betas, we’ve found degenerate pages which will exhaust the system’s virtual memory if left running overnight.
In Internet Explorer 8, we introduced the new XDomainRequest object—beyond support for making cross-origin requests, this object directly wraps URLMon and thus it doesn’t inherit the buffering behavior for MSXML. Naturally, the IE8 enginering team had test cases which confirmed that XDomainRequest objects properly support streaming, calling the onprogress event as the response blocks are received from the network.
Unfortunately, it turns out that our official test suite was based on streaming large responses back to the client. The problem is that, by-default, URLMon (the network wrapper below XDomainRequest) will not bubble up notifications of download progress in the first 2kb of the response. So, if you click “Try XDomainRequest” button on the test page, you will find that you don’t see the onprogress notifications until the entire response is returned.
A workaround for this problem is to send down a two kilobyte “prelude” at the top of the response stream—my test simply sends 2kb of spaces. After that initial block is received, the onprogress event is fired as each subsequent block is received from the network. Interestingly, you will also find that this “prelude” workaround resolves the Chrome 4.1 XHR behavior described in Point #2 above. When you send this prelude, Chrome’s XHR object will get to readyState=3 and begin reporting blocks as they are received.
Until next time,
PS: Folks using Fiddler to debug with streaming content should be aware of two factors. First, Fiddler buffers most HTTP responses by default—you must disable Fiddler's buffering if you want content to be streamed to the browser as it normally is. Second, it’s important to understand that Fiddler’s Response Inspectors will not show an incomplete HTTP response. For streaming responses that never end, Fiddler will not show the partial body unless you use the newly introduced COMETPeek command on the context menu. The COMETPeek command will copy the current partial response body into the Response Inspectors for your viewing pleasure.
This page is really useful - I have been looking for days for this topic and when I landing on this page I pretty much got all my answers - so thakyou.
I can get the streaming going on some versions of ie, but version 8.0.7600.16385 (and possibly others) seems to never stream - even with the prelude! It just shows the final complete response at the end.
Also, I find that in some version of IE, a request made to my own servlet will stream the response, but on the second attempt IE will not submit a new request to the server at all - could this be a server response cache header issue? i.e. send back "do not cache" or "cache-control:private"and IE will always resubmit the request?
@Tom: You should never see streaming of responseText using XHR in IE, any version-- the code simply hasn't ever worked that way. As for the "Don't see a second request", the only things I could imagine are that you made your XHR in synchronous mode, or you are somehow hitting the connections-per-server limit and the second request is being queued.
Thanks for your response - it really is much appreciated!
My code is using the XDomainRequest object - not XHR. So using XDR with prelude on IE, I would expect to see streaming - isn't that right? And yet on that version of IE (8.0.76...) it seems I do not.
Here is my complete response
3:Beginning XDR Test.
13:[XDR-onload]. responseText: [2kb byte prelude] Begin1... 2... 3... 4... 5... 6... 7... 8... 9... 10. Done.
Any thoughts? Could this be a settings issue on IE?
Also, with the second request going missing issue, I am also using XDR, not XHR. I should point out that the requests are not concurrent. It is a case of 1 request completes fully over a longish period of time, some time passes, then a second request is made but IE actually sends nothing to the server.
@Tom: You should only see the first behavior if you have a buffering proxy server (e.g. Fiddler in non-streaming mode).
For the second one, yes, it's possible that your first response was simply cached-- What response headers do you have on the first response?
To perform HTTP Streaming with Internet Explorer 8+ I chose XDomainRequest as a transport, but I overlooked the fact that no cookies will be sent. Ignoring that fact finally resulted in problem that every request generates new session so the client's state is not maintained.
In fact, this problem can be solved by rewriting url to contain the session id, but this solution is obtrusive and has a security issue. Besides that, Some of server-side web framework such as Ruby on Rails and Django does not allow to pass the session id. To maintain user's session with them, an additional coding is required, viloating they design principle.
Consequently I'm trying to replace XDomainRequest way with classical Hidden Iframe way being used with IE6 and IE7. Is there any room for improvement with XDomainRequest.? I don't want to use classical Iframe transport and to bother the server for maintaining session.
Thanks for any ideas.
@Donghwan: Suppressing cookies is a deliberate and integral part of the XDR security model. There are no plans to change this behavior.
Alert reader Andres notes that one downside of using XDR/XHR for streaming is that the responseText will grow larger and larger, consuming more and more memory on the client. That's true, and as far as I know, there's no great workaround short of periodically destroying the object and reconnecting using a new one. HTML5 WebSockets (supported by IE10+) and Server Sent Events (supported by non-IE) shouldn't exhibit the constant-memory-growth problem.
I'm implementing server push solution. Is there a way to clear XDomainRequest.responseText, since it gets filled with the every response chunk? (as in xhr.responseText with multipart where it contains only the last response part.)
[EricLaw] No, this is one of the limitations of using streaming in XHR/XDomainRequest, as mentioned in the article above.