I have a custom HttpHandler in which I manually enable output compression, like so:
context.Response.AppendHeader("Content-encoding", "gzip");
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
This works nicely for most requests, but when an exception is encountered the "Content-encoding" header disappears from the response, while the compression filter remains in place. The result is that the error page is gzip compressed, but the browser receives no header indicating that fact. The browser then tries to display the still-compressed data as text, which is gobbledygook.
Full test case code is shown below. Try alternately disabling the compression or not throwing the exception.
Can anyone shed some light on why the "Content-encoding" header disappears?
I suppose I could simply enable compression as the last thing the handler does, so that if an exception is encountered it never reaches the point where the compression filter is added; but the behavior I'm seeing strikes me as a bug. Can anyone confirm?
public class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
CompressResponse(context);
context.Response.Write("Hello world");
// Throw an exception for testing purposes
throw new Exception("Just testing...");
}
private void CompressResponse(HttpContext context)
{
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
if (String.IsNullOrEmpty(acceptEncoding))
{
return;
}
// gzip or wildcard
if (acceptEncoding.ToLower().Contains("gzip") || acceptEncoding.Contains("*"))
{
context.Response.AppendHeader("Content-encoding", "gzip");
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
return;
}
// Also handles deflate (not shown here)
// <snip>
}
public bool IsReusable
{
get { return true; }
}
}
EDIT: Screenshot of the still-encoded response I'm seeing with my test case: https://i.stack.imgur.com/R3Wmq.png
If you have an exception, then the server will flush the currently set headers and content, because they're wrong, as you did after all have an exeption.
At the very least, it's clear that the 200 status you were going to send (because all successful responses that don't change the status send a 200, and upon an unhandled exception it was no longer succesful) is wrong, but everything else related to something you were going to do but failed to achieve, so it's all wrong and it all goes.
Filters aren't reset though.
Either reset the headers in the error page if appropriate, or don't set the filter unless you can be sure that everything is ready to flush. I'd go for the former, no reason why error pages can't be compressed too.
You can't send a header if you've called
Flush()
, because well, because you've flushed. Where's the header going to go?