Пора в очередной раз проанализировать, в каком состоянии сейчас находится поддержка HTTP/3 в curl.

Думаю, ситуация с curl отражает положение дел во многих других HTTP-инструментах и библиотеках. HTTP/3 был и продолжает быть более сложным в плане развёртывания по сравнению с HTTP/2.

▍ cURL поддерживает четыре решения HTTP/3 на выбор


Поддержку HTTP/3 в curl можно реализовать с помощью четырёх разных подходов. Мы предоставляем несколько решений, чтобы на «рынке» был выбор, и чтобы они могли «конкурировать» друг с другом, давая пользователям возможность подобрать лучшее. Это избавляет нас от сложной задачи выбирать победителя изначально.

Подробнее обо всех этих четырёх подходах будет сказано ниже.

▍ Почему cURL ещё не использует HTTP/3?


Уже использует, если при сборке этого инструмента включить правильный набор сторонних библиотек. Кроме того, исполняемые файлы curl для Windows, предоставляемые проектом curl, поддерживают HTTP/3.

Для Linux, а также других дистрибутивов и упаковщиков операционных систем серьёзной проблемой остаётся то, что самая популярная библиотека TLS (OpenSSL) не предлагает широко поддерживаемый QUIC API, который предоставляют большинство других библиотек TLS. Напомню, что HTTP/3 задействует протокол QUIC (Quick UDP Internet Connections), внутренне использующий TLS 1.3. Это отсутствие API в OpenSSL вынуждает всех, кто хочет использовать библиотеку QUIC, использовать другую библиотеку TLS. Дело в том, что curl не предусматривает сборки с использованием нескольких библиотек TLS. Включение отдельной библиотеки TLS для QUIC наряду с другими протоколами на основе TLS не поддерживается.

Debian проводит эксперимент по внедрению HTTP/3 в поставляемую ими версию curl путём перехода на GnuTLS (и сборки с использованием ngtcp2 + nghttp3).

▍ Бэкенд HTTP/3


Чтобы curl заговорила на HTTP/3, потребуется три составляющих, а также корректировка кода самой программы:

  • поддержка TLS 1.3 для QUIC;
  • библиотека протокола QUIC;
  • библиотека протокола HTTP/3.

▍ Наглядное представление


Ниже в разных столбцах условной схемы представлены четыре решения HTTP/3, поддерживаемые curl. Все, кроме крайнего правого, считаются экспериментальными.


Поддержка бэкенда HTTP/3 в curl на июнь 2024

Слева направо:

  1. Библиотека quiche поддерживает QUIC и HTTP/3, а также обеспечивает TLS с помощью BoringSSL.
  2. msh3 — это ещё одна библиотека HTTP/3, использующая mquic для QUIC и либо семейство форков, либо Schannel для TLS.
  3. nghttp3 — это библиотека HTTP/3, которая в этой конфигурации использует стек QUIC из OpenSSL, который реализует QUIC и TLS.
  4. nghttp3 для HTTP/3 с использованием ngtcp2 для QUIC позволяет задействовать различные библиотеки TLS: семейство форков, GnuTLS и wolfSSL. (picotls тоже поддерживается, но сама curl не поддерживает её в качестве дополнительного решения TLS).

▍ ngtcp2 лидирует


Тандем библиотек ngtcp2 и nghttp3 стал первой комбинацией QUIC и HTTP/3, предоставившей уже не бета, а полноценные версии, уверенно работающие с curl. И это основная причина, по которой мы рекомендуем предпочесть именно этот подход.

Также привлекает гибкость представленных этой вертикалью решений TLS, поскольку даёт возможность использовать множество разных библиотек. К сожалению, разработчики OpenSSL решили не участвовать в этой игре, так что в приведённой конфигурации потребуется другая библиотека TLS.

▍ OpenSSL QUIC


В OpenSSL 3.2 была добавлена реализация стека QUIC, миновавшая статус «беты», которую curl может использовать в качестве альтернативного решения. Впоследствии в OpenSSL 3.3 были внесены дополнительные доработки. С начала 2024 года curl можно собирать и использовать для HTTP/3, о чём говорилось выше.

Тем не менее API, предоставляемый OpenSSL для выполнения передачи, неполноценен. В нём не хватает важной функциональности, что делает его неэффективным и иногда вынуждает curl входить в холостые циклы, чтобы определить дальнейшие действия. Этот факт и, возможно, некоторые дополнительные проблемы делают реализацию OpenSSL QUIC значительно медленнее конкурентов. Ещё одна причина рассмотреть использование других решений.

Мы продолжаем общаться с командой OpenSSL о том, что ещё они могут добавить в свой API, чтобы мы могли использовать QUIC эффективно. Надеемся, они всё же внесут необходимые доработки.

Штефан Эйссинг построил хороший сравнительный график, который я взял из его презентации «Performance» (Штефан также писал о производительности h3 ранее). В графике сравнивается три бэкенда curl с поддержкой HTTP/3. (В него не включена msh3, поскольку в curl эта библиотека работает недостаточно хорошо).

Как видно ниже, в нескольких тестовых конфигурациях OpenSSL достигает примерно лишь половины уровня быстродействия в сравнении с другими бэкенд-решениями как в количестве запросов в секунду, так и в скорости передачи. Проверка проводилась на localhost, так что операции передачи, по сути, были привязаны к скорости процессора (cpu-bound).


Производительность бэкенда HTTP/3 в curl, запросов в секунду


Производительность бэкенда HTTP/3 в curl, мегабайтов в секунду

Я считаю, что команде OpenSSL, помимо улучшения API, также нужно поработать над быстродействием QUIC.

▍ quiche и msh3


  • quiche по-прежнему отмечена как бета и использует только BoringSSL, что усложняет её применение во многих ситуациях.
  • msh3 после недавнего рефакторинга вообще перестала работать в curl.

▍ HTTP/3 нагружает процессор


Это знает любой, кто следит за разработкой протокола. Я из раза в раз говорю об этом в каждой своей презентации, посвящённой HTTP/3. И хотя таких презентаций я провёл уже несколько, мне кажется, что повторять это стоит, и что составленные Штефаном графики отчётливо проясняют ситуацию.

HTTP/3 отличается медленной скоростью передачи, когда выполнение привязано к скорости работы процессора. Естественно, в большинстве ситуаций пользователи к ней не привязаны, поскольку обычно узким местом при передаче выступает ограниченная пропускная способность сети. HTTP/3, как правило, быстрее выполняет рукопожатие — благодаря QUIC — поэтому при передаче через него первый байт зачастую доставляется быстрее, чем через любую другую версию HTTP (по TLS).

Далее мы продемонстрируем всё это наглядно на примере других предоставленных Штефаном графиков, начав с рукопожатий, которые он осуществил со своей машины в Германии. В этих тестах использовалась сборка curl 8.8.0-DEV, предшествовавшая выходу curl 8.8.0.


Скорость выполнения рукопожатия в curl через HTTP/3 и HTTP/1.1

Нет, мы не можем объяснить, почему google.com обрабатывает рукопожатия по HTTP/3 медленнее. Можно добавить, что curl.se обслуживается Fastly CDN, поэтому фактически curl сравнивается в отношении реализаций CDN трёх разных поставщиков.


Скорость передачи по HTTP/3, HTTP/2 и HTTP/1.1 в curl

Опять же, эти передачи привязаны к быстродействию процессора, поэтому реально мы наблюдаем здесь огромный объём лишней работы ЦПУ, необходимой для их осуществления. Если же вы производительностью процессора не ограничены, то передачи, естественно, будут выполняться с одной скоростью, как и в старых версиях HTTP.

Все эти сравнения не являются обобщённой оценкой протокола (если такая вообще возможна) и показывают работу curl в отношении его конкретных версий. Мы не можем сделать вывод, что в коде curl есть проблемы или непродуманные решения, которые бы частично это объяснили. Лично я подозреваю, что хоть нам и есть всегда что доработать, вряд ли здесь скрываются какие-то ощутимые преграды для производительности. Но уверенными в этом быть тоже нельзя.

QUIC в OpenSSL здесь тоже выделяется, причём не самым привлекательным образом.

▍ Распространённость HTTP/3


Данные w3techs, Mozilla и Cloudflare сходятся в том, что около 28-30% веб-трафика на данный момент относится к HTTP/3, что превосходит охват HTTP/1.1.

Интересно в этих 30% то, что все крупные игроки и CDN (Google, Facebook, Cloudflare, Akamai, Fastly, Amazon и так далее) работают по HTTP/3, и я думаю, что все вместе они занимают гораздо больше 30%. Это говорит о том, что есть значительная доля браузерного трафика, который может задействовать HTTP/3, но пока этого не делает. К сожалению, найти этому объяснение возможности у меня нет.

▍ Обзор стека HTTPS


Для тех, кто хочет освежить память, вот схематичное описание принципа работы стека HTTPS.


Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (25)


  1. TerekhinSergey
    16.06.2024 15:50
    +1

    Во кто-нибудь может объяснить, почему все так в линуксе? Ну не хватает программе возможностей системной библиотеки - положи Альтернативу рядом с ней и используй. Или только хард код-путь? Только из системой папки... Снап вроде бы попытался, но не смог. В Windows проблема более-менее решена, а тут прям 90-е полным ходом


    1. knstqq
      16.06.2024 15:50
      +7

      снап - самая отвратительная штука что есть в убунте. Кладут каждую программу в образ по 300-500мб, всё тормозит, работает отвратительно.

      +снап никто кроме canonnical не продвигает.

      Если уж хочется запакетитироваться - Flatpack тогда.

      p.s. про windows в 2024ом году сказать не могу, но в 2010ом году в windows решение было так себе. Постоянно приходится ставить "Visual C++ Redistributable for Visual Studio" или netframework, да ещё по 3-5 версий сразу. Исправили сейчас или всё ещё также ставятся?


      1. opusmode
        16.06.2024 15:50

        Да в общем-то уже лет 10 как поправили.

        Точнее так - в 2015 в десятке уже точно всё было круто, а вот в 8.1 вроде бы таких проблем небыло и оно всё само, но память может подводить.

        Но в целом я понимаю о чём вы. Семёрка была ужасна и восьмёрка сделала по ряду фронтов скачок вперёд, а уж десятка вообще сделала винду конфеткой. Хотя тоже не везде - powershell, относительно линуксов, всё ещё ужасен.


      1. alan008
        16.06.2024 15:50
        +7

        Всё так же, только версий .Net и С++ Redistributable стало гораздо больше :)


        1. TerekhinSergey
          16.06.2024 15:50
          +1

          Но при всём при том никто не говорит "ой, тут в системной библиотеке нет нужной нам функции, поэтому у нас лапки"


        1. Starl1ght
          16.06.2024 15:50

          Версия VCredist с 2015 года одна - последняя.

          Неткоры - да, каждый отдельн


          1. alan008
            16.06.2024 15:50
            +4

            У меня другая информация насчет VC++:


            1. Starl1ght
              16.06.2024 15:50
              +1

              Если бы вы кликнули по ссылке download и запустили скачанное, то увидели бы именно то, что я и написал. Но вы этого не сделали.

              И даже не погуглили, т.к. первая ссылка это



              1. alan008
                16.06.2024 15:50

                Не вижу смысла спорить, но точно существует несколько версий файла (возможно с одинаковой начинкой - это не проверял):

                Мicrosoft Visual C++ 2015 Redistributable (x64) - 14.0.23026
                Microsoft Visual C++ 2017 Redistributable (x64) - 14.16.27033
                Microsoft Visual C++ 2015-2022 Redistributable (x64) - 14.40.33810


                1. Starl1ght
                  16.06.2024 15:50

                  Начиная с VC++ 2015 - ABI не менялся, и одного последнего редиста достаточно, чтобы работало всё. Я это и написал - в 2015+ - нужна одна последняя версия.


              1. khajiit
                16.06.2024 15:50

                А толку, если весь этот балаган тащат и устанавливают приложения, и плевать им на результаты гугла и написанное на сайте M$?


      1. TerekhinSergey
        16.06.2024 15:50

        Соглашусь про снап - но это была попытка, хотя и не очень удачная, решить эту и другие проблемы


      1. domix32
        16.06.2024 15:50
        +2

        Кладут каждую программу в образ по 300-500мб,

        у flatpak иногда схожие проблемы из-за "изоляции".


        1. khajiit
          16.06.2024 15:50

          Но он научился в зависимости


          1. domix32
            16.06.2024 15:50

            Насколько мне известно он дублирует по крайней мере часть из имеющихся зависимостей рядом.


            1. khajiit
              16.06.2024 15:50

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


    1. LF69ssop
      16.06.2024 15:50
      +4

      Как раз в линуксе существует LD_PRELOAD. Ну или я не понял о чем вы.


    1. winorun
      16.06.2024 15:50

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

      Так ни кто так делать и не мешает. У меня три программы в /opt стоит. С полным набором библиотек. И это не считая игр в ~/Games, в которой некоторые библиотеки еще со времен Lenny.


      1. Arenoros
        16.06.2024 15:50

        тут речь не про рядовые зависимости типа boost, а что то в духе libatomic, pthread и с ними ещё можно относительно просто разобраться, а вот чтоб изолироваться от glibc это нужно столько приседаний сделать что одуреть можно, а в большинстве кодовых баз эти приседания в принципе не возможны (если интересны детали есть отличный доклад про это https://youtu.be/Z7WuUhPJ-cU)

        В win чтоб запускать прогу собраную самым последним тулчейном vc на win7 достаточно просто поставить последний пакет Redistributable C++, а под linux чтоб мне запустить хоть что то собранное последним gcc на условной ubuntu 12.04 в общем случае вообще ни чего не сделать это практически нереально без полного обновления ОС включая ядро. найс портируемость linux.


    1. vikarti
      16.06.2024 15:50
      +5

      Технически - можно и способов существует много (можно вообще хоть все кроме glibc статически линковать). Просто принято использовать системное а не разводить свалку.


    1. iamkisly
      16.06.2024 15:50

      Либо я не понял сути проблемы, либо тут незнание матчасти. Поэтому я тут набросал пример, и зову

      к коллайдеру !

      Пример до убогости прост. Не по стандарту, gcc хавает.. но не суть

      // helloworld.cpp
      #include "helloworld.hpp"
      #include <iostream>
      void helloWorld() {
          std::cout << "Hello, world!" << std::endl;
      }
      // helloworld.hpp
      #ifndef LIBHELLOWORLD_H_INCLUDED
      #define LIBHELLOWORLD_H_INCLUDED
      void helloWorld();
      #endif /* LIBHELLOWORLD_H_INCLUDED */
      // main.cpp
      #include <iostream>
      #include "helloworld/helloworld.hpp"
      int main ( void )
      {
          helloWorld();
          return 0;
      }
      hello_world_cpp/helloworld$ g++ -c -fPIC -o bin/helloworld.o helloworld.cpp 
      hello_world_cpp/helloworld$ gcc -shared -o bin/libhelloworld.so bin/helloworld.o
      hello_world_cpp$ g++ -v -Wall -o main main.cpp -L. -I. -Lhelloworld/bin/ -lhelloworld
      .
      ├── helloworld
      │   ├── bin
      │   │   ├── helloworld.o
      │   │   └── libhelloworld.so
      │   ├── helloworld.cpp
      │   └── helloworld.hpp
      ├── main
      └── main.cpp
      

      внимание на libhelloworld.so => not found

      /hello_world_cpp$ ldd main
              linux-vdso.so.1 (0x00007ffe53fa6000)
              libhelloworld.so => not found
              libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007662a1000000)
              libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007662a0c00000)
              libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007662a0f17000)
              /lib64/ld-linux-x86-64.so.2 (0x00007662a1365000)
              libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007662a1313000)
      

      /hello_world_cpp$ sudo cp helloworld/bin/libhelloworld.so /usr/lib/x86_64-linux-gnu/libhelloworld.so

      libhelloworld.so => /lib/x86_64-linux-gnu/libhelloworld.so

      hello_world_cpp$ ldd main
              linux-vdso.so.1 (0x00007ffe20838000)
              libhelloworld.so => /lib/x86_64-linux-gnu/libhelloworld.so (0x000073ea2c4d2000)
              libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x000073ea2c200000)
              libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000073ea2be00000)
              libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000073ea2c117000)
              /lib64/ld-linux-x86-64.so.2 (0x000073ea2c4fc000)
              libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x000073ea2c4a5000)
      
      hello_world_cpp$ export LD_LIBRARY_PATH=helloworld/bin/:$LD_LIBRARY_PATH
      hello_world_cpp$ ./main
      Hello, world!

      Тада!! libhelloworld.so => helloworld/bin/libhelloworld.so

      hello_world_cpp$ ldd main
              linux-vdso.so.1 (0x00007ffd9fdb9000)
              libhelloworld.so => helloworld/bin/libhelloworld.so (0x00007f4741431000)
              libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4741000000)
              libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4740c00000)
              libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f474132a000)
              /lib64/ld-linux-x86-64.so.2 (0x00007f474143d000)
              libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f47412fd000)
      

      Пересобираем динамическую библиотеку

      #include "helloworld.hpp"
      #include <iostream>
      void helloWorld() {
          std::cout << "Hello, Habr!" << std::endl;
      }
      hello_world_cpp/helloworld$ g++ -c -fPIC -o bin/helloworld.o helloworld.cpp 
      hello_world_cpp/helloworld$ gcc -shared -o bin/libhelloworld.so bin/helloworld.o
      hello_world_cpp$ ./main
      Hello, Habr!

      Не знаю зачем мне это понадобилось.. во имя LD_LIBRARY_PATH конечно


  1. vikarti
    16.06.2024 15:50
    +1

    Это говорит о том, что есть значительная доля браузерного трафика, который может задействовать HTTP/3, но пока этого не делает. К сожалению, найти этому объяснение возможности у меня нет.

    А разве из России на не-российские сайты QUIC в принципе работает?(спасибо в частности РКН)

    А допустим из Китая и прочих Таджикистанов?

    А во всяких Европах на мобильных сетях где DPI?


    1. blind_oracle
      16.06.2024 15:50

      Это же перевод, какое им дело до РФ и прочих Таджикистанов? :)

      А Китай просто свой GFW скорее всего обновит новые паттерны искать и всё.


      1. vikarti
        16.06.2024 15:50

        Это я к тому что многим странам проще рубить QUIC нафиг благо все равно fallback'и есть и просто немного упадет скорость чем пробовать (что еще совсем не факт что получится) понять где там хотя бы адрес хоста.


        1. blind_oracle
          16.06.2024 15:50

          Ну всякие авторитарные режимы зарубят, а остальные будут пользоваться. Ну и там в QUIC более-менее обычный TLS1.3 в потрохах, написать дисскетор его для DPI не думаю что проблема.