Please read my blog's comment policy here.
Back in March of 2011, I mentioned that we had encountered some sites and servers that were not sending proper Content-Length headers for their HTTP responses. As a result, we disabled our attempt to verify Content-Length for IE9.
Unfortunately, by April, we’d found that this accommodation had led to some confusing error experiences. Incomplete executable files were not recognized by SmartScreen’s Application Reputation feature, and other signed filetypes would show “xxxx was reported as unsafe” because WinVerifyTrust would report that the incomplete file’s signature was corrupt. This problem was very commonly reported for large files (e.g. 50mb installers) by users in locations with spotty network access (e.g. where such connections are often interrupted).
With IE10, we’ve reenabled the Content-Length / Transfer-Encoding checks in IE’s Download Manager. If the Download Manager encounters a transfer that does include the number of bytes specified by the Content-Length header, or the transfer fails to include the proper 0-sized chunk (when using Transfer-Encoding: chunked), the following message will be shown:
If the user clicks Retry, IE will attempt to resume (or restart the download). In many cases of network interruption, this feature helps ensure that the user is able to download the complete file. As a compatibility accommodation, if the retried transfer again does not provide the expected number of bytes, Internet Explorer will permit the download to be treated as “finished” anyway, so that users are not blocked from interacting with buggy servers.
For instance, one buggy pattern we've seen is a server which delivers the HTTP response body as a single chunk, then calls HttpResponse.Close() instead of the proper HttpApplication.CompleteRequest.
// Add Excel as content type and attachment Response.ContentType = "application/vnd.ms-excel"; Response.AddHeader("Content-Disposition", "attachment; filename=" + binTarget);
mStream.Position = 0; mStream.WriteTo(Response.OutputStream); Response.Flush();
// BAD PATTERN: DO NOT USE. // See http://blogs.msdn.com/b/aspnetue/archive/2010/05/25/response-end-response-close-and-how-customer-feedback-helps-us-improve-msdn-documentation.aspx Response.Close();
Calling Close() like this omits the final chunk, and would cause the server's output to fail in the Download Manager if not for the compatibility accommodation.
You can test how browser’s handle incorrect transfer sizes using these two Meddler scripts:
Thanks for posting this, Eric, but it kind of threw me for a loop and I'm left with a couple of questions.
First, how does using Response.End() fit in with this? From the MSDN blog you linked, it sounds like it's not quite as bad (doesn't just terminate the connection), but it does abort the thread and bypass the rest of the pipeline. Will that still result in wrong data being sent?
I ask, because in a couple of places on our website we allow downloading of files from an aspx page. These essentially use the pattern you describe (though with TransmitFile instead of WriteTo and Response.End instead of Close). Because of this I started looking around to find a better example of how this should be written.
What I'm finding, however, is that pretty much all MS (and other) documentation follows the "wrong" pattern. For example: support.microsoft.com/.../306654 There are also several older MSDN blogs that mention this: blogs.msdn.com/.../excel-found-unreadable-content-in-workbook-do-you-want-to-recover-the-contents-of-this-workbook.aspx
Looking for more information about the "right" way to do this, I came across this Stack Overflow question: stackoverflow.com/.../1087777 Unfortunately it's still pretty vague as to what the best way to go about it is.
I realize that you can't do much about all the old stuff online, but it's be nice to see what the "right" way to send files to the clients in ASP.NET is. From what I can tell, it involves calling CompleteRequest() AND overriding a couple of pipeline methods to prevent them from rendering markup to the client:
1. Write file contents code here
3. Override RaisePostBackEvent and only call base method if not sending a file
4. Override Render and only call base method if not sending a file
Does that sound correct?
Sorry if you're the wrong person to ask -- I won't shoot the messenger, but I will inundate him with questions :)
@Nick: Response.Close abnormally terminates the connection and fails to send the closing chunk; this "bad" example was originally provided by the Excel Services team.
I'm not sure whether Response.End behaves the same way, but it should be pretty simple to test; just hit the page in question with Fiddler and see what's in the response.
In terms of the best pattern to follow, you'd probably want to talk to some ASP.NET experts.
One way I've done in the past is to just overriding the Page.Render event to do nothing. So far no obvious problem is observed.