但是,在切换到安装了IIS8的Windows 2012服务器后,我们会间歇性地获取截断请求.大多数情况下,这在事件日志中的视图状态异常中显示,但是,对于没有ViewState的表单,我们会得到不完整的帖子(最后几个字段缺失/部分截断).
这成了问题,我们升级到微软支持,经过数周的调试,他们说这是II7及以上的“正确”行为.他们的解释是IIS管道从6改为7.
IIS6及以下将在将整个请求传递给Asp.net之前缓冲整个请求,将忽略截断的请求.
IIS7及更高版本会在发送初始标头后将请求发送到Asp.net,由应用程序来处理截断的请求.
当存在连接问题(用户在传输期间拔出电缆)或者用户在帖子期间按下停止/重新加载页面时,这就成为问题.
在我们的HTTP日志中,我们看到与截断的请求相关的“connection_dropped”消息.
我很难相信这种行为是有意的,但我们已经在几个不同的服务器上进行了测试,并在IIS7及更高版本(Windows 2008,2008 R2和2012)中获得了相同的结果.
我的问题是:
1)这种行为甚至有意义吗?
2)如果这是“正确”的行为,您如何保护您的应用程序免于可能处理不完整的数据?
3)为什么应用程序开发人员有责任检测不完整的请求?假设,为什么app开发人员会处理不完整的请求而不是忽略它?
更新
我写了一个小的asp.net应用程序和网站来演示这个问题.
服务器
Handler.ashx.cs
public class Handler : IHttpHandler { public void ProcessRequest(HttpContext context) { if (context.Request.HttpMethod == "POST") { var lengthString = context.Request.Form["Length"]; var data = context.Request.Form["Data"]; if (lengthString == null) { throw new Exception("Missing field: Length"); } if (data == null) { throw new Exception("Missing field: Data"); } var expectedLength = int.Parse(lengthString); if (data.Length != expectedLength) { throw new Exception(string.Format("Length expected: {0},actual: {1},difference: {2}",expectedLength,data.Length,expectedLength - data.Length)); } } context.Response.ContentType = "text/plain"; context.Response.Write("Hello World,Request.HttpMethod=" + context.Request.HttpMethod); } public bool IsReusable { get { return false; } } }
客户
Program.cs中
static void Main(string[] args) { var uri = new Uri("http://localhost/TestSite/Handler.ashx"); var data = new string('a',1024*1024); // 1mb var payload = Encoding.UTF8.GetBytes(string.Format("Length={0}&Data={1}",data.length,data)); // send request truncated by 256 bytes // my assumption here is that the Handler.ashx should not try and handle such a request Post(uri,payload,256); } private static void Post(Uri uri,byte[] payload,int bytesToTruncate) { var socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp) { // this allows us to disconnect unexpectedly LingerState = new LingerOption(true,0) }; socket.Connect(uri.Host,uri.Port); SendRequest(socket,uri,bytesToTruncate); socket.Close(); } private static void SendRequest(Socket socket,Uri uri,int bytesToTruncate) { var headers = CreateHeaders(uri,payload.Length); SendHeaders(socket,headers); SendBody(socket,Math.Max(payload.Length - bytesToTruncate,0)); } private static string CreateHeaders(Uri uri,int contentLength) { var headers = new StringBuilder(); headers.AppendLine(string.Format("POST {0} HTTP/1.1",uri.PathAndQuery)); headers.AppendLine(string.Format("Host: {0}",uri.Host)); headers.AppendLine("Content-Type: application/x-www-form-urlencoded"); headers.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/99.0"); headers.AppendLine("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); headers.AppendLine("Connection: Close"); headers.AppendLine(string.Format("Content-Length: {0}",contentLength)); return headers.ToString(); } private static void SendHeaders(Socket socket,string headers) { socket.Send(Encoding.ASCII.GetBytes(headers)); socket.Send(Encoding.ASCII.GetBytes("\n")); } private static void SendBody(Socket socket,int numBytesToSend) { socket.Send(payload,numBytesToSend,SocketFlags.None); }
解决方法
2)功能测试.大量的功能测试.创建测试工具并进行应用程序可以进行的所有调用,以确保其正常工作.这不是100%的解决方案,但实际上并非如此.有许多计算机科学论文解释了为什么不可能测试你的应用程序运行based on the Halting Problem的每一种可能情况.
3)因为你写了代码.您不应该有不完整的请求,因为请求可能是一个重要的数据,您需要发回一个错误,说明处理请求时出现问题,否则发布方只是看到请求已经神秘地消失了.