Spec-Zone.ru › Apache HTTP Server

Руководство по HTTP/2

Это руководство по реализации HTTP/2 в Apache httpd. Эта функция готова к использованию в производственной среде, и вы можете рассчитывать на то, что интерфейсы и директивы останутся неизменными в последующих выпусках.

Протокол HTTP/2

HTTP/2 — это развитие самого успешного в мире протокола прикладного уровня — HTTP. Он ориентирован на более эффективное использование сетевых ресурсов. Фундаментальные основы HTTP, его семантика, не меняются. По-прежнему есть запросы, ответы, заголовки и всё остальное. Поэтому, если вы уже знаете HTTP/1, вы знаете и 95% того, что нужно знать об HTTP/2.

Об HTTP/2 и принципах его работы написано немало. Самым нормативным документом, конечно, является его RFC 7540 (также доступен в более удобном для чтения формате, но результат может отличаться). Именно там вы найдёте все подробности.

Но, как это часто бывает с RFC, читать его первым не стоит. Лучше сначала понять, что должен делать протокол, а затем прочитать RFC о том, как это реализовано. Гораздо лучше начать с документа HTTP/2 простыми словами Даниэля Стенберга, автора curl. Он также доступен на постоянно растущем количестве языков!

Кратко: при чтении этого документа важно помнить о нескольких новых терминах и нюансах:

  • HTTP/2 — это двоичный протокол, в отличие от текстового HTTP 1.1. Последний предназначен для чтения человеком (например, при анализе сетевого трафика), а первый — нет. Подробнее см. в официальном ответе на этот вопрос в разделе часто задаваемых вопросов.
  • h2 — это HTTP/2 поверх TLS (согласование протокола через ALPN).
  • h2c — это HTTP/2 поверх TCP.
  • Кадр — наименьшая единица обмена данными в соединении HTTP/2, состоящая из заголовка и последовательности октетов переменной длины, структура которой определяется типом кадра. Подробнее см. в официальной документации.
  • Поток — двунаправленная последовательность кадров в соединении HTTP/2. Соответствующее понятие в HTTP 1.1 — обмен сообщениями запроса и ответа. Подробнее см. в официальной документации.
  • HTTP/2 позволяет передавать несколько потоков данных через одно и то же TCP-соединение, избегая классической проблемы блокировки начала очереди медленным запросом в HTTP 1.1 и необходимости повторно устанавливать TCP-соединение для каждого запроса и ответа (KeepAlive устранил эту проблему в HTTP 1.1, но не решил её полностью).

HTTP/2 в Apache httpd

Протокол HTTP/2 реализован в отдельном модуле httpd с подходящим названием mod_http2. Он реализует полный набор функций, описанных в RFC 7540, и поддерживает HTTP/2 как через незашифрованные соединения (http:), так и через защищённые соединения (https:). Незашифрованный вариант называется 'h2c', а защищённый — 'h2'. Для h2c он поддерживает прямой режим и Upgrade: посредством первоначального запроса HTTP/1.

Одна из новых возможностей HTTP/2 для веб-разработчиков — Server Push. О том, как использовать её в веб-приложении, читайте в соответствующем разделе.

Сборка httpd с поддержкой HTTP/2

mod_http2 использует библиотеку nghttp2 в качестве основы своей реализации. Чтобы собрать mod_http2, необходимо установить в системе как минимум версию 1.2.1 библиотеки libnghttp2.

При ./configure дерева исходного кода Apache httpd нужно передать '--enable-http2' как дополнительный аргумент, чтобы включить сборку модуля. Если ваша libnghttp2 находится в нестандартном месте (в зависимости от операционной системы), укажите её расположение с помощью '--with-nghttp2=<path>' для configure.

Для большинства этого достаточно, но некоторым может понадобиться статически скомпонованная nghttp2 в этом модуле. Для этого предусмотрен параметр --enable-nghttp2-staticlib-deps. Он работает примерно так же, как статическая компоновка openssl с mod_ssl.

К слову об SSL: большинство браузеров поддерживают HTTP/2 только для URL-адресов https:, поэтому вам понадобится сервер с поддержкой SSL. Но этого недостаточно: нужна также библиотека SSL с поддержкой расширения ALPN. Если вы используете OpenSSL, потребуется версия не ниже 1.0.2.

Базовая конфигурация

После сборки httpd с mod_http2 потребуется выполнить базовую настройку, чтобы включить модуль. Сначала, как и для любого модуля Apache, его нужно загрузить:

LoadModule http2_module modules/mod_http2.so

Вторую директиву, которую нужно добавить в конфигурацию сервера, можно записать так:

Protocols h2 http/1.1

Эта директива позволяет использовать h2, защищённый вариант, как предпочтительный протокол для соединений с сервером. Чтобы включить все варианты HTTP/2, достаточно написать:

Protocols h2 h2c http/1.1

В зависимости от места, куда вы добавите эту директиву, она будет влиять на все соединения или только на соединения с определённым виртуальным хостом. Её можно вложить, например, так:

Protocols http/1.1
<VirtualHost ...>
    ServerName test.example.org
    Protocols h2 http/1.1
</VirtualHost>

Это разрешает использовать для соединений только HTTP/1, кроме SSL-соединений с test.example.org, в которых предлагается HTTP/2.

Выберите надёжный набор шифров SSLCipherSuite

Для SSLCipherSuite необходимо настроить надёжный набор шифров TLS. Текущая версия mod_http2 не задаёт обязательных требований к шифрам, но большинство клиентов их предъявляют. Если открыть в браузере сервер с включённым h2 и неподходящим набором шифров, браузер просто откажется от соединения и переключится на HTTP 1.1. Это распространённая ошибка при первой настройке httpd для HTTP/2, поэтому помните об этом, чтобы не тратить много времени на отладку! Чтобы правильно выбрать набор шифров, исключите варианты из списка отклоняемых наборов шифров TLS для HTTP/2.

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

Protocols http/1.1 h2

наиболее предпочтителен HTTP/1, и он будет выбираться всегда, если только клиент не поддерживает только h2. Поскольку мы хотим использовать HTTP/2 с клиентами, которые его поддерживают, лучше указать протоколы в следующем порядке:

Protocols h2 h2c http/1.1

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

ProtocolsHonorOrder Off

Это означает, что порядок, в котором вы указали протоколы, не имеет значения — выбор будет определяться только порядком клиента.

И последнее: корректность написания и существование указанных протоколов не проверяются. Можно указать несуществующие протоколы, поэтому нет необходимости проверять Protocols с помощью каких-либо проверок <IfModule>.

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

Настройка MPM

HTTP/2 поддерживается всеми модулями многопроцессной обработки, входящими в состав httpd. Однако при использовании MPM prefork действуют серьёзные ограничения.

В prefork модуль mod_http2 обрабатывает только один запрос за раз для каждого соединения. Но клиенты, например браузеры, отправляют много запросов одновременно. Если обработка одного из них занимает много времени (или это длительный запрос с опросом), остальные запросы будут ждать.

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

Если ваша конфигурация это позволяет, в настоящее время лучше всего настроить MPM event (если он поддерживается вашей платформой).

Если вы вынуждены использовать prefork и хотите обрабатывать несколько запросов, можно изменить H2MinWorkers, чтобы включить такую возможность. Однако если это что-то нарушит, ответственность за последствия лежит на вас.

Клиенты

Почти все современные браузеры поддерживают HTTP/2, но только через SSL-соединения: Firefox (v43), Chrome (v45), Safari (начиная с v9), iOS Safari (v9), Opera (v35), Chrome для Android (v49) и Internet Explorer (v11 в Windows10) (источник).

Другие клиенты и серверы перечислены на вики со списком реализаций. Среди них есть реализации для c, c++, common lisp, dart, erlang, haskell, java, nodejs, php, python, perl, ruby, rust, scala и swift.

Некоторые реализации клиентов, не являющихся браузерами, поддерживают HTTP/2 через незашифрованное соединение — h2c. Самый универсальный из них — curl.

Полезные инструменты для отладки HTTP/2

Первым, конечно, стоит упомянуть curl. Убедитесь, что ваша версия поддерживает HTTP/2, проверив её Features:

    $ curl -V
    curl 7.45.0 (x86_64-apple-darwin15.0.0) libcurl/7.45.0 OpenSSL/1.0.2d zlib/1.2.8 nghttp2/1.3.4
    Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 [...] 
    Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2
    

Примечания по Homebrew для Mac OS

brew install curl --with-openssl --with-nghttp2

Для подробного анализа используйте Wireshark.

Пакет nghttp2 также включает такие клиенты, как:

  • nghttp — помогает визуализировать кадры HTTP/2 и лучше понять протокол.
  • h2load — помогает проводить нагрузочное тестирование сервера.

Chrome предоставляет подробные журналы HTTP/2-соединений на специальной странице net-internals. Также есть интересные расширения для Chrome и Firefox, которые показывают, когда браузер использует HTTP/2.

Server Push

Протокол HTTP/2 позволяет серверу отправлять клиенту ответы PUSH, о которых тот не запрашивал. Это можно представить так: «вот запрос, который вы не отправляли, и вскоре вы получите на него ответ...»

Однако есть ограничения: клиент может отключить эту функцию, а сервер может отправлять PUSH только в ответ на запрос, поступивший от клиента.

Предполагается, что сервер сможет отправлять клиенту ресурсы, которые, скорее всего, ему понадобятся: ресурс CSS или JavaScript, относящийся к запрошенной клиентом HTML-странице, набор изображений, на которые ссылается CSS, и так далее.

Преимущество для клиента заключается в экономии времени на отправку запроса — от нескольких миллисекунд до половины секунды в зависимости от расстояния между клиентом и сервером. Недостаток в том, что клиент может получить данные, которые уже есть в его кэше. Конечно, HTTP/2 позволяет досрочно отменить такие запросы, но ресурсы всё равно расходуются впустую.

Итак, универсальной стратегии использования этой возможности HTTP/2 пока нет, и все продолжают экспериментировать. Как же опробовать её в Apache httpd?

mod_http2 проверяет заголовки ответа на наличие заголовков Link в определённом формате:

Link </xxx.css>;rel=preload, </xxx.js>; rel=preload

Если соединение поддерживает PUSH, эти два ресурса будут отправлены клиенту. Веб-разработчик может добавить такие заголовки непосредственно в ответ приложения или настроить сервер с помощью

<Location /xxx.html>
    Header add Link "</xxx.css>;rel=preload"
    Header add Link "</xxx.js>;rel=preload"
</Location>

Чтобы использовать ссылки preload, не инициируя PUSH, можно указать параметр nopush, например:

Link </xxx.css>;rel=preload;nopush

Также можно полностью отключить PUSH для сервера с помощью директивы

H2Push Off

И это ещё не всё:

Модуль сохраняет для каждого соединения журнал отправленных через PUSH ресурсов (в основном хеши URL-адресов) и не отправляет один и тот же ресурс дважды. При закрытии соединения эта информация удаляется.

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

В mod_http2 также реализован ещё один экспериментальный черновик — поле заголовка Accept-Push-Policy, с помощью которого клиент может указывать для каждого запроса, какие PUSH-запросы он принимает.

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

На то, будет ли ресурс отправлен через PUSH, в первую очередь влияет смоделированный запрос. URL-адрес запроса для PUSH задаёт приложение, но откуда берутся заголовки запроса? Например, будет ли в запросе PUSH заголовок accept-language и, если да, какое значение он будет иметь?

Apache анализирует исходный запрос (тот, который инициировал PUSH) и копирует в запросы PUSH следующие заголовки: user-agent, accept, accept-encoding, accept-language, cache-control.

Все остальные заголовки игнорируются. Файлы cookie тоже не копируются. Отправка через PUSH ресурсов, для доступа к которым необходимы файлы cookie, работать не будет. Это может быть спорным решением. Но пока этот вопрос не будет более подробно обсуждён с разработчиками браузеров, лучше проявить осторожность и не передавать файлы cookie туда, где они обычно недоступны.

Ранние подсказки

Вместо отправки ресурсов через PUSH можно передавать клиенту заголовки Link ещё до того, как будет готов ответ. Эта возможность HTTP называется «ранние подсказки» и описана в RFC 8297.

Чтобы воспользоваться этой возможностью, нужно явно включить её на сервере с помощью

H2EarlyHints on

(По умолчанию эта возможность отключена, поскольку некоторые старые браузеры некорректно обрабатывали такие ответы.)

Если эта возможность включена, для отправки ранних подсказок и ресурсов через PUSH можно использовать директиву H2PushResource:

<Location /xxx.html>
    H2PushResource /xxx.css
    H2PushResource /xxx.js
</Location>

Это отправит клиенту ответ "103 Early Hints", как только сервер начнёт обрабатывать запрос. В зависимости от веб-приложения это может произойти значительно раньше, чем будут определены первые заголовки ответа.

Если включён параметр H2Push, PUSH также начнёт отправляться сразу после ответа 103. Однако если H2Push отключён, ответ 103 всё равно будет отправлен клиенту.

© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/howto/http2.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API