Как правильно анализировать входящие HTTP-запросы - PullRequest
5 голосов
/ 13 сентября 2010

Я создал приложение C ++ с использованием WinSck, в котором реализован небольшой (обрабатывает лишь несколько необходимых мне функций) http-сервер. Это используется для связи с внешним миром с помощью http-запросов. Это работает, но иногда запросы не обрабатываются правильно, потому что синтаксический анализ не выполняется. Теперь я совершенно уверен, что запросы правильно сформированы, поскольку они отправляются основными веб-браузерами, такими как firefox / chrome или perl / C # (которые имеют http-модули / dll).

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

Что я делаю сейчас, так это то, что я читаю данные, пока не найду последовательность "\r\n\r\n", которая указывает конец заголовка. Если WSAGetLastError() сообщает что-то еще, кроме 10035 (соединение закрыто / не удалось), прежде чем такая последовательность будет найдена, я отбрасываю сообщение. Когда я знаю, что у меня есть весь заголовок, я анализирую его и ищу информацию о длине тела. Однако я не уверен, является ли эта информация обязательной (я думаю, что нет), и что мне делать, если такой информации нет - значит ли это, что тела не будет? Другая проблема заключается в том, что я не знаю, должен ли я искать "\r\n\r\n" после тела (если его длина больше нуля).

Кто-нибудь знает, как надежно разобрать http-сообщение?

Примечание: я знаю, что существуют реализации http-серверов. Я хочу свой по разным причинам. И да, изобретать велосипед плохо, я тоже это знаю.

Ответы [ 4 ]

8 голосов
/ 13 сентября 2010

Если вы настроены на написание собственного парсера, я бы выбрал подход Zed Shaw : используйте компилятор конечного автомата Ragel и постройте свой парсер на его основе.Ragel может обрабатывать ввод, поступающий кусками, если вы осторожны.

Честно говоря, я бы просто использовал что-то вроде этого .

Ваш ресурс переходадолжно быть RFC 2616 , которое описывает HTTP 1.1, который вы можете использовать для создания синтаксического анализатора.Удачи!

3 голосов
/ 13 сентября 2010

Вы можете попробовать посмотреть на их код, чтобы увидеть, как они обрабатывают HTTP-сообщение.

Или вы можете посмотреть спецификацию , есть длина сообщения полей, которые вы должны использовать. Видимо, только глючные браузеры отправляют дополнительные CRLF в конце.

0 голосов
/ 13 сентября 2010

В любом случае HTTP-запрос имеет «\ r \ n \ r \ n» в конце заголовка запроса и перед данными запроса, если таковые имеются, даже если запрос «GET / HTTP / 1.0 \ r \ n \ r \ n» .

Если метод "POST", вы должны прочитать столько байтов после "\ r \ n \ r \ n", сколько указано в поле Content-Length.

Итак, псевдокод:

read_until(buf, "\r\n\r\n");
if(buf.starts_with("POST")
{
   contentLength = regex("^Content-Length: (\d+)$").find(buf)[1];
   read_all(buf, contentLength);
}

После контента будет "\ r \ n \ r \ n", только если контент содержит его. Контент может быть двоичными данными, у него нет завершающих последовательностей, и единственный способ получить его размер - использовать поле Content-Length.

0 голосов
/ 13 сентября 2010

HTTP GET / HEAD запросы не имеют тела, а POST запрос также не может иметь тела. Вы должны проверить, является ли это GET / HEAD, если это так, то у вас нет отправленного содержимого (тела / сообщения). Если это был POST, сделайте так, как говорят спецификации о разборе сообщения известной / неизвестной длины , как сказал @gbjbaanb.

...