“内容编码”如果发生异常,标头将从 HttpHandler 响应中消失
我有一个自定义的 HttpHandler,在其中手动启用输出压缩,如下所示:
context.Response.AppendHeader("Content-encoding", "gzip");
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
这对于大多数请求来说效果很好,但是当遇到异常时,“Content-encoding”标头会从响应中消失,而压缩过滤器仍保留在原位。结果是错误页面是 gzip 压缩的,但浏览器没有收到表明这一事实的标头。然后,浏览器尝试将仍然压缩的数据显示为文本,即 gobbledygook。
完整的测试用例代码如下所示。尝试交替禁用压缩或不抛出异常。
任何人都可以解释为什么“内容编码”标头消失吗?
我想我可以简单地启用压缩作为处理程序执行的最后一件事,这样,如果遇到异常,它永远不会到达添加压缩过滤器的点;但我所看到的行为让我觉得这是一个错误。有人能证实吗?
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; }
}
}
编辑:我在测试用例中看到的仍然编码的响应的屏幕截图: https://i.sstatic .net/R3Wmq.png
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.sstatic.net/R3Wmq.png
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我也遇到了这个问题。追踪起来很复杂。我不确定整个情况的具体细节,但我认为发生的是内存泄漏。
当您第一次执行此操作时:
您正在将非托管资源分配给
过滤器
。通常,这将被包装在using
语句中,以便在出现任何问题时能够正确处理它。因此,当出现问题时,就会出现问题。
Filter
包含一个流,即使响应被写入时出现黄屏死机,该流仍处于打开状态。随之而来的是疯狂(如屏幕截图所示)。不要害怕!实际上有一个简单的方法可以解决这个问题。丢弃过滤器。幸运的是,已经有一个地方可以应用这种过滤器处置的全局检查。
global.asax.cs
错误处理程序命名空间
I encountered this problem as well. It was complicated to track down. I am unsure of the exact specifics of this whole situation, but what I think happens is that there is a memory leak.
When you first do this:
you are assigning an unmanaged resource to
Filter
. Normally this would be wrapped in ausing
statement so that it would be properly disposed in case anything went wrong.So, when something does go wrong, there is a problem. The
Filter
contains a stream which is still open even though the response is being written to with the yellow screen of death. What ensues is madness (as shown in your screen shot).Fear not! There is actually an easy way to overcome this issue. Dispose of the filter. Luckily, there is already a place to apply this global check for filter disposal.
global.asax.cs
error handler namespace
如果出现异常,那么服务器将刷新当前设置的标头和内容,因为它们是错误的,就像您在出现异常之后所做的那样。
至少,很明显,您要发送的 200 状态(因为所有不更改状态的成功响应都会发送 200,并且在出现未处理的异常时,它不再成功)是错误的,但其他所有相关内容你想做但未能实现的事情,所以一切都是错误的,一切都会过去。
但过滤器不会重置。
如果合适,请重置错误页面中的标题,或者不要设置过滤器,除非您可以确定一切都已准备好刷新。我会选择前者,没有理由不能压缩错误页面。
如果您调用了
Flush()
,则无法发送标头,因为好吧,因为您已经刷新了。标题要去哪里?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?当我在 WebForms 应用程序上强制使用 gzip 时,也发生了同样的事情。为了修复它,我必须清除 Global.asax.cs 中 Application_Error 方法中的过滤器。
发生这种情况的原因是 b/c 在应用程序出现错误之前设置了过滤器。由于某种原因,黄屏错误消息会清除内容编码标头,但不会对响应过滤器执行任何操作。
I had the same thing happen when forcing gzip on a WebForms application. In order to fix it I had to clear the filter in the Application_Error method in Global.asax.cs
The reason this is happening is b/c the filter is being set before the app has an error. And for some reason the yellow screen error message clears the Content-encoding header but doesn't do anything to the response filter.
我测试了你的代码,没有发现任何问题。是的,gzip 没有设置,但是过滤器也没有设置,asp 获得了控制权并发送错误。
强制标头刷新会产生真正的问题
如果我强制 gzip 标头,则页面无法正确呈现。
两个人认为这可能是你的问题。您没有设置页面编码
,也没有设置 ContentType
也许这就是您无法正确渲染页面的原因。然而,在我的测试中,即使如此,您描述的问题也不会出现。
I test your code and I can not find any issue. Yes the gzip is not set, but the filter also not have been set and asp gets the control and send an error.
Forcing the header to flush make a real problem
If I force the gzip header then the page is not render correctly.
Two thinks here maybe is your issue. You do not have set the page encoding
and you do not have set ContentType
Maybe some of this is the reason that you get not corrected page render. How ever in my tests even with that the issue you describe not appears.