切换到IIS8后连接断开时,ASP.NET处理请求

我有一个使用WebForms的ASP.NET 3.5应用程序,目前它被托管在IIS6上。 一切都很好。

但是,切换到安装了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)为什么应用程序开发人员有责任检测不完整的请求? 假设,为什么应用程序开发人员会处理不完整的请求,而忽略它?

更新

我写了一个小型的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, payload, bytesToTruncate);

    socket.Close();
}

private static void SendRequest(Socket socket, Uri uri, byte[] payload, int bytesToTruncate)
{
    var headers = CreateHeaders(uri, payload.Length);

    SendHeaders(socket, headers);

    SendBody(socket, payload, 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, byte[] payload, int numBytesToSend)
{
    socket.Send(payload, 0, numBytesToSend, SocketFlags.None);
}

1)如果您正在集成模式下为您的3.5应用程序分配的应用程序池运行管道,那么由于ISAPI行为,您的请求处理方式可能会有问题。 您可能会生成不正确理解的请求,然后将它们截断为默认值。 您是否尝试过在经典模式下运行应用程序池?

2)功能测试。 许多功能测试。 创建一个测试工具并进行应用程序可以进行的所有调用,以确保其正常工作。 这不是一个100%的解决方案,但没有什么是真的。 有很多计算机科学论文解释了为什么不可能测试基于停机问题的应用程序可能运行的每种可能情况。

3)因为你写了代码。 你不应该有不完整的请求,因为请求可能是一个重要的数据部分,你需要发回一个错误,说明处理请求时出现问题,否则发布方只会将请求视为神秘消失。


IIS改变其行为的原因是因为我们(开发人员)需要更多的控制请求处理。 如果请求中断,我们在调查无形请求的原因方面存在问题。 我们需要在应用程序级别记录请求以进行调查和记录保存。 例如,如果请求涉及信用卡交易等金融交易,我们需要更多的控制权,并且我们需要记录每个步骤的合规性。

IIS是一个Web服务器框架,应用程序级数据验证不是他们的责任。 如果请求被破坏,那意味着输入不完整,应用程序级逻辑将决定要做什么。 应用程序必须响应正确的错误代码和失败。 这是ASP.NET mvc具有模型验证的原因,它允许您验证应用程序级别的完整输入。

您可以使用IsClientConnected检查底层套接字是否仍处于连接状态。

随着网络AJAX越来越流动,并且我们有时使用ping来检查远程服务的健康状况,我们不一定会断定断开的请求是错误,必须放弃。 我们可能仍然希望忍受破碎的请求。 这是应用程序级开发人员可以做出的选择,而不是IIS。

链接地址: http://www.djcxy.com/p/77873.html

上一篇: ASP.NET handling requests when connection is dropped after switching to IIS8

下一篇: Not sending headers in IE9