I’ve upgraded and now my application doesn’t work anymore


Scenario:

A quite common scenario when working in the support industry is a call along theese lines:

“My application worked just fine, but now that I’ve upgraded to IE7, IIS6, Vista, etc. it doesn’t work any more. This has got to be a bug! This new version of the software obviously isn’t any good, so when are you going to fix it?”


Is it a bug?

Well, possibly. But most likely the bug doesn’t lie within IE, IIS or the operating system. Instead you should look at your code to make sure you did everything following the recommended guidelines. Chances are that you didn’t do things the right way originally, and for some reasons the previous version of the software was more forgiving.

Example

A little while ago I had the following scenario on my hands:

A customer had just upgraded their webservers from Windows 2000 to Windows 2003. After the upgrade certain requests just “vanished” into thin air. The response never reached the clients. We managed to track down the problem to the following lines of code:

this.Page.Response.ClearContent();
this.Page.Response.Write(TextToWrite);
this.Page.Response.Flush();
this.Page.Response.Close();

Okay, so you probably see what is strange here. Why are they calling Response.Flush() and Response.Close()?

If we remove theese two lines and replace them with Response.End() then everything works fine:

this.Page.Response.ClearContent();
this.Page.Response.Write(TextToWrite);
this.Page.Response.End();

Okay, so this is the proper way to do it. Response.End() will call actually call Response.Flush() and then gracefully end execution of the page, while Response.Close() will simply “cut the cord”. But how come it worked in IIS5 and not IIS6? Does this mean that IIS6 is a bad product? – Not at all!

One of the things that changed with IIS6 is that it now processes responses asynchronously. This means that in IIS5 all execution will be paused until the page has been sent, while in IIS6 the response will be put in a send-buffer, allowing IIS to immediately continue execution. This is one of the reasons why IIS6 is both faster and more secure than IIS5. The thread executing the page does not have to take the connection speed of the client into consideration. It can execute the page and move on to the next. In IIS5 all execution on the thread would be stopped until the client had downloaded every last bit. This made the server more vulnerable to Denial of Service (DOS) -attacks, and something as trivial as a bunch of clients with poor modem connections could impair the performance of the server.

In brief, here’s what happened with the old code:

IIS5:

  • Response.Flush sends data to client
  • Thread waits until data has been sent
  • Response.Close closes client connection

IIS6:

  • Response.Flush puts the data in a send buffer and immediately moves to the next line of code
  • Response.Close closes the client connection before the data has been sent

Here’s what happens with the new code:

IIS5:

  • Response.End is called
    • The data is sent to the client
    • IIS gracefully ends all further execution of the page

IIS6:

  • Response.End is called
    • The data is transferred to the send buffer
    • IIS gracefully ends all further execution of the page

Summary:

The old code was incorrect, but worked anyway due to the synchronous design of IIS5. As IIS6 switched to an asynchronous response model this stopped working. I can sympathize with anyone that feels that this is a bug/mistake, but in reality it isn’t. In fact it is a very concious choice made to further improve performance and reliability.

/ Johan

Comments (11)

  1. Adam says:

    Hmmm….seems like strange semantics for Flush() to me. I’d have thought, based on most other stream/network/disk APIs in existence that for a buffered connection Write() buffers and queues data, while Flush() blocks until it’s been sent/written to the network/disk. Surely that’s the point of Flush(), to block, isn’t it?

    Hang on – what does even Flush() do in this scenario? What is Write() doing if not putting data onto a send-buffer?

    By making flush non-blocking, haven’t you just turned it into a no-op?

  2. JohanS says:

    Hi Adam,

    I’ll begin by clearing up what Response.Flush() does:

    Response.Flush() sends all buffered output to the client.

    The response buffer is the classic IIS buffer that’s been there since… – Oh, I don’t know when.

    It’s useful because you can write code like this:

    Response.Buffer = true

    Response.Write("something")

    Response.Clear() ‘ Changed my mind

    Response.Write("something else")

    Response.Flush ‘ Send what’s been buffered to the client

    Response.Write("some more")

    Response.End() ‘ Send the remaining stuff in the buffer and end

    IIS6 now also has a send buffer which means that after you call Response.Flush() it will no longer pause all execution until every last bit of data has reached the client. Instead it will just dump the data in the "outbox" and happily continue execution.

    So: The response buffer what you as a developer can control by calling Response.Buffer = true, Response.Clear(), etc. When it comes to actually sending the data IIS6 also uses a send buffer which is why the code sample in the post worked in IIS5, but not in IIS6.

    I hope that clears things out. If not, then fire away! :)

  3. Adam says:

    Response.Clear()?!? Are you guys going to add funputc() to your C libraries anytime soon for symmetry with this? Or is having API consistency something you’re actively avoiding?

    *boggles*

  4. Raj says:

    Re the IIS6’s asynchronous response mode, I am wondering how this works when Response.write encounters embedded COM or database calls that pulls in data which may take some time (depending on how complex the call is) to complete. It can’t be moving on to the next line until these calls are completed.

    But I am seeing something to the contrary. I am troubleshooting an issue where ASP pages respond slowly on one particular IIS6 server that has COM calls embedded. I have sprinkled Response.write Now all over the page to display the date/time stamp. The timestamp output is same despite the fact that the page takes more than half a minute to complete.

  5. Thank you this helped me!

  6. Arlene says:

    I removed response.flush and response.close, and replaced them with response.end.  It causes an exception:

    A first chance exception of type  ‘System.Threading.ThreadAbortException’ occurred in mscorlib.dll

  7. JohanS says:

    Hi Arlene,

    Don’t worry. That’s expected.

    All calls to Response.End will cause a first chance ThreadAbortException.

    This includes calls to Response.Redirect, which also calls Response.End.

    / Johan

  8. Problem: When using Visual Studio 2005 to debug a web application under IIS7 you will find that after

  9. Oscar says:

    This Seems to work Perfect for Your Scenario, but what if we do not want to call

    this.Page.Response.End()

    because there is more data that needs to be displayed but it will take a while to load.

    how can we send info to the client and then continue loading the rest of the page on IIS6?

  10. JohanS says:

    Hi Oscar,

    Response.Flush will send what’s currently in the buffer and then continue executing as normal.

    / Johan

  11. Sidnei says:

    Hi, Johan!

    I’ve recently upgraded from IIS 5 / W2000 to IIS 6 / W2003, and a little problem has detected: in many pages (I didn’t test all of them, but the same occurs in ALL I’ve tested), the command line Response.Flush() is ignored by the compiler…

    Let me explain this: the page in question had a long-proccess query in the database, but is quite separated into many "SELECT" clauses. Each SELECT instruction is followed by a sequence of Response.Write() method calls, building a HTML table element, and then fired to client by the Response.Flush() method. Other SELECT statement is executed, a new table element created, send to client, and so on, until the end of page was reached.

    In the old server (W2000 – IIS 5), the result is that the page is being displayed after each Response.Flush() method, so the user is able to view partial content even if the page is not totally processed.

    But in the new server (W2003  – II6), nothing is displayed until the page reach the end of the proccess – in others words, nothing appears until the last SELECT statement is executed, and NOTHING are changed in the code to cause this behaviour – the same page works fine in IIS 5, but didn’t display any information (until it ends execution) in IIS 6.

    How did I fix it?

    What I doing wrong to get this behaviour?

    I would appreciate any suggestions to fix this, and early thanks all help I could get.

    Regards,

    Sidnei