Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Ending a Response without Response.End() Exceptions?


:P
On this page:

I'm re-working some of my internal banner manager code and one thing I'd like to do is optimize the banner serving as much as possible. Banners are served from the database by default, but the banners can be cached optionally in memory so there's no data access.

Problem is though the banner click tracking still has to hit the database and while this isn't exactly slow it's slower and on occasion fires into some cleanup code. What I really want to do is something like this:

string output = "document.write(" +
                wwWebUtils.EncodeJsString(banner.RenderLink()) +
                ");";
 
context.Response.Write(output);
 
Response.End();
 
// *** Update the hit counter - plus potentially other 'stuff' 
//     that's potentially slow
manager.BannerHit(banner.BannerId);

Of course you probably already know that this won't work because Response.End() REALLY means Response.End() as it throws an exception and finishes out the request so any code following a Response.End() code doesn't ever fire. BannerHit above never runs. Instead a ThreadAbort exception is fired which kills the current request.

There's also context.ApplicationInstance.CompleteRequest() which also closes out the request, but it doesn't properly shut down the Response object and so the request still hangs.

So, I started playing around with a few other ways to get this to work. One obvious though is to turn off Response buffering and just .Flush() the content when done:

HttpResponse Response = context.Response;
Response.Clear(); 
Response.BufferOutput = false;
 
Response.ContentType = "text/html";

string output = "document.write(" +
                wwWebUtils.EncodeJsString(banner.RenderLink()) +
                ");";
 
context.Response.Write(output);
 
Response.Flush();
 
// *** Update the hit counter - plus potentially other 'stuff' 
//     that's potentially slow
manager.BannerHit(banner.BannerId);

By turning off Response buffering you're supposed to be able to flush content back to the client, but in some quick tests throwing in some Thread.Sleep() breaks I clearly failed to get the Response to flush output all the way to the  client. It looks like the headers get sent with Chunked output headers provided, but it doesn't actually 'finish' the HTTP request so that the client knows the request has completed. Still doesn't work.

The first 'buffer' sent to the client (as captured with Fiddler) looks like this and includes only the headers:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Sun, 25 May 2008 15:52:20 GMT
Connection: close

 

Then when the request finishes 5 seconds later I get the second chunk:

105
document.write("<a href='/WestWindWebToolkit/wwBanner.ashx?a=c&id=b628a170&t=633473275403436000&u=http%3a%2f%2frasnote%2fWestWindWebToolkit%2fBannerTest.aspx' target='_top'><img src='
http://www.west-wind.com/banners/webconnectionBanner50.gif' border='0'></a>");
0

Both IE and Firefox wait until the final chunk arrives to actually display the data. So this makes this mechanism fairly worthless for small content. Both IE and FireFox render content eventually when the chunked data accumulated gets longer, but for small bits like the above nothing happens until there's enough data. Not sure if this is IIS holding off on sending back the chunked data or the browsers trying to cache the content first.

Really what I need to do is Response.End() but continue running my code in the current method. Looking at the Response.End code with Reflector yields:

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();
        }
    }
}

so it really looks like there's no clean way to do this.This clearly shows that ASP.NET is bascially throwing the entire thread, which is pretty brute force. The else block looks promising except that _ended isn't accessible. I'm not quite sure where the actual ThreadAbortException comes from as neither CancelModuleException() or the CompleteRequest fire it.

After searching around for a while I gave up and just did the obvious:

string output = "document.write(" +
                wwWebUtils.EncodeJsString(banner.RenderLink()) +
                ");";
context.Response.Write(output);
 
// *** Ignore Response.End() exception
try
{
    Response.End();
}
catch { }
 
Thread.Sleep(5000);
 
// *** Update the hit counter
manager.BannerHit(banner.BannerId);

and this surprisingly works! Given the code above and the CancelModuleException() signature and implementation I'm not even sure why this even works at all, but when run the above the banner HTML is immediately pushed out and rendered.

In this handler scenario I'm working with the behavior is probably fine because there won't be other modules or end handlers that need to fire. But inside of an ASP.NET page or other more complex handler I think I'd be worried about side effects for this behavior. For now this does the trick in this scenario.

So, what are you doing to handle an 'early exit' scenario in ASP.NET when you need additional processing? I suppose another option would be to fire an asynchronous delegate and let it go off on a separate thread, but that seems like overkill as well.

It seems there should be an easier solution to this possibly common scenario.

Posted in ASP.NET  

The Voices of Reason


 

James Driscoll
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

Hi Rick,

I think this might do the trick...

        Response.Flush();
        Response.Close();


Cheers,

James

Rick Strahl
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

@James - tried Response.Close() and it does feed the current request, but subsequent requests get stuck. Some odd interaction with the active HTTP Connection if keep alives are active I suspect. Basically for my banners when I call Response.Close() the first renders immediately (no delay), but any other banners simply never render at all. Haven't had time to check what the problem on the second and third one is but it appears the handler fires correctly.

Alan
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

What if instead of just swallowing the exception you would just use try/finally? Something like

try
{
  Response.End();
}
finally
{
  // *** Update the hit 
  countermanager.BannerHit(banner.BannerId);
}

Steve
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

Yeah, I jumped in from RSS to say the same thing - this seems like a perfect case for a try/finally. And you might even want to include more inside the try{}, ie. everything after a flush that sends the banner code over, since the banner should still be visible & counted even if the rest of the page doesn't finish. (Admittedly this is a boundary case)

Rob
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

ThreadAbortExceptions are nasty little guys. Even trying to catch them has some subtle issues. Take a look at the MSDN docs for it under the "Remarks" section:

http://msdn.microsoft.com/en-us/library/system.threading.threadabortexception.aspx

"When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Since the thread can do an unbounded computation in the finally blocks, or call Thread..::.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread..::.Join method. Join is a blocking call that does not return until the thread actually stops executing."

Marco
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

I had a similar problem where I had to send email(s) as part of a request without blocking the repsonse to the user. I've search and found the .NET Power Threading Library from Jeffrey Richter (Wintellect) which suited my requirements very well.

In my site I just collect the data needed for sending the email(s) and queue using the ReaderWriterGate class of that library. Because it recycles threads it is very performant and I found out that without load the delegate queued in the RWG lock is executed immediately after the response has ended - using the same thread. This also explains why Response.Close() doesn't work correctly for subsequent requests: Response.End() resp. the ThreadAbortException thrown by it doesn't do anythiong else than putting the current thread back into the ASP.NET thread pool. If it is not put back and you have some load, the thread pool maybe becomes empty and there are no threads available for processing the waiting requests. I didn't have much load yet so I can't really say how it is handled then, but I guess it will let process the ASP.NET client requests and execute the queued calls between them if there is not so much load for a moment.

http://www.wintellect.com/PowerThreading.aspx

Dave
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

IMO, that's the perfect scenario to use a delegate. I think by the time you found the right combination of Response.End/Flush/Clear/try/catch machinations, the delegate would actually be simpler.

Speednet
May 26, 2008

# re: Ending a Response without Response.End() Exceptions?

A few months ago I went through a couple of days trying to crack this nut, and after all that I returned to the simple Response.End(), forcing myself to ignore the fact that exceptions are being thrown.

I'm talking about using it in a major web app, not a small one-off like is outlined in the example here.

It is troubling that there is no simple solution for this, and that the issue is not even widely discussed. (Thank you Rick for bring up the issue.)

In the help docs there is some brief discussion about the fact that an issue exists, and some hints at how to solve it, but it is all completely useless to try to use in a consistent, global fashion throughout a major web app.

The same exact issue exists for Server.Transfer(), which halts the current page executing and transfers control to another one. The same exception is thrown, which is pretty bad.

The worst scenario I came across was on a complex page that has code for several events, including Init, Load, PreRender, etc., and is also testing in various events for postbacks, AJAX partial postbacks, etc. I tried to gracefully do a Server.Transfer() to another page early in the page life cycle, and quickly realized that it was all but impossible, if I wanted to maintain my sanity.

Rick, hopefully your blog post will get noticed by someone at MS, who is driven to try to fix the situation.

sebastien crocquesel
May 27, 2008

# re: Ending a Response without Response.End() Exceptions?

Why not simply start an asynchronous operation ? One simplest way is by using delegate.
http://msdn.microsoft.com/en-us/library/22t547yb.aspx

Rick Strahl
May 27, 2008

# re: Ending a Response without Response.End() Exceptions?

Thanks for all the feedback. A few comments.

The following DOES NOT work:

try
{
    Response.End();
}
finally
{ 

    Thread.Sleep(5000);
    // *** Update the hit counter
    manager.BannerHit(banner.BannerId);
}


Same scenario as if just letting the code run - banners wait 5 seconds before displaying even though Response.End() has fired.

Using an anonymous delegate also works:

string output = "document.write(" +
                wwWebUtils.EncodeJsString(banner.RenderLink()) +
                ");";
context.Response.Write(output);

Action<object> d = delegate(object val)
{
    Thread.Sleep(5000);

    // *** Update the hit counter
    manager.BannerHit(banner.BannerId);
};
d.BeginInvoke(null,null,null);               

Response.End();

Mark Bracketyt
May 27, 2008

# re: Ending a Response without Response.End() Exceptions?

A couple of thoughts, that may or may not be helpful:
1. The ThreadAbortException obviously comes from the Thread.Current.Thread.Abort() call. You must've missed that. ;)
2. Since ThreadAbortExceptions are actually uncatchable (as mentioned above, they are simply reraised by the CLR after your catch{} ends) - I think what's going on is that you're tripping into the else{} block - which means that you've tripped HttpContext.IsInCancellablePeriod to be false. Quickly reflecting on that, though, I don't see any obvious clues *how* you're tripping that - so I'm going to guess that it's simply because you're in a try{} block.
3. According to MS http://support.microsoft.com/kb/312629, and seemingly verified by reflection into HttpResponse.End(), the correct way to do this is to call HttpContext.Current.ApplicationInstance.CompleteRequest() http://msdn.microsoft.com/en-us/library/system.web.httpapplication.completerequest.aspx. This looks to finish out your page, but raise the EndRequest event for any interested modules.
4. I wonder what happens in both your scenario, CompleteRequest(), and a delegate if an Exception occurs?
5. Should this type of logic be moved into an HttpModule? That'd certainly look cleaner to me - though you'd still have to fire off a new thread to avoid blocking, I guess....

alex
May 27, 2008

# re: Ending a Response without Response.End() Exceptions?

I wrote the following code and found the behavior is browser dependent

...
this.Page.Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
this.Page.Response.WriteFile(fullPath);
this.Page.Response.End();
File.Delete(fullPath);


The above works out in Firefox 2 and 3 but not in IE7.

Eber Irigoyen
May 27, 2008

# re: Ending a Response without Response.End() Exceptions?

why not just update the banners on a totally separate request using ajax?

Rick Strahl
May 28, 2008

# re: Ending a Response without Response.End() Exceptions?

Because in essence that's already what's happening. The HTML fed is loading in separate IFrame through script anyway. It's already out of the mainline and can if desired be completely offloaded to a separate banner server.

But that's not really the point. In this simple scenario it really doesn't matter, but it's one of those things that I've run into in a few occasions, and there's no clear way to do the Response.End() code and continue on, apparently short of offloading to a delegate or separate thread.

Alex O
May 28, 2008

# re: Ending a Response without Response.End() Exceptions?

Have you tried turning off chunked encoding?

cscript adsutil.vbs set /W3SVC/AspEnableChunkedEncoding "FALSE"

martin
May 28, 2008

# re: Ending a Response without Response.End() Exceptions?

I concurs with the delegate idea. Terminating a request might break stuff.
If u r trying to avoid extra cycles caused by db why not use msmq? This way u delegate all non asp request code to a different thread.

More work 4 sure, but u could use the same idea for other tasks
enterprise library already comes with plumbing to do this

Rick Strahl
May 29, 2008

# re: Ending a Response without Response.End() Exceptions?

@Alex - no haven't turned of Chunkedencoding because that's a global setting that can be useful for optimization of requests (ASP.NET and static data alike). I also don't like mucking with special settings like this for a generic scenario.R

Douglas
June 13, 2008

# re: Ending a Response without Response.End() Exceptions?

In my application (delivering large files with response.transmitfile) replacing response.close with this: "HttpContext.Current.ApplicationInstance.CompleteRequest()" seems to work perfectly.

Response.TransmitFile(objFileInfo.FullName)
Response.Flush()
' Used to be Response.Close()
HttpContext.Current.ApplicationInstance.CompleteRequest()

Thomas Hansen
June 18, 2008

# re: Ending a Response without Response.End() Exceptions?

You really shouldn't do that Rick - Response.End...
The way to do "custom Ajax" stuff (with e.g. jQuery in client-layer) is to use Response.Filter...
You can see a solution for that in our library (which is Open Source and all code is embedded in download)

.t

anon
November 23, 2008

# re: Ending a Response without Response.End() Exceptions?

The problem with the delegate solution is that exceptions generated in the async code do not go through the "usual" flow. E.g. on our website, uncaught exceptions are handled by a custom error handler page, which displays a generic message to the user and logs all the exception details.
Exceptions thrown in the delegate does not trigger this page, actually, I don't really know where do they go.

Rhomas Rehm
May 06, 2009

# re: Ending a Response without Response.End() Exceptions?

I think the easiest way should be to write a small WebService, which calls manager.BannerHit(banner.BannerId);

Then you can call it async from your .aspx!?

So the aspx renders out the html, starts the webservice and ends it self on a normal way.

Haven't tested, but should work?!

Cybertones
December 03, 2009

# re: Ending a Response without Response.End() Exceptions?

Internet Explorer has a receive buffer and holds off rendering until it gets aminimum number of characters. You can send some initial whitespace to fill that buffer like this:

Response.Write(new string(" ".Chars(0), 255));

After doing that, subsequent calls to flush should get the content to appear in the browser.

Tarik
December 24, 2009

# re: Ending a Response without Response.End() Exceptions?

Hi,

What I've done is working fine for me :


//This is my extension method to make things easier
public static class ExtensionMethods {
      
        public static void WriteJSONObject(this HttpResponse response, object content) {
            response.ContentType = "application/json";
            response.Write(new JavaScriptSerializer().Serialize(content));
            response.End();
 
        }
    }


and here I do my logic :

 try {
            Response.WriteJSONObject(new { Result = "See hello" });
 
            } catch (Exception err) {
                if (err.Message != "Thread was being aborted.")
                    Response.WriteJSONObject(new { Result = err.Message });
                else {
                    Response.End();
                }
                    
            }

Andrew Csontos
March 02, 2011

# re: Ending a Response without Response.End() Exceptions?

Rick,

What solution did you end up using?

I was using Response.Close() for years, and found that it doesn't work any more under IIS 7.5

Thanks

Andrew

Rick Strahl
March 02, 2011

# re: Ending a Response without Response.End() Exceptions?

@Andrew - Response.Close() doesn't do quite the same thing as Response.End(). It simply shuts down the response object and doesn't output any more content, but continues to run the code that follows and may even render the markup template (without actually rendering to Response which is a total waste).

One way to do deal with the thread exceptions is to trap for them specifically in a Application_Error handler and ignore them (or handle them as you see fit).

Andrew Csontos
March 02, 2011

# re: Ending a Response without Response.End() Exceptions?

I have been using Response.Close() in a web page that was used like a web service that returns xml.

If a specific parameter is passed in for "async" mode, then I would Response.Write some XML with a GUID in it, call Response.Close(), and continue processing.

Then when processing was complete, I would stick the result in a cache, using the guid as a key.

The client code can poll the cache to find the result.

However in IIS 7.5, Response.Close won't close the connection. So I was looking into some alternate ideas. Perhaps the delegate idea mentioned above will work.

Sunil
December 17, 2013

# re: Ending a Response without Response.End() Exceptions?

I found the following worked for me when using Response.End.
       private void EndResponse()
        {
            try
            {
                Context.Response.End();
            }
            catch (System.Threading.ThreadAbortException err)
            {
                System.Threading.Thread.ResetAbort();
            }
            catch (Exception err)
            {
            }
        }

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024