簡體   English   中英

在切換到IIS8后刪除連接時ASP.NET處理請求

[英]ASP.NET handling requests when connection is dropped after switching to IIS8

我有一個使用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)為什么應用程序開發人員有責任檢測不完整的請求? 假設,為什么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, 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的選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM