Привет! Меня зовут Глеб Гончаров, я руководитель группы разработки клиентского продукта в СберМаркете. Это вторая часть в серии статей об истории развития самого популярного протокола Всемирной паутины — протокола HTTP.
В первой части я рассказал про старую группу протокола HTTP (HTTP/0.9, HTTP/1.0, HTTP-NG, HTTP/1.1): причины их появления, видах запросов и недостатках. Сегодня же поговорим о новой группе, которая включает в себя протоколы SPDY, HTTP/2, gQUIC и HTTP/3.
SPDY
В 2009 году Google представил первую наработку нового протокола для устранения фундаментальных проблем с дизайном HTTP/1.1 под названием SPDY (произносится как «спиди»). К тому времени браузер Chrome уже получил широкое распространение и 30-миллионную базу активных пользователей, а развитие телекоммуникационных сетей позволило Google доставлять обновления пользователям и проводить широкие технические эксперименты.
Основной задачей SPDY стало уменьшение задержки загрузки веб-страниц путем устранения некоторых известных ограничений производительности HTTP/1.1. Важно отметить, что протокол SPDY не является заменой HTTP, а представляет новый транспортный слой для HTTP-трафика. Протокол так и не был стандартизирован и являлся больше лабораторным экспериментом для сбора данных о производительности.
Данные в SPDY передаются в пределах одного TCP-соединения. Благодаря тому, что полностью сохранена семантика HTTP, разработчикам не требовалось адаптировать свои приложения, а эксплуатации достаточно было использовать новый транспортный слой на стороне клиента и сервера.
Дизайн SPDY предполагает управление потоками и мультиплексирование для доступа к ресурсам одновременно, также возможно приоритизировать запросы, сжимать данные и HTTP-заголовки, удаляя избыточность. Кроме того, есть поддержка server push и server hint.
Server push позволяет отправить ресурс клиенту без подтверждения с его стороны. Это интересная возможность для реализации уведомлений. Однако server push полностью игнорируют кеш клиента и использовать его для загрузки статических ресурсов, пожалуй, не следует.
Server hint отправляет URL до документов, позволяя клиенту самому валидировать документ и загружать связанные ресурсы, не дожидаясь основного тела HTML-документа.
Работа криптографии в SPDY полагается на расширение TLS NPN (Next Protocol Negotiation). Используя NPN, сервер сообщает клиенту какие протоколы он знает, а клиент может выбрать и использовать наиболее предпочтительный. Сейчас TLS NPN полностью вытеснен в пользу расширения TLS ALPN (Application Level Protocol Negotiation), впоследствии ставшего стандартом RFC 7301.
Протокол SPDY подразумевает пересылку бинарных сообщений — кадров (фреймов). Клиент отправляет фрейм SYN_STREAM с запросом, а сервер отвечает фреймами SYN_REPLY и последовательной серией DATA-фреймов. Помимо перечисленного, SPDY поддерживает несколько управляющих фреймов (RST_STREAM, SETTINGS, PING, GOAWAY, WINDOW_UPDATE и CREDENTIAL). RST_STREAM, например, позволял прервать передачу потока от клиента к серверу. Ранее такая возможность отсутствовала в HTTP/1.1: запрос и данные передавались целиком, а прерывание передачи каждый веб-сервер реализовывал по-своему: зачастую на транспортном уровне через отправку клиенту TCP RST.
Эксперимент SPDY был признан удачным. Согласно отчёту Google, в некоторых случаях удалось сократить время загрузки веб-сайтов на 60%. Это стало возможным благодаря эффективному использованию транспортного протокола, снижению чувствительности к задержкам в сети (пусть и не полностью избавиться от Head-of-line блокировок) и сокращения избыточности HTTP-заголовков за счёт использования алгоритма HPACK.
SPDY прекрасно выполнил свою задачу и продемонстрировал выдающиеся результаты производительности загрузки веб-страниц в результате смены парадигмы, сменив текстовый протокол бинарным с поддержкой параллелизма и мультиплексирования. Хоть SPDY не являлся стандартом и разрабатывался преимущественно инженерами Google с незначительным участием открытого сообщества, рабочая группа HTTP (HTTP-WG) извлекла важные уроки для появления нового протокола HTTP/2.
gQUIC
В 2012 году Google начинает работать над протоколом QUIC. Это протокол транспортного уровня, сочетающий в себе функции протоколов уровня представления и приложения.
Google QUIC (gQUIC) — это бинарный протокол, первый черновик которого был представлен в 2012 году. Дизайн gQUIC представляет собой монолит, включающий как возможности прикладного уровня, так и обязательного криптографического слоя из состава QUIC crypto с рядом функций транспортного протокола TCP как проверка целостности, контроль перегрузок и коррекция ошибок. Однако наиболее значимое отличие gQUIC от предшественников — это ориентированность на пакетную передачу по UDP.
Благодаря переходу на UDP, gQUIC сокращает время установки соединения до 1RTT или до 0RTT и реализует миграцию между узлами за счёт возобновляемых сессий. Представьте, что вы смотрите видео и перемещаетесь точками доступа, а вам не требуется повторно инициировать соединение. Также это работает и со стороны сервера: миграция подключений позволит долгим запросам переехать на новые узлы без простоя.
При всех преимуществах, были и недостатки:
Всё же изначально gQUIC неофициальный протокол от Google;
Использовал собственную реализацию криптографии, несовместимой с OpenSSL, BoringSSL и др. открытыми реализациями, что затрудняло повсеместное внедрение.
Использовал чувствительный к потерям алгоритм сжатия заголовков HPACK из SPDY. Учитывая, что основной транспортный уровень стал UDP и потери датаграмм неизбежны, компрессия заголовков была узким местом производительности.
gQUIC смешивает несколько протоколов в один уровень, что затруднит развитие протокола в будущем.
Тем не менее, это позволило продемонстрировать эффективность решений. К примеру, в период разработки gQUIC компания Google экспериментировала, предлагая пользователям Chrome и Chromium использовать новый протокол в своих сервисах (YouTube, GMail и пр.). К моменту начала обсуждения IETF QUIC (iQUIC) в 2015 году, в мире около 7.8% всего трафика уже работало на Google QUIC.
HTTP/2
Тем временем в мае 2015 года IETF заканчивает стандартизацию HTTP/2. Любопытно, что буквально в апреле того же года Google сообщает о планах передачи gQUIC в IETF в роли нулевого черновика в IETF для разработки HTTP/3.
Итак, HTTP/2 — это бинарный протокол, ориентированный на передачу пакетов, ставший стандартом развития HTTP. Работает поверх TCP с опциональным SSL/TLS. Примечательно, что стандарт HTTP/2 поддерживает работу поверх TCP без SSL/TLS, однако на практике практически нигде не поддерживается без шифрования: основные вендоры браузеров Mozilla и Google сразу заявили, что не будут реализовывать h2c и сконцентрируются только на h2.
Основные возможности берут лучшее и проверенное из SPDY и gQUIC. Основное улучшение — передача данных в пределах одного TCP-соединения. Также заимствовали реализацию управления потоками и мультиплексирования для распараллеливания загрузки. Дополнительно реализовали отправку нескольких запросов (HTTP pipelining), server push и механизм обновления протокола.
В HTTP/2 клиент отправляет фрейм HEADERS с заголовками, а сервер отвечает фреймом HEADERS и последовательной серией DATA-фреймов. HTTP/2 также поддерживает несколько управляющих фреймов (PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, GOAWAY, PING, WINDOW_UPDATE, CONTINUATION). Как и в SPDY, можно прерывать поток передачи отправкой фрейма RST_STREAM.
Результатом внедрения HTTP/2 стало сокращение времени установлена соединения до 2RTT или 1RTT, а стандарт подтолкнул вендоров реализовать поддержку нового протокола. Также удалось снизить чувствительность к задержкам в сети.
К сожалению, использование преимуществ HTTP/2 означало отказ от старых хаков HTTP/1.1, а потому разработчикам требовалось адаптировать свои приложения для достижения высокой скорости загрузки и рендера страниц. Также при увеличении задержки преимущества протокола также нивелировались. Также не удалось полностью исключить чувствительность к задержкам, поскольку транспортный слой остался на TCP. Это означало те же проблемы: при потере одного пакета может «сорваться» доставка всех последующих, сводя на нет мультиплексирование.
В ходе эксплуатации HTTP/2 также выяснилось, что практически ни один из промышленных веб-серверов не реализовал server push в виду низкой популярности. Также в Nginx в HTTP/2 не работают websockets и, похоже, не заработают.
В любом случае, в 2022 году на HTTP/2 работает 95% браузеров и 66% веб-серверов в мире и с уверенностью можно сказать, что протокол достаточно хорошо освоен и в значительной мере улучшил пользовательский опыт в Интернете.
HTTP/3
В ноябре 2018-го года IETF сообщил о переименовании QUIC+HTTP/2 в HTTP/3, назвав его новым протоколом WWW. Изначально предполагалось, что стандарт выйдет в ноябре, но обсуждения продолжились до лета 2019 года. Думаю, сейчас уже становится очевидным, это не принципиально новый протокол, а эволюционное эволюционное развитие HTTP/2, логическое продолжение идей прошлого.
В результате в стандарт вошли как проверенные возможности HTTP/2 (за небольшими модификациями в виде QPACK, ставшего заменой HPACK для корректной работы поверх UDP), так и наработки gQUIC — альтернативные сервисы, обязательное шифрование, миграция подключений. Дополнительно IETF предложили реализацию защиты от спуфинга IP-адреса отправителя и от amplification-атак.
Стандарт позволил решить практически все имеющиеся проблемы предшественников, вобрав лучшие из наработок gQUIC и HTTP/2, однако сейчас есть несколько трудностей с его внедрением. Во-первых, в ядрах основных серверных ОС есть слабые места в работе UDP. За десятилетия эксплуатации HTTP, протокол TCP уже достаточно хорошо оптимизирован, в то время как с UDP могут быть проблемы с производительностью (со слов LWH).
Также отсутствуют готовые промышленные реализации, чтобы оценить работу на местах. Сейчас уже есть реализации в libcurl и экспериментальных веб-серверов, но на это едва ли будут готовы крупные компании. Технологическое preview того же Nginx было в 2020-м году. Появление возможности обещали к концу 2021 года, но пока без изменений.
Также не ясно как быстро разработчики популярных криптографических библиотек адаптируются под изменения. Например, условный Nginx работает только с BoringSSL, а OpenSSL и LibreSSL пока не поддерживают TLS over QUIC.
Что дальше?
Итак, мы рассмотрели краткую историю развития протокола HTTP. Как вы думаете, что ждёт нас в HTTP/4?
Tech-команда СберМаркета завела соцсети с новостями и анонсами. Если хочешь узнать, что под капотом высоконагруженного e-commerce, следи за нами в Telegram и YouTube.
Fr0sT-Brutal
Статьи интересные, спасибо. Вот только печально, что чем более мудреный протокол, тем сложнее его реализовать, тем больше дырок в реализациях может встретиться. Тут уже не накидаешь на сокетах простенький запрос, когда разработчики серверов решат, что им надоело тащить 1.1 и выпилят его. В итоге будет две-три либы и куча оберток. С одной стороны, плотнее тестирование и совместная работа над ними, с другой - узвимости будут сразу влиять на миллионы серверов. Помним OpenSSL. Также интересно, как UDP смогут подружить со шлюзами, NAT и проксями.