IIS 7.5中Chunked编码缺失结束标记问题修复咨询
我基于.NET Framework 4.8创建了一个简单ASP.NET应用,编写了通用Web处理器HelloFilterWithClose.ashx。该处理器在ProcessRequest方法中设置HttpResponse.Filter为自定义的MyFilter,向HTTP客户端响应PDF文件;写入PDF文件到HTTP主体后,执行Flush并关闭HttpResponse。
在Windows Server 2008 R2的IIS 7.5上运行时,响应的Transfer-Encoding被设置为chunked,Wireshark显示仅存在一个数据块,但缺失chunked编码结束标记0\r\n及尾部\r\n;而在IIS 10上运行则无此问题。
MyFilter 代码
public class MyFilter : Stream { private Stream m_stream; public MyFilter(Stream stream) { m_stream = stream; } public override bool CanRead { get { return m_stream.CanRead; } } public override bool CanSeek { get { return m_stream.CanSeek; } } public override bool CanWrite { get { return m_stream.CanWrite; } } public override long Length { get { return m_stream.Length; } } public override long Position { get { return m_stream.Position; } set { m_stream.Position = value; } } public override void Close() { m_stream.Close(); } public override void Flush() { m_stream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return m_stream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return m_stream.Seek(offset, origin); } public override void SetLength(long value) { m_stream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { m_stream.Write(buffer, offset, count); } }
HelloFilterWithClose.ashx 代码
public class HelloFilterWithClose : IHttpHandler { public void SimpleRequest(HttpContext context) { context.Response.BufferOutput = false; context.Response.ContentType = "application/octet-stream"; context.Response.AddHeader("Content-Disposition", "attachment;FileName=filterWithClose.pdf"); context.Response.AddHeader("Content-Length", "32038"); context.Response.WriteFile("hello.pdf"); context.Response.Flush(); context.Response.Close(); } private void SetFilter(HttpContext context) { HttpResponse response = context.Response; Stream filterStream = response?.Filter; if (null == filterStream) { return; } response.Filter = new MyFilter(filterStream); } public void ProcessRequest(HttpContext context) { SetFilter(context); SimpleRequest(context); } public bool IsReusable { get { return false; } } }
1. 替换Response.Close()为Response.End()或移除强制关闭逻辑
直接调用Response.Close()会强制切断连接,跳过IIS 7.5中chunked编码的收尾流程。改用Response.End(),它会先完成响应的完整写入(包括chunked结束标记)再终止请求:
// 替换原代码中的 context.Response.Close(); context.Response.End();
如果不需要立即终止请求,也可以直接移除Flush()和Close(),让ASP.NET框架自然完成响应流程,自动处理编码收尾。
2. 给自定义Filter补充正确的Dispose实现
MyFilter继承自Stream但未实现标准的Dispose逻辑,IIS 7.5可能依赖该方法完成后续编码处理。修改MyFilter添加以下代码:
protected override void Dispose(bool disposing) { if (disposing) { m_stream.Dispose(); } base.Dispose(disposing); }
Close()方法默认会调用Dispose(true),保留原Close()实现即可,确保底层流能被正确释放。
3. 动态设置正确的Content-Length
手动硬编码的Content-Length如果和实际文件大小不匹配,会触发IIS 7.5自动切换到chunked编码,后续强制关闭会导致收尾标记缺失。改为动态获取文件大小:
string filePath = context.Server.MapPath("hello.pdf"); long fileSize = new FileInfo(filePath).Length; context.Response.AddHeader("Content-Length", fileSize.ToString());
当Content-Length正确且BufferOutput=false时,IIS会直接按指定长度发送响应,从根源避免chunked编码的收尾问题。
4. 调整IIS 7.5应用池高级设置(可选)
进入IIS管理器,找到对应应用程序池的“高级设置”,适当调整“响应缓冲限制”或启用“内核模式缓存”,确保响应能被IIS正确处理。此方法优先级低于代码层面的修改。
内容的提问来源于stack exchange,提问作者woniu17

