Прямой прокси не сохраняет границы TCP-сегментов
Разделяйте сегментацию TCP, чтения из сокета и кадрирование HTTP, когда изучаете трафик по обе стороны прямого прокси.
Предположим, что HTTP-ответ после прохождения через Squid появляется в нескольких записанных TCP-сегментах. Это может быть обычное поведение TCP, а не доказательство того, что ответ был разделён неправильно.
Прямой прокси обычно имеет два соединения:
client ⇄ TCP connection A ⇄ proxy ⇄ TCP connection B ⇄ origin
У каждого соединения своё состояние TCP, свои порядковые номера и своя пакетизация. Прокси может читать данные из одного соединения и записывать их в другое, не сохраняя исходные размеры сегментов. Буферизация, момент отправки, перегрузка и ограничения размера пути могут дать другое группирование.
Три границы, которые стоит держать раздельно
| Граница | Чем она определяется |
|---|---|
| TCP-сегмент | Транспортный и сетевой путь |
| Результат чтения из сокета | Доступные байты потока и буфер вызывающего |
| Сообщение или кадр HTTP | Правила кадрирования протокола HTTP |
Одно чтение может содержать часть заголовка, весь заголовок вместе с байтами тела или данные, пересекающие более одной границы прикладного уровня. TCP не привязывает «конец HTTP-ответа» к концу успешного чтения.
Поэтому анализатор HTTP/1.1 накапливает заголовки и применяет применимые правила кадрирования тела, включая длину, кодирование передачи или разрешённое закрытие соединения. Кадр HTTP/2 — это структура протокола, которая сама может пересекать TCP-сегменты; это не другое название сетевого пакета.
Успешный туннель CONNECT сохраняет туннелированный поток байтов, но по-прежнему использует два участка TCP. Границы его сегментов могут различаться даже тогда, когда прокси не менял байты внутри туннеля.
При расследовании повреждений сравнивайте пересобранные данные приложения. Сравнение числа пакетов или ожидание одинаковых длин пакетов с обеих сторон требует от TCP гарантии границ, которой он не даёт.