2014-03-04 37 views
7

我有一個使用WebForms的ASP.NET 3.5應用程序,目前它正在IIS6上承載。一切都很好。ASP.NET在切換到IIS8後連接斷開時處理請求

但是,在切換到安裝了IIS8的Windows 2012服務器後,我們會間歇性地收到截斷的請求。大多數情況下,這會在我們的事件日誌中顯示一個視圖狀態異常,但是,在沒有ViewState的表單上,我們會收到不完整的帖子(最後幾個字段丟失/部分截斷)。

這成爲那麼多問題,我們升級到微軟的支持,和調試星期後,他們說這是II7和上面的「正確」的行爲。他們的解釋是,從6 IIS管道7

IIS6的變化,下面將其傳遞到Asp.net之前將緩衝整個請求,截斷請求將被忽略。
IIS7及以上版本會在發送初始頭文件後將請求發送到Asp.net,這將由應用程序處理截斷的請求。

時要麼有連接問題(用戶tranmission期間拔掉他們的有線電視)或一個職位期間,當用戶按下停止/重新加載頁面後,此成爲問題。

在我們的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); 
} 

回答

2

1)如果你正在運行的管道的應用程序池到您的3.5應用程序以集成模式分配,您可能在處理請求時遇到問題編號due to ISAPI behavior。您可能會生成不正確理解的請求,然後將它們截斷爲默認值。您是否嘗試過在經典模式下運行應用程序池?

2)功能測試。許多功能測試。創建一個測試工具並進行應用程序可以進行的所有調用,以確保其正常工作。這不是一個100%的解決方案,但沒有什麼是真的。有很多計算機科學論文解釋了爲什麼不可能測試您的應用可能運行based on the Halting Problem的每種可能情況。

3)因爲您編寫了代碼。你不應該有不完整的請求,因爲請求可能是一個重要的數據部分,你需要發回一個錯誤,說明處理請求時出現問題,否則發佈方只會將請求視爲神祕消失。

+0

當用戶在瀏覽器上按下停止按鈕時,可能會出現此問題。無論我處於集成還是經典模式都無關緊要。至於你的第二點,這看起來像是框架應該保護你的東西,asp.net應該是HTTP之上的抽象。雖然這絕對是一個抽象的漏洞,但我覺得這是我不應該寫的一個問題。寫這個問題非常困難。我將用測試程序更新原始問題以證明問題。 – Matthew

+0

測試程序可能是最好的。與此同時,是的,我可以理解你對預測這種低槓桿錯誤感到沮喪,但正如你所說,這是一個漏洞抽象和漏洞抽象只能保護你不受這麼多。 FWIW,我從來沒有見過這種情況發生在4.0或4.5。 – gfish3000

+0

我更新了原始問題的複製 – Matthew

2

IIS改變它的行爲的原因,因爲我們(開發人員)需要更多的控制請求處理。如果請求中斷,我們在調查無形請求的原因方面存在問題。我們需要在應用程序級別上記錄請求&記錄保存。例如,如果請求涉及信用卡交易等金融交易,我們需要更多的控制權,並且我們需要記錄每個步驟的合規性。

IIS是一個Web服務器框架,應用程序級數據驗證不是他們的責任。如果請求被破壞,那意味着輸入不完整&應用程序級邏輯將決定要做什麼。應用程序必須響應正確的錯誤代碼和失敗。這是ASP.NET mvc具有模型驗證的原因,它允許您驗證應用程序級別的完整輸入。

您可以使用IsClientConnected檢查底層套接字是否仍連接。

由於網絡AJAX更多,移動性更強,而且我們有時使用ping來檢查遠程服務的運行狀況,所以我們不一定認爲斷開的請求是錯誤,必須放棄。我們可能仍然希望忍受破碎的請求。這是應用程序級開發人員可以做出的選擇,而不是IIS。