2010-07-26 3 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.0 не будет работать, так как вы являетесь сервером, и это клиент, который будет указывать HTTP/1.1 в запросе. Конечно, вы можете отправить сообщение об ошибке с версией HTTP/1.0 в качестве версии и надеяться, что клиент изменится на 1.0, но кажется неэффективным.

сообщения HTTP выглядит следующим образом:

REQUEST LINE 
HEADERS 
(empty line) 
BODY 

Единственный способ узнать, когда ответ сделано, то для поиска заголовка Content-Length. Просто найдите «Content-Length:» в буфере запросов и извлеките все для перевода строки. (Но обрезать найденное значение перед преобразованием в int).

Другой альтернативой является использование анализатора в моем webserver, чтобы получить все заголовки. Должно быть довольно просто использовать только парсер и больше ничего из библиотеки.

Обновление: Существует лучший парсер здесь: HttpParser.cs

+0

Я знаю формат HTTP-сообщения, я пытался не искать его вообще, так как в RFC для HTTP Content-Length нужно указывать только, если его * известно * раньше времени, а если нет заголовок «Длина передачи» (я считаю, что я правильно понял), и в любом случае они указывают длину тела * перед кодировкой *. Я посмотрю ваш код в любом случае. Спасибо за ссылку. – GrayWizardx

+0

В HTTP/1.0 уверенная длина содержимого не требуется указывать (соединение закрывается, когда тело передается). Но в HTTP/1.1 это необходимо, так как соединение может оставаться открытым (для других запросов). Есть одно исключение, и именно тогда происходит перекодирование передачи. Но тогда каждая часть тела имеет свою собственную длину, которую нужно проанализировать. – jgauffin

-1

Использование блокировки IO и нескольких потоков может быть вашим ответом. В частности,

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

Это текстовые данные, однако обработка двоичных файлов аналогична.

+0

Я не знаю, размер входных данных, и у меня будет много клиентов (вверх тысяч за один раз), так что я не хотите полностью заблокировать ответ, или сохранить весь ответ в памяти до тех пор, пока он не завершится. – GrayWizardx

+0

Бывают ситуации, когда вы, возможно, никогда не знаете фактический размер данных. Более того, даже сервер может не иметь информации (например: он передается из сценария CGI). Таким образом, у вас нет решения «одного размера подходит всем». Вы либо должны реализовать какой-то механизм тайм-аута/ограничения, либо вам придется дождаться завершения каждого запроса (или тайм-аута системой). – sukru

3

Если вы сделаете запрос HTTP/1.0 вместо 1.1, сервер должен закрыть соединение, как только оно закончится, поскольку ему не нужно поддерживать соединение открытым для другого запроса.

Кроме этого, вам действительно нужно проанализировать заголовок длины контента в ответе, чтобы получить наилучшее значение.

+0

Я передаю запрос напрямую, поэтому я не могу контролировать тип запрашиваемого запроса. Я просто молча копирую данные в автономную очередь для анализа позже. Было бы неплохо, если бы у меня был такой контроль. – GrayWizardx

+0

У вас есть данные для изменения запроса на использование HTTP/1.0, вам просто нужно иметь возможность динамически изменять запрос пользователя. Вероятно, было бы проще просто найти длину содержимого запроса. – David

Смежные вопросы