20

I have written a custom http handler. I have done this by writing a class which implements the IHttphandler.

Inside that class I have code like this,

context.Response.Clear();
context.Response.ClearHeaders();
context.Response.AddHeader("Content-Disposition", "attachment;filename=" + attachmentFileName);
context.Response.AddHeader("Content-Length", new FileInfo(downloadFile).Length.ToString());
context.Response.ContentType = GetMimeType(attachmentFileName);
context.Response.TransmitFile(downloadFile);
context.Response.Flush();
context.Response.Close();

Occasionally I receive an error like this,

Exception HttpException The remote host closed the connection The error code is 0x800703E3

Or this,

Exception HttpException The remote host closed the connection The error code is 0x80070040

In both cases the stack trace is this,

at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush)
at System.Web.HttpResponse.Flush()

This occurs in production, and if I look back over the last few days errors have occurred 23 times, and in total the above code has been called 497 times.

I suspect this failure relates to the user clicking the link to initiate the above code more than once (which will give them multiple download dialogs) then they cancel some of them. Having said that if it was something like that I would have expected the connection to close gracefully at both ends.

How can I prove the exact cause of this error? I have tried to enable .NET tracing like this Why don't trace listeners log custom handler traffic? but couldn't get it to work.

What I found though is that I have enabled IIS tracing to log failed requests. The failure occurred again, and NOTHING was in that log.

Any other tracing I can enable for instance?

The next thing I tried was this,

if (context.Response.IsClientConnected)
{
    context.Response.Flush();
    context.Response.Close();
}
else
{
    LogMessage("Client has disconnected before flush was called", Severity.Information);
}

But that didn't make any difference. The reason though I guess is that the client disconnected while the download was taking place, not before flush was called.

Community
  • 1
  • 1
peter
  • 13,009
  • 22
  • 82
  • 142

7 Answers7

14

Take out both the Flush() and Close() call. You really don't need them. Once your handler is done, it'll exit, and ASP.NET will handle closing the request.

Besides, Flush() should be used when you're streaming content to the client (adding parts to the response stream in blocks). You don't need to use it with TransmitFile().

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • But thinking about this, is that just going to 'hide' the problem? If closing the request causes an HttpException I am not going to have visibility of that any more. – peter May 25 '11 at 02:55
  • 1
    I now remember why I required the flush. It is like this, http://stackoverflow.com/questions/2275894/calling-response-transmitfile-from-static-method if I don't call the flush my code deletes the temporary file that is being downloaded, and it can end up deleting it too early. – peter May 25 '11 at 02:57
  • @peter, if you're generating the file then you should look at a way to generate the content and stream to the client without saving to disk at all. Other than that, you can look at leaving the temporary file in place and then using a separate background thread/process to delete files on a schedule. – Samuel Neff May 25 '11 at 11:01
  • OK, I'll try that. What about my second comment there 'is this going to hide the problem'? – peter May 25 '11 at 21:33
  • @peter, I'm not convinced there is any problem at all. An exception doesn't necessarily mean there is a problem. You're talking about a networking exception when a client disconnects (big deal) and a thread abort exception when a request is forcefully ended (expected). – Samuel Neff May 26 '11 at 00:03
  • OK, I'm just being pessimistic remembering that this system is in production. I removed the need to save the file locally on the web server as you suggested, and also removed the Flush() and Close(). I'll test it in production to see how it goes. – peter May 26 '11 at 02:13
  • @peter, good, developers should always be pessimistic. Makes for better developers to think of all the things that can go wrong before they do. We do need a bit of balance between pessimism and realism though. :-) – Samuel Neff May 26 '11 at 02:25
8

I was into similar issues, got this article which explains that Response.End() should be avoid ed and instead suggests to use CompleteRequest() method. MSDN documentation has also been updated with this information. I hope this helps someone.

http://blogs.msdn.com/b/aspnetue/archive/2010/05/25/response-end-response-close-and-how-customer-feedback-helps-us-improve-msdn-documentation.aspx

DotNetUser
  • 6,494
  • 1
  • 25
  • 27
4

Use Response.End() instead of Response.Flush()

This is what the source code for Response.End() looks like:

public void End()
{
    if (this._context.IsInCancellablePeriod)
    {
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    }
    else if (!this._flushing)
    {
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        {
            this._context.ApplicationInstance.CompleteRequest();
        }
    }
}
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
  • 1
    Hmmm, not sure about this. When I use Response.End() it throws a thread abort exception and everything else turns to custard. – peter Apr 05 '11 at 23:11
  • @peter, throwing a `ThreadAbortException` is exactly what `Response.End()` is designed to do. This exception shouldn't cause other problems. – Samuel Neff May 25 '11 at 18:00
  • OK, so you are saying it is a good idea to use Response.End() or not? – peter May 25 '11 at 21:52
  • @peter, it's a good idea if you need to end a request in the middle of processing for some reason. If you have a dedicated handler that does one thing and reaches the end of it's natrual processing, you don't need to do anything, just return from your handler. If you do have a reason to call `Response.End()`, yes, it will throw a ThreadAbortException, but no, that should not be considered a problem. – Samuel Neff May 26 '11 at 00:05
  • @peter, the ThreadAbortException is handled in the ASP.NET pipeline, so `Response.End()` will effectively send the HttpResponse and cancel the processing of any of your code that would occur after the call. – smartcaveman May 26 '11 at 21:20
2

What .NET framework are you using? This forum thread here describes a similar problem using IIS7 with .NET2.0 specifically to do with the client disconnect, a problem which was addressed in .NET framework 3.5

The actual error code maps to

0x800703E3 "The I/O operation has been aborted because of either a thread exit or an application request."
Dan
  • 1,489
  • 12
  • 16
1

try to set up the max size file on the web config to a bigger one.

You can set the maxRequestLength (in kb)

try to not reclycle app pools on iis to often also.

pedrommuller
  • 15,741
  • 10
  • 76
  • 126
  • try not to recycle app pools on IIS too often also? What do you mean by that? – peter Apr 04 '11 at 01:50
  • I tested this on IIS locally on my machine, and I didn't need the MaxRequestLength set, and I downloaded a file that was 8mb. Anybody know why it works? I cannot upload over 4mb without that entry in the web.config. – peter Apr 04 '11 at 05:08
  • by default almost all browsers supports only 2mb size (aprox) to upload, if you try to upload without configuring that you are going to get error 500 or 404, you have tu override that default behaviour with the maxRequestLength and in your production server when and app pool gets recycled the worker process that host you application, stops that "maybe" are killing your requests in the application, Rather than abruptly stopping the worker process, which can cause service interruptions try ro recycle app pools on demand. – pedrommuller Apr 05 '11 at 20:57
1

For more details about this exception visit http://support.microsoft.com/kb/977453

Sooraj
  • 89
  • 1
  • 2
0

I have the error sometimes:

Exception message: An error occurred while communicating with the remote host. The error code is 0x80070057.

Is random in production. Its not reproduce in Development or QA.

I will apply this solution: Response.IsClientConnected I hope it will fix the error.

Source:

https://stackoverflow.com/a/11441375/1536197

Hernaldo Gonzalez
  • 1,977
  • 1
  • 21
  • 32