2010-07-26 81 views
4

我正在建立一個簡單的代理,它將記錄通過它傳遞的某些請求。代理不需要干擾通過它的流量(此時在項目中),所以我試圖儘可能少地解析原始請求/響應(在請求和響應被推送關閉隊列以在代理之外記錄)。如何確定HTTP響應是否完成

我的示例工作正常,除了無法可靠地告訴何時「響應」已完成,因此我的連接保持打開時間超過所需時間。相關代碼如下:

var request = getRequest(url); 
byte[] buffer; 
int bytesRead = 1; 
var dataSent = false; 
var timeoutTicks = DateTime.Now.AddMinutes(1).Ticks; 

Console.WriteLine(" Sending data to address: {0}", url); 
Console.WriteLine(" Waiting for response from host..."); 
using (var outboundStream = request.GetStream()) { 
    while (request.Connected && (DateTime.Now.Ticks < timeoutTicks)) { 
     while (outboundStream.DataAvailable) { 
     dataSent = true; 
     buffer = new byte[OUTPUT_BUFFER_SIZE]; 
     bytesRead = outboundStream.Read(buffer, 0, OUTPUT_BUFFER_SIZE); 

     if (bytesRead > 0) { _clientSocket.Send(buffer, bytesRead, SocketFlags.None); } 

     Console.WriteLine(" pushed {0} bytes to requesting host...", _backBuffer.Length); 
     } 

     if (request.Connected) { Thread.Sleep(0); } 
    } 
} 

Console.WriteLine(" Finished with response from host..."); 
Console.WriteLine(" Disconnecting socket"); 
_clientSocket.Shutdown(SocketShutdown.Both); 

我的問題是,是否有一種簡單的方法來告訴響應是不完整的解析頭。鑑於此響應可能是任何東西(編碼,加密,gzip'ed等),我不想要解碼實際的響應來獲得長度,並確定我是否可以斷開我的套接字。

回答

2

大衛指出,連接應該保持一段時間。除非客戶端執行此操作(或者保持活動時間間隔到期),否則不應關閉連接。

因爲您是服務器,並且它將在請求中指定HTTP/1.1,所以更改爲HTTP/1.0將不起作用。當然,你可以用HTTP/1.0作爲版本發送錯誤消息,並希望客戶端更改爲1.0,但效率不高。

HTTP消息是這樣的:

REQUEST LINE 
HEADERS 
(empty line) 
BODY 

只有這樣,才能知道什麼時候完成的響應是搜索的Content-Length頭。只需在請求緩衝區中搜索「Content-Length:」並將所有內容提取到換行。 (但在轉換爲int之前修整找到的值)。

另一種方法是使用我的webserver中的解析器獲取所有標題。應該很容易使用解析器,而不需要使用庫。

更新:有一個更好的解析器在這裏:HttpParser.cs

+0

我知道HTTP消息的格式,我試圖避免必須根據HTTP Content-Length的RFC來搜索它,只需要在* *提前知道它*的情況下指定,並且如果沒有一個Transfer-length頭部(我相信我讀的是正確的),並且無論哪種方式,他們都指定* *之前的身體長度。我會以任何方式看看你的代碼。感謝您的參考。 – GrayWizardx 2010-07-26 18:29:06

+0

在HTTP/1.0中,不需要指定內容長度(傳輸正文時連接關閉)。但在HTTP/1.1中是必需的,因爲連接可以保持打開狀態(用於其他請求)。有一個例外,它是傳輸編碼被分塊的時候。但是,每個身體部位都有它自己的長度,你需要解析。 – jgauffin 2010-07-26 20:09:46

-1

使用阻塞IO和多線程可能是您的答案。特別是

using(var response = request.GetResponse()) 
using(var stream = response.GetResponseStream()) 
using(var reader = new StreamReader(stream) 
    data = reader.ReadToEnd() 

這是用於文本數據,但二進制處理是類似的。

+0

我不知道輸入數據的大小,我會(在同一時間十萬以上)很多客戶,所以我不想要完全阻止響應,或者將整個響應保存在內存中直到完成。 – GrayWizardx 2010-07-26 18:03:37

+0

有些情況下,您可能永遠不會知道實際的數據大小。此外,即使服務器可能沒有這些信息(例如,它正在從CGI腳本中進行流式傳輸)。 因此,您的問題沒有「一刀切」的解決方案。您必須實施某種超時/限制機制,否則您必須等待每個請求完成(或由系統超時)。 – sukru 2010-07-26 18:22:40

3

如果您發出HTTP/1.0請求而不是1.1,則服務器應該儘快關閉連接,因爲它不需要保持連接對另一個請求打開。

除此之外,你真的需要解析響應中的內容長度頭以獲得最佳值。

+0

我通過直接傳遞請求,所以我無法控制請求的類型。我只是靜靜地將數據複製到脫機隊列中以供日後分析。如果我有這樣的控制水平,這將是一個很好的選擇。 – GrayWizardx 2010-07-26 18:02:40

+0

你有可用的數據來改變請求使用HTTP/1.0,你只需要能夠動態修改用戶的請求。查找請求內容長度可能會更容易。 – David 2010-07-26 18:28:23