Как устроен HTTP-запрос и как разделяются несколько запросов в рамках одного соединения?

Ответ

В основе лежит тот факт, что TCP — это потоковый протокол, и у него нет встроенного понятия "сообщение". HTTP вводит свой собственный формат, чтобы структурировать данные в этом потоке.

Структура одного HTTP-запроса:

  1. Стартовая строка (Request Line): Содержит метод (GET, POST), URI (путь к ресурсу) и версию протокола (HTTP/1.1).

    GET /path/to/resource HTTP/1.1
  2. Заголовки (Headers): Пары Ключ: Значение, предоставляющие метаинформацию о запросе. Каждый заголовок заканчивается CRLF (rn).

    Host: example.com
  3. Пустая строка: Один CRLF (rn) после последнего заголовка. Эта строка является критически важным разделителем, который сигнализирует о конце секции заголовков.
  4. Тело запроса (Body): Необязательные данные, например, JSON для POST-запроса. Наличие и размер тела определяются заголовками.

Разделение запросов:

  • Конец заголовков: Сервер знает, что заголовки закончились, когда встречает двойной CRLF (rnrn).
  • Конец тела: Для определения конца тела запроса используются заголовки:
    • Content-Length: <size>: Указывает точный размер тела в байтах. Сервер читает ровно столько байт после пустой строки.
    • Transfer-Encoding: chunked: Тело передается частями (чанками), каждая из которых имеет свой размер. Конец передачи сигнализируется чанком нулевой длины.

В рамках одного Keep-Alive соединения, где несколько запросов могут идти последовательно, сервер, прочитав один полный запрос (включая его тело), обрабатывает его, отправляет ответ и затем начинает читать следующий запрос из того же TCP-потока, снова ожидая стартовую строку.