Как определить, завершен ли ответ HTTP - PullRequest
4 голосов
/ 26 июля 2010

Я работаю над созданием простого прокси, который будет регистрировать определенные запросы, которые передаются через него.Прокси не должен мешать трафику, проходящему через него (на данном этапе в проекте), и поэтому я пытаюсь сделать как можно меньше анализа необработанного запроса / ответа во время процесса (запрос и ответ выдвигаютсяв очередь для входа за пределы прокси).

Мой пример работает нормально, за исключением того, что я не могу достоверно сказать, когда «ответ» завершен, поэтому у меня остаются открытые соединения дольше, чем необходимо.Ниже приведен соответствующий код:

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'ом и т. Д.), Я не хочу декодировать фактический ответ, чтобы получить длину и определить, могу ли я отключить свой сокет.

Ответы [ 3 ]

3 голосов
/ 26 июля 2010

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

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

2 голосов
/ 26 июля 2010

Как указывал Дэвид, связи должны оставаться открытыми в течение определенного периода времени.Не следует закрывать соединения, если это не выполняется на стороне клиента (или если не истек интервал поддержания активности).

Переключение на HTTP / 1.0 не будет работать, поскольку вы являетесь сервером, и именно клиент будет указывать HTTP / 1.1в запросе.Конечно, вы можете отправить сообщение об ошибке с HTTP / 1.0 в качестве версии и надеяться, что клиент изменится на 1.0, но это кажется неэффективным.

HTTP-сообщения выглядят так:

REQUEST LINE
HEADERS
(empty line)
BODY

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

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

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

0 голосов
/ 26 июля 2010

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

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

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

...