HTTP/2 в действии [Барри Поллард] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

HTTP/2 в действии

Издание предназначено для веб-разработчиков
и администраторов веб-сайтов.

«Отличная книга про новый
стандарт HTTP/2 для начинающих с подробными объяснениями и очень хорошими примерами».
Ален Куньо, STIB-MIVB
«Подробно описано наиболее
существенное изменение этого
протокола за два десятилетия.
Полезное чтиво для вебразработчиков».
Рональд Крэнстон, Sky UK
«Незаменимый ресурс для изучения всех нюансов HTTP/2
и того, как он повлияет
на дальнейшую разработку».
Том Маккерни,
«Прикладные информационные
науки»
«Самое простое объяснение
HTTP/2, которое я видел на сегодняшний день. Настоятельно
рекомендую».
Эдвин Квок, Red Soldier

Рассматриваемые темы:
• информация о HTTP/2 для веб-разработчиков;
• советы касаемо обновлений и устранения неполадок;
• примеры из реальной практики и тематические
исследования;
• информация о QUIC и HTTP/3.
Барри Поллард – профессионал с двадцатилетним опытом
разработки, поддержки и настройки программного обеспечения
и инфраструктуры.
ISBN 978-5-97060-920-0

Интернет-магазин: www.dmkpress.com
Оптовая продажа: КТК “Галактика”
books@alians-kniga.ru

www.дмк.рф

9 785970 609200

HTTP/2 в действии

HTTP (Hypertext Transfer Protocol – протокол
передачи гипертекста) – это стандарт для
обмена сообщениями между веб-сайтами
и браузерами. Спустя 20 лет он получил
крайне необходимое обновление. HTTP/2,
благодаря внедрению таких концепций, как
поддержка потоков, server push, сжатие заголовков и приоритизация, позволяет получить
преимущества в аспектах скорости, безопасности и эффективности.
Вы научитесь всему, что вам нужно знать
для эффективного использования HTTP/2.
Узнаете, как оптимизировать веб-производительность с помощью новых функций, таких
как фреймы, мультиплексирование и push.
Изучите реальные примеры по управлению
потоками и зависимостям.
Книга представляет собой практическое руководство с готовыми советами и передовыми
методами, которое обязательно поможет вам
быстро освоиться в мире HTTP/2!

HTTP/2
в действии

Барри Поллард

Барри Поллард

HTTP/2 в действии

HTTP/2 in Action

B A R RY P O L L A R D

HTTP/2 в действии

Б А Р Р И П О Л Л А РД

Москва, 2021

УДК 004.432
ББК 32.972.1
П26

П26

Поллард Б.
HTTP/2 в действии / пер. с анг. П. М. Бомбаковой. – М.: ДМК Пресс, 2021. –
424 с.: ил.
ISBN 978-5-97060-920-0
После изучения этой книги читатели приобретут четкое представление о том, что
представляют собой протокол HTTP/2 и связанные с ним технологии.
В части I обсуждается предыстория появления протокола и объясняется, в чем
состоят его преимущества перед HTTP/1.1. Рассматриваются способы переноса
веб-сайта на HTTP/2; приводятся инструкции по установке таких популярных вебсервисов, как Apache, nginx, IIS. Часть II описывает сам протокол и порядок установки HTTP/2-соединения, рассказывает об основном формате фреймов HTTP/2;
отдельная глава посвящена push-серверу HTTP/2, который является новой частью
протокола. Часть III содержит информацию о глубинных компонентах протокола, на
которые не могут повлиять ни веб-разработчики, ни администраторы веб-серверов,
и освещает различия между реализациями развертывания протокола HTTP/2. Наконец, в части IV обозначены перспективы развития протокола HTTP и способы его
дальнейшего улучшения.
Издание адресовано веб-разработчикам, администраторам веб-сайтов и тем, кто
интересуется интернет-технологиями, в частности оптимизацией веб-произ­во­ди­
тельности. В книге приведены ссылки, которые пригодятся читателям для дальнейшего изучения темы.

УДК 004.432
ББК 32.972.1
Original English language edition published by Manning Publications USA, USA. Russian-language
edition copyright © 2021 by DMK Press. All rights reserved.
Все права защищены. Любая часть этой книги не может быть воспроизведена в какой
бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

ISBN 978-1-6172-9516-4 (анг.)
ISBN 978-5-97060-920-0 (рус.)

© Manning Publications, 2019
© Оформление, издание, перевод, ДМК Пресс, 2021

В память о Ронане Рафферти (Ronan Rafferty) (1977–2018) –
веб-разработчике и друге

https://t.me/it_boooks

Оглавление
Часть I
1
2
3
Часть II
4
5
6
Часть III
7
8













Переход на HTTP/2..................................................................................24
Веб-технологии и HTTP..........................................................................25
Путь к HTTP/2..........................................................................................61
Переход на HTTP/2..................................................................................97
Использование HTTP/2......................................................................... 120
Основы протокола HTTP/2.................................................................... 121
Реализация HTTP/2 push....................................................................... 173
Оптимизация в HTTP/2......................................................................... 218
Продвинутый уровень использования HTTP/2................................... 262
Расширенные возможности HTTP/2.................................................... 263
Cжатие заголовков HPACK.................................................................... 289

Часть IV Будущее HTTP........................................................................................... 318
9  TCP, QUIC и HTTP/3................................................................................ 319
10  Дальнейшее развитие HTTP................................................................. 358

Содержание
Оглавление. .................................................................................................6
Предисловие..............................................................................................13
Благодарности...........................................................................................15
Об этой книге............................................................................................18
Об авторе....................................................................................................22
Об иллюстрации на обложке. ................................................................23

Часть I

1

ПЕРЕХОД НА HTTP/2.......................................................24

Веб-технологии и HTTP................................................................25
1.1

1.2
1.3

1.4
1.5

О том, как работает сеть...............................................................26

Internet и Всемирная паутина . ..............................................26
Что происходит, когда вы просматриваете
веб-страницы? .......................................................................27
Что такое HTTP?.............................................................................32
Синтаксическая структура HTTP и история его создания....39
1.3.1 HTTP/0.9..................................................................................39
1.3.2 HTTP/1.0..................................................................................40
1.3.3 HTTP/1.1..................................................................................47
Введение в HTTPS..........................................................................52
1.1.1
1.1.2

Инструменты для просмотра, отправки и получения
HTTP-сообщений. ..........................................................................56
1.5.1

Использование инструментов разработчика
в веб-браузерах........................................................................57
1.5.2 Отправка HTTP-запросов......................................................58
1.5.3 Другие инструменты для просмотра и отправки
HTTP-запросов........................................................................59
Резюме........................................................................................................60

Содержание

8

2

Путь к HTTP/2....................................................................................61

3

Переход на HTTP/2. ..........................................................................97

2.1

Основные проблемы с производительностью HTTP/1.1........65
Конвейеризация HTTP/1.1.......................................................67
Использование каскадных диаграмм для анализа
производительности..............................................................68
2.2 Пути решения проблем с производительностью. ..................70
2.2.1 Создание параллельных HTTP-соединений.............................71
2.2.2 Сокращение количества запросов..........................................74
2.2.3 Вывод.......................................................................................76
2.3 Другие проблемы HTTP/1.1. ........................................................76
2.4 Практические примеры................................................................77
2.4.1 Пример 1: amazon.com............................................................77
2.4.2 Пример 2: imgur.com...............................................................82
2.4.3 Насколько проблема серьезна?................................................83
2.5 Переход от HTTP/1.1 к HTTP/2. ...................................................84
2.5.1 SPDY........................................................................................85
2.5.2 HTTP/2.....................................................................................87
2.6 Значение HTTP/2 для веб-производительности.........................88
2.6.1 Пример предельной производительности HTTP/2.................88
2.6.2 Какой прирост производительности может обеспечить
HTTP/2?...................................................................................91
2.6.3 Обходные пути для HTTP/1.1 как потенциальные тупики....96
Резюме........................................................................................................96

3.1

2.1.1
2.1.2
2.1.3

Поддержка HTTP............................................................................97

Поддержка HTTP/2 cо стороны веб-браузера........................98
Поддержка HTTP/2 серверами.............................................. 104
Откат к предыдущим версиям, в случае если поддержка
HTTP/2 невозможна.............................................................. 106
3.2 Способы перехода вашего сайта на HTTP/2........................... 107
3.2.1 HTTP/2 на вашем веб-сервере............................................... 107
3.2.2 HTTP/2 с обратным прокси-сервером.................................. 110
3.2.3 HTTP/2 и CDN........................................................................ 113
3.2.4 Вывод по реализации HTTP/2................................................ 115
3.3 Устранение неполадок при настройке HTTP/2. .................... 115
Резюме...................................................................................................... 119

Часть II

4

HTTP/1.1 и современная Всемирная паутина. ........................62

3.1.1
3.1.2
3.1.3

ИСПОЛЬЗОВАНИЕ HTTP/2....................................... 120

Основы протокола HTTP/2....................................................... 121
4.1

Почему HTTP/2, а не HTTP/1.2?................................................. 121

4.1.1

Двоичный, а не текстовый................................................... 123

Содержание

9

Мультиплексирование вместо синхронности..................... 124
Приоритет потоков и управление ими............................... 128
Сжатие заголовков............................................................... 129
Server push............................................................................. 130
4.2 Как устанавливается HTTP/2-соединение.............................. 130
4.2.1 Использование HTTPS-рукопожатия................................... 131
4.2.2 HTTP-заголовок Upgrade........................................................ 138
4.2.3 Применение заранее известного протокола........................ 141
4.2.4 Протокол HTTP Alternative Services...................................... 142
4.2.5 Преамбула соединения HTTP/2............................................. 143
4.3 Фреймы HTTP/2............................................................................ 144
4.3.1 Просмотр фреймов HTTP/2.................................................. 144
4.3.2 Формат фреймов HTTP/2..................................................... 151
4.3.3 Исследование потока сообщений HTTP/2 на примерах........ 153
4.3.4 Дополнительные фреймы..................................................... 168
Резюме...................................................................................................... 172
4.1.2
4.1.3
4.1.4
4.1.5

5

Реализация HTTP/2 push............................................................. 173
5.1
5.2

Что такое HTTP/2 server push?................................................... 173
Как отправлять push-сообщения.............................................. 177

5.2.1

Отправка push-сообщений с помощью HTTP-заголовка
ссылки.................................................................................... 177
5.2.2 Просмотр ресурсов, отправленных с помощью
HTTP/2 push........................................................................... 180
5.2.3 Загрузка ресурсов посредством push из нисходящих
систем с помощью заголовков ссылок . ................................ 183
5.2.4 Предварительная push-загрузка ресурсов............................ 186
5.2.5 Другие способы push-загрузки............................................... 193
5.3 Как работает HTTP/2 push в браузере...................................... 195
5.3.1 Как работает кеш push........................................................ 196
5.3.2 Отказ от push с помощью RST_STREAM.............................. 199
5.4 Условная push-загрузка.............................................................. 200
5.4.1 Отслеживание push на стороне сервера.............................. 200
5.4.2 Условные HTTP-запросы....................................................... 201
5.4.3 Push-загрузка с помощью куки-файлов................................ 201
5.4.4 Дайджесты кеша................................................................... 202
5.5 К каким ресурсам применим HTTP/2 push............................ 204
5.5.1 К чему может быть применим push?................................... 204
5.5.2 К чему должен быть применим push?................................... 205
5.5.3 Автоматизация push-загрузки ........................................... 206
5.6 Решение проблем HTTP/2 push................................................. 207
5.7 Влияние HTTP/2 push на производительность. .................... 209
5.8 Push или предварительная загрузка?...................................... 211
5.9 Другие варианты использования HTTP/2 push..................... 214
Резюме...................................................................................................... 217

Содержание

10

6

Оптимизация в HTTP/2.............................................................. 218
6.1
6.2

6.3

Значение HTTP/2 для веб-разработчиков.............................. 219
Оптимизация HTTP/1.1 мешает HTTP/2?............................... 220
6.2.1
6.2.2
6.2.3
6.2.4
6.2.5
6.2.6
6.2.7

Запросы HTTP/2 по-прежнему затратны............................ 220
Возможности HTTP/2 не безграничны................................. 224
Для больших ресурсов эффективнее сжатие........................ 225
Ограничение пропускной способности и конкуренция
ресурсов................................................................................. 227
Сегментирование данных..................................................... 229
Встраивание ресурсов........................................................... 230
Заключение............................................................................ 230

Методы повышения веб-производительности все еще
актуальны для HTTP/2. ............................................................... 231

Уменьшение объема передаваемых данных........................... 232
Предотвращение повторной отправки данных
с помощью кеширования....................................................... 239
6.3.3 Снижение нагрузки на сеть посредством
сервис-воркеров..................................................................... 244
6.3.4 Не отправляйте то, что вам не нужно............................... 245
6.3.5 Подсказки для ресурсов HTTP............................................... 245
6.3.6 Сокращение задержки «последней мили».............................. 248
6.3.7 Оптимизация HTTPS............................................................ 248
6.3.8 Методы повышения веб-производительности,
не связанные с HTTP.............................................................. 252
6.4 Оптимизация и для HTTP/1.1, и для HTTP/2. ........................ 252
6.4.1 Измерение трафика HTTP/2................................................. 252
6.4.2 Отслеживание поддержки HTTP/2 на стороне сервера....... 254
6.4.3 Отслеживание поддержки HTTP/2 на стороне клиента..... 257
6.4.4 Объединение соединений....................................................... 258
6.4.5 Сколько времени занимает оптимизация
для пользователей HTTP/1.1................................................. 260
Резюме...................................................................................................... 261
6.3.1
6.3.2

Часть III

7

ПРОДВИНУТЫЙ УРОВЕНЬ
ИСПОЛЬЗОВАНИЯ HTTP/2. ..................................... 262

Расширенные возможности HTTP/2................................... 263
7.1
7.2

7.3

Состояния HTTP/2-потока. ........................................................ 264
Управление потоками информации........................................ 268

Пример управления потоками информации........................ 269
Настройка управления потоком информации
на сервере.............................................................................. 272
Приоритеты потоков................................................................... 273
7.3.1 Зависимости потоков.......................................................... 274
7.2.1
7.2.2

Содержание

11

Взвешивание потока............................................................. 277
Почему приоритизация – это так сложно?......................... 280
Приоритизация в веб-серверах и браузерах......................... 280
7.4 Проверка совместимости с HTTP/2.......................................... 285
7.4.1 Проверка совместимости сервера....................................... 285
7.4.2 Проверка совместимости клиента...................................... 287
Резюме...................................................................................................... 287
7.3.2
7.3.3
7.3.4

8

Cжатие заголовков HPACK....................................................... 289
8.1
8.2

Для чего нужно сжатие заголовков?........................................ 289
Как работает сжатие.................................................................... 291

Таблицы подстановки........................................................... 292
Более эффективные методы кодировки............................... 293
Ретроспективное сжатие.................................................... 295
8.3 Сжатие HTTP-тел. ........................................................................ 295
8.4 Сжатие заголовка HPACK для HTTP/2...................................... 297
8.4.1 Статическая таблица HPACK............................................. 298
8.4.2 Динамическая таблица HPACK............................................. 299
8.4.3 Типы заголовков HPACK........................................................ 300
8.4.4 Таблица кодировки Хаффмана.............................................. 305
8.4.5 Скрипт кодирования по Хаффману...................................... 306
8.4.6 Почему кодирование Хафманна подходит не во всех
случаях................................................................................... 308
8.5 Практические примеры сжатия HPACK.................................. 309
8.6 HPACK в реализациях клиента и сервера............................... 315
8.7 Ценность HPACK........................................................................... 316
Резюме...................................................................................................... 317

Часть IV

9

8.2.1
8.2.2
8.2.3

БУДУЩЕЕ HTTP................................................................... 318

TCP, QUIC и HTTP/3....................................................................... 319
9.1

9.2

HTTP и слабые стороны TCP...................................................... 320

Задержка предустановки HTTP/2......................................... 321
Неэффективность системы контроля перегрузки в TCP.... 323
Влияние слабых мест TCP на HTTP/2................................... 331
Оптимизация TCP................................................................. 336
Будущее TCP и HTTP.............................................................. 341
QUIC. ............................................................................................... 342
9.2.1 Преимущества QUIC в производительности....................... 344
9.2.2 Место QUIC в стеке Internet................................................. 344
9.2.3 Что такое UDP и почему он является основой QUIC........... 345
9.2.4 Стандартизация QUIC......................................................... 349
9.2.5 Различия между HTTP/2 и QUIC............................................ 351
9.2.6 Инструменты QUIC.............................................................. 354

9.1.1
9.1.2
9.1.3
9.1.4
9.1.5

Содержание

12

9.2.7 Реализации QUIC................................................................... 355
9.2.8 Стоит ли переходить на QUIC?........................................... 356
Резюме...................................................................................................... 356

10

Дальнейшее развитие HTTP. ................................................... 358
10.1 Споры о HTTP/2 и его недостатках.......................................... 359

Споры о SPDY........................................................................ 359
Проблемы конфиденциальности и состояния в HTTP......... 361
HTTP и шифрование.............................................................. 366
Проблемы транспортного протокола................................. 370
HTTP/2 слишком сложен....................................................... 374
HTTP/2 – временная мера..................................................... 375
10.2 HTTP/2 в реальном мире............................................................ 376
10.1.1
10.1.2
10.1.3
10.1.4
10.1.5
10.1.6

10.3 Будущие версии HTTP/2 и возможности HTTP/3
или HTTP/4. ................................................................................... 378

QUIC – это HTTP/3?.............................................................. 378
Дальнейшее развитие двоичного протокола HTTP.............. 378
Развитие HTTP над транспортным уровнем...................... 379
Какие расширения требуют создания новой версии
HTTP?.................................................................................... 382
10.3.5 Как могут быть представлены будущие версии HTTP......... 383
10.4 HTTP как базовый транспортный уровень. ........................... 383
10.4.1 Использование семантики и сообщений HTTP
для доставки внутреннего трафика.................................... 383
10.4.2 Использование концепции двоичного фрейминга HTTP/2.... 385
10.4.3 Использование HTTP для запуска другого протокола.......... 385
Резюме...................................................................................................... 390
10.3.1
10.3.2
10.3.3
10.3.4

Приложение. Обновление популярных веб-серверов
до HTTP/2....................................................................................................... 391
A.1

A.2

Обновление вашего веб-сервера для поддержки
HTTP/2. ........................................................................................... 391

Apache.................................................................................... 392
nginx....................................................................................... 407
Microsoft Internet Information Services (IIS)............................ 416
Другие серверы...................................................................... 417
Настройка HTTP/2 через обратный прокси-сервер. ............ 418
A.2.1 Apache.................................................................................... 418
A.2.2 nginx....................................................................................... 419

A.1.1
A.1.2
A.1.3
A.1.4

Предметный указатель........................................................................ 420

Предисловие
Я рано заинтересовался темой HTTP/2. Появление новой технологии
было интригующим. Она обещала почти неограниченный прирост производительности, потенциально устраняя необходимость в некоторых
запутанных обходных путях, которые вынуждены были использовать
веб-разработчики. Однако на практике все оказалось немного сложнее.
Я потратил некоторое время на то, чтобы выяснить, как применить ее
в моем сервере Apache. А затем я изо всех сил пытался объяснить увиденные мной результаты влияния на производительность. Ситуацию
осложняло отсутствие документации. Я сделал пару публикаций в своем
блоге о том, как применять эту технологию, и эти публикации пользовались популярностью. Вместе с этим я начал принимать участие в некоторых проектах, связанных с HTTP/2, на веб-сервисе GitHub, а также
просматривать соответствующие темы в системе Stack Overflow и помогать тем, у кого были проблемы, схожие с моими. Когда со мной связалось издательство Manning и предложило начать работу над книгой
о HTTP/2, я воспользовался этой возможностью. Я не участвовал в разработке этой технологии, однако я чувствовал, что могу помочь многим
веб-разработчикам, столкнувшимся с трудностями. Они, как и я, слышали об этой технологии, но не обладали достаточными знаниями, чтобы
применить ее.
За те полтора года, которые я писал эту книгу, технология HTTP/2
стала широко востребованной, и сейчас ее используют в разработке все
большего количества веб-сайтов. Некоторые проблемы развертывания
решались по мере обновления программного обеспечения. Я надеюсь,
что некоторые из проблем, описанных в этой книге, со временем уйдут
в прошлое. Я предполагаю, что нам потребуется еще несколько лет, чтобы полностью доработать HTTP/2.
Если вы научитесь использовать протокол HTTP/2, он обеспечит вам
мгновенный прирост производительности веб-приложений без необходимости настройки или детального понимания. Однако в этой жизни
ничего не дается даром, поэтому владельцам сайтов пойдет на пользу
глубокое понимание всех тонкостей и нюансов протокола и его развертывания. Сегодня оптимизация производительности веб-приложений

14

Предисловие

находится в центре внимания, и HTTP/2 является еще одним инструментом, который даст нам новые интересные методики и возможности как
сейчас, так и в будущем.
В сети доступно огромное количество информации для тех, у кого есть
время и желание искать, фильтровать и понимать ее. Весьма приятно
читать различные мнения и даже общаться непосредственно с разработчиками и исполнителями протоколов. Однако тема HTTP/2 очень обширна, и только объем и глубина книжного формата дают мне возможность
полностью объяснить технологию, затрагивая связанные с ней темы,
а также дать вам ссылки для дальнейшего изучения, если что-то вызовет
у вас интерес. Я надеюсь, что в этой книге достиг своей цели.

Благодарности
Прежде всего я хотел бы поблагодарить мою невероятно понимающую
жену Айне (Aine), которая провела последние полтора года, заботясь
о наших двух маленьких детях (ставших «тремя маленькими детьми»
во время написания этой книги!), в то время как я запирался в кабинете и яростно стучал по клавиатуре. Возможно, она была еще счастливее
чем я, когда узнала, что эту книгу наконец опубликовали! Особую благодарность стоит выразить также моим родственникам (семье Бакли) (the
Buckleys), которые помогали Айне развлекать наших детей и держать их
подальше от моего домашнего кабинета, чтобы я смог сосредоточиться.
Команда издательства Manning оказывала мне огромную поддержку
на протяжении всего процесса работы над книгой. В частности, я хотел
бы поблагодарить Брайана Сойера (Brian Sawyer), который первым связался со мной и предложил мне написать эту книгу. Я ценю его помощь
при подаче заявки на публикацию, благодаря чему книга была выбрана издателем. Кевин Харрелд (Kevin Harreld) проделал огромную работу
в качестве консультирующего редактора, аккуратно подталкивая меня
в правильном направлении и терпеливо отвечая на мои многочисленные вопросы. Томас Маккирни (Thomas McKearney) в роли технического
редактора обеспечивал потрясающее техническое руководство, а также
давал подробные отзывы по всем главам. Во время написания книги
Иван Мартинович (Ivan Martinovic) организовал три этапа проверки. Они
позволили получить обратную связь от руководства и узнать их мнение
о том, что написано хорошо, а что нуждается в улучшении. Также хочу
поблагодарить Матко Хрватина (Matko Hrvatin), который провел колоссальную работу по организации программы раннего доступа от Manning
(MEAP), помогающей получить обратную связь от реальных читателей.
Еще я хотел бы поблагодарить всю маркетинговую группу издательства
Manning, которая с самого начала помогала донести до читателей суть
книги, а особую благодарность выразить Кристоферу Кауфману (Christopher Kaufman), который терпел мои, казалось, бесконечные просьбы
о редактировании рекламного материала. Подготовка книги к выпуску
была непростой задачей, поэтому спасибо Винсенту Нордхаусу (Vincent
Nordhaus) за то, что он провел мой драгоценный результат через этот

16

Благодарности

процесс. Кэти Симпсон (Kathy Simpson) и Элисон Бренер (Alyson Brener)
внесли неизмеримый вклад и провели редактирование и корректуру
книги, сделав ее более читабельной. Перед ними стояла незавидная задача, так как я слишком часто задавал вопросы касаемо их поправок (гораздо лучших, чем моих!) относительно формулировки и грамматики.
Спасибо также другим корректорам, дизайнерам, верстальщикам и наборщикам, которые работали на заключительных этапах написания этой
книги. И хотя на обложке напечатано только мое имя, но все эти люди,
среди прочих, помогли оформить мой извилистый ход мыслей в профессиональную публикацию. Любые присутствующие в книге ошибки – это,
несомненно, только моя вина.
Я получил множество отзывов от людей за пределами издательства
Manning, в частности от обозревателей книжного рынка и рецензентов
рукописей (особенно спасибо тем из вас, кто принимал участие во всех
трех обзорах!) до читателей MEAP. Отдельно я хотел бы поблагодарить
Лукаса Пардью (Lucas Pardue) и Робина Маркса (Robin Marx), которые
кропотливо изучали рукопись и предоставляли мне ценные экспертные
знания о HTTP/2 на протяжении всего этого процесса. Среди других рецензентов хочу отметить такие имена, как Ален Кауньо (Alain Couniot),
Анто Аравинт (Anto Aravinth), Арт Бергквист (Art Bergquis), Камаль Какар
(Camal Caka), Дебмалья Джаш (Debmalya Jash), Эдвин Квок (Edwin Kwok),
Итан Риветт (Ethan Rivett), Эван Уоллес (Evan Wallace), Флорин-Габриэль Барбучану (Florin-Gabriel Barbuceanu), Джон Мэтьюз (John Matthews),
Джонатан Томс (Jonathan Thoms), Джошуа Хорвиц (Joshua Horwitz), Джастин Коулстон (Justin Coulston), Мэтт Деймел (Matt Deimel), Мэттью Фаруэлл (Matthew Farwell), Мэттью Халверсон (Matthew Halverson), Мортеза
Киади (Morteza Kiadi), Рональд Крэнстон (Ronald Cranston), Райан Берроуз (Ryan Burrows), Сандип Хурана (Sandeep Khurana), Симеон Лейзерзон
(Simeon Leyzerzon), Тайлер Коваллис (Tyler Kowallis) и Уэсли Бири (Wesley
Beary). Спасибо вам всем.
Говоря о технологиях, я должен поблагодарить сэра Тима БернерсаЛи (Sir Tim Berners-Lee) за то, что много лет назад он запустил всю эту
веб-систему, а также Майка Белша (Mike Belshe) и Роберта Пеона (Robert
Peon) за то, что вместе с Мартином Томпсоном (Martin Thompson), выступающим в качестве редактора, они изобрели SPDY, а затем оформили
его как стандарт HTTP/2. Разработка и оформление стандарта стали возможными только благодаря трудолюбивым добровольцам рабочей группы инженеров Internet (Internet Engineering Task Force, IETF) и, в частности, рабочей группы HTTP, возглавляемой Марком Ноттингемом (Mark
Nottingham) и Патриком МакМанусом (Patrick McManus). Без них всех,
а также без разрешения их работодателей уделять время разработке не
было бы HTTP/2 и, следовательно, не было бы необходимости в этой книге.
Я всегда удивлялся, как много времени и усилий технологическое сообщество вкладывает в волонтерскую работу. В процессе создания проектов с открытым исходным кодом, сайтов сообществ, таких как Stack
Overflow, GitHub и Twitter, различных блогов и презентаций многие люди

Благодарности

17

тратят большую часть своего времени без какого-либо вознаграждения,
оказывая помощь другим и расширяя свои собственные знания. Я благодарен тому, что являюсь частью этого сообщества, и очень горжусь
этим. Эта книга не была бы написана без изучения трудов экспертов по
веб-производительности Стива Саундерса (Steve Sounders), Йоава Вайса (Yoav Weis), Ильи Григорика (Ilya Grigorik), Пэта Мигана (Pat Meehan),
Джейка Арчибальда (Jake Archibald), Хумана Бехешти (Hooman Beheshti)
и Дэниела Стенберга (Daniel Stenberg), на которых я ссылался. Особую
благодарность я выражаю Стефану Эйссингу (Stefan Eissing), проделавшему огромную работу над реализацией Apache HTTP/2, которая изначально привлекла мой интерес, и Тацухиро Цудзикаве (Tatsuhiro Tsujikawa), создавшему базовую библиотеку Nghttp2, входящую в упомянутую
реализацию (наряду со многими другими разработками HTTP/2). Кроме
того, написание этой книги стало возможным благодаря тому, что многие инструменты, такие как WebPagetest, HTTP Archive, W3Techs, Draw.io,
TinyPng, nghttp2, curl, Apache, nginx и Let’s Encrypt в значительной части
находятся в свободном доступе. Я хотел бы выразить особую благодарность тем компаниям, которые разрешили использовать свои инструменты при работе над этой книгой.
Наконец, я хотел бы поблагодарить вас, читатели, за проявленный интерес к этой книге. Хотя выпускать книгу мне помогали многие люди, так
или иначе они делают это только ради таких людей, как вы, благодаря
которым книги живут и становятся достойными публикации. Я надеюсь,
что из этой книги вы извлечете ценные идеи и разберетесь в вопросах,
касающихся HTTP/2.

Об этой книге
Я написал эту книгу для того, чтобы доходчиво и на реальных примерах
объяснить читателю, как протокол работает на практике. Обычно специ­
фикации протоколов изложены довольно сухо и сложны для понимания,
поэтому цель данной книги – объяснить все детали на примерах, которые будут понятны каждому пользователю.

Кому следует прочесть эту книгу?
Эта книга создана для веб-разработчиков, администраторов веб-сайтов
и тех, кто просто заинтересован в Internet-технологиях. Книга призвана
полностью осветить тему HTTP/2 и все тонкости, связанные с протоколом. В блогах можно найти множество публикаций на эту тему, однако
большинство из них написано для профессионалов или детально освещает лишь какой-либо отдельный аспект. Данная книга ориентирована
на охват сразу всех аспектов функционирования протокола. Она должна
подготовить читателя и помочь ему разобраться в спецификации протокола. Также, если после прочтения этой книги читатель захочет изучить тему более детально, ему будет гораздо проще понять некоторые
сложные публикации в блогах. HTTP/2 был создан прежде всего для повышения производительности, поэтому любой, кто интересуется оптимизацией веб-производительности, обязательно извлечет из этой книги
полезную информацию и сделает для себя некоторые выводы. Кроме
того, в книге содержится множество ссылок, которые могут пригодиться
для дальнейшего изучения темы.

Как устроена эта книга
Книга состоит из 10 глав и разделена на 4 части.
В части I говорится о предыстории появления протокола, а также
о том, почему необходимо перейти на HTTP/2 и как это осуществить:
„„ в главе 1 дается предыстория, необходимая для полного понимания
книги. Такой подход дает возможность пользователям, обладающим
лишь базовыми знаниями, хорошо разобраться в теме;

Об этой книге

19

в главе 2 мы рассмотрим проблемы с HTTP/1.1 и причины, по которым
необходим был переход на HTTP/2;
„„ в главе 3 мы обсудим способы переноса вашего веб-сайта на HTTP/2
и некоторые сложности, связанные с этим процессом. Эта глава дополняется приложением, содержащим инструкции по установке таких популярных веб-сервисов, как Apache, nginx, и IIS.
Часть II содержит еще больше полезной информации. В ней мы рассмотрим непосредственно сам протокол и его значение для практик вебразработки:
„„ в главе 4 дается описание характеристик протокола HTTP/2, порядок
установки HTTP/2-соединения, а также рассказывается об основном
формате фреймов HTTP/2;
„„ глава 5 посвящена push-серверу HTTP/2, который является новой частью протокола. Он позволяет владельцам веб-сайтов отправлять
ресурсы до того, как их запросят браузеры;
„„ в главе 6 мы рассмотрим практическое значение HTTP/2 для вебразработки.
Часть III содержит информацию о глубинных компонентах протокола,
на которые не могут повлиять ни веб-разработчики, ни даже администраторы веб-серверов:
„„ в главе 7 говорится о других моментах, касающихся спецификации
HTTP/2, таких как состояние, управление потоком и механизм приоритетности. Кроме того, здесь мы рассмотрим различия между реализациями развертывания протокола HTTP/2;
„„ в главе 8 подробно рассматривается протокол HPACK, созданный
для сжатия заголовков HTTP в HTTP/2.
В части IV мы поговорим о перспективах дальнейшего развития протокола HTTP:
„„ глава 9 посвящена TCP, QUIC и HTTP/3. Технологии постоянно развиваются, и теперь, когда протокол HTTP/2 стал доступен, разработчики уже ищут способы его улучшить. В этой главе мы обсудим
недоработки в протоколе HTTP/2 и способы их устранения в его
преемнике – HTTP/3;
„„ в главе 10 рассмотрим другие способы улучшения HTTP помимо протокола HTTP/3, а также поразмышляем о проблемах, которые были
обнаружены во время разработки и оформления стандарта HTTP/2,
и о том, актуальны ли эти проблемы в реальной практике.
После изучения этой книги читатели приобретут четкое представление о том, что представляет из себя протокол HTTP/2 и связанные с ним
технологии. Также книга поможет лучше разобраться в оптимизации
веб-производительности. Кроме того, когда QUIC и HTTP/3 станут доступны широкой публике, люди, познакомившиеся с этой книгой, будут
готовы к работе с ними.
„„

Об этой книге

20

Примеры кода
Эта книга отличается от других книг технической направленности. Здесь
вы не увидите большого количества кода, потому что книга посвящена
именно протоколу, а не языку программирования. Книга нацелена на то,
чтобы познакомить вас с методиками высокого уровня, которые вы сможете применить к любому веб-серверу или языку программирования,
используемому для обслуживания страниц в сети. Однако в книге есть
несколько примеров на языках NodeJS и Perl, а также фрагменты конфигурации веб-сервера.
Для того чтобы отделить программный код и фрагменты конфигурации от обычного текста, в книге мы используем моноширинный шрифт.
В некоторых местах мы выделяем части кода жирным шрифтом, чтобы
показать изменения, произошедшие в результате предыдущих шагов,
описанных в главе, например когда к строке кода добавляется новая порция информации.
Все программные коды, используемые в примерах, доступны для загрузки с веб-сайтов издательства по адресу https://www.manning.com/
books/http2-in-action или GitHub по адресу https://github.com/bazzadp/
http2-in-action.

Онлайн-ресурсы
Остались вопросы?
„„ Официальная страница HTTP/2 доступна по адресу https://http2.
github.io/. На этой странице вы сможете найти информацию о спе­
ци­фикации HTTP/2 и HPACK, реализации HTTP/2 и ответы на часто
задаваемые вопросы.
„„ Официальная веб-страница рабочей группы HTTP доступна по
адресу https://httpwg.org/. Большая часть информации о работе группы находится в открытом доступе на веб-странице GitHub
https://github.com/httpwg/ и в списке рассылок (https://lists.w3.org/
Archives/Public/ietf-http-wg/).
„„ Вы можете найти дополнительную информацию по тегу HTTP/2
в Stack Overflow: https://stackoverflow.com/questions/tagged/http2.
Здесь же имеются ответы на вопросы от автора.

Отзывы и пожелания
Мы всегда рады отзывам наших читателей. Расскажите нам, что вы думаете об этой книге, – что понравилось или, может быть, не понравилось. Отзывы важны для нас, чтобы выпускать книги, которые будут для
вас максимально полезны.
Вы можете написать отзыв на нашем сайте www.dmkpress.com, зайдя
на страницу книги и оставив комментарий в разделе «Отзывы и рецензии». Также можно послать письмо главному редактору по адресу dmkpress@gmail.com; при этом укажите название книги в теме письма.

Об этой книге

21

Если вы являетесь экспертом в какой-либо области и заинтересованы
в написании новой книги, заполните форму на нашем сайте по адресу
http://dmkpress.com/authors/publish_book/ или напишите в издательство
по адресу dmkpress@gmail.com.

Скачивание исходного кода примеров
Скачать файлы с дополнительной информацией для книг издательства
«ДМК Пресс» можно на сайте www.dmkpress.com или www.дмк.рф на
странице с описанием соответствующей книги.

Список опечаток
Хотя мы приняли все возможные меры для того, чтобы обеспечить высокое качество наших текстов, ошибки все равно случаются. Если вы найдете ошибку в одной из наших книг, мы будем очень благодарны, если
вы сообщите о ней главному редактору по адресу dmkpress@gmail.com.
Сделав это, вы избавите других читателей от недопонимания и поможете нам улучшить последующие издания этой книги.

Нарушение авторских прав
Пиратство в интернете по-прежнему остается насущной проблемой. Издательства «ДМК Пресс» и Manning Publications очень серьезно относятся
к вопросам защиты авторских прав и лицензирования. Если вы столкнетесь в интернете с незаконной публикацией какой-либо из наших книг,
пожалуйста, пришлите нам ссылку на интернет-ресурс, чтобы мы могли
применить санкции.
Ссылку на подозрительные материалы можно прислать по адресу
элект­ронной почты dmkpress@gmail.com.
Мы высоко ценим любую помощь по защите наших авторов, благодаря которой мы можем предоставлять вам качественные материалы.

Об авторе
Барри Поллард (Barry Pollard) – профессиональный разработчик программного обеспечения. Почти 20 лет он занимается вопросами разработки и поддержки программного обеспечения и IT-инфраструктуры. Он
проявляет большой интерес к веб-технологиям, повышению производительности, безопасности и практическому использованию технологий.
Вы можете найти блог Барри Полларда на сайте https://www.tunetheweb.
com. Кроме того, он ведет Твиттер (@tunetheweb).

Об иллюстрации
на обложке
Иллюстрация на обложке книги называется «Одеяние русской торговки,
1768». Мы взяли ее из книги Томаса Джеффериса (Thomas Jefferys) A Collection of the Dresses of Different Nations, Ancient and Modern, опубликованной
в Лондоне в 1757–1772 гг. На титульном листе указано, что это гравюра,
выполненная вручную на медной пластине с использованием гуммиарабика. Томаса Джеффериса (1719–1771) называли «географом короля
Георга III». Он был ведущим английским картографом того времени.
Он гравировал и печатал карты для правительственных и других официальных органов, а также выпускал большое количество карт и атласов
для обычных граждан; особенно он известен своими картами Северной
Америки. Будучи картографом, он проявлял интерес к традиционной
одежде, что носили люди, жившие на территории, которую он исследовал и наносил на карты. Эти наряды блестяще представлены в его коллекции. Он увлекался дальними странами и темой путешествий и делал
это ради удовольствия. В конце XVIII века такой род занятий был относительно новым, и коллекции, подобные этой, были популярны, так как
они знакомили туристов и людей, которые никогда не путешествовали,
с культурой жителей других стран. Разнообразие иллюстраций в томах
Джеффериса говорит нам о том, насколько уникальны и индивидуальны были культуры народов мира около 200 лет назад. С тех пор манера
ношения одежды изменилась, а ее разнообразие в регионах и странах,
столь богатое в то время, исчезло. Сегодня зачастую бывает трудно отличить жителей одного континента от жителей другого. Если взглянуть на
это более оптимистично, возможно, мы предпочли культурному и визуальному разнообразию более насыщенную личную жизнь – или выбрали
вариант разнообразить свою жизнь за счет интеллектуального развития
или новых технологий. В наше время существует огромное количество
компьютерной литературы, среди которой бывает трудно отличить одну
книгу от другой. Издательство Manning особенно ценит изобретательность и инициативу в компьютерном бизнесе и старается отразить это
в дизайне книжных обложек. Издательство выбрало именно такую обложку, потому что в этой иллюстрации Джефферис возвратил к жизни
богатое разнообразие культур регионов двухвековой давности.

Часть I
Переход на HTTP/2

Ч

тобы понять, почему в области оптимизации производительности веб-приложений существует такой ажиотаж вокруг протокола HTTP/2, сначала вам необходимо узнать, зачем он нужен и какие проблемы он решает. Поэтому первая часть этой книги знакомит
с HTTP/1 читателей, которые не знают, что это такое и как это работает.
Затем мы объясним, почему было необходимо создание версии 2. Сначала поговорим о том, как работает HTTP/2 на более высоком уровне,
а разбор работы нижних уровней оставим на потом. Вместо этого в конце первой части мы расскажем о различных методах, которые вы можете
использовать для развертывания HTTP/2 на своем сайте.

1

Веб-технологии и HTTP

В этой главе мы рассмотрим:
как браузер загружает веб-страницу;
„„ что такое HTTP и как появился HTTP/1.1;
„„ основы HTTPS;
„„ основные инструменты HTTP.
„„

В этой главе говорится о том, как устроен веб в современном мире, а также дается объяснение некоторых ключевых концепций, которые помогут
вам лучше понять смысл последующих глав книги. Затем мы поговорим
о самом понятии HTTP и об истории версий протоколов, предшествующих ему. Мы полагаем, что многие читатели хотя бы немного знакомы
с большей частью информации, заключенной в этой главе. Однако мы
все же рекомендуем прочесть ее, чтобы освежить свои базовые знания.
ПРИМЕЧАНИЕ РЕДАКТОРА ПЕРЕВОДА Автор книги обращает
наше внимание на различие между Всемирной паутиной (World
Wide Web), представляющей собой вместилище общедоступного контента – сайтов, файлов, потоковых ресурсов и т. д., и сетью
передачи данных Internet, которая представляет собой маршрутизируемые каналы передачи данных и набор протоколов разного
уровня. Поскольку в этой книге речь идет именно о втором случае,
мы, как и автор, далее используем термин «Internet» в его техническом понимании, а интернетом называем только Всемирную
паутину.

26

1.1

Глава 1

Веб-технологии и HTTP

О том, как работает сеть
Internet стал неотъемлемой частью повседневной жизни. В нем мы совершаем покупки, производим банковские операции, а также общаемся
и развлекаемся. По мере развития интернета вещей (IoT – Internet of
Things) к сети подключается все больше и больше устройств, что обеспечивает нам возможность получения удаленного доступа к ним. Реализация такого доступа стала возможной благодаря разработке нескольких
технологий, в числе которых протокол передачи гипертекста (Hypertext
Transfer Protocol, HTTP). Данный протокол является ключевым методом запроса удаленного доступа к веб-приложениям и ресурсам. Большинство людей умеет выходить в Internet через веб-браузер. Однако не
каждый знает, как работает эта технология на самом деле. Многие люди
не имеют понятия о том, почему HTTP является основной частью веба
и почему следующая версия (HTTP/2) вызывает такой ажиотаж в вебсообществе.

1.1.1 Internet и Всемирная паутина
Многие люди считают, что понятия Internet и «Всемирная паутина» (или
просто «веб») являются синонимами, но на самом деле это два разных
термина, которые очень важно различать.
Internet представляет собой глобальную структуру объединенных
компьютерных сетей, использующих общий интернет-протокол(Internet Protocol, IP) для маршрутизации сообщений. Он состоит из множества сервисов, таких как веб, электронная почта, система предоставления общего доступа к файлам и интернет-телефония. Таким образом,
веб является лишь одной из систем, существующих в сети Internet, хотя
и самой заметной. Люди часто пользуются электронной почтой с помощью внешних веб-интерфейсов (например, Gmail, Hotmail и Yahoo!)
и поэтому ошибочно отождествляют понятия «веб» и Internet.
HTTP – это протокол, при помощи которого веб-браузер запрашивает
и получает веб-страницы с сервера. Это одна из трех основных технологий, созданных Тимом Бернерсом-Ли (Tim Berners-Lee), который также
является автором Всемирной паутины (worldwide web, WWW), уникальных идентификаторов ресурсов (ставших основой для унифицированных
указателей ресурсов или URL-адресов) и языка гипертекстовой разметки
(Hypertext Markup Language, HTML). Другие системы в Internet созданы
по своим собственным протоколам и стандартам, определяющим их работу, а также маршрутизацию их сообщений (например, маршрутизация
электронной почты происходит посредством протоколов SMTP, IMAP
и POP). HTTP связан именно с вебом, однако эта граница постепенно
размывается. Появление сервисов без традиционных клиентских вебинтерфейсов, основанных на HTTP, означает, что определить границы
Всемирной паутины становится все сложнее и сложнее! Такие сервисы
(известные под аббревиатурами REST и SOAP) могут быть применимы

О том, как работает сеть

27

как к веб-страницам, так и к другим ресурсам, размещенным в Internet
(например, к мобильным приложениям). Интернет вещей представляет
собой совокупность устройств, предоставляющих пользователям определенные функции. С ними могут взаимодействовать другие устройства
(компьютеры, мобильные приложения и даже другие устройства интернета вещей). В большинстве случаев связь между ними происходит посредством HTTP-запросов. Таким образом, посредством отправки HTTPсообщения вы можете включить или выключить лампочку, например,
с помощью приложения на своем мобильном телефоне.
Хотя в структуре Internet существует огромное количество сервисов,
большая их часть со временем используется все реже и реже, в то время
как количество пользователей Всемирной сети продолжает расти. Некоторые читатели вспомнят такие аббревиатуры, как BBS и IRC. Сегодня
они считаются устаревшими, а на их место пришли веб-форумы, социальные сети и мессенджеры.
Как уже было сказано, термин «Всемирная паутина» часто ошибочно отождествляется с понятием Internet. Однако непрерывное развитие Всемирной сети (а также специально созданного для нее протокола
HTTP) может означать, что очень скоро такое понимание окажется не
столь далеким от истины, как раньше.

1.1.2 Что происходит, когда вы просматриваете
веб-страницы?
Сейчас мы возвращаемся к первоначальному (и основному) назначению
HTTP: передаче веб-запросов. Когда вы открываете веб-сайт в своем любимом браузере на стационарном или портативном компьютере, планшете, мобильном телефоне или на любом другом из множества устройств,
обеспечивающих доступ в Internet, происходит очень много процессов.
Чтобы извлечь максимум пользы из этой книги, вам необходимо понять,
какие процессы происходят, когда мы загружаем веб-страницу.
Предположим, что вы запускаете браузер и переходите на сайт www.
google.com. В течение нескольких секунд произойдет процесс, показанный на рис. 1.1.
1 Браузер запросит реальный адрес www.google.com с сервера системы доменных имен (Domain Name System, DNS), который переведет понятное человеку имя www.google.com в удобный для машины IP-адрес.
Если представить, что IP-адрес – это телефонный номер, то DNS –
это телефонная книга. Этот IP-адрес будет являться либо адресом
более старого формата IPv4, который относительно понятен человеку (например, 216.58.192.4), либо адресом нового формата IPv6,
который могут обрабатывать только машины (например, 2607:
f8b0:4005:801:0:0:0:2004). Когда в городе заканчиваются свободные
телефонные номера, приходится менять телефонный код города.
Приблизительно по этой же причине введен протокол IPv6. Он не-

Глава 1

28

Веб-технологии и HTTP

обходим, чтобы справиться с ростом количества устройств, подключающихся к Internet сейчас и в будущем.
Сервер DNS

1. DNS
Браузер

Сетевые запросы браузер – веб-сервер

Веб-сервер

2. Открытие TCP-соединения

3. Запрос веб-страницы

4. Отправка главной страницы или перенаправление
5. О
 бработка
вернувшегося
запроса
6. Запрос других ресурсов (CSS, JavaScript, изображения)
7. Отображение
веб-страницы
8. Запрос других ресурсов (CSS, JavaScript, изображения)
9. Событие
Onload
10. Запрос других ресурсов (CSS, JavaScript, изображения)

Рис. 1.1 Типичное взаимодействие при просмотре веб-страницы

Имейте в виду, что по причине глобального характера сети Internet крупные компании часто имеют несколько серверов по всему
миру. Когда вы запрашиваете IP-адрес у DNS, он обычно предоставляет IP-адрес ближайшего сервера, благодаря чему вы получаете наиболее быстрый доступ к веб-страницам. Например, ответ на
запрос IP-адреса для www.google.com в Америке и в Европе будет
отличаться. Поэтому не волнуйтесь, если ваши значения IP-адресов
для www.google.com будут отличаться от тех, что вы найдете в нашей книге.

29

О том, как работает сеть

Где же версия IPv5?
Если Internet-протокол версии 4 (IPv4) был заменен версией 6 (IPv6), то где
же тогда версия 5? И почему вы никогда не слышали об IPv1 или IPv3?
Первые четыре бита в IP-пакете используются для хранения версии протокола. В теории максимально возможная версия – это версия 15. До того как
IPv4 начали использовать повсеместно, существовало четыре экспериментальных версии (версия 0 – версия 3). Однако ни для одной из них, кроме
версии 4, не было создано официального стандартаa. Затем создали версию 5,
которая предназначалась для протокола реального времени Internet Stream
Protocol, обеспечивающего потоковую передачу аудио и видео в реальном
времени, подобно современной IP-телефонии (Voice over IP, VoIP). Однако
версия IPv5 так и не стала популярной, так как имела те же ограничения
адресного пространства, что и версия 4. Когда появилась версия 6, работа
над IPv5 была остановлена, и версия 6 стала преемником IPv4. Судя по всему,
изначально IPv6 была названа версией 7, потому что многие ошибочно полагали, что версия 6 уже использованаb. Номера 7, 8 и 9 также применялись
для нумерации версий, но эти версии больше не используются. Если когданибудь и появится преемник IPv6, то это, скорее всего, будет IPv10 или более
поздняя версия. В связи с этим, несомненно, возникнут вопросы, подобные
тем, с которых начинается это примечание!
___________________________________
a
 Стандарт протокола https://tools.ietf.org/html/rfc760 позже был обновлен и заменен на https://tools.ietf.org/html/rfc791.
b
 Обратите внимание на информацию в публикации https://archive.is/QqU73#selec­
tion-417.1-417.15.
1

2 Через веб-браузер ваш компьютер устанавливает TCP-соединение
2

по IP-адресу через стандартный сетевой порт (порт 80) или стандартный защищенный сетевой порт (порт 443).
Передача информационных потоков в Internet осуществляется
с помощью протокола IP. Протокол TCP обеспечивает надежность
при передаче данных, а также возможность повторной передачи
(«Здравствуйте, вы все поняли?», «Нет, не могли бы вы повторить
последний бит, пожалуйста?»). Эти две технологии часто используются вместе, поэтому их называют TCP/IP. Именно на основе этих
протоколов создана большая часть сервисов в Internet.
Один сервер может использоваться несколькими службами (например, электронная почта, FTP, HTTP и HTTPS [HTTP Secure]серверы). Порт позволяет различным службам размещаться под

1

2

Google начал экспериментировать с протоколом QUIC, поэтому, если вы подключаетесь к сайту Google из Chrome, то может быть задействована эта технология. Я расскажу про QUIC в главе 9.
Некоторые веб-сайты, в том числе Google, используют технологию HSTS для
автоматической активации защищенного HTTP-соединения (HTTPS) через
порт 443, поэтому, даже если вы попытаетесь подключиться через HTTP, соединение автоматически обновится до HTTPS до отправки запроса.

30

Глава 1

Веб-технологии и HTTP

одним IP-адресом, так же как, например, в компании может быть
добавочный номер телефона для каждого сотрудника.
3 После того как браузер подключился к веб-серверу, он начинает отправлять запросы веб-сайту. На этом этапе в дело вступает протокол HTTP. Однако мы рассмотрим порядок его работы в следующем
разделе. А сейчас сосредоточимся на работе браузера и рассмотрим
пример, где с помощью HTTP он запрашивает у сервера Google домашнюю страницу.
ПРИМЕЧАНИЕ На этом этапе ваш браузер автоматически исправляет сокращенный адрес (www.google.com) на синтаксически
верный URL-адрес (http://www.google.com). В абсолютном полном URL-адресе указывается номер порта (например, http://www.
google.com:80). Если используются стандартные порты (80 для
HTTP и 443 для HTTPS), то веб-браузер скроет их номера. Если используются нестандартные порты, их номера будут отображаться
в URL. Например, некоторые системы, особенно в среде разработки, используют порт 8080 для HTTP или 8443 для HTTPS.
Если используется протокол HTTPS (более подробно мы рассмот­
рим его в разделе 1.4), то для настройки шифрования, защищающего соединение, требуются дополнительные шаги.
4 Сервер Google отвечает вне зависимости от типа URL-адреса, который вы запрашиваете. Как правило, ответ приходит в виде текста
веб-страницы в формате HTML. HTML – это стандартизированная,
структурированная система разметки текстового содержимого
веб-страниц. Документ на языке HTML обычно представляет собой набор элементов, начало и конец которых определяется HTMLтегами и ссылками на блоки другой информации, необходимой для
создания мультимедийных веб-страниц, которые вы привыкли видеть (каскадные таблицы стилей [CSS], код Java­Script, изображения,
шрифты и т. д.).
Однако иногда вместо HTML-страницы ответом может быть перенаправление на верный адрес. Например, Google работает только
на HTTPS, поэтому, если вы запросите URL http://www.google.com,
ответом будет специальная HTTP-инструкция (обычно код такого ответа 301 или 302), которая перенаправит вас на новый адрес
https://www.google.com. Такой ответ запускает процессы некоторых или всех предыдущих шагов снова, в зависимости от того, как
выглядит адрес перенаправления. Он может иметь другой порт на
другом сервере, другой порт на том же сервере (например, перенаправление на HTTPS) или это может быть другая страница на том
же сервере и порте.
При возникновении ошибки вы увидите на экране ответ в виде
HTTP-кода, самым известным из которых является 404 Not Found.
5 Затем браузер обрабатывает возвращенный запрос. Браузер рассчитан на ответы в формате HTML, поэтому он начинает анализи-

О том, как работает сеть

31

ровать HTML-код и строит в памяти объектную модель документа (document object model, DOM), которая отображает внутреннее
строение страницы. Скорее всего, в процессе анализа веб-браузер
найдет и другие ресурсы, необходимые для правильного отображения страницы (например, CSS, Java­Script и изображения).
6 Веб-браузер запросит необходимые ему дополнительные ресурсы.
Веб-страницы Google используют небольшое количество ресурсов.
На момент написания этой книги их требуется всего 16. Каждый из
этих ресурсов запрашивается аналогичным образом, следуя шагам 1–6, так как эти ресурсы в свою очередь могут запрашивать
другие ресурсы. Как правило, страницы обычных веб-сайтов более
разнообразны, чем страницы Google. Такие веб-сайты нуждаются
в 75 ресурсах1 и зачастую из разных доменов, поэтому шаги 1–6
должны быть выполнены для каждого ресурса. Именно из-за таких
ситуаций и замедляется работа браузера. Протокол HTTP/2 помогает оптимизировать запрос дополнительных ресурсов. Об этом мы
поговорим в следующих главах.
7 Когда браузер получает достаточно критически необходимых ресурсов, он начинает отображать страницу на экране. Старт рендеринга веб-страницы является не таким простым процессом, как
кажется. Браузеру требуется много времени для загрузки ресурсов,
необходимых для отображения веб-страницы. Поэтому соединение
становится еще более медленным и не дает ожидаемых пользователем результатов. Если веб-браузер начнет отображать страницу
слишком рано, она будет «прыгать» по мере загрузки большего количества контента. Особенно раздражает, когда страница начинает
«прыгать» после того, как вы прочитали уже половину статьи. Четкое понимание технологий, на которых основан веб, особенно HTTP
и HTML/CSS/Java­Script, помогает владельцам веб-сайтов убрать эти
раздражающие скачки и оптимизировать загрузку страниц, однако
многие из них все еще этого не делают.
8 После отображения страницы веб-браузер продолжает в фоновом
режиме загружать другие ресурсы, необходимые странице, и обновляет страницу по мере их обработки. Он загружает второстепенные элементы, такие как изображения и скрипты отслеживания рекламы. Именно поэтому бывает так, что, когда вы загружаете
веб-страницу, на ней какое-то время не отображаются изображения (особенно при медленном соединении), а затем, по мере их загрузки, они появляются.
9 Когда страница полностью загружена, браузер останавливает
значок загрузки (в большинстве браузеров вращающийся значок
в адресной строке или рядом с ней) и запускает событие OnLoad Java­
Script, которое является маркером для Java­Script и означает, что
страница готова к работе.
1

https://httparchive.org/reports/page-weight#reqTotal.

Глава 1

32

Веб-технологии и HTTP

10 Времена,

когда веб-страница была статична, уже давно прошли,
поэтому браузер продолжает отправлять запросы, даже когда она
полностью загружена. Сегодня многие веб-страницы являются
многофункциональными приложениями, взаимодействующими
с различными серверами в Internet для отправки или загрузки дополнительного контента. Отображение контента может быть инициировано действиями пользователя. Например, вы вводите запросы в строку поиска на домашней странице Google и мгновенно
видите предложенные варианты запроса без нажатия кнопки поиска. Также это могут быть действия, управляемые приложением,
например автоматическое обновление ленты Facebook или Twitter
без нажатия кнопки обновления. Эти действия часто происходят
в фоновом режиме. Они скрыты от вас. В качестве примера можно
привести рекламные и аналитические скрипты, которые отслеживают ваши действия на сайте, для того чтобы сообщать аналитические данные владельцам веб-сайтов и/или рекламным сетям.
Как видите, после того как вы вводите URL-адрес, происходит огромное количество процессов, и в большинстве случаев они протекают
очень быстро. Описание любого из этих шагов могло бы лечь в основу
целой книги с различными вариациями при определенных обстоятельствах. В нашей книге мы делаем упор на шаги 3–8 (загрузка веб-сайта
с помощью HTTP). Также в некоторых главах (в частности, в главе 9)
рассказывается о шаге 2 (базовое сетевое соединение, используемое
HTTP).

1.2

Что такое HTTP?
Предыдущий раздел освещает детали протокола HTTP, благодаря чему
вы имеете представление о его функционировании в контексте всего Internet. В этом разделе мы кратко опишем, как данный протокол функционирует и где он используется.
Как уже было сказано, HTTP расшифровывается как «протокол передачи гипертекста». Как следует из его названия, HTTP изначально предназначался исключительно для передачи гипертекстовых документов
(документов, содержащих ссылки на другие документы). Первая версия
не могла передать ничего, кроме таких документов. Название «протокол
передачи гипертекста» не совсем актуально, так как разработчики быстро поняли, что протокол может быть использован для передачи других
типов файлов (например, изображений). Однако, сейчас HTTP настолько
распространен, что переименовывать его уже слишком поздно.
Сетевая модель TCP/IP, построенная на одном из типов физического
соединения (Ethernet, Wi-FI и т. д.), обеспечивает надежное сетевое соединение, необходимое для функционирования HTTP. Структура протоколов передачи данных разделена на уровни, каждый из которых отвечает за определенный процесс. HTTP не имеет отношения к установке
сетевого соединения. Несмотря на то что HTTP-приложения должны

Что такое HTTP?

33

уметь справляться с сетевыми сбоями или отключениями, сам протокол
не предполагает решения этих задач.
Модель взаимодействия открытых систем (Open Systems Interconnection, OSI) – это многоуровневая модель сетевых протоколов. Она состоит
из 7 уровней, хотя они соотносятся с сетями, и особенно с Internet-тра­
фи­ком, с некоторой долей условности. TCP (Transmission Control Protocol)
является рабочим протоколом как минимум двух уровней, а возможно,
и трех (это зависит от того, как вы определяете эти уровни). На рис. 1.2
вы можете увидеть, как данная модель соотносится с Internet-тра­фи­ком
и как в нее вписывается протокол HTTP.

7

Прикладной уровень
(Hypertext Transport Protocol – HTTP)

6

Уровень представления
(такие форматы файла, как ASCII, UTF-8, JPG, PNG... и т. д.)

5

Сеансовый уровень
(Secure Sockets Layer – SSL / Transport Layer Security – TLS)

4

Транспортный и cеансовый уровни
(Transmission Control Protocol – TCP)

3

Сетевой уровень
(Internet Protocol – IP)

2

Канальный уровень (к примеру, Ethernet)

1

Физический уровень
(сетевой кабель / Wi-Fi / мобильная связь)

Рис. 1.2 Транспортные уровни Internet-трафика

К слову, сегодня до сих пор ведутся споры о точном определении уровней в модели. В Internet, как и в других сложных системах, не все поддается классификации, и разграничить что-либо бывает не так легко, как
этого хотелось бы разработчикам. На самом деле, согласно мнению Ин-

Глава 1

34

Веб-технологии и HTTP

женерного совета интернета, не обязательно делать слишком большой
упор на деление модели по уровням1. Однако на более высоком уровне такой подход может помочь понять, как в модель вписывается HTTP,
а также как она зависит от других протоколов. Многие веб-приложения
используют HTTP, поэтому в таких случаях прикладной уровень может
больше относиться к сетевому уровню, чем к приложениям Java­Script.
По сути, HTTP является протоколом запроса и ответа. Браузер совершает запрос к веб-серверу, используя синтаксис HTTP. Веб-сервер в свою
очередь отвечает сообщением, содержащим запрошенный ресурс. HTTP
широко распространен именно благодаря своей простоте. Однако, как
вы увидите в последующих главах, HTTP/2 немного сложнее, чем HTTP,
но в то же время он и более эффективен.
После того как открывается соединение, базовый синтаксис HTTP-за­
проса выглядит следующим образом:
GET /page.html↵,
где символ ↵ означает возврат каретки / начало новой строки (клавиша
Enter или Return). HTTP очень прост в своей базовой форме! Вы указываете один из нескольких HTTP-методов (в данном случае GET), за которым следует нужный вам ресурс (/page.html). Помните, что на этом этапе
вы уже подключились к соответствующему серверу посредством TCP/
IP, поэтому просто запрашиваете нужный ресурс с этого сервера. Вас не
должно беспокоить то, как работает и чем управляется это соединение.
Первая версия HTTP (0.9) поддерживала только базовый синтаксис
и могла использовать только метод GET. В данном случае вам может быть
интересно, зачем использовали метод GET для запроса HTTP 0/9. Здесь
он кажется неуместным, но этот и другие методы появились в последующих версиях HTTP, так что спасибо изобретателям HTTP за то, что они
предвидели их появление. В следующем разделе мы обсудим различные
версии протокола HTTP, однако синтаксис по-прежнему соответствует
формату запроса HTTP GET.
Рассмотрим пример из реальной жизни. Для получения HTTP-за­про­
сов веб-серверу требуется только соединение TCP/IP, и вы можете эмулировать работу браузера с помощью протокола Telnet. Это простой
протокол, который реализует соединение с сервером с помощью TCP/IP
и позволяет вводить текстовые команды, а также просматривать текс­
товые ответы. Данный протокол необходим при использовании HTTP,
но ближе к концу главы мы рассмотрим и другие, более подходящие
инструменты для просмотра HTTP-страниц. К сожалению, некоторые
технологии теряют свою популярность, и Telnet является одной из них;
многие операционные системы больше не включают в себя клиент Telnet по умолчанию. Возможно, вам потребуется установить клиент Telnet,
чтобы попробовать реализовать некоторые простые команды HTTP, или
же вы можете использовать в качестве эквивалента утилиту nc (netcat),
которая уже установлена в большинстве Linux-подобных систем, вклю1

https://tools.ietf.org/html/rfc3439#section-3.

35

Что такое HTTP?

чая macOS. Для рассмотрения простых примеров, которые представлены
в нашей книге, она подойдет почти также, как и Telnet.
Для Windows лучше использовать утилиту PuTTY1 вместо клиента, поставляемого в комплекте с Windows по умолчанию (который обычно не
устанавливается и должен добавляться вручную). Клиент, установленный
по умолчанию, имеет проблемы с отображением: например, он может не
отображать то, что вы печатаете, или записывать новую информацию
поверх уже имеющейся. После установки и запуска PuTTY вы увидите
окно конфигурации, в котором вы можете ввести хост (www.google.com),
порт (80) и тип подключения (Telnet). Убедитесь, что вы выбрали опцию
Never (Никогда) для закрытия окна после окончания сеанса; в противном случае вы не увидите результатов. Все эти настройки вы можете
наблюдать на рис. 1.3. Также обратите внимание на то, что вы можете
изменить режим Telnet Negotiation на пассивный (Connections > Telnet
> Telnet Negotiation) в случае, если у вас возникли проблемы с вводом
любой из следующих команд и вы получаете сообщения о неправильном
формате запроса.

Введите в строке
«Host Name» адрес
www.google.com
Затем 80 в строке
«Port»
В «Connection type»
выберите Telnet

Установите значение
Never в группе «Close
window on exit»

Рис. 1.3

Настройки PuTTY для соединения с Google

Если вы используете компьютер Apple Macintosh или ОС Linux, вы можете выполнить команду Telnet непосредственно из командной строки
оболочки, если Telnet уже установлен:
$ telnet www.google.com 80

1

https://www.putty.org/.

Глава 1

36

Веб-технологии и HTTP

или, как я уже упоминал ранее, используйте команду nc таким же образом:
$ nc www.google.com 80

Когда вы открываете сеанс Telnet и устанавливаете соединение, вы
видите пустой экран или, в зависимости от вашего приложения Telnet,
некоторые команды, такие как:
Trying 216.58.193.68...
Connected to www.google.com.
Escape character is '^]'.

Независимо от того, отображается это сообщение или нет, вы должны
иметь возможность вводить свои HTTP-команды, поэтому введите GET/,
а затем нажмите клавишу ввода, которая сообщает серверу Google, что
вы ищете страницу по умолчанию (/) и (поскольку вы не указали версию
HTTP) хотите использовать HTTP/0.9. Обратите внимание, что некоторые клиенты Telnet не повторяют то, что вы печатаете по умолчанию
(особенно клиент Telnet по умолчанию в комплекте Windows, как я уже
упоминал ранее), поэтому может быть трудно точно увидеть, что вы печатаете. Но вы все равно должны отправлять команды.

Использование Telnet через прокси-сервер организации
Если у вашего компьютера нет прямого доступа в Internet, вы не сможете
подключиться к Google напрямую с помощью Telnet. Этот сценарий часто
встречается в корпоративных сетях, где для ограничения прямого доступа
используется прокси (мы поговорим о прокси-серверах в главе 3). В этом
случае вы можете использовать в качестве примера один из ваших внутренних веб-серверов (например, ваш сайт во внутренней сети), а не Google.
В разделе 1.5.3 мы рассмотрим другие инструменты, которые могут работать
с прокси, но пока стоит сосредоточиться на Telnet.

Скорее всего, сервер Google ответит, используя HTTP/1.0, несмотря на
то, что вы запросили HTTP/0.9 по умолчанию (так как ни один сервер
больше не использует HTTP/0.9). В ответ появится код ответа 200 (если
команда была выполнена) или 302 (если сервер хочет перенаправить вас
в другое место). Затем соединение закроется. Более подробно я расскажу
об этом процессе в следующем разделе, поэтому не стоит сейчас зацик­
ливаться на этих деталях.
Ниже приведен один из некоторых ответов из командной строки на
сервере Linux, выделенный жирным шрифтом. Стоит отметить, что возвращаемый HTML-контент не отображается полностью для краткости:
$ telnet www.google.com 80
Trying 172.217.3.196...
Connected to www.google.com.
Escape character is '^]'.
GET /
HTTP/1.0 200 OK

Что такое HTTP?

37

Date: Sun, 10 Sep 2017 16:20:09 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See
https://www.google.com/support/accounts/answer/151657?hl=en for more info.
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Set-Cookie:
NID=111=QIMb1TZHhHGXEPjUXqbHChZGCcVLFQOvmqjNcUIejUXqbHChZKtrF4Hf4x4DVjTb01R
8DWShPlu6_aQ-AnPXgONzEoGOpapm_VOTW0Y8TWVpNap_1234567890-p2g; expires=Mon,
12-Mar-2018 16:20:09 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
#U▒p▒I▒7E T▒▒~n▒EG▒I▒^▒▒
+.`x▒\w ▒▒CR ▒▒▒U3V▒▒O>6b▒y8▒S▒▒cHj .▒▒▒F▒▒4=xw
(▒F▒Bc▒]Zu▒ ▒~Hj▒i▒▒▒R▒▒G▒mH▒.|▒▒▒ Chrome > Параметры командной строки). В Firefox есть аналогичные опцииа. Кроме того, вы
можете тестировать производительность и на своих собственных веб-сайтах.
___________________________________
а
https://www.webpagetest.org/forums/showthread.php?tid=14162.

2.4.1 Пример 1: amazon.com
Если до этого мы разбирали теорию, то сейчас пришло время перейти
к практике. Если сделать тест www.amazon.com на www.webpagetest.org,
получится каскадная диаграмма как на рис. 2.10. На нем мы можем увидеть многие проблемы HTTP/1.1.

Глава 2

78

Путь к HTTP/2

Рис. 2.10 Фрагмент результатов для amazon.com
„„

Первый запрос – запрос домашней страницы. На рис. 2.11 мы можем рассмотреть его ближе.
Отправка одного запроса, поиска DNS, запуск подключения и согласование SSL/TLS HTTPS – все эти действия отнимают время. По
отдельности процессы занимают не так уж и много времени (чуть
больше 0,1 с, согласно рис. 2.11), но, когда затраченное время складывается, задержка становится ощутимой. И при выполнении первого запроса с этим нечего не поделаешь. Как было сказано в главе 1,
это неотъемлемая часть того, как работает Internet. Вы можете оптимизировать HTTPS и сократить задержку при согласовании с SSL,
но это в любом случае не коснется первого запроса. Лучшее, что вы
можете сделать, – это убедиться в том, что ваши серверы быстро реагируют на запросы, и – в идеале – находятся рядом с пользователями. Таким образом, вы сможете сократить время приема–передачи
настолько, насколько это возможно. В следующей главе мы обсудим
сети доставки контента (Content Delivery Network, CDN), которые
помогают решить вышеописанную проблему.

Рис. 2.11 Первый запрос домашней страницы

Практические примеры

79

После запуска на диаграмме мы видим небольшую паузу, вероятно, произошедшую из-за неточного тайминга или проблем
брау­зера Chrome (при выполнении теста в браузере Firefox пауза
отсутствовала). Далее выполняется первый HTTP-запрос (процесс
обозначен светлым оттенком), а затем загружается, анализируется
и обрабатывается HTML-код (процесс обозначен оттенком темнее).
„„ HTML содержит ссылки на несколько загружаемых CSS-файлов, как
показано на рис. 2.12.

Рис. 2.12 Пять запросов CSS-файлов

Данные файлы размещены на другом домене (images-na.ssl-imagesamazon.com), который был отделен от основного домена для
улучшения производительности. В связи с этим, прежде чем использовать этот домен для загрузки CSS, для второго запроса вам
следует выполнить поиск нового DNS, использовать новое сетевое
соединение и заново согласовать HTTPS. Доменное разделение
применяется для решения проблем с производительностью, и если
задержка для настройки первого запроса неизбежна, то время на
организацию второго запроса тратится напрасно. Также обратите
внимание, что CSS-файл появляется еще на ранней стадии обработки HTML-страницы первого запроса, в результате чего второй
запрос начинается немного раньше отметки 0,4 с, несмотря на то
что HTML-страница загружается не раньше чем через 0,5 с. Браузер
не дождался загрузки и полной обработки HTML-страницы; вместо
этого, как только он заметил указанный домен, он запросил дополнительное HTTP-соединение (несмотря на то что сам ресурс не начал загружаться до тех пор, пока браузер не получил весь HTML-код
ввиду задержки установки соединения).
„„ Затем идет запрос еще одного CSS-файла в том же отделенном домене. Ввиду того, что HTTP/1.1 не позволяет выполнять два запроса
одновременно, браузер создает еще одно соединение. Теперь вам
не нужно искать DNS, потому что вы уже знаете IP-адрес данного
домена из запроса 2. Однако, прежде чем запросить CSS, вам все
же нужно установить TCP/IP-соединение и согласовать HTTPS, на
что снова тратится много времени. Напомню, что дополнительное
соединение создается в целях решения проблем с производительностью HTTP/1.1.
„„

Глава 2

80

Путь к HTTP/2

Затем браузер запросит еще три CSS-файла, которые загрузятся
через два уже установленных соединения. На диаграмме не видно,
почему браузер не запросил эти файлы сразу – для этого пришлось
бы создать еще несколько дополнительных соединений, что привело бы к соответствующим затратам. При просмотре исходного кода
страниц Amazon выяснилось, что перед запросом данных файлов
стоит тег , который блокирует последующие запросы, до тех
пор пока сценарий не будет полностью обработан. Именно поэтому
запросы 4, 5 и 6 не могут выполняться одновременно с запросами 2
и 3. К этой теме мы вернемся несколько позже. Несмотря на то что
неэффективность HTTP/1.1 является серьезной проблемой и решается она путем улучшений протокола HTTP (например, HTTP/2), это
далеко не единственная причина низкой производительности веба.
„„ После обработки CSS-файлов из запросов 2–6 загружаются изображения. Браузер начинает их загрузку, как показано на рис. 2.13.

„„

Рис. 2.13

Загрузка изображений

Первый файл .png в запросе 7 представляет собой спрайт-файл, содержащий несколько изображений (как на рис. 2.13) и реализующий
нескольких других хитростей по повышению производительности
от Amazon. В следующих запросах загружается несколько файлов
формата .jpg.
„„ В процессе осуществления запросов изображений браузеру приходится установить более ресурсоемкие соединения. Это нужно для
того, чтобы другие файлы загружались параллельно в запросах 9, 10,
11 и 15 и в запросах 14, 17, 18 и 19 для новых доменов.
„„ В некоторых случаях (в запросах 9, 10 и 11) браузер предвидел, что
потребуется больше соединений, и настроил их заранее. Именно
поэтому установка соединения и настройка SSL выполняются раньше, ввиду чего браузер может запрашивать изображения в запросах 7 и 8.
„„ Amazon добавил в оптимизацию производительности предвыборку DNS1 для m.mediaamazon.com. Как ни странно, для fls-na.amazon.
com этого не сделали. По этой причине в запросе 17 поиск DNS про„„

1

https://css-tricks.com/prefetching-preloading-prebrowsing/.

Практические примеры

81

исходит на отметке 0,6 с, т. е. заранее. В главе 6 мы поговорим об
этом поподробнее.
После выполнения всех вышеперечисленных запросов загрузка все
еще продолжается, но даже на примере первых нескольких запросов
видно, какие проблемы существуют у HTTP/1.1.
Организация большого количества соединений нужна для предотвращения очередей. Время, затраченное на это, очень часто удваивает
время, необходимое для загрузки ресурса. WebPageTest организует данные соединений в удобном для восприятия формате1 (как показано на
рис. 2.14).

Рис. 2.14

Визуализация загрузки amazon.com

Как видите, для загрузки основного контента amazon.com приходится
установить 20 подключений, а для загрузки рекламных ресурсов к ним
добавляется еще 28 (процесс не показан на рис. 2.14). С помощью первых 6 соединений images-na.sslimages-amazon.com загружается большое количество ресурсов (соединения 3–8), а остальные соединения (соединения 9–12, а также 15–20) загружают по одному или два ресурса. Таким
образом, затраты большого количества времени на установку последних
соединений нерациональны.
Причина, по которой images-na.sslimages-amazon.com устанавливает эти
соединения (и по которой Chrome зачастую устанавливает более 6 подключений на домен), довольно интересна и требует небольшого исследования. Запросы можно отправлять как с идентификационными данными (обычно это файлы куки), так и без них (тогда Chrome обработает их
через отдельные соединения). По соображениям безопасности, ввиду
того, что запросы из разных источников обрабатываются в браузерах

1

https://www.webpagetest.org/result/170820_NR_53c5bf9ca1e67301a933947d80a3
2a53/1/details/#connectionView_fv_1.

Глава 2

82

Путь к HTTP/2

по-разному1, при запросах файлов Java­Script, не содержащих идентификационных данных, Amazon использует метод setAttribute("crossorigin",
"anonymous"). Существующие соединения уже не используются, а вместо
них создается больше новых соединений. Для прямых запросов Java­
Script, содержащих тег в HTML-коде, использование такого метода не требуется. Также он не требуется при загрузке ресурсов, размещенных в основном домене, что еще раз показывает, что доменное
разделение на уровне HTTP не всегда эффективно.
На примере Amazon мы видим, что использование обходных путей
все-таки снижает производительность HTTP1/1. К тому же их довольно
сложно реализовать. От сайтов требуется, чтобы они могли управлять несколькими доменами и спрайт-файлами или объединять весь Java­Script
(или CSS) в один файл. Не каждый сайт способен на это, и не у каждого
сайта есть ресурсы оптимизации, как у Amazon. Небольшие сайты зачас­
тую менее оптимизированы и больше страдают от ограничений HTTP/1.

2.4.2 Пример 2: imgur.com
Что будет, если не использовать оптимизацию? В качестве примера приведем сайт imgur.com. Поскольку этот сайт предназначен для обмена
изображениями, он загружает огромное количество изображений на
главную страницу, но не группирует их в спрайт-файлы. На рис. 2.15 показана часть каскадной диаграммы данного сайта от WebPagetest.
Первая часть загрузки страницы (до запроса 31) во многом похожа на
загрузку страницы amazon.com, поэтому ее мы пропустим. На рисунке
мы можем видеть, что для загрузки запросов 31–36 используется 6 соединений, а остальные поставлены в очередь. По завершении каждого
из данных запросов могут быть запущены еще 6 новых, за которыми может последовать еще столько же. За счет этого и образуется характерная
каскадная форма, за счет которой такие диаграммы и получили свое название. Обратите внимание, что ресурсы не связаны между собой, а их
загрузка может завершиться в разное время (как это и происходит на
нашей диаграмме). Однако, если их размер совпадает, их загрузка нередко заканчивается в одно и то же время. Таким образом, возникает
иллюзия того, что ресурсы связаны, но на уровне HTTP это не так (хотя
пропускная способность у них одинакова, а также они обращаются к одному серверу).
На рис. 2.16 изображена каскадная диаграмма для Chrome. Здесь
проблема становится более очевидной, поскольку на диаграмме видна
длительность задержки с момента, когда уже можно было бы отправить
запрос ресурса. Как видите, у более поздних запросов задержка перед
запросом изображения увеличивается (выделено прямоугольником),
а время загрузки становится относительно короче.
1

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.

Практические примеры

Рис. 2.15

83

Часть каскадной диаграммы сайта imgur.com

Время ожидания

Рис. 2.16 Каскадная диаграмма в панели инструментов разработчика Chrome
для сайта imgur.com

2.4.3 Насколько проблема серьезна?
Несмотря на недостатки HTTP, существуют способы их обойти. Однако
они требуют времени, денег и достаточного уровня знаний для реализации и поддержки в будущем, а также у них есть собственные проблемы с производительностью. Заставлять разработчиков тратить время на
работу с неэффективным протоколом довольно дорого (не говоря уже
о том, что многие владельцы сайтов не осознают влияние низкой производительности на трафик). Многочисленные исследования показыва-

Глава 2

84

Путь к HTTP/2

ют, что пользователи отказываются от использования медленных вебсайтов, что приводит к снижению продаж1,2.
Также необходимо понимать, насколько серьезна данная проблема
по сравнению с другими проблемами производительности. Существует
множество причин, по которым веб-сайты работают медленно: качество подключения, размер веб-сайта, избыток кода Java­Script, а также
перегрузка неоптимизированной рекламой и средствами отслеживания
поведения пользователей. Но все же невозможность быстрой и эффективной загрузки ресурсов является одной из главных составляющих
проб­лемы. Однако, несмотря на это, многие веб-сайты все еще не оптимизированы. Очевидно, что многие владельцы сайтов обеспокоены
этим, поэтому они прибегают к обходным путям, но для некоторых это
слишком сложный способ.
Еще одна проблема состоит в том, что обходные пути имеют собственные ограничения, вследствие чего становятся неэффективными. Кроме
того, поскольку веб-сайты продолжают расти в размерах и становятся
все сложнее, в какой-то момент эти обходные пути вовсе перестанут
работать. Несмотря на то что браузеры способны открывать шесть соединений на один домен и даже могут увеличить это число, расходы на
это, по сравнению с достижением положительного результата, достигают точки убывающей отдачи. Именно поэтому браузеры и ограничивают
количество подключений шестью, даже несмотря на то, что многие владельцы сайтов пытались обойти это ограничение с помощью доменного
разделения.
Так или иначе, каждый веб-сайт индивидуален, и каждый владелец
веб-сайта или веб-разработчик должен уделить должное время поиску
узких мест в своих ресурсах, и в этом им могут помочь такие инструменты, как каскадные диаграммы, на которых видно, насколько сильно
недостатки HTTP/1.1 влияют на производительность.

2.5

Переход от HTTP/1.1 к HTTP/2
С момента выхода версии HTTP/1.1 в 1999 году HTTP практически не
изменялся. В 2014 году был опубликован новый Request for Comments
(RFC), в котором уточнялась спецификация протокола. Однако какихлибо изменений в нем не произошло, а документ был создан для того,
чтобы официально задокументировать протокол. Также какое-то время велась работа над обновленной версией (HTTP-NG), которая должна
была кардинально поменять работу HTTP. В 1999 году от этой разработки
отказались, так как многие считали такие изменения не применимыми
к внедрению в реальную практику.
1

2

https://developers.google.com/web/fundamentals/performance/why-performance-matters/.
https://developer.akamai.com/blog/2016/09/14/mobile-load-time-user-abandonment.

Переход от HTTP/1.1 к HTTP/2

85

2.5.1 SPDY
В 2009 году Майк Бэлш (Mike Belshe) и Роберт Пеон (Robert Peon) из Google
объявили, что работают над новым протоколом под названием SPDY (название не является акронимом и произносится как speedy). Они экспериментировали с этим протоколом в лабораторных условиях и получили отличные результаты – скорость загрузки страницы увеличивалась
до 64 %. Эксперименты проводились на копиях 25 реальных веб-сайтов,
а не на гипотетических веб-сайтах.
SPDY создан на основе HTTP, и при этом не внес в протокол кардинальных изменений, подобно HTTPS. HTTP-методы (GET, POST и т. д.)
и концепция HTTP-заголовков в SDPY остались неизменными. SPDY
работает на нижнем уровне, поэтому практически прозрачен для вебразработчиков, владельцев серверов и, что особенно важно, для пользователей. Любой HTTP-запрос просто преобразовывается в запрос SPDY,
отправляется на сервер, а затем конвертируется обратно. Этот запрос
выглядит как любой другой HTTP-запрос к приложениям более высокого уровня (таким как приложения Java­Script на стороне клиента).
Кроме того, SPDY работает только по защищенному протоколу HTTP
(HTTPS), что позволяет скрыть структуру и формат сообщения от других пользователей сети Internet. Все существующие сети, маршрутизаторы, коммутаторы и другие элементы инфраструктуры обрабатывают
SPDY-сообщения точно так же, как и сообщения HTTP/1. SPDY обладает
обратной совместимостью, и его внедрение не требует грандиозных изменений и снижает риски. Именно поэтому SPDY добился успеха, чего
мы не можем сказать о HTTP-NG. И если HTTP-NG пытался решить многочисленные проблемы HTTP/1, то основной целью SPDY было устранение ограничений производительности HTTP/1.1. В нем представлено
несколько важных концепций, таких как:
„„ мультиплексированные потоки (запросы и ответы использовали
одно TCP-соединение и разбивались на чередующиеся пакеты,
сгруппированные в отдельные потоки);
„„ приоритизация запросов (позволила избежать появления новых
проблем с производительностью при одновременной отправке всех
запросов);
„„ сжатие HTTP-заголовков (появилась возможность сжимать не только тела, но и заголовки HTTP-запросов).
С помощью текстового протокола запроса и ответа внедрить эти концепции не представлялось возможным, поэтому SPDY стал бинарным
протоколом. Благодаря этому одиночное соединение получило возможность обрабатывать небольшие сообщения, которые вместе формировали более крупные HTTP-сообщения, во многом так же, как TCP сам разбивает большие HTTP-сообщения на множество меньших TCP-пакетов,
которые прозрачны для большинства реализаций HTTP. SPDY реализовал концепции TCP на уровне HTTP, так что теперь благодаря ему несколько HTTP-сообщений могут передаваться одновременно.

Глава 2

86

Путь к HTTP/2

Дополнительные расширенные функции, такие как отправка по инициативе сервера, позволяли серверу добавлять дополнительные ресурсы. Если вы запросили домашнюю страницу, в ответ push-сервер может
предоставить файл CSS, необходимый для ее отображения. Этот процесс
предотвращает задержки производительности при запросе CSS-файла
(в обе стороны), а также упрощает встраивание критически важных CSS.
Компания Google в то время находилась в уникальном положении,
так как контролировала как браузер (Chrome), так и некоторые из самых
популярных веб-сайтов (например, www.google.com). Вследствие этого
компания имела возможность проводить масштабные эксперименты
с новым протоколом на реальных веб-сайтах на обеих сторонах соединения. SPDY был выпущен для Chrome в сентябре 2010 года, а уже к январю
2011 года все сервисы Google получили поддержку SPDY1.
SPDY возымел почти мгновенный успех, и в скором времени его начали поддерживать другие браузеры и серверы. Сначала в 2012 году это
сделали Firefox и Opera, затем сервер Jetty и другие серверы, включая
Apache и nginx. Подавляющее большинство веб-сайтов, поддерживающих SPDY, находились на двух последних веб-серверах. Веб-сайты, поддерживающие SPDY (например, Twitter, Facebook и WordPress), показали
такой же прирост производительности, что и Google, с минимальными
неудобствами, если не считать необходимость начальной установки. По
данным w3techs.com2, SPDY охватил до 9,1 % всех веб-сайтов. Сегодня,
когда появился HTTP/2, некоторые браузеры перестали поддерживать
SPDY. С начала 2018 года использование SPDY резко упало (рис. 2.17).

10
9
8
7
6
5
4
3
2
1
0
1 Sep’17 1 Oct

1 Nov

1 Dec 1 Jan’18 1 Feb

1 Mar

1 Apr

1 May

1 Jun

1 Jul

1 Aug

Использование SPDY веб-сайтами, 2 сен 2018, W3Techs.com

Рис. 2.17 Сокращение поддержки веб-сайтами SPDYпосле запуска HTTP/2
1
2

https://groups.google.com/d/msg/spdy-dev/TCOW7Lw2scQ/T2kM5aPDydwJ.
https://w3techs.com/technologies/details/ce-spdy/all/a.

1 Sep

Переход от HTTP/1.1 к HTTP/2

87

2.5.2 HTTP/2
SPDY доказал возможность улучшения HTTP/1.1 не только теоретически, но и на реальных примерах. В 2012 году рабочая группа HTTP в составе IETF отметила успех SPDY и запросила предложения по следующей версии HTTP1. SPDY стал основой для следующей версии. Однако
рабочая группа предпочитала оставаться открытой для любых предложений (хотя некоторые люди оспаривают эту позицию, как описано
в главе 10).
Какое-то время рассматривались и другие предложения, но в конечном итоге в ноябре 2012 года2 был создан первый проект, где в основу
HTTP/2 SPDY лег именно SPDY. Впоследствии проект претерпел некоторые изменения и улучшения (в частности, в плане использования потоков и сжатия). В главах 4, 5, 7 и 8 мы рассмотрим технические детали этой
версии намного подробнее.
К концу 2014 года спецификация HTTP/2 была представлена в качестве стандарта, а в мае 2015 года она была официально утверждена как
RFC 74503. Так как спецификация основывалась на SPDY, многие серверы почти сразу же ввели поддержку новой версии. С февраля 2015 го­
да поддержку HTTP/2 ввел Firefox, а с марта 2015-го Chrome и Opera.
Чуть позже, в этом же году, к ним примкнули Internet Explorer 11, Edge
и Safari.
Веб-серверы быстро адаптировались к новому протоколу, и многие из
них реализовали различные версии по мере их стандартизации. Одними из первых реализациями были LiteSpeed4 и H2O5. К концу 2015 года
три основных веб-сервера, используемых подавляющим большинством
Internet-пользователей (Apache, IIS и nginx), создали свои реализации,
хотя изначально они были отмечены как экспериментальные.
По данным w3tech.com6, по состоянию на сентябрь 2018 года HTTP/2
был доступен на 30,1 % всех веб-сайтов. Такой охват во многом связан
с сетями доставки контента и более крупными сайтами, поддерживающими HTTP/2. Для столь молодой технологии это блестящий результат.
Как вы увидите в главе 3, использование HTTP/2 на стороне сервера в настоящее время требует изрядных усилий.
Сегодня протокол HTTP/2 уже доступен и работает на практике. Вскоре стало очевидно, что данный протокол значительно улучшает производительность именно за счет того, что решает проблемы с HTTP/1.1,
которые мы обсуждали в этой главе.

1
2
3
4

5
6

https://lists.w3.org/Archives/Public/ietf-http-wg/2012JanMar/0098.html.
https://tools.ietf.org/html/draft-ietf-httpbis-http2-00.
https://tools.ietf.org/html/rfc7540.
https://blog.litespeedtech.com/2015/04/17/lsws-5-0-is-out-support-for-http2esi-litemage-cache/.
https://h2o.examp1e.net/.
https://w3techs.com/technologies/details/ce-http2/all/all.

88

2.6

Глава 2

Путь к HTTP/2

Значение HTTP/2 для веб-производительности
Мы познакомились с проблемами производительности, возникающими при использовании HTTP/1, и узнали, что они могут быть решены
с помощью HTTP/2, но все ли проблемы с Internet-производительностью
решаются с помощью HTTP/2, и на какой прирост скорости мы можем
рассчитывать?

2.6.1 Пример предельной производительности HTTP/2
Существует множество примеров, демонстрирующих улучшение производительности при использовании HTTP/2. Одним из них является мой
персональный сайт https://www.tunetheweb.com/performance-test-360/.
Он доступен при использовании HTTP 1.1, HTTP 1.1 через HTTPS и HTTP/2
через HTTPS. Как было сказано в главе 3, браузеры поддерживают HTTP/2
только при HTTPS-соединении, следовательно, тестирование HTTP/2
невозможно без использования протокола HTTPS. Мы можем провести
анализ с помощью https://www.httpvshttps.com/. Данный сайт загружает 360 уникальных изображений по трем технологиях (HTTP, HTTPS,
HTTP/2) с использованием Java­Script. Затем можно сравнить результаты.
Результат проведенного нами теста показан на рис. 2.18.

Рис. 2.18 Сравнение HTTP, HTTPS и HTTP/2

Согласно проведенному тесту, версии HTTP для загрузки страницы
и всех изображений понадобилось 10,471 с, а версии HTTPS – 10,533 с.
Исходя из этого, мы видим, что использование HTTPS практически не
снижает производительность, а разница между ним и текстовым протоколом практически не видна. Данный тест показал, что HTTPS работает ненамного быстрее, чем HTTP, что фактически не имеет смысла (поскольку HTTPS требует дополнительной обработки). Для данного сайта
дополнительная обработка находится в пределах погрешности.
HTTP/2 загрузил сайт за 1,731 с, что на 83 % быстрее, чем HTTP и HTTPS.
На каскадных диаграммах видна причина этого. Сравните диаграммы
HTTPS и HTTPS/2 на рис. 2.19 и 2.20.

Значение HTTP/2 для веб-производительности

89

Рис. 2.19 Каскадная диаграмма теста HTTPS (игнорируйте выделенную строку 18
с ответом 302)

Рис. 2.20

Каскадная диаграмма теста HTTP/2

При использовании HTTPS происходит уже знакомая нам задержка,
возникающая при установке дополнительных соединений и последующей загрузке изображений партиями по 6 штук. Однако при использовании HTTP/2 все изображения запрашиваются одновременно, и задержки
не происходит. Для краткости примера мы решили показать только первый 21 запрос, а не все 360. Однако даже на кратком примере заметно
существенное преимущество HTTP/2 при загрузке сайтов такого типа.
Также обратите внимание на рис. 2.19. На нем показано, что в запросе 10, после того как было установлено максимальное количество со-

90

Глава 2

Путь к HTTP/2

единений, браузер выбирает загрузку Google Analytics. Данный запрос
относится уже к другому домену, где лимит возможных соединений еще
не превышен. На рис. 2.20 мы видим, что параллельные запросы сильно ограничены, вследствие чего может быть запрошено гораздо больше
изображений. Запрос Google Analytics в данной диаграмме находится
ниже 21 запроса.
Внимательные читатели могут заметить, что загрузка изображений
по протоколу HTTP/2 занимает больше времени (490 мс), по сравнению
с HTTP/1.1 (115 мс), не учитывая загрузку первые 6 запросов. Время загрузки ресурсов по протоколу HTTP/2 может разниться из-за различных
способов измерения. В каскадных диаграммах время загрузки обычно
измеряется с момента ответа отправки запроса до момента получения
ответа и не включает в себя время ожидания в очереди. К примеру, в запросе 16 при работе с HTTP/1 ресурс запрашивается примерно на отметке 1,2 с, а ответ приходит через 118 мс (1,318 мс). В данном случае
изображение было для браузера необходимым ресурсом. Таким образом,
браузер обработал HTML и совершил первый запрос уже через 0,75 с, что
совпадает с цифрами, которые мы имеем при использовании HTTP/2.
Следовательно, задержка 0,45 с в каскадных диаграммах HTTP/1 не отображается, и, возможно, отсчет должен начинаться с отметки 0,75. Как
отмечалось в разделе 2.4.2, каскадная диаграмма Chrome включает в себя
информацию о времени ожидания; таким образом, на ней мы можем наблюдать истинное общее время загрузки, которое дольше, чем у HTTP/2.
Однако при использовании HTTP/2 в некоторых случаях запросы могут
занимать больше времени из-за низкой пропускной способности у клиента или сервера. Необходимость использовать несколько соединений
в HTTP/1 всегда создает очереди из 6 запросов. HTTP/2 использует лишь
одно соединение, разделенное на потоки. Теоретически в этом случае
ограничения устраняются, однако во многих реализациях могут возникать иные ограничения. Например, страница из примера размещена на
сервере Apache, который по умолчанию ограничивается 100 запросами
на одно соединение. Отправка большого количества запросов одновременно приводит к тому, что запросы начинают использовать одни и те
же доступные ресурсы, ввиду чего загрузка занимает больше времени. На
рис. 2.20 мы видим, что время загрузки изображений постепенно увеличивается (если в строке запроса 4 оно составляет 282 мс, то в строке запроса 25 уже 301 мс). На рис. 2.21 показаны те же результаты в строках запросов 88–102. Здесь время запросов изображений составляет до 720 мс,
что в 6 раз дольше, чем при использовании HTTP/1. Кроме того, по достижении предела в 100 запросов загрузка приостанавливается до тех пор,
пока первые запросы не будут выполнены, и только после этого осуществляются остальные запросы. Происходит практически то же самое, что
и при использовании HTTP/1. Однако при использовании HTTP/2 такой
эффект возникает намного реже, а если и возникает, то несколько позже,
так как лимит запросов здесь увеличен. Обратите внимание, что во время
этой паузы в запросе 104 запрашивается Google Analytics. На рис. 2.19 мы
видим аналогичный исход, но намного раньше, в запросе 10.

Значение HTTP/2 для веб-производительности

Рис. 2.21

91

Задержки в каскадной диаграмме HTTP/2

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

2.6.2 Какой прирост производительности
может обеспечить HTTP/2?
Пример в разделе 2.6.1 показывает нам преимущества HTTP/2. Прирост
производительности составляет 83 %, и это впечатляет. Однако такой исход возможен далеко не у всех веб-сайтов. В данном примере мы наблюдаем условия, при которых HTTP/2 дает наилучшие результаты (к слову,
это еще одна причина, по которой лучше использовать реальные, широко
используемые сайты). Некоторые сайты при переходе на HTTP/2 могут
не получить такого прироста быстродействия в силу других недостатков,
на фоне которых недостатки HTTP/1 не выглядят главной проблемой.
Существует две причины, по которым переход на HTTP/2 может не
принести желаемых результатов. Первая причина состоит в том, что
многие веб-сайты уже настроены на использование обходных путей,
описанных в разделе 2.2, и поэтому недостатки HTTP/1 для них не столь
заметны. Однако даже такие сайты подвержены некоторым проблемам
с производительностью, так как обходные пути также имеют свои недостатки. Кроме того, для их внедрения требуются значительные усилия.
В теории, HTTP/2 может обеспечить сайтам лучшую производительность
путем эффективного использования спрайт-файлов и встроенных CSS,
Java­Script и изображений, затрачивая при этом намного меньше усилий.

Глава 2

92

Путь к HTTP/2

Вторая причина заключается в том, что некоторые веб-сайты имеют
куда более серьезные проблемы с производительностью. Например, на
многих из них размещено большое количество изображений в высоком качестве или слишком много Java­Script. Все это требует больших затрат времени для загрузки (здесь HTTP/2 может помочь) и обработки (здесь HTTP/2
бессилен). HTTP/2 не сможет помочь сайтам, которые работают медленно
даже после загрузки или страдают от зависания (когда браузер изо всех сил
пытается справиться с прокруткой страницы веб-сайта пользователем),
так как данный протокол решает проблемы производительности только на
сетевой стороне. Потеря пакетов (которую мы рассмотрим в главе 9) также
может замедлить работу HTTP/2/, однако это крайние случаи.
Несмотря на все вышеупомянутое, мы уверены, что HTTP/2 крайне
полезен для повышения производительности. Также его использование
поможет избежать использования обходных путей. Но при этом важно
понимать, какие проблемы HTTP/2 способен решить, а какие – нет. В противном случае пользователи могут заметить прирост производительности не сразу и разочароваться. На момент написания данной книги мы,
вероятно, находимся на пике завышенных ожиданий (стоит упомянуть
о циклах зрелости технологий от Gartner1). Сегодня пользователи ждут,
что новая технология сможет решить все проблемы. Владельцам вебсайтов следует тщательно изучить проблемы с производительностью их
сайтов, ведь низкая пропускная способность в HTTP/1 – это лишь одно из
узких мест. Впрочем, чаще всего после перехода на HTTP/2 производительность все же возрастает, так как HTTP/2 не должен быть медленнее,
чем HTTP/1. Но иногда такие случаи все же происходят. Например, сайты
с низкой пропускной способностью (на которых размещено множество
высококачественных изображений) могут работать на HTTP/2 медленнее,
если порядок запросов, обеспечиваемый ограниченным количеством соединений в HTTP/1.1, загружает критически важные ресурсы быстрее.
Одна компания, специализирующаяся на графическом дизайне, опубликовала интересный пример2. Но даже в таких случаях работу сайта можно
ускорить посредством правильной настройки HTTP/2 (см. главу 7).
Возвращаясь к реальным практическим примерам, взглянем на
табл. 2.2. В ней приведено время загрузки копии сайта Amazon с помощью HTTP/1 и HTTP/2 (оба раза через HTTPS), где все ссылки были изменены на локальные.
Таблица 2.2 Прирост производительности сайта Amazon,
полученный благодаря HTTP/2
Протокол
HTTP/1
HTTP/2
Разница

Время загрузки
2,616
2,337
11 %

1
2

Первый байт
0,409 с
0,421 с
–3 %

Начать отрисовки
1,492 с
1,275 с
15 %

Визуальная готовность
2,900 с
2,600 с
10 %

Индекс скорости
1692
1317
22 %

https://www.gartner.com/technology/research/methodologies/hype-cycle.jsp.
https://99designs.ie/tech-blog/blog/2016/07/14/real-world-http-2-400gb-of-images-per-day/.

Значение HTTP/2 для веб-производительности

93

В данной таблице представлены несколько терминов, широко используемых в области веб-производительности:
„„ время загрузки – это время, необходимое странице для отправки события onload, после загрузки всех CSS и блокирующих Java­Script;
„„ первый байт – это время, необходимое для получения первого ответа от веб-сайта. Обычно он представляет собой первый реальный
ответ, не предусматривающий перенаправления;
„„ начало рендеринга – это момент начала визуализации страницы.
Данный показатель является ключевым параметром производительности, так как, если у сайта возникнут проблемы с визуализацией, пользователи, скорее всего, откажутся от его использования;
„„ завершение рендеринга – это момент, когда страница перестает меняться. Зачастую асинхронный Java­Script все еще меняет вид страницы после события окончания загрузки;
„„ индекс скорости – это показатель WebPagetest, который указывает
среднее время загрузки элементов страницы в мс1.
HTTP/2 позволяет улучшить большую часть этих показателей. Время
получения первого ответа немного увеличилось, но повторные тесты показали обратное, таким образом, можем сделать вывод, что этот результат находится в пределах погрешности.
Однако стоит признать, что данный пример создан искусственно,
так как сайт реализован не в точности как у Amazon. Здесь используется только один домен (а не доменное разделение), а каждый ресурс сохранен как статический файл, а не динамический контент, как у Amazon
(в таком случае возникли бы задержки). Тем не менее, несмотря на то
что ограничения существуют и в HTTP/1, и в HTTP/2, в тестах HTTP/2 наблюдаются явные улучшения.
На рис. 2.22 и 2.23 мы видим каскадные диаграммы загрузок. В диаграмме HTTP/2 заметны ожидаемые улучшения: дополнительные подключения отсутствуют, а ступенчатая каскадная загрузка ресурсов в начале заметно меньше.
Код обоих типов запроса на веб-сайте не изменился благодаря HTTP/2.
При загрузке сайта по данному протоколу мы все еще можем наблюдать
эффект водопада, так как веб-технологии носят зависимый характер: например, для загрузки изображений веб страницам требуются CSS. Настройка соединений и время ожидания в очереди занимает меньше времени, поэтому эффект водопада, полученный в результате ограничений
HTTP, исчезает. Цифры могут показаться небольшими, но повышение
производительности на 22 % – это уже отличный результат, притом что
фактически никаких серьезных изменений не потребовалось.
Сайты, оптимизированные для протокола HTTP/2 и успешно использующие его новые функции (о которых мы поговорим позже), в любом
случае получат положительный результат. На данный момент у нас есть
1

https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/metrics/
speed-index.

94

Глава 2

Путь к HTTP/2

20-летний опыт оптимизации сайтов под HTTP/1, но почти нет опыта
оптимизации под HTTP/2.

Рис. 2.22

Загрузка копии домашней страницы Amazon с помощью HTTP/1

В качестве примера мы используем Amazon, так как данный сайт очень
популярен, но (на момент написания книги) еще не перешел на HTTP/2.
Кроме того, его хорошо (но не идеально!) оптимизировали для HTTP/1.
Мы не хотим сказать, что сайт Amazon написан или работает плохо. Мы
лишь показываем улучшения, которые может внести HTTP/2, не прибе-

Значение HTTP/2 для веб-производительности

95

гая к обходным путям HTTP/1, на реализацию которых затрачиваются
значительные усилия.

Рис. 2.23

Загрузка копии домашней страницы Amazon с помощью HTTP/2

С момента начала написания этой главы Amazon уже перешел на
HTTP/2, и результаты перехода получились аналогичны результатам, полученным из наших примеров. Однако мы рассматривали его как реальный сложный веб сайт, который уже реализовал некоторые оптимизации производительности HTTP/1, но все еще не перешедший на HTTP/2.

Глава 2

96

Путь к HTTP/2

2.6.3 Обходные пути для HTTP/1.1
как потенциальные тупики
В теории не должно быть необходимости раскрывать тему обходных путей HTTP/1, так как HTTP/2 устраняет проблемы предыдущей версии.
Многие считают, что обходные пути становятся тупиками, так как могут
помешать вам получить все преимущества HTTP/2. Преимущества использования одного TCP-соединения сводятся на нет, если владелец вебсайта использует доменное разделение (в главе 6 мы поговорим об объединении подключений, которое позволяет решить данную проблему).
HTTP/2 позволяет по умолчанию создать высокопроизводительный сайт.
К сожалению, в действительности все не так просто. Как будет сказано
в последующих главах (особенно в главе 6), отказываться от обходных
путей еще слишком рано, ведь HTTP/2 еще не получил должного распространения. На стороне клиента пользователи все еще применяют
HTTP/1.1. Они также могут использовать старые браузеры или проксисерверы, которые еще не поддерживают HTTP/2 (например, антивирусные сканеры или корпоративные прокси).
Кроме того, реализации на сторонах и клиента и сервера все еще подвержены изменениям в поиске условий, при которых протокол будет
работать эффективнее. После запуска HTTP/1.1 область оптимизации
веб-производительности росла и процветала, а разработчики обучались
оптимизировать веб-сайты для работы с HTTP. Мы надеемся, что протоколу HTTP/2 потребуется меньше времени. Пока что разработчики не
привыкли к новому протоколу, и многие передовые практики и методы
требуют основательного изучения.
Сейчас мы с нетерпением ждем повсеместного внедрения HTTP/2.
В главе 3 мы поговорим о том, как перейти на него. Чуть позже мы вернемся к теме оптимизации производительности и рассмотрим, как можно проследить изменения при переходе на HTTP/2 и как использовать
его наиболее эффективно. Эта глава должна была объяснить читателям
преимущества нового протокола, и мы надеемся, что нам это удалось.

Резюме
В HTTP/1.1 существуют проблемы с производительностью, особенно
если речь идет о загрузке нескольких ресурсов.
„„ Существуют обходные пути для решения этих проблем (использование параллельных подключений, шардинг, спрайтинг и т. д.). Однако
они также имеют свои недостатки.
„„ С помощью таких инструментов, как WebPagetest, вы можете сгенерировать каскадные диаграммы, на которых можно легко отследить
проблемы производительности вашего веб-сайта.
„„ Для решения этих проблем был разработан SPDY.
„„ HTTP/2 – это стандартизированная версия SPDY.
„„ Не все проблемы с производительностью можно решить с помощью
HTTP/2.
„„

3

Переход на HTTP/2

В этой главе мы рассмотрим:
какие браузеры и серверы поддерживают HTTP/2;
„„ различные пути перехода вашего сайта на HTTP/2;
„„ обратные прокси-серверы и CDN, их влияние на HTTP/2;
„„ устранение ошибок, по причине которых HTTP/2
не используется повсеместно.
„„

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

3.1

Поддержка HTTP
Относительно недавно, в мае 2015 года, HTTP/2 был официально признан интернет-стандартом. Как в случае со всеми новыми технологиями, разработчикам необходимо принять решение, когда лучше всего
начать внедрение. Если начать внедрение слишком рано, технология
будет считаться передовой и рискованной, так как в будущем, она, вероятно, претерпит изменения или даже окажется неудачной. К тому же
пользователи, использующие старые стандарты, могут быть не готовы
поддерживать новую технологию. Это означает, что не стоит торопиться

Глава 3

98

Переход на HTTP/2

с внедрением технологий в реальный мир. Но, с другой стороны, именно
люди, впервые перешедшие на новую технологию, открывают для нее
путь в массы.
К счастью, развитие HTTP/2 проходит не совсем по обычному технологическому циклу, ведь ранее эффективность протокола отчасти была
доказана в его предыдущем воплощении SPDY (как было сказано в главе 2). По данным w3tech.com1, на момент написания нашей книги уже
более 30 % всех веб-сайтов перешли на использование нового стандарта.
К тому времени, как вы прочитаете эту книгу, цифра, вероятно, увеличится. Эффективность HTTP/2 уже доказана. Многие сайты уже перешли
на использование данного протокола.
Возможность использования новой-веб технологии на вашем сайте
сводится к трем аспектам:
„„ поддерживается ли технология веб-браузерами?
„„ возможен ли переход на нее с вашими техническими возможно­
стями?
„„ существует ли надежный запасной вариант на случай, если технология вам не подойдет?
В целом HTTP/2 соответствует всем трем аспектам. Данную технологию поддерживают практически все браузеры и серверные ПО. Кроме того, если ваш веб-сайт не сможет поддерживать HTTP/2, вы всегда
сможете вернуться к HTTP/1.1. Однако существуют некоторые тонкости
и нюансы.

3.1.1 Поддержка HTTP/2 cо стороны веб-браузера
Практически все существующие в наши дни веб-браузеры поддерживают HTTP/2/. Мы можем сделать такой вывод, основываясь на данных caniuse.com2 (рис. 3.1).
На западе позже всех на поддержку нового стандарта в своем веббраузере перешла платформа Android. Однако на момент написания нашей книги переход на HTTP/2 все еще не осуществил браузер UC, популярный в Китае, Индии, Индонезии и других азиатских странах. Браузер
Opera Mini получает готовый рендер страниц с серверов Opera, поэтому
имеет мало отношения к нашему обсуждению.
На рис. 3.2 показано, как HTTP/2 используется веб-браузерами. Величина графы указывает на процент пользователей новой версии. Как мы
можем заметить, браузер UC использует довольно большое количество
пользователей. Ввиду того, что он не поддерживает HTTP/2, для распространения протокола это является серьезным препятствием.

1
2

https://w3techs.com/technologies/details/ce-http2/all/all.
https://caniuse.com/#feat=http2.

Поддержка HTTP

Рис. 3.1 Статистика caniuse.com для HTTP/2

Рис. 3.2 Относительная поддержка HTTP/2 браузерами согласно caniuse.com

99

Глава 3

100

Переход на HTTP/2

HTTP/2 поддерживают не все браузеры. Однако на момент написания
книги около 83,21 % браузеров по всему миру уже перешли на использование данной версии, и со временем их количество будет только расти.
Caniuse.com позволяет также просматривать статистику использования
по странам. Таким образом, если вы обслуживаете пользователей какойлибо одной страны, вы можете получить еще более точную статистику по
вашей пользовательской базе (исходя из которой может выясниться, что
ваши пользователи используют UC не так уж и часто). В этой статистике,
однако, существует несколько важных нюансов.

HTTP/2 и HTTPS для браузеров
На приведенных выше рисунках маленькой цифрой «2» отмечены брау­
зеры, которые поддерживают HTTP/2 только через TLS (https). Таким образом, веб-сайты, использующие незащищенные подключения, не могут
получить все преимущества от использования HTTP/2. Такая же ситуация
была и со SPDY, поэтому многие настаивали на том, чтобы HTTPS был
включен в официальную спецификацию HTTP/2. В конечном итоге это
требование не включили в официальный документ, однако все создатели браузеров заявили, что они будут поддерживать HTTP/2 только вместе
с HTTPS, что сделало это требование стандартом де-факто. Конечно же, такой стандарт, несомненно, огорчит владельцев веб-сайтов, использующих
HTTP. Однако в пользу использования HTTPS есть два веских аргумента.
Первый из них носит исключительно практический характер. Использование HTTP/2 через HTTPS снижает вероятность возникновения проб­
лем с совместимостью. Многие сервисы в Internet, поддерживающие
HTTP, не смогут работать с сообщениями HTTP/2. При отправке через
HTTPS-соединение HTTP-сообщения скрываются, тем самым предотвращаются проблемы совместимости. (HTTPS-сообщение может быть
прочитано только получателем, но в следующем разделе мы рассмотрим
особые случаи перехвата прокси.)
Второй аспект носит идеологический характер. Многие создатели
брау­зеров (и другие пользователи, в том числе автор данной книги) верят в то, что отказ от незашифрованного HTTP-соединения возможен,
и считают, что все веб-сайты должны поддерживать исключительно
HTTPS. Поэтому внедрение новых функций зачастую требует использования HTTPS-соединения.
HTTPS обеспечивает безопасность, конфиденциальность и целостность соединения с веб-сайтами. Данные аспекты важны для всех сайтов,
а не только для электронных торговых площадок, где необходимо защищать платежные данные1. История поиска и просматриваемых страниц
является конфиденциальными личными данными. Различные регистрационные формы запрашивают ваш адрес электронной почты, т е. личную информацию, поэтому такие формы должны быть защищены. Для
формата блога перехват и изменение данных кажется маловероятным.
1

https://tools.ietf.org/html/rfc7258.

Поддержка HTTP

101

Однако операторы мобильной связи и Wi-Fi в самолетах размещают на
страницах рекламные объявления, которые видят их пользователи при
использовании незащищенного соединения. Многие злоумышленники
могут распространять более опасный контент, например код Java­Script
или вредоносное ПО для майнинга криптовалюты.
Со временем владельцам веб-сайтов избегать использования HTTPS
будет все сложнее. HTTP/2, требующий защищенного соединения, является еще одной причиной для перехода.
Однако даже при использовании HTTPS у вас могут возникнуть некоторые проблемы. HTTP/2 нуждается в надежном HTTPS. Маленькая цифра «4» на рисунках выше отмечает браузеры, поддерживающие HTTP/2
только в случае поддержки серверами согласования протокола через
ALPN. Такими браузерами являются Chrome, Firefox и Opera. В разделе
3.1.2 мы обсудим эту тему подробнее, но пока имейте ввиду, что согласование протокола уровня приложения (application layer protocol negotiation,
ALPN) поддерживается только серверами, использующими HTTPS, и что
некоторые браузеры не смогут использовать HTTP/2, если ALPN для них
недоступен. Кроме того, многие браузеры требуют новых и безопасных
наборов шифров для использования HTTP/21.

Перехват прокси
Для использования HTTP/2 требуется поддержка этой версии и браузером, и сервером. Однако, если прокси пользователей разрывают HTTPсоединение и к тому же не поддерживают HTTP/2, может возникнуть
серь­езная проблема.
Во многих корпоративных средах распространено использование
прокси и ограничение прямого доступа в Internet. Такой подход позволяет сканировать соединения на наличие угроз и предотвращать доступ
сторонних лиц к определенным сайтам (например, к личным учетным
записях электронной почты). Существуют прокси-серверы и для домашнего пользования. Их создают антивирусные продукты, через которые
проходит и сканируется на наличие угроз веб-трафик.
Для HTTPS-трафика такие прокси представляют серьезную проблему,
поскольку они не могут читать зашифрованные сообщения. Таким образом, если вы используете прокси-сервер, которому необходимо читать HTTPS-трафик, ваш браузер будет создавать соединение с прокси,
а прокси в свою очередь создаст отдельное HTTPS-соединение с веб-сай­
том. Получается, что прокси отправляет поддельный сертификат HTTPS,
выдавая себя за настоящий сайт. Обычно браузеры предупреждают об
этом, поскольку суть HTTPS отчасти заключается в проверке подлинности издателя сертификата. Установка таких прокси включает в себя настройку программного обеспечения прокси в качестве утвержденного
поставщика сертификатов на компьютере, для того чтобы веб-браузер
принимал их поддельные сертификаты.
1

https://tools.ietf.org/html/rfc7540#appendix-A.

Глава 3

102

Переход на HTTP/2

Разделение трафика на две части позволяет прокси читать его, но ваш
браузер фактически не подключается напрямую к веб-сайту. В таком
случае вы сможете использовать HTTP/2 только тогда, когда его поддерживает прокси. Если прокси не поддерживает HTTP/2, получается, что
он переводит ваше соединение на HTTP/1.1. Это приводит к тому, что,
во-первых, вы уже не используете HTTP/2, а во-вторых, возникает путаница. Вы не понимаете почему произошел переход на более раннюю
версию, если и браузер, и сервер поддерживают HTTP/2 (см. раздел 3.3).
Многие специалисты, занимающиеся Internet-безопасностью, считают, что посредничество прокси вызывает больше проблем, чем решает, так как создатели браузеров активно работают над улучшением использования HTTPS, а разрыв соединения означает, что все это делается
впустую. Тем не менее прокси все еще используются, и это необходимо
учитывать при попытках разобраться в переходе на HTTP/2. Исследования показали, что из-за использования прокси перехватывается от 4 до
9 % всего трафика, причем 58 % этого трафика перехватывается антивирусным ПО, а 35 % – корпоративными прокси1. Самый простой способ узнать, использует ли компьютер прокси, – посмотреть сертификат
HTTPS и удостовериться в его подлинности, является ли он продуктом
настоящего центра сертификации или частью ПО. На рис. 3.3 показано
различие между прямым подключением и перехватом подключения антивирусным сканером Avast в Internet Explorer.
Издатель
сертификата

Рис. 3.3 Сертификат HTTPS для прямого подключения к Google и подключения,
перехватываемого антивирусным продуктом

Если взглянуть более позитивно, перехватывающие прокси обычно
используются в домашних или корпоративных средах, где соединение
в любом случае довольно быстрое, а использование HTTP/2 менее эффективно. Перехват мобильного трафика встречается реже, поэтому сети
с малой задержкой (например, мобильные) больше других нуждаются
в переходе на HTTP/2.

Вывод о поддержке HTTP/2 браузерами
Многие браузеры поддерживают HTTP/2. Появление так называемых
вечнозеленых браузеров, которые обновляются для использования новых версий интернет-протоколов автоматически (см. сноску ниже),
свидетельствует о том, что развертывание HTTP/2 на стороне браузеров
1

https://jhalderm.com/pub/papers/interception-ndss17.pdf.

Поддержка HTTP

103

произошло довольно легко. Однако для эффективного использования
HTTP/2 существует несколько условий. Одними из них являются правильная настройка HTTPS на стороне сервера, а также в случае использования перехватывающих прокси-серверов.
Факт того, что для использования HTTP/2 требуется HTTPS, а также
строгий характер данного протокола, сильно усложняет переход на новую версию и может запутать пользователя. Данная сложность присуща
только стороне сервера, как уже было сказано в 3.1.2. Постепенно все серверы переходят на HTTPS. Недостатки использования незашифрованных
HTTP-соединений со временем будут становится все более явными. На
момент написания данной книги более 75 % интернет-трафика осуществляется по HTTPS-соединениям1. Однако эта цифра несколько неточна
ввиду высокой посещаемости некоторых крупных веб-сайтов. В любом
случае, если ваш веб-сайт все еще не перевели ваш сайт на HTTPS, то
в скором времени вам необходимо будет сделать это.

«Вечнозеленые» браузеры
Такие браузеры, как Chrome и Firefox, обновляются автоматически в фоновом режиме без запроса пользователя. Такие браузеры называются «вечнозелеными». Пользователи, использующие последние версии этих браузеров,
вероятно, будут использовать и HTTP/2.
Такие браузеры существовали не всегда. В истории веб-разработки было
много случаев, когда разработчики были вынуждены определять версии
браузеров и внедрять различные хаки для пользователей, которые, к примеру, все еще использовали Internet Explorer 5 или подобный браузер.
К сожалению, на деле все не так радужно. Chrome, Firefox и Opera неплохо работают на ПК и относятся к категории «вечнозеленых», однако другие
браузеры и платформы не обновляются автоматически. Обновления Safari
зачастую происходят вместе с обновлением операционной системы, в том
числе и на мобильных устройствах. Операционная система iOS обновляется
довольно часто, но глобальные обновления происходят один раз в год. Такие
технологии, как HTTP/2, обычно входят в глобальные обновления. Android
перешел на «вечнозеленый» Webview Chromium в версии Android 5 (Lollipop), однако пользователям все же часто приходится устанавливать обновления через Play Store. Еще один известный «вечнозеленый» браузер –
это Edge. Сейчас его обновления связаны с обновлениями операционной
системыa, но Microsoft пообещали в скором времени исправить этоb.
Случается, так, что пользователи сами отключают функцию автоматического
обновления. Многие корпоративные сети отказываются от автоматических
обновлений, но позже не уделяют должного внимания развертыванию обновлений вручную.
___________________________________
a
https://www.scirra.com/blog/173/just-how-evergreen-is-microsoft-edge.
b
 https://blogs.windows.com/windowsexperience/2018/12/06/microsoft-edge-making-theweb-better-through-more-open-source-collaboration/.
1

https://letsencrypt.org/stats/.

Глава 3

104

Переход на HTTP/2

3.1.2 Поддержка HTTP/2 серверами
Поддержка HTTP/2 серверами развивалась не так быстро, как браузерами, но сегодня практически все серверы добавили поддержку новой
версии. На сайте Github мы можем найти список реализаций HTTP/2 как
на стороне клиента, так и на стороне сервера1. Исходя из этого, мы легко можем отследить, какие серверы поддерживают новый протокол. По
данным Netcraft2, наиболее популярные веб-серверы (на которые приходится более 80 % активных сайтов в Internet) – это Apache, nginx, Google
и Microsoft IIS. Каждый из этих серверов поддерживает версию HTTP/2.
Успешное применение HTTP/2 на стороне сервера зависит не от того,
поддерживает ли протокол последняя версия его программного обеспечения, а от того, поддерживает ли ее та версия, на которой работают вебсайты в данный момент. Большинство реализаций не имеют функции
автоматического обновления, и сделать ее не так легко, как для браузеров. В связи с этим на серверах зачастую используются более старые версии программного обеспечения, созданные задолго до выхода HTTP/2.
Версии программного обеспечения очень часто привязаны к операционной системе (например, поддержка Microsoft IIS была добавлена только в IIS 10.0 в Windows Server 2016) или к диспетчеру пакетов операционной системы (например, диспетчер yum для Red Hat/CentOS/Fedora, где
на момент написания нашей книги не установлена версия Apache или
ngix с поддержкой HTTP/2).
Процесс обновления версии серверного программного обеспечения
может быть довольно сложным. В системах на Linux для обновления
требуется загрузка исходного кода и его компиляция, что требует соответствующих навыков и квалификации разработчиков, которые, кроме
того, должны поддерживать актуальность ПО или как минимум следить
за обновлениями патчей безопасности. В том, что процессом обновления
руководит операционная система или диспетчер, кроется большое преимущество, состоящее в том, что проблемы безопасности разрешаются
с каждым новым обновлением, выходящим периодически (или даже
автоматически). Решение проблем безопасности вне обновлений системы требует соответствующих затрат и проведения серьезной работы,
и к тому же существует большое количество рисков, связанных с этим.
При работе с некоторыми ОС возможно использовать сторонние репозитории, предоставляющие новые версии ПО. Однако в таком случае речь
идет об отказе от официальных репозиториев в пользу третьей стороны.

Криптографические библиотеки и их поддержка
Одна из самых больших проблем, возникающих на стороне сервера, –
строгие требования, предписывающие использование HTTPS (особенно
это характерно для Linux), о чем мы говорили ранее в этой главе. Большинство серверов используют для работы с тонкостями HTTPS отдельные
1
2

https://github.com/http2/http2-spec/wiki/Implementations.
https://news.netcraft.com/archives/category/web-server-survey/.

105

Поддержка HTTP

библиотеки – обычно это OpenSSL, хотя существуют и другое варианты,
такие как LibreSSL и BoringSSL. Зачастую криптографическая библиотека
является частью операционной системы. Обновление сервера – сложный
процесс, а обновление SSL/TLS представляет собой еще более серьезную
задачу, так как оно потенциально может повлиять на остальное программное обеспечение сервера.
В разделе 3.1.1 было сказано, что Chrome и Opera поддерживают
HTTP/2 только с помощью расширения для SSL/TLS ALPN, а более раннее
расширение NPN (Next Protocol Negotiation) для них не подходит. ALPN,
как и NPN, позволяет серверу указывать, какие протоколы приложений
сервер поддерживает в рамках согласования HTTPS; более подробно
мы поговорим об этой теме в главе 4. Проблема заключается в том, что
поддержка ALPN доступна только в последних версиях OpenSSL (версия
1.0.2 и новее), ввиду чего расширение недоступно на многих платформах. RedHat и CentOS добавили поддержку OpenSSL 1.0.2 только в августе и сентябре 2017 года соответственно. Однако коробочные версии
программного обеспечения таких веб-серверов, как Apache, зачастую
скомпилированы с использованием более старой версии 1.0.1, которая
не позволяет использовать ALPN и, следовательно, HTTP/2 для Chrome
и Opera. Точно так же Ubuntu добавил поддержку в версии 16, а не в версии 14, которая используется намного чаще. Debain начал поддерживать
OpenSSL c ALPN в версии 9 (Stretch). Иногда, даже если у сервера существует библиотека OpenSSL, код HTTP/2 может быть не включен в нее по
умолчанию. Обратите внимание на табл. 3.1.
Таблица 3.1

Поддержка ALPN операционными системами Linux

Операционная система
RHEL/CentOS < 7.4
RHEL/CentOS 7.4 & 7.5
Ubuntu 14.04 LTS
Ubuntu 16.04 LTS
Ubuntu 18.04 LTS
Debian 7 (“Wheezy”)
Debian 8 (“Jessie”)
Debian 9 (“Stretch”)

ALPN в OpenSSL
по умолчанию
N (1.0.1)
Y (1.0.2)
N (1.0.1)
Y (1.0.2)
Y (1.1.0)
N (1.0.1)
N (1.0.1)
Y (1.1.0)

ALPN в Apache/nginx
по умолчанию
N
N/Y
N
Y
Y
N
N
Y

HTTP/2 в Apache/nginx
по умолчанию
N
N/Y
N
N/Y
Y
N
N
N
Y

В таблице выше мы видим, что из всех представленных дистрибутивов Linux только коробочные версии Ubuntu 18.04 и Debian 9 содержат
HTTP/2 для Apache (хотя его необходимо активировать во время настройки веб-сервера). Для RHEL/CentOS HTTP/2 для Apache необходимо
устанавливать из какого-либо источника или из другого репозитория не
по умолчанию.
Для nginx HTTP/2 может быть установлен через репозиторий nginx 91.
Таким образом, HTTP/2 обычно настраивается для nginx последних версий, однако настройка по-прежнему зависит от версии OpenSSL.
1

http://nginx.org/en/download.html.

106

Глава 3

Переход на HTTP/2

Вывод о поддержке HTTP/2 серверами
В теории поддержка HTTP/2 серверами важна в той же степени, что
и браузерами. Однако на практике большинство людей использует старые версии программного обеспечения серверов, которые не поддерживают HTTP/2. Эти версии требуют обновлений, которые в свою очередь могут быть простыми или сложными. Ситуация изменится, когда
новые версии операционных систем будут использоваться повсеместно, однако это может стать проблемой для освоения HTTP/2. Хорошая
новость в том, что инициаторами обновления должны стать владельцы
веб-сайтов, которые могут принять на себя обязанности по обновлению
своего программного обеспечения до версий, которые будут поддерживать HTTP/2, на стороне серверов. Как только эта работа будет проделана,
большинство клиентского программного обеспечения также поддержит
эти изменения. Если бы серверы поддерживали HTTP/2, а клиенты нет,
владельцам сайтов так или иначе пришлось бы ждать, пока их пользователи обновят свое ПО. Если у вас нет возможности или желания менять
свое программное обеспечение, то вы можете воспользоваться другими
реализациями, которые мы обсудим в разделе 3.2.

3.1.3 Откат к предыдущим версиям, в случае если поддержка
HTTP/2 невозможна
Если HTTP/2 не поддерживается вашим сервером, это никак не отразится на работе веб-сайтов, так как в таком случае они могут снова перейти
на HTTP/1.1. Данная версия протокола будет продолжать использоваться
еще долгое время. В теории если ваш сервер поддерживает HTTP/2, то его
работа будет максимально эффективной.
Однако переход на HTTP/2 и сопутствующие ему изменения на вебсайтах могут смутить пользователей HTTP/1.1. Сайт, конечно же, будет
работать, но, возможно, медленнее, чем до этого. Насколько серьезна
эта проблема для вас, зависит от того, какой у вас трафик HTTP/1.1. Мы
вернемся к этой теме в главе 6.
Намного сложнее оценить проблемы реализации на стороне клиента
или сервера. HTTP/2 все еще является достаточно молодой технологией
и, несмотря на то что многие уже активно используют его на практике,
пока что он находится на ранней стадии внедрения. Несомненно, в реализациях будут обнаружены ошибки, которые могут повлиять на загрузку вашего веб-сайта через HTTP/2. Такие ошибки обычно приводят
к замедлению работы HTTP/2, однако серьезных проблем за собой не несут. Тем не менее, прежде чем переходить на HTTP/2 (или при установке
любого другого крупного обновления), вам следует провести большое
количество тестов.

Способы перехода вашего сайта на HTTP/2

3.2

107

Способы перехода вашего сайта на HTTP/2
Самое очевидное решение – просто подключить HTTP/2 на вашем вебсервере, однако этот процесс может требовать обновления ПО. Сущест­
вует еще несколько способов перехода. Например, вы можете просто
добавить на свой сервер новый элемент инфраструктуры, например
программу или сервис CND, который будет обрабатывать HTTP/2-со­еди­
не­ния. Какой метод подойдет именно вам, зависит от нескольких факторов: от того, поддерживает ли ваш веб-сервер HTTP/2, насколько сложно
для вас будет реализовать поддержку HTTP/2 и хотите ли вы усложнить
среду реализацией некоторых других опций.
После подключения HTTP/2 вы можете увидеть, что трафик все еще
передается с помощью HTTP/1.1. В разделе 3.3 мы обсудим устранение
подобных неполадок. Если вы уже перевели свой сервер на HTTP/2, но
он не работает в вашей среде, можете сразу перейти к данному разделу.

3.2.1 HTTP/2 на вашем веб-сервере
Подключение вашего сервера к HTTP/2 позволяет использовать новый
протокол клиентам, которые имеют возможность поддерживать его. На
рис. 3.4 показана простая схема подключения HTTP/2.
Веб-браузер

Internet

Веб-сервер

HTTP/2

Рис. 3.4

HTTP/2 на вашем веб-сервере

Главная проблема состоит в том, что использование данного протокола на вашем сервере может быть недоступно. Как было сказано в разделе 3.1.2, вам может потребоваться обновить веб-сервер до новой версии,
что повлечет за собой иобновление операционной системы, на которой
работает ваш веб-сервер. Возможно и такое, что программное обеспечение вашего сервера не будет поддерживать HTTP/2 даже после обновления до новейшей версии. В табл. 3.2 приведен список некоторых широко
известных веб-серверов и серверов-приложений и их версий, в которые
была добавлена поддержка HTTP/2.

Глава 3

108

Переход на HTTP/2

Таблица 3.2. Версии популярных серверов, в которые была добавлена
поддержка HTTP/2
Веб-сервер
Apache HTTPD
IIS
Jetty
Netty
Nginx
Node.JS
Tomcat

Добавленная версия HTTP/2
2.4.17 (хотя отмечен как экспериментальный до 2.4.26)
10.0
9.3
4.1
1.9.5
8.4.0 (хотя не включен по умолчанию до 9.0 и отмечен как экспериментальный до 10.10)
8.5

Программное обеспечение Linux обычно устанавливается c помощью
диспетчеров пакетов (таких как yum и aptget) при использовании официальных репозиториев, что упрощает установку и обновление программ. Многие из представленных в таблице серверов новым функциям
предпочитают стабильность. Поскольку HTTP/2 является относительно
молодым протоколом, версии веб-серверов по умолчанию обычно не
включают в себя его поддержку. Если ваша операционная система не позволяет легко перевести сервер на HTTP/2, но вы хотите использовать
именно эту версию протокола, у вас остается не так уж много вариантов,
а установка приложений из неофициальных источников – это большой
риск, и вы должны понимать возможные последствия, прежде чем идти
по этому пути (см. сноску ниже).

Риски, возникающие при установке приложений
из неофициальных источников
Установка приложений из неофициальных источников представляет собой
загрузку готового пакета с другого сайта, добавление репозитория для диспетчера пакетов или установку исходного кода.
Загрузка готового пакета от третьего лица означает, что вы полностью доверяете ему ключевую часть своей инфраструктуры и root (как обычно это
делают веб-серверы). Кроме того, многие готовые пакеты статически компилируются в соответствии с версией OpenSSL, поэтому, если в OpenSSL обнаружена уязвимость, вам необходимо обновить свой веб-сервер и исправить
ошибки. Многих компаний не устраивает ни одно из этих ограничений. Если
вам удобно использовать готовый пакет стороннего производителя, вы можете воспользоваться сайтом CodeIta, который предоставляет репозиторий
с готовыми пакетами Apache и nginx и предоставляет инструкции по их установке.
Также вы можете «упаковать» свой сервер в контейнер, например с помощью Docker. Доступны образы контейнеров с соответствующими версиями
обычных веб-серверов. В этом случае вы также доверяете свой сервер стороннему лицу, однако ввиду того, что вы «упаковываете» приложения в отдельный контейнер, для стороннего лица доступна лишь часть информации.
Такой тип программного обеспечения является отдельной темой, поэтому мы
не будем обсуждать его в нашей книге.

Способы перехода вашего сайта на HTTP/2

109

Для тех, кто не привык доверять третьим лицам, существует еще один вариант. Например, вы можете установить программное обеспечение из оригинального исходного кода. Исходный код должен принадлежать авторитетному источнику (в идеале создателю операционной системы). После загрузки
код необходимо подтвердить либо путем проверки подписи, либо путем
проверки хеша загрузки.
Даже если вы пользуетесь официальными источниками, вы управляете программным обеспечением уже не с помощью инструментов диспетчера пакетов, поэтому вам будут недоступны патчи безопасности, которые обычно
устанавливает диспетчер; таким образом, вам придется делать это вручную.
В стандартных репозиториях RHEL 7 и CentOS уже есть Apache 2.4.6. Однако
это не оригинальная версия. Red Hat постоянно обновляет его, чтобы включить в новые версии все необходимые обновления безопасности. Установив
версию вне диспетчера пакетов, вы лишитесь всех этих обновлений. Если вы
не установите их вручную, вы рискуете получить небезопасное программное
обеспечение, уязвимое для атак.
Еще один вариант – использование полуофициальных репозиториев. Многие создатели операционных систем предоставляют альтернативные репозитории для хранения программного обеспечения (например, Red Hat
Software Collections). Кроме того, они могут предоставлять и официальные
репозитории (как nginx). Здесь преимуществами являются простота установки и простота исправлений ошибок.
Таким образом, необходимо решить, какой вариант подходит именно вам.
___________________________________
a
https://codeit.guru/en_US.

В приложении к этой книге приведены инструкции по установке и обновлению программного обеспечения для поддержки HTTP/2 некоторых
распространенных веб-серверов и платформ. В зависимости от операционной системы и пользователей вашего веб-сервера процесс может быть
довольно сложным. Со временем он станет проще, так как настройки по
умолчанию будут обновлены до версий с поддержкой HTTP/2. Однако
в ближайшие несколько лет многим из нас придется потрудиться для
включения поддержки HTTP/2 в программное обеспечение своих вебсерверов.
Как вы увидите в следующих двух разделах, сегодня доступны и другие
пути перехода на HTTP/2. При определенных настройках балансировщика нагрузки ваш сервер может не предоставлять HTTP/2 пользователям.
Такое случается, когда сам балансировщик не поддерживает HTTP/2.
Если вы хотите настроить простой веб-сервер для экспериментов
с HTTP/2 и, возможно, последовать некоторым примерам из этой книги,
я рекомендую выбрать веб-сервер, который вам больше всего подходит.
Если у вас нет особых предпочтений, Apache является наиболее полнофункциональным из популярных веб-серверов благодаря его доступности на многих платформах, а также поддержке HTTP/2 push и HTTP/2
прокси (о которых мы поговорим позже).

Глава 3

110

Переход на HTTP/2

3.2.2 HTTP/2 с обратным прокси-сервером
Еще одним вариантом реализации HTTP/2 выступает установка обратного прокси-сервера, поддерживающего данную версию протокола; такие
прокси могут переводить запросы на HTTP/1.1 и передавать их на ваш
веб-сервер, как показано на рис. 3.5.
Веб-браузер

Internet

Обратный
прокси-сервер

HTTP/2

Рис. 3.5

Веб-сервер

HTTP/1

Реализация HTTP/2 с обратным прокси

Обратные прокси-серверы, как следует из их названия, работают ровно наоборот относительно стандартных перехватывающих прокси. Последние «защищают» сеть от внешнего мира и прокладывают путь исходящему трафику, за счет чего и происходит связь с Internet. Обратные
прокси обрабатывают входящий трафик и обеспечивают ему доступ
к серверам, к которым нет прямого доступа. Такой тип прокси достаточно распространен и используется повсеместно обычно по одной из двух
следующих причин:
„„ обратный прокси работает как балансировщик нагрузки;
„„ он облегчает работу HTTPS или HTTP/2.
Если обратный прокси используется в качестве балансировщика нагрузки, он работает как минимум с двумя веб-серверами и отправляет
трафик на любой из них в зависимости от режима настройки (режим реального времени или режим ожидания). Балансировщики нагрузки, работающие в реальном времени, используют специальный алгоритм, который помогает им решить, как правильно разделить трафик (например,
на основе исходного IP-адреса или алгоритма циклического перебора).
На рис. 3.6 изображена реализация обратного прокси как балансировщика нагрузки.
Если вы используете обратный прокси именно таким образом, возможно, вам удастся перевести на HTTP/2 только балансировщик, при
этом не затрагивая веб-сервер. Как упоминалось ранее, перевод вебсервера на HTTP/2 может не дать положительного эффекта, если весь
трафик сначала проходит через балансировщик нагрузки. Некоторые существующие балансировщики (такие как F5, Citrix Netscaler и HAProxy)
уже поддерживают HTTP/2. Остальные также скоро добавят поддержку
данной версии.

111

Способы перехода вашего сайта на HTTP/2

Веб-браузер

Internet

Обратный
прокси-сервер
с балансировкой
нагрузки

Веб-серверы

Веб-сервер 1
HTTP/2

Веб-сервер 2

Рис. 3.6 Обратный прокси-сервер как балансировщик нагрузки

Необходимо ли использовать HTTP/2 постоянно?
При реализации HTTP/2 c обратным прокси происходит следующее: соединение проходит через HTTP/2 до момента контакта с обратным прокси, после чего открывается отдельное соединение (возможно, проходящее через
HTTP/1.1). Этот процесс схож с тем, как при использовании того же обратного
прокси, HTTPS-соединение переходит в HTTP после контакта с ним. Такой
способ является распространенным вариантом упрощения настройки HTTPS,
так как здесь сертификаты необходимо настраивать лишь в точке входа, где
ими также можно управлять). Данный вариант пользуется популярностью по
причине того, что в прошлом для установки HTTPS-соединений требовалось
большое количество ресурсов (сейчас, благодаря увеличению вычислительной мощности, потребность в этом снизилась).
Итак, необходимо ли все же постоянно использовать HTTP/2, и что вы теряете при использовании HTTP/1.1-соединений на стороне сервера?
Основным преимуществом HTTP/2-соединений является высокая скорость,
в сравнении с другими соединениями с высокой задержкой и низкой пропускной способностью. Такие характеристики могут быть присущи соединениям, с помощью которых пользователи подключаются к вашему пограничному серверу (в данном случае это обратный прокси). Здесь проблемы
производительности HTTP/1.1 не так страшны, так как путь, который должен пройти трафик от вашего обратного прокси до остальной части вебинфраструктуры, вероятнее всего, небольшой.
Использовать отдельное соединение для прохождения HTTP/2-трафика от
обратных прокси к остальным серверам не совсем разумно, так как последние не ограничиваются шестью соединениями, установленными браузерами.
Более того, существует мнение, что использование отдельного соединения
может привести к проблемам с производительностью, однако это зависит

Глава 3

112

Переход на HTTP/2

от того, как оно реализовано на обратном прокси и на конечном сервере.
Отчасти по этой причине nginx заявил, что откажется от реализации HTTP/2
для прокси-соединенийa.
Таким образом, как и в случае с HTTPS, для базовой поддержки HTTP/2 нет
необходимости использовать эту версию протокола на всех участках вашей
инфраструктуры. Даже функции, работающие исключительно при поддержке HTTP/2, например HTTP/2 push, можно настроить и реализовать в такой
системе. Об этом мы поговорим в главе 5.
___________________________________
a
http://mailman.nginx.org/pipermail/nginx/2015-December/049445.html.

В других случаях, когда обратный прокси используется не в качестве
балансировщика нагрузки, часто бывает, что веб-сервер (например,
Apache или ngix) располагается вне внутренних серверов приложения
(например, Tomcat или Node.js). Таким образом, веб-сервер передает некоторые запросы еще и внутреннему серверу, а не только прокси (см.
рис. 3.7).
Такой способ имеет ряд преимуществ. Основным из них является то,
что вы освобождаете веб-сервер от лишней загрузки, например статических ресурсов (изображений, CSS-файлов, библиотек Java­Script и т. д.).
Кроме того, вы разгружаете работу HTTPS и, конечно, HTTP/2. Снижая
нагрузку на сервер приложений, вы позволяете ему лучше выполнять
свою работу: обрабатывать динамические ресурсы и выполнять поиск
в базе данных.
Веб-браузер

Internet

HTTP/2

Сервер приложений
(например, Tomcat)

Веб-сервер
(например,
Apache)

Сервер базы
данных
(например, Oracle)

HTTP/1.1

Статические файлы
(изображения, CSS,
JavaScript)

Код для динамического
контента (JSP, сервлеты,
JavaScript)

Рис. 3.7 Веб сервер, располагающийся вне сервера приложений/сервера базы данных

Кроме того, данный способ имеет преимущества с точки зрения безопасности, поскольку первой точкой контакта является веб-сервер, который в свою очередь может защитить более уязвимый сервер приложений

113

Способы перехода вашего сайта на HTTP/2

или сервер базы данных от вредоносных запросов. Поэтому поддержку
HTTP/2 возможно реализовать и на сервере приложений: просто разместите перед ним другой сервер, поддерживающий HTTP/2.
Обратный прокси может помочь и при тестировании HTTP/2 и проверке его влияния на ваш сайт. Для этого следует разместить обратный
прокси рядом с вашим сервером под другим именем (например, http2.
example.com вместо test.example.com). Затем прокси сделает запрос к основному серверу, используя HTTP/1.1 через высокоскоростное локальное соединение, как изображено на рис. 3.8.
Веб-браузер

Internet

Веб-сервер
(https://www.example.com)

HTTP/1.1

HTTP/1.1

HTTP/2

Веб-браузер

Internet

Временный обратный прокси
(https://http2.example.com)

Рис. 3.8 Тестирование HTTP/2 с помощью временного добавления
обратного прокси

Если ваш сервер уже поддерживает HTTP/2, но вы еще не переключились на него, вам может не потребоваться создание отдельного проксисервера. Если вы создадите виртуальный хост с выключенным HTTP/2
и отдельным именем, вы сможете запустить как сайты на HTTP/1.1, так
и на HTTP/2; следовательно, вы сможете протестировать HTTP/2 перед
его использованием на реальном хосте, которым пользуются посетители
веб-сайта.

3.2.3 HTTP/2 и CDN
CDN – это всемирная сеть серверов, которые могут выступать локальной
точкой контакта для вашего веб-сайта. Посетители сайта подключаются
к ближайшему серверу CDN, так как сеть располагает большим количеством DNS-записей по всему миру. Запросы направляются на ваш сервер

Глава 3

114

Переход на HTTP/2

(исходный сервер), а их копии кешируются в CDN, что ускоряет процесс
передачи идентичного запроса в последующие разы. Большинство CDNсерверов уже поддерживает HTTP/2, поэтому вы можете воспользоваться
ими, оставив при этом исходный сервер на HTTP/1.1. Такой способ схож
с использованием обратного прокси. Однако отличаются они тем, что
сети CDN имеют собственные обратные прокси и управляют ими вместо
вас. Вариант реализации вышеописанного способа показан на рис. 3.9.

Сервер-источник
HTTP/2
HTTP/2

Сервер CDN
HTTP/1.1

Сервер CDN

HTTP/1.1

HTTP/2
HTTP/2

HTTP/1.1
HTTP/1.1
HTTP/2

Сервер CDN
HTTP/2

Сервер CDN

Рис. 3.9

Переключение на HTTP/2 с помощью CDN

Несмотря на то что при реализации данного способа вы используете
дополнительные элементы инфраструктуры, подключение через CDN
может быть значительно быстрее прямого, поскольку оно позволяет
локальному серверу обрабатывать некоторые запросы настройки подключения для клиента (например, установка исходного TCP-соединения
или согласование HTTPS). Преимущества такого подхода перекрывают
недостатки наличия дополнительного серверного перехода при совершении запроса. Кроме того, CDN-серверы позволяют кешировать еще
и ответы, таким образом, все дополнительные запросы обрабатываются
локальным сервером, а не исходным. Таким образом, экономится время,
снижается нагрузка на исходный сервер и увеличивается его пропускная
способность.
CDN – это форсированные обратные прокси-серверы. До появления
HTTP/2 их использовали преимущественно для повышения производительности, но, как оказалось, они также могут облегчить переключение
сервера на новую версию протокола.
CDN могут работать и с HTTPS-подключениями, необходимыми для
HTTP/2. Однако, если исходный сервер не поддерживает HTTPS, то трафик будет шифроваться только до контакта с ним. В основном HTTPS
используют для снижения рисков на клиентской стороне (например,

Устранение неполадок при настройке HTTP/2

115

подключение к неизвестной сети Wi-Fi сопряжено с рисками, которые
в силах решить HTTPS), но предпочтительнее все же использовать HTTPS
от начала и до конца соединения. Многие люди считают, что недобросовестно выгружать HTTPS в CDN, если весь оставшийся путь будет проходит через HTTP-соединение, так как в таком случае посетители веб-сайта
не будут осведомлены о том, что их пароли потенциально передаются по
незащищенному соединению. Настроить HTPPS в соответствии со всеми
требованиями HTTP/2 (например, ALPN) непросто. Однако, по крайней
мере, вы можете использовать для этой цели CDN, а для подключения
к исходному серверу использовать старую конфигурацию HTTPS через
HTTP/1.1.
Сети CND дают нам множество преимуществ, в том числе они могут
обеспечить поддержку HTTP/2. Они давно внедрили поддержку HTTP/2,
а некоторые CND предоставляют доступ к их функционалу бесплатно,
что будет полезно для небольших сайтов. CDN также могут выполнять
расшифровку трафика, поэтому работать с такой третьей стороной вам
будет довольно комфортно.

3.2.4 Вывод по реализации HTTP/2
Существует несколько способов реализации HTTP/2 на вашем сайте.
Выбор нужного вам способа зависит от того, какую инфраструктуру для
обработки трафика вы используете. К сожалению, прямое подключение
HTTP/2 – это довольно сложно, а также требует большого количества
настроек, которые необходимо выполнить вручную. По мере распространения данной версии и обновления серверного программного обеспечения ситуация будет меняться. Однако сейчас это все еще трудный
процесс.
Помимо прямого подключения существую и другие варианты. Например, использование обратных прокси или CDN. Данные методы упрощают переход на HTTP/2, пока поддержка новой версии распространена не
на всех широко известных серверах.
Сегодня вы можете выбрать способ, который лучше всего подходит
для вашего веб-сайта, а также провести некоторые эксперименты, которые покажут, как работает выбранный вами способ. Остальная часть
нашей книги будет полезна для тех, кто уже имеет серверы с поддержкой
HTTP/2, на которых они смогут опробовать ряд примеров. Однако некоторые примеры будут приведены и на общедоступных веб-сайтах.

3.3

Устранение неполадок при настройке HTTP/2
Вы можете узнать, используется ли HTTP/2, заглянув в инструменты разработчика в вашем браузере. Иногда, несмотря на то что сервер использует HTTP/2, бывает сложно запустить работу протокола ввиду ряда причин, упомянутых в этой главе. Рассмотрим некоторые из них.

Глава 3

116

Переход на HTTP/2

HTTP/2 не поддерживается на вашем сервере. Очевидно, что для
использования HTTP/2 ваш сервер должен его поддерживать. Как
уже было сказано, на сегодняшний день большинство серверов не
поддерживает HTTP/2 по умолчанию. Вам следует проверить, какую версию серверного программного обеспечения вы используете
и была ли в нее добавлена поддержка HTTP/2. Обратите внимание,
что установка последних обновлений (например, с помощью yum
update или apt-get) не гарантирует того, что ваш сервер начнет поддерживать HTTP/2.
„„ HTTP/2 не включен на вашем сервере. Даже если сервер поддерживает
HTTP/2, использование этой версии может быть отключено. Некоторые серверы (например, IIS) по умолчанию настроены на HTTP/2.
На других серверах (например, Apache) поддержка HTTP зависит
от используемой конфигурации или сборки. Например, сборки
ApacheHaus для Windows включают HTTP/2 по умолчанию, но при
установке из исходного кода такая функция не работает. Кроме того,
начиная с версии 2.4.27, Apache не поддерживает HTTP/2 при использовании модуля prefork mpm1.
Также некоторые параметры компиляции (например, --enablehttp2 для Apache и --withhttp_v2_module для nginx) необходимы для
включения HTTP/2, но в свою очередь не включают его по умолчанию. Если HTTP/2 не работает на вашем сервере, посмотрите соответствующие инструкции в документации.
„„ На вашем сервере не включен HTTPS. Как вы уже знаете из раздела 3.1.1, веб-браузеры поддерживают HTTP/2 только через HTTPSсоединения. Если ваш сайт использует HTTP-соединение, вы не
сможете включить HTTP/2.
„„ На вашем веб-сервере не включена поддержка ALPN. ALPN – это расширение протокола TLS, на основе которого создается сеанс HTTPS,
позволяющий использовать HTTP/2. Некоторые браузеры (например, Safari, Edge и Internet Explorer на момент написания данной
книги) позволяют перейти на HTTP/2 и при использовании более
старого NPN, так и при использовании более нового ALPN. Другие
же браузеры (например, Chrome, Firefox и Opera) требуют использования именно ALPN.
Вы можете проверить поддержку ALPN с помощью онлайн-инструментов, таких как SSLLabs2 (который запускает комплексный
тест для настройки HTTPS, однако его проведение занимает несколько минут) или KeyCDN HTTP/2 Test3 (который работает быст­
рее, поскольку тестирует только HTTP/2 и ALPN). Если ваш сервер
не является общедоступным, вы не можете использовать выше­
описанные инструменты, зато можете пользоваться инструмента„„

1
2
3

https://github.com/icing/mod_h2/releases/tag/v1.10.7.
https://www.ssllabs.com/ssltest/.
https://tools.keycdn.com/http2-test.

Устранение неполадок при настройке HTTP/2

117

ми командной строки, такими как s_client OpenSSL (при условии,
что ваша версия OpenSSL поддерживает ALPN):
openssl s_client -alpn h2 -connect www.example.com:443 –status

В качестве альтернативы вы можете использовать инструмент
testssl1, который способен выполнить большинство тестов SSLLabs.
Однако для полного тестирования поддержки HTTP/2 все же требуется версия OpenSSL с поддержкой ALPN.
Как и браузеры, некоторые веб-серверы (например, Apache) используют исключительно ALPN; другие (например, nginx) поддерживают и ALPN, и NPN. Поддерживает ли ваш сервер ALPN, зависит
от версии библиотеки TLS, которую вы используете. В табл. 3.3 приведен список библиотек, поддерживающих ALPN. Если вы не знаете,
какую библиотеку TLS используете, скорее всего, это будет OpenSSL
для Linux, LibreSSL для macOS или SChannel для Windows.
Таблица 3.3 Поддержка ALPN TLS-библиотеками
Библиотека TLS
OpenSSL
LibreSSL
SChannel (использовалась приложениями Microsoft)
GnuTLS

Версия с поддержкой ALPN
1.0.2
2.5.0
8.1/ 2012 R2
3.2.0

Даже если ваша библиотека TLS поддерживает ALPN, ваше серверное программное обеспечение могло быть создано с другой ее
версией. Например, в RHEL/CentOS 7.4 используется OpenSSL 1.0.2,
но версии Apache и nginx, установленные по умолчанию, собраны
еще с применением OpenSSL 1.0.1, поэтому они не поддерживают
ALPN. При перезапуске Apache обычно добавляет строку в журнал
ошибок, в которой указана версия OpenSSL:
[mpm_worker:notice] [pid 19678:tid 140217081968512] AH00292:
Apache/2.4.27 (Unix) OpenSSL/1.0.2k-fips configured -- resuming normal
operations

В качестве альтернативы вы можете добавить ldd перед mod_ssl:
$ ldd /usr/local/apache2/modules/mod_ssl.so | grep libssl
libssl.so.10 => /lib64/libssl.so.10 (0x00007f185b829000)
$ ls -la /lib64/libssl.so.10
lrwxrwxrwx. 1 root root 16 Oct 15 16:07 /lib64/libssl.so.10 ->
libssl.so.1.0.2k

Для того чтобы узнать сборку ngix, вы можете использовать параметр –V:
$ nginx -V
nginx version: nginx/1.13.6
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
1

https://testssl.sh/.

Глава 3

118

Переход на HTTP/2

built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --with-http_ssl_module --with-http_v2_module

Способы для других серверов прописаны в их документации.
На вашем веб-сервере не поддерживаются надежные шифры HTTPS.
В спецификации HTTP/2 перечислен ряд шифров, которые клиент
не должен использовать для HTTP/2-соединения1. Некоторые браузеры (например, Chrome) не используют их, поэтому, если вы хотите
перейти на HTTP/2, вам следует пользоваться такими шифрами, как
ECDHE GCM или POLY, которые на момент написания данной книги являются наиболее подходящими. В большинство реализаций по
умолчанию включены более надежные шифры, однако при переносе старой конфигурации шифра из предыдущей реализации они
могут быть не включены.
Настроить шифр можно с помощью инструмента онлайн-тестирования SSLLabs. Поначалу вам, возможно, будет сложно в нем разобраться, однако именно он способен дать полную информацию
о настройке HTTPS на вашем сервере, а также указать, поддерживается ли HTTP/2 для обычных клиентов.
Также вы можете воспользоваться ресурсом Mozilla SSL Configuration Generator2. Большинство сайтов должно использовать конфигурацию Modern, хотя для старых клиентов подойдет и Intermediate.
„„ Используется перехватывающий прокси, который переводит вас на
HTTP/1.1. Прокси (например, в корпоративной среде) или антивирусное программное обеспечение могут переводить вас на HTTP/1.1,
поскольку они перехватывают HTTPS-соединение. Мы обсуждали
это в разделе 3.1.1. Очень важно удостовериться, что сертификат
HTTPS для вашего веб-сайта был выдан настоящим центром сертификации.
Если ваш сайт является общедоступным, такие инструменты, как
SSLLabs или KeyCDN HTTP/2 Test3 помогут узнать, поддерживает ли
он HTTP/2. Если да, то проблема может носить локальный характер
и, возможно, она может быть вызвана перехватывающим прокси.
Также перехватывать HTTPS или добавлять некоторые сайты
в список разрешенных могут различные вредоносные программы.
„„ Заголовок обновления был перенаправлен неверно. Внутренний сервер (например, Apache) может использовать заголовок Upgrade: h2
при переключении на HTTP/2. Если этот заголовок неверно перенаправляется прокси-сервером, могут возникнуть проблемы. Браузер пытается перейти на HTTP/2 (как следует из заголовка) и терпит неудачу, поскольку обратный прокси-сервер не поддерживает
HTTP/2. В таком случае прокси не должен отправлять заголовок Upgrade. В главе 4 мы обсудим эту проблему подробнее. Браузер Safari
„„

1
2
3

https://httpwg.org/specs/rfc7540.html#BadCipherSuites.
https://mozilla.github.io/server-side-tls/ssl-config-generator/.
https://tools.keycdn.com/http2-test.

Резюме

119

подвержен возникновению подобных ситуаций и зачастую выдает
ошибку nsposixerrordomain:100.
„„ Заголовки HTTPS недействительны. При наличии недействительных заголовков (если, например, в них присутствуют пробелы или
двойные двоеточия) браузер Chrome отправляет сообщение ERR_
SPDY_PROTOCOL_ERROR1. К слову, при использовании HTTP/1.1 он прощает подобные недочеты. Safari в такой ситуации может выдать уже
знакомую нам ошибку nsposixerrordomain: 100.
„„ В кеш попадают элементы исходного протокола загрузки. Если вы
обновили сервер до поддержки HTTP/2 и пытаетесь его протестировать, не очистив предварительно кеш, в тесте будут использованы
кешированные ресурсы. Кешированные элементы показывают версию HTTP, которая использовалась для загрузки запроса (это может
быть и HTTP/1.1). В таком случае браузер отправит ответ 304 Not Modified.

Резюме
Почти все основные браузеры предоставляют поддержку HTTP/2 на
стороне клиента.
„„ Поддержка HTTP/2 на стороне сервера доступна в более новых версиях, но зачастую эти версии нелегко установить без полного обновления сервера и/или вручную.
„„ Доступны различные варианты реализации поддержки HTTP/2, в том
числе и посредством использования сторонней инфраструктуры, такой как CDN.
„„ Существует ряд причин, по которым HTTP/2 не может использоваться
даже после того, как он был включен.
„„

1

https://www.michalspacek.com/chrome-err_spdy_protocol_error-and-an-invalidhttp-header.

Часть II
Использование HTTP/2

В

первой части этой книги я рассказал о необходимости и преимуществах перехода на новую версию HTTP, описал сущность HTTP/2,
а также представил способы его настройки для вашего веб-сайта.
Большинство людей захочет перейти на HTTP/2 уже на этом этапе.
HTTP/2 разрабатывался так, чтобы переход на него был довольно прос­
тым, а сразу после перехода пользователь мог ощутить все преимущества новой версии.
Однако, чтобы ощутить эти преимущества в полной мере, предпочтительнее разобраться в более глубинных частях работы протокола.
В этой части книги описаны основные аспекты новой версии. В главах 4
и 5 рассматриваются технические детали протокола, благодаря которым
владельцы веб-сайтов и разработчики смогут извлекать из HTTP/2 как
можно больше преимуществ. В главе 6 я несколько отойду от самой сущности протокола и расскажу о его значении для веб-производительности,
а также рассмотрю возможное изменение методов, которое позволит оптимизировать их для мира HTTP/2.

4

Основы протокола HTTP/2

В этой главе мы рассмотрим:
основы протокола HTTP/2: что это такое и чем он отличается от HTTP/1.1;
„„ переход на HTTP/2 со стороны клиента и сервера;
„„ фреймы HTTP/2 и их отладку.
„„

В этой главе мы поговорим об основах HTTP/2 (фреймы, потоки и мультиплексирование). Более сложные части протокола (такие как приоритет
потоков и управление ими) мы обсудим в главах 7 и 8. Исчерпывающая
информация о протоколе содержится в спецификации HTTP/21. Во время
или после прочтения данной главы вы можете обращаться к этому документу. Однако в главе 4 я обращаю ваше внимание на дополнительные
детали и привожу интересные примеры, которые (я надеюсь) упростят
процесс изучения протокола.

4.1

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

https://tools.ietf.org/html/rfc7540.

Глава 4

122

Основы протокола HTTP/2

протокол стал двоичным, а не текстовым;
передача данных осуществляется мультиплексированно, а не синхронно;
„„ добавлено управление потоками информации;
„„ стала возможна установка приоритета потоков;
„„ появилась возможность сжатия заголовков;
„„ реализована технология server push.
Все вышеперечисленные изменения (которые мы позже обсудим более подробно) являются фундаментальными и не имеют обратной совместимости. В то время как веб-сервер, работающий на HTTP/1.0, мог
принимать сообщения от серверов HTTP/1.1, игнорируя при этом новый
функционал, перешедшие на HTTP/2 серверы используют уже совсем
иную структуру и формат данных и поэтому не могут принимать запросы старых версий. Именно по этой причине протокол HTTP/2 считается
базовым обновлением версии.
Различия между старой и новой версией представлены в основном
на уровне отправки и получения сообщений сервером и клиентом. На
других уровнях, с которыми сталкивается большинство разработчиков
(например, семантика HTTP)1, работа новой версии во многом схожа со
старой. У протокола HTTP/2 можно наблюдать уже знакомые методы
(GET, POST, PUT и т. д.), URL-адреса, коды ответа (200, 404, 301, 302) и HTTPза­головки (большинство). Однако HTTP/2 выполняет отправку все тех же
HTTP-запросов намного эффективнее.
Во многих аспектах HTTP/2 похож на HTTPS. Он так же эффективно
«обертывает» стандартные HTTP-сообщения в специальный формат перед отправкой и «разворачивает» их после получения. Таким образом,
несмотря на то что клиент (веб-браузер) и сервер (веб-сервер) требуют
полного соответствия версий на уровне протокола, обработка сообщений данных версий на более высоких уровнях происходит практически
одинаково, поскольку они используют одни и те же базовые концепции
HTTP. Однако разработка сайтов с помощью HTTPS и HTTP/2 отличается.
Точно так же, как всестороннее изучение HTTP/1 позволило разработчикам оптимизировать многие сайты (как уже обсуждалось в главе 2),
исследование HTTP/2 поможет разработать новые варианты оптимизации, повысить эффективность работы веб-разработчиков и увеличить
скорость работы сайтов. Именно по этой причине важно понять, какие
новшества были введены в новую версию протокола.
„„
„„

HTTP/2.0 или HTTP/2?
Изначально HTTP/2 назывался HTTP/2.0, но впоследствии рабочая группа
HTTP решила отказаться от дополнительного номера версии (.0). Название
HTTP/2 характеризует все те новшества, которые были добавлены в него
(такие как двоичный формат, мультиплексирование и т. д.). Разработчики
предполагают, что новые версии (например, HTTP/2.1) и их реализации бу1

https://tools.ietf.org/html/draft-ietf-httpbis-semantics.

Почему HTTP/2, а не HTTP/1.2?

123

дут совместимы с основным протоколом. Похожая ситуация была и с HTTP/1
(данное название не прижилось, однако в книге мы используем его для обозначения версий HTTP/1.0 и HTTP/1.1), протоколом, имеющим текстовый
формат, структура которого состоит из заголовков и тел.
Кроме того, в сообщениях HTTP/2, в отличие от формата сообщений предыдущих версий, не указывается номер версии. Например, в HTTP/2 не используются запросы наподобие GET /index.html. Однако дополнительный но-

мер версии часто фигурирует в файлах журналов некоторых реализаций.
Например, в файлах журнала Apache при обработке запросов в формате
HTTP/1 отображается номер версии HTTP/2.0:

78.1.23.123 - - [14/Jan/2018:15:04:45 +0000] 2 "GET / HTTP/2.0" 200 1797
"-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
Таким образом, в журнале вы видите сообщение типа HTTP/1 (несмотря на
утверждение об обратном выше). Но на самом деле этот запрос не настоящий, он искусственно создается сервером в целях упрощения обработки
журнальных сообщений. Фактически название HTTP/2.0 встречается только
в предисловии спецификации протокола (см. раздел 4.2.5).

4.1.1 Двоичный, а не текстовый
Одно из основных различий между HTTP/1 и HTTP/2 состоит в том, что
последний является двоичным протоколом на основе пакетов, в то время как первый полностью основан на тексте. С текстовыми протоколами
проще работать людям, однако компьютерам их обрабатывать сложнее.
Изначально HTTP был простым протоколом запроса–ответа, и текстовый формат был для него приемлем, однако такой формат все больше
ограничивает его использование в современной сети Internet.
Текстовый формат предполагает последовательную отправку запросов, т. е. перед отправкой нового запроса предыдущий должен быть
полностью завершен, и на него должен быть получен ответ. Именно
так и работал HTTP на протяжении последних 20 лет, хотя в него были
внесены небольшие улучшения. Например, в HTTP/1.0 ввели двоичный
формат тела ответа, благодаря чему ответы могли содержать изображения и другие медиафайлы, а в HTTP/1.1 впервые появилась конвейеризация запросов (см. главу 2) и фрагментированная передача сообщений
неопределенной длины. Последнее позволило отправлять содержимое
тел запросов по частям, например сначала одну часть, а затем – по мере
доступности – остальное содержимое. Таким образом, клиент, получивший фрагментированный ответ (или сервер, принимающий фрагментированный запрос), может начать обработку полученных частей, не
дожидаясь остальных. Такую технологию часто используют в случаях,
когда объем динамически сгенерированных данных заранее неизвестен.
И фрагментированная передача, и конвейеризация подвержены проб­
леме блокировки заголовка (HOL), в результате которой сообщение из
верхней части очереди прерывает отправку последующих ответов. Стоит

124

Глава 4

Основы протокола HTTP/2

также отметить, что на практике крайне мало веб-серверов поддерживает конвейеризацию.
HTTP/2 полностью перешел на двоичный формат, где HTTP-сообщения
разделяются и отправляются в отдельных фреймах. При использовании
HTTP/2 все сообщения по умолчанию отправляются по частям. Специ­
фикация HTTP/2 гласит:
Механизм фрагментированной передачи данных, определенный
в разделе 4.1 RFC7230, НЕ ДОЛЖЕН использоваться в HTTP/2.
Вышеупомянутые фреймы похожи на TCP-пакеты, на которых основывается большинство HTTP-соединений. Полное HTTP-сообщение
складывается из всех полученных фреймов. Несмотря на то что HTTP/2
во многом похож на TCP, он обычно работает поверх TCP, а не заменяет его (хотя в Google, экспериментируя с заменой TCP на QUIC, создали
более простую реализацию, где HTTP/2 работает поверх него, о чем мы
поговорим в главе 9). TCP – это базовый протокол, который гарантирует
доставку и правильный порядок сообщений, для чего он и используется
в HTTP/2.
Двоичный формат HTTP/2 предназначен для отправки и получения сообщений, в то время как сами сообщения похожи на сообщения формата HTTP/1. Двоичные фреймы обычно обрабатываются клиентами нижнего уровня или библиотеками (браузеров или серверов). Как уже было
сказано ранее, приложения более высокого уровня, такие как Java­Script,
не заботятся о том, как именно отправляются сообщения. Поэтому они
обрабатывают HTTP/2-соединение практически так же, как и HTTP/1.1.
Однако понимание сути фреймов HTTP/2, а также их просмотр помогает при отладке неожиданных ошибок. Особенно актуален этот аспект
на ранней стадии внедрения протокола, так как в это время могут возникать (надеемся, что редко!) проблемы реализации при определенных
сценариях.

4.1.2 Мультиплексирование вместо синхронности
HTTP/1.1 являлся синхронным протоколом, позволяющим отправлять
запросы (и получать на них ответы) по одному: клиент отправляет сообщение, а сервер получает ответ. В главе 2 мы уже обсудили, почему
данный протокол неэффективен применимо к современной Всемирной
паутине, где веб-сайты могут состоять из сотен ресурсов. Для подобных
проблем были созданы обходные пути, в HTTP/1 решение представляло
собой создание сразу нескольких соединений или отправку меньшего количества объемных запросов вместо множества маленьких. Однако оба
этих пути оказались неэффективными, так как создавали новые проб­
лемы. На рис. 4.1 показано создание трех TCP-соединений для параллельной отправки и приема трех запросов HTTP/1. Обратите внимание,
что запрос 1 не отображается на главной странице, так как он является
начальным, и только после его завершения в запросах 2–4 может быть
параллельно запрошено несколько ресурсов.

125

Почему HTTP/2, а не HTTP/1.2?

HTTP/2 позволяет выполнять несколько запросов одновременно в одном соединении, и для этого он использует разные потоки для каждого
HTTP-запроса или ответа. Такая технология стала возможной благодаря переходу на использование двоичных фреймов, где каждый фрейм
имеет свой идентификатор потока. Принимающая сторона сможет восстановить сообщение целиком, после того как все фреймы для данного
потока будут получены.
HTTP/1.1

Клиент
(веб-браузер)
Запрос 2
GET /styles.css

TCP-соединение 1
Запрос

Сервер
(веб-сервер)
Запрос 2
GET /styles.css

Ответ
TCP-соединение 2

Запрос 3
GET /script.js

Запрос

Запрос 3
GET /script.js

Ответ
TCP-соединение 3

Запрос 4
GET /image.jpg

Запрос
Ответ

Запрос 4
GET /image.jpg

Рис. 4.1 Параллельное выполнение нескольких запросов HTTP/1 требует
нескольких TCP-соединений

Фреймы – это ключ к возможности параллельной отправки нескольких сообщений. Каждый фрейм содержит метку, которая указывает какому сообщению (потоку) он принадлежит. Таким образом, вы можете
отправлять или получать один, два, три или даже сто сообщений одновременно в одном мультиплексном соединении, в то время как при использовании HTTP/1 большинство браузеров ограничивалось шестью.
На рис. 4.2 также показаны три запроса, но, в отличие от 4.1, они отправляются последовательно в одном соединении (аналогично, как при конвейеризации HTTP/1.1), а ответы отправляются обратно в смешанном
виде (что уже невозможно при конвейеризации).
На данном примере мы видим, что запросы отправляются в разное
время, поскольку в конечном итоге каждый фрейм должен отправляться
по одному и тому же HTTP/TCP-соединению после предыдущего. Так же
происходит и при использовании HTTP/1.1, так как для запросов, хотя
они и кажутся параллельными, существует только одно сетевое соединение, и поэтому все они помещаются в очередь для отправки на сетевом
уровне. Основное отличие заключается в том, что соединение HTTP/2 не
блокируется после отправки запроса до того момента, пока на него не
будет получен ответ (в HTTP/1.1, как было описано в главе 2, соединение
закрывается).

Глава 4

126

HTTP/2

Клиент
(веб-браузер)

Поток 9
GET
/img.jpg
headers

Поток 7
GET
/script.js
headers

Поток 5 Поток 7 Поток 5 Поток 7
/style.css /script.js /style.css /script.js
headers headers
body
body

Поток 5
GET
/style.css
headers

Поток 9
/img.jpg
headers

Поток 9
/img.jpg
body

Уровень фрейминга HTTP/2

GET /script.js

Уровень фрейминга HTTP/2

Запрос 3

Рис. 4.2

Запрос 2

Запросы

GET
/styles.css

GET
/image.jpg

Сервер
(веб-сервер)

Одиночное TCP-соединение

Запрос 2

Запрос 4

Основы протокола HTTP/2

Ответы

GET
/styles.css

Запрос 3
GET /script.js

Запрос 4
GET
/image.jpg

Запрос трех ресурсов с помощью мультиплексного соединения HTTP/2

Точно так же ответы могут быть отправлены обратно в смешанном
виде (потоки 5 и 7 на рис. 4.2) или последовательно (поток 9 на рис. 4.2).
Порядок, согласно которому сервер отправляет ответы, полностью определяется на серверной стороне, однако клиент все же может указывать
приоритетные потоки. Если существует возможность отправки сразу нескольких ответов, сервер может отдавать приоритет важным ресурсам
(таким как CSS и Java­Script), а к второстепенным относить другие ресурсы (такие как изображения). Подробнее об этом мы поговорим в главе 7.
Каждый запрос получает новый инкрементный идентификатор потока (ID) (5, 7 и 9 на рис. 4.2), в соответствии с которым позже отправляются ответы. Таким образом, потоки являются двунаправленными, как
и сами HTTP-соединения. После получения ответа потоки закрываются.
Поток HTTP/2 не является прямым аналогом соединения HTTP/1.1, так
как при использовании новой версии протокола они отбрасываются и не
используются повторно, в то время как в HTTP/1.1-соединение остается
открытым и его можно использовать для отправки другого запроса.
В целях предотвращения конфликтов идентификаторов потоков была
придумана следующая система: запросам, инициированным клиентом,
присваиваются нечетные номера (например, 5, 7 и 9 на наших рисунках),
а запросам, инициированным сервером, – четные. Обратите внимание,
что на момент написания этой книги, серверы не обладают технической
возможностью инициировать потоки, за исключением определенных
случаев, где в конечном итоге они являются ответом на поток клиента.
Об этом мы поговорим в главе 5. Как упоминалось ранее, ответы помечаются идентификаторами, соответствующими запросам. Идентификатор
потока 0 (не показан на рисунках) – это поток управления соединением,
который может использовать как клиент, так и сервер.
Понимание рис. 4.2 – ключ к пониманию HTTP/2. Если вы поняли эту
концепцию и ее отличия от HTTP/1, вы прошли долгий путь изучения

127

Почему HTTP/2, а не HTTP/1.2?

HTTP/1. Конечно же, существуют некоторые сложности и тонкости, но
на данном рисунке можно увидеть две главные особенности протокола
HTTP/2:
„„ для отправки HTTP-запросов и ответов через одно TCP-соединение
HTTP/2 создает несколько двоичных фреймов и использует мультиплексированные потоки;
„„ HTTP/2 отличается от HTTP/1 на уровне отправки сообщений, а на
более высоком уровне основные концепции HTTP остаются неизменны. Запросы все также содержат метод (например, GET), информацию о необходимом ресурсе (например, /styles.css), заголовки,
тело, коды состояния (например, 200, 404), кешированные куки-файлы и т. д.
Первый пункт означает, что работа HTTP/2 может быть изображена
как на рис. 4.3, где каждый поток ведет себя как отдельное соединение
HTTP/1. Однако данный рисунок может быть неверно истолкован теми,
кто использует HTTP/1, поскольку потоки HTTP/2 не используются повторно (как соединения в HTTP/1), но и не являются полностью независимыми, и, как вы увидите в главе 7, это сделано не зря.
HTTP/2

Клиент
(веб-браузер)

Сервер
(веб-сервер)

Одиночное TCP-соединение
Поток 5

GET /styles.css

GET /styles.css

Запрос 3
GET /script.js

Запрос 4

Поток 7

Поток 9

GET /image.jpg

Рис. 4.3

Уровень фрейминга HTTP/2

Запрос 2
Уровень фреймингаHTTP/2

Запрос 2

Запрос 3
GET /script.js

Запрос 4
GET /image.jpg

Потоки HTTP/2 аналогичны соединениям HTTP/1

Второй пункт поясняет, почему HTTP/2 все еще не получил широкого
распространения. Веб-браузеры и веб-серверы могут обрабатывать содержимое HTTP/2 на низких уровнях, поэтому пользователи (и разработчики) не задумываются о его работе и относятся к нему как к обычному
протоколу. В разделе 4.2 указано, что HTTP/2 даже не нуждается в новой
схеме (часть https:// в начале URL-адреса). Фактически большинство
людей уже использует HTTP/2 и даже не подозревает об этом. Данным
протоколом пользуются Google, Twitter и Facebook, поэтому, если вы посещали данные сайты, вы тоже пользовались им.

128

Глава 4

Основы протокола HTTP/2

Глубокое понимание принципа работы HTTP/1 позволило веб-раз­
ра­ботчикам создавать более качественные сайты с высокой производительностью. Точно так же те из вас, кто уделит достаточно времени
изучению HTTP/2, смогут пользоваться всеми преимуществами данного
протокола.

4.1.3 Приоритет потоков и управление ими
До появления HTTP/2 протокол HTTP был простым протоколом, позволяющим в одном соединении отправлять один запрос и получать один
ответ, поэтому в установке приоритетов не было необходимости. Клиент
(обычно веб-браузер) определял приоритет и порядок отправки сообщений вне протокола HTTP. Кроме того, он использовал ограниченное количество HTTP/1-cоединений (обычно шесть). Согласно такому принципу высоким приоритетом обладали критические ресурсы (блокирующие
рендеринг элементы HTML, CSS и критический код Java­Script), а другие
элементы, не блокирующие рендеринг (такие как изображения и асинхронный Java­Script), загружались позже. Запросы образовывали очередь
и ожидали свободного HTTP/1-соединения, а порядок этой очереди,
устанавливаемый браузером, определял приоритет.
HTTP/2 же обладает куда более высоким лимитом количества одновременно выполняемых запросов (во многих реализациях лимит по
умолчанию составляет 100 активных потоков). Таким образом, браузеру
уже не нужно составлять очередь из запросов, ведь большинство из них
может быть отправлено в одно и то же время. Однако это может привести к снижению пропускной способности при загрузке ресурсов с более
низким приоритетом (например, изображений), вследствие чего может
казаться, что при использовании HTTP/2 страница загружается даже
медленнее. Установка приоритетов потоков необходима для того, чтобы в первую очередь отправлялись наиболее важные ресурсы. Установка приоритетов реализуется сервером, который, в то время как очередь
фреймов ожидает отправки, отправляет больше фреймов для запросов
с высоким приоритетом, чем для запросов с низким приоритетом. Кроме того, приоритизация обеспечивает большую степень контроля потоков, чем в HTTP/1, где все соединения были независимы друг от друга.
В HTTP/1, если не считать, что соединение может быть указано как не
подлежащее использованию, отсутствовала возможность установки приоритетов. Например, если вам необходимо отправить пять критических
ресурсов и один некритический, то при использовании HTTP/1 критические ресурсы могут быть отправлены по шести отдельным соединениям
с одинаковым приоритетом, а шестой – задержан. При использовании
HTTP/2 все шесть запросов могут быть отправлены с соответствующим
приоритетом, и таким образом распределяется количество ресурсов для
отправки каждого ответа.
Необходимость управления потоками – это еще одно следствие использования нескольких потоков в одном и том же соединении. И этот
процесс крайне важен. Если получатель не может обрабатывать входя-

Почему HTTP/2, а не HTTP/1.2?

129

щие сообщения так же быстро, как отправитель, часть работы остается
невыполненной, и эта часть должна буферизироваться, что в конечном
итоге приводит к тому, что некоторые пакеты требуют повторной отправки. TCP позволяет контролировать пропускную способность соединения, однако HTTP/2 требует того же на уровне потока. Возьмем, к примеру, веб-страницу, на которой размещаются видеоматериалы прямой
трансляции. Если пользователь останавливает видео, то было бы разумно приостановить загрузку данного потока, но при этом разрешить другим ресурсам продолжать загрузку через другие потоки.
В главе 7 мы еще раз затронем тему приоритизации потоков и управления ими, а также расскажем чуть подробнее о самих потоках. Мы поговорим об этом позже, так как обычно эти процессы контролируются
браузером и сервером, поэтому пользователи и веб-разработчики практически не касаются их (на момент написания книги).

4.1.4 Сжатие заголовков
Посредством HTTP-заголовков отправляется дополнительная информация о запросах и ответах от клиента к серверу и обратно. Зачастую они
могут повторяться. Рассмотрим некоторые заголовки, которые отправляются с каждым запросом и часто дублируют предыдущие:
Cookie; куки-файлы отправляются с каждым запросом, поступающим в домен (за исключением нестандартных запросов, которые
использует Amazon (см. главу 2), однако они, скорее, относятся к исключениям). Заголовки куки-файлов зачастую необходимы только
для HTML-ресурсов, однако при этом они отправляются для каждого ресурса и могут быть довольно объемными;
„„ User-Agent; в данном заголовке указывается используемый веб-брау­
зер. Данный параметр никогда не меняется в течение сеанса, однако такие заголовки отправляются с каждым запросом;
„„ Host; данный заголовок используется для полного определения URL
запроса. Заголовок будет повторяться для каждого запроса к одному и тому же хосту;
„„ Accept; этот заголовок определяет формат ожидаемого ответа (например, допустимые для браузера форматы изображений и т. д.).
Поскольку форматы, поддерживаемые браузером, обычно меняются только при обновлениях, заголовок Accept меняется в зависимости от типа запроса (изображение, документ, шрифт и т. д.), однако
для одного и того же события будет использоваться один и тот же
запрос;
„„ Accept-Encoding; этот заголовок определяет форматы сжатия (обычно
это алгоритмы gzip, deflate, а также br для браузеров, поддерживающих формат сжатия brotli). Как и заголовок Accept, в течение сеанса
он остается неизменным.
„„

Вышеописанные заголовки ответов могут дублироваться, а также напрасно занимать большой объем. Некоторые специализированные за-

Глава 4

130

Основы протокола HTTP/2

головки, например заголовки политики безопасности контента (Content
Security Policy), могут быть весьма большими. Особенно серьезной проблемой такие заголовки становятся в небольших запросах, в которых
они составляют пропорционально большую часть всего объема.
HTTP/1 позволяет сжимать тело HTTP (посредством заголовка AcceptEncoding, как упоминалось выше), но не заголовки. HTTP/2 позволяет
сжимать заголовки, но, как мы обсудим позже в главе 8, вместо сжатия
тел он вводит новый метод и разрешает сжатие перекрестных запросов,
предотвращая тем самым возникновение проблем безопасности при использовании алгоритмов сжатия тел запросов HTTP.

4.1.5 Server push
Еще одно важное различие между HTTP/1 и HTTP/2 заключается в том,
что HTTP/2 добавляет концепцию push-загрузки, которая позволяет серверу отвечать на запрос более чем одним ответом. При использовании
HTTP/1 при получении запрошенной домашней страницы браузер должен сначала обработать ее, а затем запросить другие ресурсы (такие как
CSS и Java­Script) и только потом начать отображение страницы. С помощью концепции push-загрузки, введенной в HTTP/2, вышеупомянутые
ресурсы могут быть отправлены вместе с первым ответом. Кроме того,
они должны быть доступны для использования браузером при необходимости.
Push-загрузка в протоколе HTTP/2 – это новая полезная концепция
HTTP, однако при неправильной реализации она может привести к потере пропускной способности. Так происходит, когда ресурсы не нужны
браузеру, но все равно отправляются сервером. Это особенно невыгодно,
если они дублируют предыдущие запросы и находятся в кеше браузера.
Правильное внедрение данной концепции является основным аспектом
максимально эффективного использования этой функции. Именно поэтому технологии push-загрузки HTTP/2 посвящена отдельная глава нашей книги (глава 5).

4.2

Как устанавливается HTTP/2-соединение
HTTP/2 сильно отличается от HTTP/1 на уровне соединения, поэтому для
правильного использования клиентский браузер и сервер должны уметь
обрабатывать и отправлять HTTP/2-сообщения. Ввиду того, что в этом
процессе задействованы две независимые стороны, должен быть процесс, в ходе которого каждая сторона должна иметь возможность сказать, что желает и может использовать HTTP/2.
Такая возможность появилась благодаря переходу на HTTPS, который был осуществлен на основе новой схемы URL (https://). HTTPSсоединения обслуживаются по умолчанию через другой порт (если для
HTTP это был порт 80, то для HTTPS 443). Переход позволил четко разделить протоколы и явно указать, какой из них используется для связи.

Как устанавливается HTTP/2-соединение

131

Однако у перехода на новую схему или новый порт (или и то и другое)
есть несколько недостатков, например:
„„ пока переход не станет практически всеобщим, по умолчанию необходимо оставить схему http:// (или https://, если, как надеются
многие, она когда-либо станет схемой по умолчанию). Следовательно, добавление новой схемы, как подразумевает HTTPS, потребовало бы перенаправления для использования HTTP/2, что неизбежно
привело бы к снижению скорости соединения (как раз к тому, с чем
HTTP/2 призван бороться);
„„ сайтам необходимо поменять ссылки в соответствии с новой схемой. Внутренние ссылки можно легко заменить относительными
(например, /images/image.png, а не https://example.com/images/image.png), однако внешние ссылки должны включать в себя полный
URL, в том числе и схему. Многим сайтам было сложно перейти на
HTTPS ввиду того, что им приходилось менять каждый URL;
„„ возникают проблемы совместимости с существующей сетевой инф­
раструктурой (например, брандмауэры блокируют любые новые нестандартные порты).
Именно по вышеперечисленным причинам, а также с целью облегчить
переход на HTTP/2 для любых серверов, в HTTP/2 (и SPDY, лежащем в его
основе) разработчики решили отказаться от новой схемы, но предусмотрели альтернативные методы создания HTTP/2-соединения. В специ­
фикации HTTP/21 описаны три способа создания соединения (кроме
того, позже был добавлен еще один способ, упомянутый в разделе 4.2.4):
„„ использование HTTPS-соединений;
„„ использование HTTP-заголовка Upgrade;
„„ использование заранее известного протокола.
В теории HTTP/2 доступен при использовании как незашифрованных
соединений (HTTP), где он указывается как h2c, так и зашифрованных
(HTTPS) под именем h2. На практике же все веб-браузеры поддерживают HTTP/2 только при HTTPS-соединениях (h2). Вариант подключения
через HTTP применяется для согласования HTTP/2 браузерами. Обмен
данными между серверами HTTP/2 может осуществляться по незашифрованному протоколу HTTP (h2c) или HTTPS (h2), поэтому он может использовать любой метод в зависимости от того, какая схема используется.

4.2.1 Использование HTTPS-рукопожатия
Одним из важных этапов настройки HTTPS-соединения является этап
согласования протокола. Перед установкой соединения и началом обмена HTTP-сообщениями необходимо согласовать протокол SSL/TLS, шифр
и другие необходимые параметры. Данный этап является достаточно
гибким, так как он позволяет вводить и использовать новые протоколы
1

https://tools.ietf.org/html/rfc7540#section-3.

Глава 4

132

Основы протокола HTTP/2

и шифры, однако только в случае согласия обеих сторон. Частью HTTPSрукопожатия может быть и соглашение о поддержке HTTP/2. В таком случае любые перенаправления для обновления протокола, которые должны были быть выполнены при установке соединения, сохраняются до
момента отправки первого сообщения.

HTTPS-рукопожатие
И в HTTP/1, и в HTTP/2 HTTPS-соединения представляют собой стандартные HTTP-соединения, зашифрованные с помощью SSL/TLS. Для
того чтобы разобраться в различиях между всеми вышеперечисленными названиями, обратитесь к врезке «SSL, TLS, HTTPS и HTTP» в главе 1.
Шифрование с открытым ключом называют ассиметричным шифрованием, поскольку для шифрования и дешифрования сообщений здесь
используются разные ключи. Данный тип шифрования позволяет обеспечить безопасную связь с сервером, к которому вы подключаетесь
впервые, но работает медленно, поэтому используется для согласования
симметричного ключа, впоследствии применяемого для шифрования
остальной части соединения. Это согласование происходит в самом начале, во время TLS-рукопожатия. В TLSv1.2, который в данный момент
является основной версией, для установки зашифрованного соединения
также используется рукопожатие (см. рис. 4.4). В версии TLSv1.3, для которой недавно был выпущен стандарт, рукопожатие немного изменилось, о чем мы поговорим в главе 9.
Клиент

Сервер
1. ClientHello

2. ServerHello
3. ServerCertificate
4. ServerKeyExchange
5. CertificateRequest
6. ServerHelloDone

7. ClientCertificate
8. ClientKeyExchange
9. CertificateVerify
10. ChangeCipherSpec
11. Finished

12. ChangeCipherSpec
13. Finished
Не зашифровано

Рис. 4.4

HTTPS-рукопожатие

Шифрование
с открытым/закрытым
ключом

Шифрование
с согласованным
секретным ключом

Как устанавливается HTTP/2-соединение

133

Рукопожатие включает в себя четыре набора сообщений:
клиент отправляет сообщение ClientHello, где подробно описываются его возможности шифрования. Поскольку метод шифрования
еще не согласован, данное сообщение отправляется в незашифрованном виде;
„„ в ответ сервер отправляет аналогичное сообщение – ServerHello, где
указывается протокол HTTPS (например, TLSv1.2), который он выбрал на основе отправленных клиентом данных. Также он выбирает и отправляет клиенту шифр, который будет использоваться для
данного соединения (например, ECDHE-RSA-AES128-GCM-SHA256). Затем
он отправляет сертификат HTTPS (ServerCertificate). Детали секретного ключа зависят от выбранного шифра (ServerKeyExchange) и от
необходимости наличия клиентского сертификата HTTPS (CertificateRequest, не требуется для большинства веб-сайтов). После того
как все предыдущие пункты выполнены, сервер сообщает, что все
готово (ServerHelloDone);
„„ клиент проверяет сертификат сервера и при следующем запросе отправляет свой сертификат (ClientCertificate, не требуется для
большинства сайтов). Затем он отправляет сведения о своем секретном ключе (ClientKeyExchange). Эти данные отправляются в зашифрованном виде с помощью открытого ключа согласно сертификату
сервера, поэтому дешифровать их может только сервер (с помощью
секретного ключа). Если сертификат клиента все же потребовался,
отправляется сообщение CertificateVerify, подписанное личным
ключом с целью подтвердить право собственности на сертификат
клиента. Клиент использует данные ServerKeyExchange и ClientKeyExchange для определения зашифрованного симметричного ключа
и отправляет сообщение ChangeCipherSpec, чтобы проинформировать
клиента о начале шифрования, затем отправляется зашифрованное
сообщение Finished;
„„ сервер переключается на зашифрованное соединение (ChangeCipherSpec) и отправляет зашифрованное сообщение Finished.
Криптография с открытым ключом, помимо согласования симмет­
ричного ключа шифрования, используется также и для подтверждения
личности, так как данный аспект является одной из основ безопасности.
Идентификация выполняется успешно, если сообщения подписываются секретным личным ключом сервера, который можно разблокировать
с помощью открытого ключа в сертификате. Каждый сертификат SSL/
TLS также криптографически подписан доверенным центром сертификации. Аналогичный процесс работает и с клиентскими сертификатами,
но только в обратном порядке. Кроме того, домен сервера является частью подписанного сертификата SSL/TLS. Если домен сервера неверен
(например, www.amaz0n.com, а не www.amazon.com), возможно, вы установили соединение совсем не с той стороной, с которой предполагали.
Как уже упоминалось в главе 1, эта ситуация вызывает большую путаницу. Значок зеленого замка не гарантирует безопасность сайта, он гарантирует только то, что обмен данными с ним надежно зашифрован.
„„

Глава 4

134

Основы протокола HTTP/2

После того как все вышеописанные шаги были выполнены, сеанс
HTTPS полностью настроен, и все дальнейшие коммуникации защищены согласованным ключом (ключами). Таким образом, перед отправкой
первого запроса происходит как минимум два дополнительных цикла
приема и передачи данных. HTTPS всегда считался довольно медленным, поскольку даже несмотря на то, что благодаря техническому прогрессу шифрование и дешифрование отнимают все меньше времени,
задержка при использовании данного протокола все же заметна, как
вы можете видеть на каскадных диаграммах в главе 2. После настройки сеанса остальным HTTP-сообщениям уже не требуется согласование.
Точно так же дальнейшие соединения (будь то дополнительные параллельные или повторные соединения) могут возобновить сеанс TLS, если
они используют ключи повторно.
Проблему с задержкой нельзя решить никак, кроме попытки ограничить создание новых соединений (как это делает HTTP/2). Большинство
людей сходится во мнении, что HTTPS все же имеет больше преимуществ,
чем недостатков. На момент написания этой книги версия TLSv1.31 уже
полностью доработана. Данная версия работает намного эффективнее,
поскольку позволяет сократить количество циклов приема и передачи до
одного (или даже вообще отказаться от них при переходе от предыдущего согласования). Однако для ее принятия потребуется некоторое время,
и вам все же придется проходить хотя бы один дополнительный цикл.

Согласование протокола уровня приложений
ALPN2 расширил сообщение ClientHello. В этом расширении клиенты могут объявлять о том, что они поддерживают протокол уровня приложений («Эй, я поддерживаю h2 и http/1. Если хотите, используйте любой из
них».). Кроме того, изменилось и сообщение ServerHello, благодаря чему
серверы могут подтверждать, какой протокол приложения они будут использовать после согласования HTTPS («Хорошо, давайте использовать
h2».). Весь вышеописанный процесс изображен на рис. 4.5.
ALPN – довольно простой протокол. Он может использоваться для
согласования HTTP/2 для уже существующих HTTPS-сообщений без добавления каких-либо дополнительных циклов передачи, перенаправлений или других задержек, обычно возникающих при обновлении. Единственная проблема ALPN – это то, что он относительно новый, и не все
пользователи поддерживают его. Например, его не поддерживают многие серверы, где используются более старые версии TLS-библиотек (см.
главу 3). Если не поддерживается ALPN, сервер обычно предполагает, что
клиент не поддерживает HTTP/2 и переходит на HTTP/1.1.
Помимо HTTP/2, ALPN может использоваться и для других протоколов.
На момент написания нашей книги он поддерживает работу с HTTP/2
и SPDY, лежащим в его основе. Однако были зарегистрированы и дру1
2

https://tools.ietf.org/html/draft-ietf-tls-tls13.
https://tools.ietf.org/html/rfc7301.

135

Как устанавливается HTTP/2-соединение

гие применения ALPN, включая три начальных версии HTTP: HTTP/0.9,
HTTP/1.0 и HTTP/1.1.61. Разработка ALPN была завершена в июле 2014 го­
да, еще до появления HTTP/2, а RFC для ALPN2 содержит информацию
только для HTTP/1.1 и SPDY. Расширение ALPN для HTTP/2 (h2) было зарегистрировано позже в составе спецификации HTTP/23.
Клиент

Сервер
1. ClientHello
с опциями ALPN

Дополнения
к рукопожатию TLS
для ALPN

2. ServerHello
с выбором ALPN
3. ServerCertificate
4. ServerKeyExchange
5. CertificateRequest
6. ServerHelloDone

7. ClientCertificate
8. ClientKeyExchange
9. CertificateVerify
10. ChangeCipherSpec
11. Finished

12. ChangeCipherSpec
13. Finished
Не зашифровано

Рис. 4.5

Шифрование
с открытым/закрытым
ключом

Шифрование
с согласованным
секретным ключом

HTTPS-рукопожатие с ALPN

Next Protocol Negotiation
NPN (Next Protocol Negotiation), предшественник ALPN, работает по аналогичному принципу. Несмотря на то что его использовало множество
браузеров и серверов, он так и не был оформлен в качестве официального Internet-стандарта (хотя был разработан даже проект спецификации)4.
Стандарт ALPN в свою очередь был официально оформлен. Он в значительной степени основан на NPN аналогично тому, как HTTP/2 является
стандартизированной версией SPDY.
Основное отличие состоит в том, что при использовании NPN выбор,
какой протокол использовать, остается за клиентом, а при использова1

2
3
4

https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontypevalues.xhtml#alpn-protocol-ids.
https://tools.ietf.org/html/rfc7301.
https://tools.ietf.org/html/rfc7540#section-11.1.
https://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04.

Глава 4

136

Основы протокола HTTP/2

нии ALPN это делает сервер. В первом случае в сообщении ClientHello
клиент указывает, что хотел бы использовать NPN. Сообщение ServerHello содержит перечень всех поддерживаемых сервером протоколов NPN,
а затем, после установки зашифрованного соединения, клиент выбирает
протокол NPN (например, h2) и отправляет сообщение, оповещающее
сервер об этом выборе. Рисунок 4.6 иллюстрирует этот процесс; обратите
внимание на три элемента, выделенных на этапах 1, 2 и 11.
Клиент

Сервер
1. ClientHello
с NPN-меткой

Дополнения
к рукопожатию TLS
для NPN

2. ServerHello
с NPN-выбором
3. ServerCertificate
4. ServerKeyExchange
5. CertificateRequest
6. ServerHelloDone

7. ClientCertificate
8. ClientKeyExchange
9. CertificateVerify
10. ChangeCipherSpec
11. NPN Choice
12. Finished
13. ChangeCipherSpec
14. Finished
Не зашифровано

Рис. 4.6

Шифрование
с открытым/закрытым
ключом

Шифрование
с согласованным
секретным ключом

HTTPS-рукопожатие с NPN

NPN включает в себя три этапа, тогда как ALPN – два. Однако они оба
повторно используют уже существующие шаги установки HTTPS-со­
еди­нения и не добавляют новых циклов приема и передачи (хотя стоит
отметить, что NPN добавляет одно сообщение для подтверждения выбранного протокола). Кроме того, при использовании NPN информация
о выбранном протоколе приложения будет зашифрована (этап 11 на
рис. 4.6, тогда как ALPN отправляет ее в незашифрованном cообщении
ServerHello. Ввиду того, что в NPN эти данные отправляются в незашифрованном виде в сообщении ServerHello и поскольку некоторым сетевым
решениям необходимо владеть информацией о применимом протоколе,
рабочая группа TLS решила внести изменения в этот процесс в ALPN, где
выбор протокола приложения (и других параметров HTTPS) остается за
сервером.
По сравнению с ALPN, NPN считается устаревшим, и, как было сказано
в главе 3, многие браузеры перестали поддерживать его для соединений

Как устанавливается HTTP/2-соединение

137

HTTP/2; некоторые веб-серверы (например, Apache) вообще никогда не
поддерживали его. Ожидается, что со временем и другие реализации
прекратят его использование. В спецификации HTTP/2 сказано, что следует использовать ALPN1, а NPN не упоминается вовсе, поэтому с технической точки зрения реализации, продолжающие использовать NPN, не
соответствуют спецификации.
Повсеместное прекращение поддержки NPN браузерами становится
серьезной проблемой для серверов, которые еще не перешли на ALPN.
Ввиду того, что NPN старше, его поддерживало большинство серверов
(или по крайней мере их TLS-библиотеки). Более новые версии поддерживают ALPN, однако старые версии, коих на момент написания нашей
книги большинство (например, OpenSSL 1.0.1), поддерживают только
старое расширение. К слову, такая ситуация является одной из причин,
по которой невозможно использовать HTTP/2 даже при должной настройке сервера (см. главу 3).

Пример HTTPS-рукопожатия с ALPN
Для просмотра HTTPS-рукопожатия вы можете использовать несколько
инструментов. Самым простым и доступным из них является curl2, однако, возможно, что ваша версия не поддерживает расширение ALPN.
Впрочем, те читатели, которые пользуются Git Bash, могут не переживать
по этому поводу, ведь он включает в себя версию с поддержкой ALPN.
Пример ниже иллюстрирует, что происходит, когда вы подключаетесь
к Facebook с помощью HTTP/2 и инструмента curl. Жирным шрифтом
выделены части кода, относящиеся к ALPN и HTTP/2:
$
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
1
2

curl -vso /dev/null --http2 https://www.facebook.com
Rebuilt URL to: https://www.facebook.com/
Trying 31.13.76.68...
TCP_NODELAY set
Connected to www.facebook.com (31.13.76.68) port 443 (#0)
ALPN, offering h2
ALPN, offering http/1.1
successfully set certificate verify locations:
CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none } [5 bytes data]
TLSv1.2 (OUT), TLS handshake, Client hello (1): } [214 bytes data]
TLSv1.2 (IN), TLS handshake, Server hello (2): { [102 bytes data]
TLSv1.2 (IN), TLS handshake, Certificate (11): { [3242 bytes data]
TLSv1.2 (IN), TLS handshake, Server key exchange (12): { [148 bytes data]
TLSv1.2 (IN), TLS handshake, Server finished (14): { [4 bytes data]
TLSv1.2 (OUT), TLS handshake, Client key exchange (16): } [70 bytes data]
TLSv1.2 (OUT), TLS change cipher, Client hello (1): } [1 bytes data]
TLSv1.2 (OUT), TLS handshake, Finished (20): } [16 bytes data]
TLSv1.2 (IN), TLS handshake, Finished (20): { [16 bytes data]
SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
ALPN, server accepted to use h2
Server certificate:
https://tools.ietf.org/html/rfc7540#section-3.3.
https://curl.haxx.se/.

138

Глава 4

Основы протокола HTTP/2

* subject: C=US; ST=California; L=Menlo Park; O=Facebook, Inc.;
CN=*.facebook.com
* start date: Dec 9 00:00:00 2016 GMT
* expire date: Jan 25 12:00:00 2018 GMT
* subjectAltName: host "www.facebook.com" matched cert's "
*.facebook.com" * issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert
SHA2
High
Assurance Server CA
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)

В данном примере клиент заявляет, что для поддержки HTTP/2 (h2)
и HTTP/1.1 (http/1.1) он будет использовать ALPN. Затем он проходит
многочисленные этапы подтверждения (показаны не все, но этого достаточно для создания общего представления), и наконец устанавливается соединение с TLSv1.2, шифр ECDHE-ECDSA-AES128-GCM-SHA256 и h2 ALPN.
После этого отображается сертификат сервера, и curl переключает сеанс
на HTTP/2. Curl может помочь вам проверить, поддерживает ли сервер
ALPN (конечно, при условии, что ваша версия curl поддерживает ALPN).
Если вам интересно, вы можете протестировать NPN с помощью опции
--no-alpn. Этот тест, однако, не покажет вам столь большой объем информации и исключит все строки ALPN, которые можно увидеть в предыдущем примере, без замены каких-либо эквивалентов NPN, хотя последние
две строки идентичны в обоих примерах:
$ curl -vso /dev/null --http2 https://www.facebook.com --no-alpn
...
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)

4.2.2 HTTP-заголовок Upgrade
Клиент может запросить обновление существующего HTTP/1.1-соеди­
не­ния до HTTP/2, отправив HTTP-заголовок Upgrade. Данный заголовок
следует использовать только для незашифрованных HTTP-соединений
(h2c). При работе с HTTPS-соединениями HTTP/2 (h2) не следует согласовывать HTTP/2 с помощью этого заголовка – согласование HTTPS должно происходить с помощью ALPN. Как уже было отмечено, веб-браузеры
поддерживают HTTP/2 только через зашифрованные соединения, следовательно, для них этот метод не нужен. Для тех, кто работает с внешними
приложениями, например через API, может быть интересно узнать более
подробную информацию о том, как работает данный метод.
Если заголовок Upgrade отправляет клиент, то он полностью зависит от
отправителя. Заголовок может отправляться с каждым запросом, только
с начальным запросом или только в тех случаях, когда сервер объявляет
о поддержке HTTP/2 c помощью заголовка Upgrade в HTTP-ответе. Примеры ниже показывают, как работает данный заголовок.

Как устанавливается HTTP/2-соединение

139

Пример 1: неудачный запрос с заголовком Upgrade
Здесь запрос HTTP/1.1 выполняется с использованием данного заголовка, поскольку клиент поддерживает HTTP/2 и хочет использовать именно его:
GET / HTTP/1.1
Host: www.example.com
Upgrade: h2c
HTTP2-Settings:

Такой запрос должен включать в себя заголовок HTTP-Settings, который
представляет собой настройки HTTP/2 в кодировке base-64, о которых
мы поговорим позже.
Сервер, не поддерживающий HTTP/2, отвечает на запрос сообщением
HTTP/1.1, проигнорировав заголовок Upgrade:
HTTP/1.1 200 OK
Date: Sun, 25 Jun 2017 13:30:24 GMT
Connection: Keep-Alive
Content-Type: text/html
Server: Apache



…и т. д.

Пример 2: успешный запрос с заголовком Upgrade
Сервер, поддерживающий HTTP/2, вместо того чтобы игнорировать запрос на обновление и отправлять обратно ответ HTTP/1.1 200, присылает
в ответ HTTP/1.1 101, что свидетельствует о переходе на использование
нового протокола:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c

Затем сервер немедленно переключается на HTTP/2 и отправляет сначала фрейм SETTINGS (см. раздел 4.3.3), а после – ответ на исходное сообщение уже в формате HTTP/2.

Пример 3: заголовок Upgrade отправляет сервер
Итак, клиент выполняет запрос HTTP/1.1, и предполагает, что сервер
не поддерживает HTTP/2, поэтому в запросе нет заголовка Upgrade:
GET / HTTP/1.1
Host: www.example.com

Однако, если сервер поддерживает HTTP/2, он отправляет ответ с кодом 200 и включает в него заголовок Upgrade. Но этот заголовок будет являться предложением, а не запросом на обновление, поскольку все по-

Глава 4

140

Основы протокола HTTP/2

добные запросы инициируются клиентом. Ниже приведен пример, где
сервер сообщает о поддержке h2 (HTTP/2 через HTTPS) и h2c (HTTP/2
через HTTP):
HTTP/1.1 200 OK
Date: Sun, 25 Jun 2017 13:30:24 GMT
Connection: Keep-Alive
Content-Type: text/html
Server: Apache
Upgrade: h2c, h2



…и т. д.

Клиент в свою очередь может использовать эту информацию чтобы
инициировать переход на HTTP/2. Для этого он отправляет заголовок Upgrade в следующем запросе:
GET /styles.css HTTP/1.1
Host: www.example.com
Upgrade: h2c
HTTP2-Settings:

На этот запрос сервер даст ответ 101 и обновит соединение, как это
было описано ранее. Обратите внимание, что заголовок Upgrade и метод
согласования нельзя использовать для h2-соединений – только для h2cсоединений. В данном примере сервер объявил как о h2, так и о h2c, но,
если клиент хочет использовать h2, ему следует переключиться на HTTPS
и использовать ALPN для согласования.

Проблемы с отправкой заголовка Upgrade
На момент написания этой книги все браузеры поддерживают HTTP/2
только с HTTPS. Именно поэтому заголовок Upgrade, скорее всего, никогда не будет использоваться браузерами, что может вызвать некоторые
проблемы.
Рассмотрим этот сценарий. Предположим, что на одной стороне соединения находится браузер, поддерживающий HTTP/2, а на другой
перед сервером приложений, поддерживающим HTTP/2, находится еще
один сервер, который поддерживает только HTTP/1.1 (например, более
старая версия Apache). В таком случае последний действует как обратный прокси и, вероятно, пропускает через себя все запросы клиента
(веб-браузера) и конечного сервера (сервер приложений Tomcat), которые поддерживают HTTP/2. Сервер приложений может попытаться отправить заголовок Upgrade, чтобы перейти на более эффективную версию протокола. Промежуточный сервер может вслепую переслать этот
заголовок, и клиент решит, что это действительно неплохая идея. Однако
впоследствии окажется, что промежуточный сервер все-таки не поддерживает HTTP/2.

Как устанавливается HTTP/2-соединение

141

В похожем сценарии веб-сервер передает браузеру информацию
в формате HTTP/2, а на сервер приложений проксирует запросы с помощью HTTP/1.1. Сервер приложений может отправить предложение
о переходе на новую версию протокола, и, если это предложение будет
перенаправлено к браузеру, может возникнуть путаница, поскольку браузер и так использует соединение h2.
Подобные проблемы довольно часто возникали на практике до того, как
был развернут HTTP/2. Например, Safari отправлял ответ с кодом ошибки,
когда видел h2-заголовок Upgrade в соединении HTTP/2 (см. главу 3).
На момент написания данной книги команда nginx исправила ошибку, при которой заголовок Upgrade передавался вслепую1, например сервером Apache. Некоторые конфигурации (такие как proxy_hide_header Upgrade) позволяют скрыть данный заголовок, однако мало кто знает, как
добавить его обратно во избежание проблем. Кроме того, некоторые
клиенты или серверы могут неправильно реализовать заголовок Upgrade.
Во время экспериментов с HTTP/2 была замечена проблема с отключением NodeJS, после того как Apache передавал заголовок Upgrade2. В новых версиях NodeJS данная проблема была исправлена, однако в старых,
которые все еще используются, она присутствует.
К моменту публикации этой книги все описанные проблемы могут
быть уже исправлены, однако не исключено и появление новых подобных проблем. Подводя итог всему вышесказанному, лучше, чтобы реализации сервера не предлагали обновить протокол (по крайней мере, по
умолчанию). На мой взгляд, при таких сценариях этот подход привносит больше проблем, чем решает. Большинство версий серверов (и все
веб-браузеры), скорее всего, будет использовать согласование HTTPS.
Метод предположения протокола по умолчанию может использоваться
для внутренних серверов, где HTTPS не поддерживается или не требуется. Одним из «нарушителей» является Apache, которому предложили
прекратить использовать Upgrade по умолчанию3. Однако вы можете и самостоятельно отключить отправку данного заголовка в разделе конфигурации Apache mod_headers. Если вы используете Apache с поддержкой
HTTP/2, мы настоятельно советуем сделать это (хотя это решение может
вызвать проблемы с другими протоколами, которым необходимо использовать заголовок Upgrade, например WebSockets, поэтому его нельзя
использовать в некоторых случаях):
Header unset Upgrade

4.2.3 Применение заранее известного протокола
Третий и последний способ перехода на HTTP/2, согласно спецификации,
заключается в том, что клиент уже заранее знает, что сервер поддержи1
2
3

https://trac.nginx.org/nginx/ticket/915.
https://github.com/nodejs/node/issues/4334.
https://bz.apache.org/bugzilla/show_bug.cgi?id=59311.

Глава 4

142

Основы протокола HTTP/2

вает HTTP/2. В этом случае можно сразу запустить HTTP/2-соединение,
не прибегая к использованию запросов на обновление.
Клиент может получить предварительную информацию о том, что сервер поддерживает HTTP/2 несколькими разными способами. Например,
если вы используете обратный прокси для разгрузки HTTPS, возможно,
вы предпочтете сразу передавать сообщения HTTP/2 своим внутренним
серверам через HTTP (h2c), так как вы знаете, что они поддерживают
HTTP/2. Кроме того, вы можете получить предварительную информацию
из альтернативного источника благодаря заголовку Alt-Svc или фрейму
ALTSVC (см. раздел 4.2.4).
Данный вариант является весьма рискованным, так как информация
о том, что сервер использует HTTP/2, может быть не совсем точной. Клиенты, прибегнувшие к нему, должны позаботиться о том, чтобы любые
сообщения обрабатывались надлежащим образом, даже если предварительная информация окажется неверной. Ответ сервера на предварительное сообщение, касающееся HTTP/2 (о котором мы поговорим
позже), имеет огромное значение при выборе такого способа использования. Данный метод следует использовать только в случае, если вам доступно управление и клиентом, и сервером.

4.2.4 Протокол HTTP Alternative Services
Четвертый способ, не прописанный в спецификации HTTP/2, заключается в использовании протокола HTTP Alternative Services (альтернативные службы HTTP)1, который впоследствии был оформлен в отдельный
стандарт уже после публикации HTTP/2. Он позволяет серверу с помощью HTTP/1.1 информировать клиента (через HTTP-заголовок Alt-Svc)
о том, что запрошенный ресурс доступен в другом месте (например, на
другом сервере или порту) с использованием другого протокола. Данный протокол можно использовать для запуска HTTP/2 в соответствии
с предварительно полученной информацией.
HTTP Alternative Services предназначен не только для HTTP/1, но и для
HTTP/2 (через новый фрейм ALTSVC, о котором мы поговорим позже)
в случае, если клиент хочет переключиться на другое соединение (например, которое расположено ближе или меньше загружено). Этот стандарт относительно молод и еще не получил широкого распространения.
Именно поэтому по-прежнему происходит запуск одного соединения
с последующим переключением. Данный способ занимает намного
больше времени, чем запуск HTTP/2 через ALPN или на основании заранее полученной информации. Кроме того, он открывает некоторые
интересные возможности, которые выходят за рамки нашей книги, но,
похоже, только одна сеть доставки контента намерена в полной мере использовать его2.

1
2

https://tools.ietf.org/html/rfc7838.
https://blog.cloudflare.com/cloudflare-onion-service/.

Как устанавливается HTTP/2-соединение

143

4.2.5 Преамбула соединения HTTP/2
Первое сообщение, которое должно быть отправлено в соединении
HTTP/2 (независимо от того, какой метод используется для установления
этого соединения), – это преамбула соединения, или так называемая «волшебная» строка. Это первое сообщение клиента в новом соединении.
Оно представляет собой последовательность из 24 октетов. В шестнадцатеричном представлении сообщение выглядит следующим образом:
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a

В кодировку ASCII (American standard code for information interchange)
это переводится так:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

Это сообщение может показаться странным, однако оно не случайно
похоже на сообщение в формате HTTP/1:
PRI * HTTP/2.0↵

SM↵


PRI здесь – это HTTP-метод (вместо GET или POST), * – ресурс, а HTTP/2.0 –
номер версии HTTP. Затем идут два символа возврата каретки (поскольку нет заголовков запроса), за которыми следует тело запроса SM.
Назначение этого внешне бессмысленного сообщения становится понятным, когда клиент передает сообщение в формате HTTP/2 серверу,
который его не поддерживает. Такой сервер пытается обработать сообщение так же, как он обрабатывает любые другие HTTP-сообщения
и терпит неудачу. Он не может распознать бессмысленный для него метод (PRI) и версию HTTP (HTTP/2.0), поэтому отклоняет сообщение. Обратите внимание, что преамбула – это единственная часть официальной
спецификации, где название версии употребляется с дополнительным
номером (HTTP/2.0), в то время как везде пишется просто HTTP/2 (мы
обсуждали это ранее во врезке раздела 4.1 «HTTP/2.0 или HTTP/2?». Сервер, который на основе входящего сообщения делает вывод о том, что
клиент использует HTTP/2, не отправит подобный вышеописанному ответ. В качестве своего первого сообщения он отправит фрейм SETTINGS
(который может быть пустым).

Зачем нужны PRI и SM?
Согласно ранним спецификациям протокола в HTTP/2 использовались идентификаторы FOO и BARa или BAb – известные имена-заглушки в программировании. Однако в версии 4 рабочей спецификации они были без каких-либо разъяснений заменены на PRI и SMc.
По всей видимости, такое нововведение было сделано в ответ на тогдашние
разоблачения Эдварда Сноудена (Edward Snowden)d касательно программы

144

Глава 4

Основы протокола HTTP/2

для сбора интернет-трафика различных компаний под названием PRISM.
Сторонники свободного интернета (некоторые из которых связаны с разработкой Internet-стандартов) были расстроены такими открытиями и посчитали, что было бы забавно начинать каждое соединение HTTP/2 с небольшого
напоминания.
Изначально содержание данного сообщения не имело никакого значения,
ведь предполагалось что оно не должно распознаваться как действительное.
Было предложено несколько других вариантов идентификаторов, например
STA RT, но в конечном итоге именно идентификаторы PRI SM вошли в окончательную спецификацию с пометкой «Предпочтения редакционного совета
касательно магической строкиe».
___________________________________
a
https://tools.ietf.org/html/draft-ietf-httpbis-http2-02#section-3.2.
b
https://tools.ietf.org/html/draft-ietf-httpbis-http2-03#section-3.2.
c
https://tools.ietf.org/html/draft-ietf-httpbis-http2-04#section-3.5.
d
https://blog.jgc.org/2015/11/the-secret-message-hidden-in-every.html.
e
 https://github.com/http2/http2-spec/commit/ac468f3fab9f7092a430eedfd69ee1fb2e23c944.

4.3

Фреймы HTTP/2
После установки HTTP/2-соединения вы можете отправлять сообщения. Как я уже говорил, сообщения состоят из фреймов данных, которые отправляются в потоках по одному мультиплексированному соединению. Фреймы – это реализация низкого уровня, которой многие
веб-разработчики не занимаются, однако всегда полезно понимать детали таких технологий. Многие ошибки, представленные в главе 3, легче
устранить, разобрав HTTP/2 на уровне фреймов, именно поэтому данная информация имеет как теоретическое, так и практическое значение.
Рассмотрим работу с фреймами HTTP/2 на реальных примерах.
В данном разделе мы изучим различные типы фреймов, которые на
первый взгляд могут показаться запутанными и даже пугающими. Читателям, однако, следует сосредоточиться именно на общей концепции фреймов HTTP/2, а знать все о каждом из них необязательно. Далее
в этой книге вы найдете разделы, посвященные отдельным фреймам
и их настройкам, но вам не нужно заучивать всю эту информацию чтобы понять оставшуюся часть книги или разобраться в общих вопросах
работы HTTP/2 в реальном мире.

4.3.1 Просмотр фреймов HTTP/2
Просматривать фреймы HTTP/2 можно с помощью ряда инструментов,
в числе которых net-export в Chrome, nghttp и Wireshark. Кроме того, для
того чтобы отображать отдельные фреймы, ваш веб-сервер может вести
более подробные логи на уровне фреймов, однако при потенциально

Фреймы HTTP/2

145

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

Chrome net-export
Страница net-export в Chrome позволяет просматривать HTTP-фреймы
без установки дополнительного программного обеспечения. Раньше такая функция была доступна в инструменте net-internals, но в Chrome 71
по ряду причин она была перемещена в net-externals1, и теперь для того,
чтобы просмотреть фреймы, требуется немного больше усилий. Для
доступа к net-export необходимо открыть браузер Chrome и набрать
в адресной строке следующее:
chrome://net-export/

Затем нажмите на кнопку Start Logging to Disk (Начать запись на
диск) и выберите место для сохранения файла. В другой вкладке откройте сайт, использующий HTTP/2 (например, https://www.facebook.com),
и после окончания его загрузки нажмите кнопку Stop Logging (Остановить запись). На этом этапе вы можете использовать средство просмотра NetLog (https://netlog-viewer.appspot.com), которое позволит открыть
и проверить созданный файл (примечание: этот инструмент просматривает файл локально и не загружает его на сервер). Далее слева выберите
параметр HTTP/2, а затем сайт (например, www.facebook.com). В результате вы должны увидеть соответствующие исходные сообщения HTTP/2,
как показано на рис. 4.7.
Chrome добавляет много своей служебной информации, и, кроме того,
многие фреймы разбиваются на несколько строк. Следующие строки
взяты из одного фрейма SETTINGS:
t= 1646 [st=
t= 1647 [st=

t= 1647 [st=

t= 1647 [st=

t= 1647 [st=

t= 1647 [st=

1

1] HTTP2_SESSION_RECV_SETTINGS
2] HTTP2_SESSION_RECV_SETTING
--> id = "1 (SETTINGS_HEADER_TABLE_SIZE)"
--> value = 4096
2] HTTP2_SESSION_RECV_SETTING
--> id = "5 (SETTINGS_MAX_FRAME_SIZE)"
--> value = 16384
2] HTTP2_SESSION_RECV_SETTING
--> id = "6 (SETTINGS_MAX_HEADER_LIST_SIZE)"
--> value = 131072
2] HTTP2_SESSION_RECV_SETTING
--> id = "3 (SETTINGS_MAX_CONCURRENT_STREAMS)"
--> value = 100
2] HTTP2_SESSION_RECV_SETTING
--> id = "4 (SETTINGS_INITIAL_WINDOW_SIZE)"
--> value = 65536

https://docs.google.com/document/d/1Ll7T5cguj5m2DqkUTad5DWRCqtbQ3L1q9
FRvTN5-Y28/.

Глава 4

146

Основы протокола HTTP/2

Рис. 4.7 Просмотр фреймов HTTP/2 в Chrome

При использовании данного инструмента читать отдельные фреймы
может быть труднее, чем при помощи двух других инструментов, однако он отображает нам ту же информацию, что и они. С другой стороны,
инструмент удобен тем, что вы можете получить такой уровень детализации в браузере без установки дополнительного программного обеспечения, а для форматирования результатов вы можете использовать дополнительные инструменты1. На момент написания данной книги мне
не известныдругие браузеры, обеспечивающие такой уровень детализации. Впрочем, Opera имеет аналогичные возможности ввиду того, что
у нее база исходного кода такая же, как и у Chrome.

Использование nghttp
nghttp – это инструмент командной строки, разработанный на основе
библиотеки nghttp2 на языке C. Веб-серверы и клиенты широко используют эту библиотеку для решения задач поддержки HTTP/2 на нижнем
уровне. Если для вашего сервера установлена библиотека nghttp2 (они,
например, требуются для Apache), возможно, у вас установлен данный инструмент. Вы можете пользоваться им для просмотра HTTP/2сообщений так же, как и инструментом Chrome net-export. Взгляните на
пример:
1

https://github.com/rmurphey/chrome-http2-log-parser.

Фреймы HTTP/2

147

$ nghttp -v https://www.facebook.com
[ 0.042] Connected
The negotiated protocol: h2
[ 0.109] recv SETTINGS frame
(niv=5)
[SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[ 0.109] recv WINDOW_UPDATE frame
(window_size_increment=10420225)
[ 0.109] send SETTINGS frame
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
…и т. д.

Использование Wireshark
Wireshark1 позволяет отслеживать весь трафик, отправляемый и получаемый вашим компьютером. Данный инструмент может быть полезен при
отладке системы на низком уровне, так как благодаря ему вы можете просматривать отправленные и полученные необработанные сообщения.
К сожалению, пользоваться этим инструментом тоже довольно сложно.
Одна из сложностей заключается в том, что Wireshark не является
клиентом. Он отслеживает трафик, который ваш браузер отправляет на
сервер. Все браузеры используют HTTP/2 через HTTPS, поэтому, если вы
не знаете, какие ключи шифрования и дешифрования SSL/TLS, вы не
сможете прочитать трафик, что и является сутью HTTPS. Разработчики
Chrome и Firefox приняли это во внимание, поэтому данные браузеры позволяют сохранять ключи HTTPS в отдельный файл, чтобы впоследствии
вы могли использовать для отладки такие инструменты, как Wireshark.
Очевидно, что при отладке необходимо выключить данный инструмент.
Все, что вам нужно сделать, это указать браузеру (Chrome или Firefox)
файл, в котором необходимо сохранить ключи. Вы можете сделать это,
указав имя файла в переменной окружения SSLKEYLOGFILE или введя следующий код в командной строке запуска Chrome:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --ssl-key-logfile=%USERPROFILE%\sslkey.log

ПРИМЕЧАНИЕ Обязательно убедитесь, что вы используете правильные дефисы. Многие приложения, например Microsoft Office,
автоматически заменяют короткий дефис (-) на короткое (–) или
длинное тире (—). Запомните, что это три отдельных символа, которые командная строка может не распознать как указатель на аргументы. В результате получится пустой файл без ключей SSL, что
может вызвать серьезные недоразумения.
1

https://www.wireshark.org/.

Глава 4

148

Основы протокола HTTP/2

Для macOS необходимо указать файл в переменной среды SSLKEYLOGFILE:
$ export SSLKEYLOGFILE=~/sslkey.log
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome

или упомянуть его как аргумент командной строки:
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome--ssl-keylog-file=/Users/barry/sslkey.log

Затем вам необходимо запустить Wireshark и указать расположение
файла. Для этого выберите Edit > Preferences > Protocols > SSL и укажите
имя файла в поле (Pre)-Master-Secret, как показано на рис. 4.8.

Выберите SSL в списке
протоколов

Введите
имя файла в поле
(Pre) -Master-Secret

Рис. 4.8

Настройка файла секретного ключа HTTPS с помощью Wireshark

На данном этапе вам необходим доступ ко всем данным HTTPS, которые использует Chrome. Следовательно, если вы перейдете на https://
www.facebook.com и проведете фильтрацию трафика в Wireshark по
http2, вы сможете увидеть все сообщения, включая преамбулу соединения, о которой мы говорили в разделе 4.1.5 (доступно в Wireshark), что
представлено на рис. 4.9.
На рис. 4.9 показано множество элементов и процессов. Если вы не
пользовались Wireshark, это количество может показаться пугающим.
В следующем списке описаны сегменты, пронумерованные на рисунке
от 1 до 5.
1 В сегменте 1 можно задавать различные параметры фильтра. На
рисунке показано, что отфильтрованы сообщения http2. Если у вас
открыто много HTTP/2-соединений, вы можете конкретизировать
фильтр, например фильтровать по IP-адресу сервера, к которому
вы подключены. Следующий фильтр показывает только сообщения
HTTP/2, отправленные на IP-адрес 31.13.90.2 и полученные с него

Фреймы HTTP/2

149

(сервер Facebook; найти его адрес можно в инструментах разработчика вашего браузера):
http2 && (ip.dst==31.13.90.2 || ip.src==31.13.90.2)
2 В

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

1

2

3

4
5

Рис. 4.9

«Волшебная» преамбула HTTP/2 в Wireshark

3 Далее

представлена полная информация о сообщении. Если Wireshark распознает протокол (в большинстве случаев), он отобразит
каждый протокол, к которому относится сообщение, в удобном для
чтения формате.
Приведенный выше пример начинается с собственного базового
фрейма (не путать с фреймами HTTP/2). Базовые фреймы отправляются как сообщения в сети Ethernet, которые обрабатываются
IPv4, отправляются через TCP, затем через SSL/TLS и наконец преобразовываются в сообщения HTTP/2. Wireshark позволяет просматривать сообщения на любом из этих уровней. На предоставленном
скриншоте развернуты сегменты HTTP/2 и «волшебной» преамбулы соединения. Если, например, вас больше интересует просмотр
Ethernet, IP, TCP или уровень SSL/TLS, вы можете развернуть соответствующие сегменты аналогичным образом.
4 В предпоследнем сегменте показаны необработанные данные, обычно отображаемые в шестнадцатеричном формате и формате ASCII.

Глава 4

150

Основы протокола HTTP/2

5 Вкладки

в последнем сегменте помогут вам решить, как отображать необработанные данные. Наверняка формат распакованного
заголовка (или, как для не имеющего сжатых заголовков «волшебного» сообщения, расшифрованный SSL) заинтересует вас больше,
чем формат необработанного фрейма.
Кроме того, с помощью Wireshark вы можете просматривать сообщения согласования HTTPS (включая запросы расширения ALPN в сообщении ClientHello и ответы в сообщениях ServerHello). На рис. 4.10 стрелки
указывают, что клиент выбирает сначала h2, а затем http/1.1.

Протоколы ALPN,
поддерживаемые
браузером

Рис. 4.10. Расширения ALPN как часть сообщения ClientHello в Wireshark

Проблемы с дешифровкой трафика Wireshark?
К сожалению, использование Wireshark для расшифровки HTTPS-трафика
может быть, мягко говоря, немного нестабильным, поэтому вам, возможно,
придется предпринять несколько попыток.
Одна из причин заключается в том, что данный процесс работает только
для новых HTTPS-сеансов с полностью выполненным TLS-рукопожатием.
Проблема состоит в том, что при последующих соединениях сайт может использовать настройки шифрования повторно, ввиду чего рукопожатие выполняется лишь частично, а этого недостаточно для расшифровки трафика
с Wireshark. Чтобы узнать, используется ли возобновление сеанса HTTPS,
установите фильтр на ssl, а не на http2. Посмотрите на первое сообщение
ClientHello и проверьте, отличается ли значение идентификатора сеанса,
а также проверьте, не являются ли значения Session ID и SessionTicket TLS не-

Фреймы HTTP/2

151

нулевыми. В таком случае текущий сеанс является возобновленным, и Wireshark не сможет расшифровать сообщения (если он не был запущен сразу
после того, как установлен исходный сеанс).
Стоит также отметить, что ни один из браузеров не дает нам надежного
способа удалить ключи SSL/TLS / удостоверения предыдущего сеанса, что
могло бы обеспечить установку нового полного рукопожатия. Разработчики
Chrome a и Firefox b получали множество просьб добавить такую функцию,
однако реализация этой заявки была отложена на неопределенный срок.
Пользователи отмечают, что заставить Wireshark работать на macOS на порядок сложнее. Например, последняя версия Firefox отказалась от функции
постоянного ведения лога ключей SSL.
Мы рекомендуем вам убедиться, что у вас установлена последняя версия
Wireshark. Вы, конечно, можете возвращаться к сеансам по их истечению,
однако такой способ малоэффективен. В качестве альтернативы вы можете
попробовать альтернативный браузер, в котором предыдущий сеанс не сохраняется.
___________________________________
a
https://bugs.chromium.org/p/chromium/issues/detail?id=90454.
b
https://bugzilla.mozilla.org/show_bug.cgi?id=285440.

Какой инструмент выбрать?
Пользуйтесь тем инструментом, который наиболее удобен именно для
вас, или, если хотите, каким-либо альтернативным инструментом. Wireshark нет равных, так как он предоставляет наиболее подробную информацию структуры и формата сообщений. Однако во многих случаях
такой уровень детализации ни к чему. Кроме того, настроить данный инструмент довольно сложно. Если вы не знакомы с Wireshark, возможно,
лучше использовать один из двух других инструментов.
В следующей части этой главы я буду приводить примеры из nghhtp,
поскольку этот инструмент наиболее прост и отлично подходит для использования в нашей книге. Независимо от того, какой инструмент вы
используете, сообщения будут похожи. Однако порядок отображения
данных или настройки могут слегка отличаться.
Данные инструменты полезны при отладке на низком уровне или
просмотре деталей протокола, однако большинство людей не использует
их постоянно каждый день. Разумеется, большинству разработчиков достаточно стандартных инструментов разработчика в браузерах, и им не
придется углубляться в net-export нижнего уровня или уровень детализации Wireshark. Однако все три инструмента очень хороши, поскольку
они легкодоступны и помогают закрепить ваше понимание протокола.

4.3.2 Формат фреймов HTTP/2
Прежде чем начать рассматривать примеры фреймов HTTP/2, необходимо понять, что они из себя представляют. Каждый фрейм состоит из

152

Глава 4

Основы протокола HTTP/2

заголовка фиксированной длины (подробно описано в табл. 4.1) и информационного наполнения.
Таблица 4.1 Формат заголовков фреймов HTTP
Поле
Length

Объем Описание
24 бит Длина фрейма, не включая все поля заголовка, указанные в этой таблице,
с максимальным размером 224 – 1 октет; ограничена значением переменной
SETTINGS_MAX_FRAME_SIZE, в которой по умолчанию задан наименьший размер
214 октетов
Type
8 бит
В настоящее время определено 14 типов фреймовa:
 DATA (0x0);
 HEADERS (0x1);
 PRIORITY (0x2);
 RST_STREAM (0x3);
 SETTINGS (0x4);
 PUSH_PROMISE (0x5);
 PING (0x6);
 GOAWAY (0x7);
 WINDOW_UPDATE (0x8);
 CONTINUATION (0x9);
 ALTSVC(0xa), добавлен в RFC 7838b;
 (0xb), в настоящее время не используетсяc;
 ORIGIN (0xc), добавлен в RFC 8336d;
 CACHE_DIGEST, рекомендуемыйe
Flags
8 бит
Флажки для конкретных фреймов
Reserved Bit 1 бит
В настоящее время не используется и должен быть установлен в значение 0
Stream
31 бит Беззнаковое целое число, идентифицирующее фрейм
Identifier
a
b
c
d
e

https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml.
https://tools.ietf.org/html/rfc7838.
https://github.com/httpwg/http-extensions/pull/323.
https://tools.ietf.org/html/rfc8336.
https://datatracker.ietf.org/doc/draft-ietf-httpbis-cache-digest/?include_text=1.

В HTTP/2 четко определены фреймы, что и делает его двоичным
протоколом. Фиксированные фреймы HTTP/2 отличаются от текстовых HTTP/1-сообщений переменной длины, которые необходимо
было анализировать путем сканирования разрывов строк и пробелов (малоэффективный процесс, в ходе которого существует вероятность возникновения ошибок). Строгий и четко определенный формат
фреймов в HTTP/2 упрощает синтаксический анализ и сокращает объем сообщений благодаря использованию кодов (например, использование кода 0x01 для типа фрейма HEADERS вместо полной формулировки
сообщения).

Октеты или байты?
В спецификации HTTP/2, как и во многих других официальных документах
Internet-протоколов, используется понятие октет, а не байт, так как последнее допускает иные толкования. Октет составляет ровно 8 бит, но, в зависимости от архитектуры системы, байт может также составлять 8 бит.

Фреймы HTTP/2

153

Поле Length, как я надеюсь, не требует пояснений. В последующих разделах мы детально рассмотрим все типы фреймов. Поле Flags зависит от
типа фрейма и описывается отдельно для каждого из них. Поле Reserved
Bit в настоящее время не используется. Поле Stream Identifier также не
требует особых объяснений. Вероятнее всего, причиной для ограничения его длины 31 битом стала совместимость с Java, поскольку данный
язык не поддерживает 32-битное целое число без знака1.
Значение флажков и информационное наполнение зависят от типа
фрейма. HTTP/2 создан с возможностью дальнейшего расширения.
В первоначальной версии спецификации было прописаны только фреймы 0–92, затем было добавлено еще три, и, несомненно, их количество
будет расти.

4.3.3 Исследование потока сообщений HTTP/2 на примерах
При изучении фреймов HTTP/2 лучше всего рассматривать их использование на реальных примерах. К примеру, если мы перейдем на www.
facebook.com (один из множества сайтов, поддерживающих HTTP/2)
с по­мощью nghttp, мы получим следующий результат:
$ nghttp -va https://www.facebook.com | more
[ 0.043] Connected
The negotiated protocol: h2
[ 0.107] recv SETTINGS frame flags=0x00, stream_id=0>
(niv=5) [SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[ 0.107] recv WINDOW_UPDATE frame
(window_size_increment=10420225)
[ 0.107] send SETTINGS frame
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.107] send SETTINGS frame
; ACK
(niv=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=0, weight=101, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=7, weight=1, exclusive=0)
1

2

https://stackoverflow.com/questions/39309442/why-is-the-stream-identifier31-bit-in-http-2-and-why-is-itpreceded-with-a-rese.
https://tools.ietf.org/html/rfc7540.

154

Глава 4

Основы протокола HTTP/2

[ 0.107] send PRIORITY frame
(dep_stream_id=3, weight=1, exclusive=0)
[ 0.107] send HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /
:scheme: https
:authority: www.facebook.com
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.28.0
[ 0.138] recv SETTINGS frame
; ACK
(niv=0)
[ 0.138] recv WINDOW_UPDATE frame
(window_size_increment=10420224)
[ 0.257] recv (stream_id=13) :status: 200
[ 0.257] recv (stream_id=13) x-xss-protection: 0
[ 0.257] recv (stream_id=13) pragma: no-cache
[ 0.257] recv (stream_id=13) cache-control: private, no-cache, no-store,
must-revalidate
[ 0.257] recv (stream_id=13) x-frame-options: DENY
[ 0.257] recv (stream_id=13) strict-transport-security: max-age=15552000;
Preload
[ 0.257] recv (stream_id=13) x-content-type-options: nosniff
[ 0.257] recv (stream_id=13) expires: Sat, 01 Jan 2000 00:00:00 GMT
[ 0.257] recv (stream_id=13) set-cookie: fr=0m7urZrTka6WQuSGa..BaQ42y.61.A
AA.0.0.BaQ42y.AWXRqgzE; expires=Tue, 27-Mar-2018 12:10:26 GMT; Max-Age=7776 000;
path=/; domain=.facebook.com; secu
re; httponly
[ 0.257] recv (stream_id=13) vary: Accept-Encoding
[ 0.257] recv (stream_id=13) content-encoding: gzip
[ 0.257] recv (stream_id=13) content-type: text/html; charset=UTF-8
[ 0.257] recv (stream_id=13) x-fb-debug: yrE7eqv05dkxF8R1+i4VlIZmUNInVI+AP
DyG7HCW6t7NCEtGkIIRqJadLwj87Hmhk6z/N3O212zTPFXkT2GnSw==
[ 0.257] recv (stream_id=13) date: Wed, 27 Dec 2017 12:10:26 GMT
[ 0.257] recv HEADERS frame
; END_HEADERS
(padlen=0)
; First response header



…и т. д.
[ 0.243] recv DATA frame

[ 0.243] recv DATA frame


Фреймы HTTP/2

155

[ 0.264] recv DATA frame

[ 0.267] send WINDOW_UPDATE frame
(window_size_increment=33706)
[ 0.267] send WINDOW_UPDATE frame
(window_size_increment=33706)

[416.688] recv DATA frame
; END_STREAM
[417.226] send GOAWAY frame
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

В данном примере большинство фреймов DATA вырезано и заменено на
…и т. д., однако некоторые из них все же присутствуют.
Чтобы просмотреть только заголовки, поставьте в командной строке
флажок –n:
$ nghttp -nv https://www.facebook.com | more

Несмотря на то что мы убрали информационную составляющую,
код выглядит довольно сложным, поэтому мы расскажем вам о нем по­
дробнее.
Сначала вы подключаетесь и согласовываете HTTP/2 через HTTPS (h2).
Первым вы получаете фрейм SETTINGS, поскольку nghttp не выводит настройки HTTPS или преамбулу / «волшебное» сообщение HTTP/2:
$ nghttp -v https://www.facebook.com | more
[ 0.043] Connected
The negotiated protocol: h2
[ 0.107] recv SETTINGS frame
(niv=5)
[SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]

Фрейм SETTINGS
Фрейм SETTINGS (0x4) – это первый фрейм, который отправляют сервер
и клиент (после преамбулы / «волшебного» сообщения HTTP/2). Фрейм
может быть или «пустышкой», или состоять из нескольких пар поле/значение, как показано в табл. 4.2.
Данный фрейм определяет только один флажок, который может
быть установлен в общем заголовке: ACK (0x1). Если настройки соединения HTTP/2 устанавливаете вы, присвойте флажку значение 0; для
подтверждения настроек, установленных другой стороной, присвойте
значение 1. При подтверждении (флажок имеет значение 1) нет необходимости устанавливать дополнительные настройки информационного
наполнения.

Глава 4

156

Основы протокола HTTP/2

Таблица 4.2 Формат фрейма HEADERS
Поле
Объем
Identifie 16 бит

Value

a
b

Описание
В спецификации определены шесть настроек, и еще две были добавлены недавно
(скорее всего, будут добавлены и другие). Предлагаемые настройки официально
не стандартизированы:
 SETTINGS_HEADER_TABLE_SIZE (0x1);
 SETTINGS_ENABLE_PUSH (0x2);
 SETTINGS_MAX_CONCURRENT_STREAMS (0x3);
 SETTINGS_INITIAL_WINDOW_SIZE (0x4);
 SETTINGS_MAX_FRAME_SIZE (0x5);
 SETTINGS_MAX_HEADER_LIST_SIZE (0x6);
 SETTINGS_ACCEPT_CACHE_DIGEST (0x7)a;
 SETTINGS_ENABLE_CONNECT_PROTOCOL(0x8)b.
Примечание: SETTINGS_ACCEPT_CACHE_DIGEST – это предполагаемый параметр,
еще не стандартизованный официально и потенциально подверженный
изменениям
32 бита В этом поле указывается значение параметра. Обратите внимание: если параметр
не определен, используются значения по умолчанию. Предлагаемые настройки еще
официально не стандартизированы:
 4096 октетов;
 1;
 Безлимитный;
 65 535 октетов;
 16 384 октетов;
 Безлимитный;
 0 – Нет;
 0 – Нет.
Примечание: SETTINGS_ACCEPT_CACHE_DIGEST – это предполагаемый параметр,
еще не стандартизованный официально и потенциально подверженный
изменениям

https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest.
https://tools.ietf.org/html/rfc8441.

Ознакомившись с информацией из таблицы, взгляните на первое сообщение еще раз:
[ 0.107] recv SETTINGS frame
(niv=5)
[SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]

Длина информационного наполнения полученного фрейма SETTINGS
составляет 30 октетов, флажок подтверждения не установлен (поэтому
фрейм подтверждения отсутствует), фрейм использует идентификатор
потока 0. Идентификатор потока 0 зарезервирован для управляющих сообщений (фреймы SETTINGS и WINDOW_UPDATE), поэтому сервер и использует
поток 0 для отправки данного кадра SETTINGS.
Затем вы получаете сами настройки. В данном примере их пять (niv=5),
объем каждой из них составляет 16 (идентификатор) + 32 (значение) бит.
Всего в примере 48 бит, или 6 октетов, что составляет длину, указанную
в заголовке, – 30 октетов (5 заголовков × 6 октетов = 30 октетов). Все идет
нормально. Теперь взгляните на индивидуальные настройки.

Фреймы HTTP/2

157

1 Значение параметра SETTINGS_HEADER_TABLE_SIZE у Facebook – 4069 ок-

тетов. Данный параметр используется для сжатия HTTP-заголовка
HPACK, о котором мы поговорим в главе 8.
2 Также Facebook использует параметр SETTINGS_MAX_FRAME_SIZE со значением16 384 октета, благодаря чему вашему клиенту (nghttp) нет
необходимости отправлять объемное информационное наполнение по этому соединению.
3 Затем Facebook устанавливает для параметра SETTINGS_MAX_HEADER_
LIST_SIZE значение 131 072 октета, что запрещает клиенту отправлять объемные несжатые заголовки.
4 Сервер устанавливает параметр SETTINGS_MAX_CONCURRENT_STREAMS на
100 потоков. В главе 2 представлен пример, в котором была предпринята попытка загрузить более 100 изображений с сервера
с ограничением в 100 потоков. В том случае запросы были поставлены в очередь в ожидании свободного потока, аналогично тому,
как это происходит в очереди запросов в HTTP/1, однако с лимитом
подключений ниже шести, что меньше чем у большинства браузеров. HTTP/2 позволяет значительно увеличить число выполняемых
параллельных запросов, но зачастую их количество ограничивается сервером (обычно до 100 или 128 потоков), хотя по умолчанию
ограничений нет.
5 Наконец, Facebook присваивает параметру SETTINGS_INITIAL_WINDOW_
SIZE значение 65 536 октетов. Данный параметр используется для
управления потоком, и мы поговорим о нем в главе 7.
В этом, казалось бы, простом фрейме присутствует несколько нюансов. Во-первых, настройки, описанные выше, могут идти в любом порядке, например SETTINGS_MAX_CONCURRENT_STREAMS, в спецификации определенный как настройка 3 (0x03), может идти после SETTINGS_MAX_HEADER_LIST_SIZE,
который является настройкой 6 (0x06). Во-вторых, начальные значения
многих параметров установлены по умолчанию, поэтому сервер может
отправить сокращенный фрейм SETTINGS всего с тремя настройками, ничего при этом не теряя:
[ 0.107] recv SETTINGS frame
(niv=3)
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]

Однако в увеличении количества настроек нет ничего плохого, напротив, такой способ помогает указать более точные значения.
В данном примере мы видим, что значение параметра SETTINGS_INITIAL_
WINDOW_SIZE на 1 октет больше, чем значение по умолчанию (65 535 октетов), что кажется слегка странным, поскольку нет необходимости менять
значение по умолчанию.
К тому же обратите внимание, что сервер Facebook не устанавливает
параметр SETTINGS_ENABLE_PUSH, который предназначен для отправки со-

158

Глава 4

Основы протокола HTTP/2

общений сервером клиенту (используется на стороне клиента). Для сервера нет смысла устанавливать данный параметр, однако мы предполагаем, что его можно использовать для того, чтобы объявлять клиентам
о том, что сервер поддерживает push-загрузку (если авторы спецификации решили использовать его для этой цели). В случаях, если клиент
не поддерживает (или не хочет использовать) HTTP/2, лучше убрать этот
параметр из фрейма SETTINGS.
Давайте рассмотрим еще один пример использования фрейма SETTINGS:
[ 0.107] recv SETTINGS frame
(niv=5)
[SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):131072]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
[ 0.107] send SETTINGS frame
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.107] send SETTINGS frame
; ACK
(niv=0)

[ 0.138] recv SETTINGS frame
; ACK
(niv=0)

Сначала nghttp получает начальный фрейм SETTINGS сервера (уже знакомый нам), затем клиент отправляет фрейм SETTINGS с парой настроек.
Далее клиент подтверждает фрейм SETTINGS сервера. Данный фрейм довольно прост, он имеет флажок ACK(0x01), длина составляет 0, и, следовательно, у него нет настроек (niv=0). Чуть ниже следует подтверждение
сервером фрейма SETTINGS клиента в идентичном простом формате.
В данном примере существует отрезок времени, в течение которого
одна из сторон уже отправила фрейм SETTINGS, но еще не получила ответ. В это время запрещено использовать настройки не по умолчанию.
Однако такая ситуация не вызывает серьезных проблем, поскольку все
реализации HTTP/2 должны иметь возможность обрабатывать значения
по умолчанию, а также фрейм SETTINGS должен отправляться первым.

Фрейм WINDOW_UPDATE
Также сервер отправил фрейм WINDOW_UPDATE:
[ 0.107] recv WINDOW_UPDATE frame
(window_size_increment=10420225)

Фрейм WINDOW_UPDATE (0x8) используется для управления потоком, например, с целью ограничения возможного количества отправленных

Фреймы HTTP/2

159

данных во избежание перегрузки получателя. В HTTP/1 одновременно может выполняться только один запрос. При перегрузке клиент он
останавливает обработку TCP-пакетов; затем механизм управления
TCP-по­то­ком (аналогично управлению потоком HTTP/2) тормозит отправку данных до тех пор, пока получатель не будет готов обрабатывать
новые данные. В HTTP/2 в одном и том же соединении может быть несколько потоков, и поэтому вы не зависите от управления TCP-потоком
и должны реализовать свой собственный метод замедления для каждого потока.
Первоначальный размер отправляемого окна данных устанавливается в фрейме SETTINGS, а фрейм WINDOW_UPDATE применяется для увеличения
его размера. Поэтому WINDOW_UPDATE является простым фреймом без каких-либо меток и с одним значением (и зарезервированным битом), как
показано в табл. 4.3.
Таблица 4.3 Формат фрейма WINDOW_UPDATE
Поле
Reserved Bite
Window Size Increment

Объем
1 бит
31 бит

Описание
Не используется
Количество октетов, которое может быть отправлено до того, как должен
быть получен следующий фрейм WINDOW_UPDATE

WINDOW_UPDATE не определяет флажки и применяется к текущему потоку
или, если значение этого потока 0, применяется ко всему соединению
HTTP/2. Поэтому отправители должны отслеживать как уровень потока,
так и уровень соединения.
Управление потоком HTTP/2 применяется только к фреймам DATA. Все
другие типы фреймов (или по крайней мере те, которые определены на
данный момент) могут продолжать отправку, даже если места в окне
управления потоком уже нет. Данная функция предотвращает блокировку важных управляющих сообщений (таких как само сообщение WINDOW_
UPDATE) большими фреймами DATA. Кроме того, DATA является единственным фреймом, размер которого не ограничен.
Мы исследуем механизм управления потоком HTTP/2 в главе 7.

Фрейм PRIORITY
Далее следует несколько фреймов PRIORITY (0x2):
[ 0.107] send PRIORITY frame frame
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.107] send PRIORITY frame
( dep_stream_id=0, weight=101, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=7, weight=1, exclusive=0)
[ 0.107] send PRIORITY frame
(dep_stream_id=3, weight=1, exclusive=0)

Глава 4

160

Основы протокола HTTP/2

Данный код создает несколько потоков с различными приоритетами
использования в nghttp. Фактически nghttp не использует потоки 3–11
напрямую; применяя параметр dep-stream-id, он «вешает» на них другие потоки, те, которые он устанавливал в самом начале. Использование
предварительно созданных приоритетных потоков позволяет соответствующим образом приоритизировать запросы. Благодаря этому способу у вас отпадает необходимость устанавливать приоритеты для каждого
последующего нового потока. Не все клиенты HTTP/2 используют этот
подход, и nghttp основывает свою реализацию на модели Firefox1, поэтому не беспокойтесь, если вы используете другой инструмент и не видите
эти фреймы PRIORITY.
Приоритеты потоков HTTP/2 – довольно сложная тема, поэтому мы отложим ее рассмотрение до главы 7. На данном этапе имейте в виду, что
некоторым запросам (например, исходному HTML, критическому CSS
и критическому Java­Script) можно дать приоритет над менее важными
запросами (такими как изображения или некритический асинхронный
Java­Script). Формат таких фреймов представлен в табл. 4.4, но вы разберетесь в нем лучше после прочтения главы 7.
Таблица 4.4 Формат фреймов PRIORITY
Поле
E (Exclusive)

Объем
1 бит

Stream Dependency

31 бит

Weight

8 бит

Описание
Указывает, является ли поток эксклюзивным (устанавливается, только если для
этого фрейма установлен флажок Priority)
Индикатор того, от какого потока зависит этот заголовок (устанавливается,
только если для этого кадра установлен флажок Priority)
Вес этого потока (устанавливается, только если для этого фрейма установлен
флажок Priority)

Фрейм PRIORITY (0x2) имеет фиксированную длину и не определяет
флажки.

Фрейм HEADERS
Наконец, после установки всех настроек, мы переходим к прямому применению протокола и можем совершить запрос HTTP/2. Запросы отправляются в фрейме HEADERS (0x1):
[ 0.107] send HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /
:scheme: https
:authority: www.facebook.com
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.28.0
1

https://nghttp2.org/documentation/nghttp.1.html#dependency-based-priority.

Фреймы HTTP/2

161

Все строки, кроме первых двух, выглядят довольно похожими на запросы HTTP/1. Как вы, возможно, помните из главы 1, запрос HTTP/1 состоит из первой строки и обязательного заголовка хоста (и любых других
заголовков HTTP):
GET / HTTP/1.1↵
Host: www.facebook.com↵

В HTTP/2 нет специального типа фреймов для запросов, а во фрейме HEADERS каждая строка отправляется в виде заголовка. Кроме того, для
определения различных частей строки запроса HTTP создан ряд новых
псевдозаголовков (строки, которые начинаются с двоеточия).
:method
: GET
:path: /
:scheme: https
:authority: www.facebook.com

Обратите внимание, что псевдозаголовок :authority заменил заголовок HTTP/1.1 Host. В отличие от стандартных HTTP-заголовков псевдозаголовки HTTP/2 строго определены1, их нельзя добавить, не изменив
протокол, и поэтому вы не сможете создать новый псевдозаголовок вроде этого:
:barry: value

Однако вы можете создавать обычные HTTP-заголовки. Для этого
в любых заголовках, предназначенных специально для какого-то приложения, уберите начальное двоеточие:
barry: value

Новые спецификации все же позволяют создавать псевдозаголовки,
и на момент написания данной книги такая спецификация существует. В документе Bootstrapping WebSockets with HTTP/2 RFC 2 был добавлен
псевдозаголовок :protocol. Возможно, использование новых псевдозаголовков потребует обновления параметра SETTINGS, где необходимо будет
указывать, поддерживает ли клиент или сервер такие новшества.
Такие псевдозаголовки могут отображаться в клиентских инструментах, например в инструментах разработчика Chrome (рис. 4.11), поэтому их наличие также указывает на то, что запрос совершен с помощью
HTTP/2 (хотя на момент написания книги многие браузеры, такие как
Firefox, их не отображают).
Стоит отметить, что HTTP/2 применяет строчные имена заголовков
HTTP. Согласно официальной спецификации HTTP/1 данная версия не
учитывала регистр для имен заголовков, хотя некоторые реализации
и поступились этим правилом. Разные регистры могут быть в значениях
заголовков, но не в их названиях. HTTP/2 не одобряет вольности в фор1
2

https://tools.ietf.org/html/rfc7540#section-8.1.2.
https://tools.ietf.org/html/rfc8441#section-3.

Глава 4

162

Основы протокола HTTP/2

матировании заголовков. Излишние пробелы в начале строки, двойные
двоеточия и символы возврата строки могут вызвать проблемы в работе
протокола HTTP/2, несмотря на то что большинство реализаций HTTP/1
с этой проблемой справляется. Данный пример является одним из наиболее эффективных вариантов распознавания сообщений HTTP/2 на
уровне фрейма. Описанные выше ошибки обычно возникают именно
на этом уровне. Когда клиенты сталкиваются с недопустимыми заголовками, которые мешают работе вашего сайта, они выдают пользователю
загадочные сообщения об ошибках (например, ERR_SPDY_PROTOCOL_ERROR
в Chrome). Формат фрейма HEADERS представлен в табл. 4.5.

HTTP/2-псевдозаголовки
на вкладке Network (Сеть)
в инструментах
разработчика Chrome

Рис. 4.11

Псевдозаголовки HTTP/2 в инструментах разработчика Chrome

Таблица 4.5 Формат фреймов HEADERS
Поле
Pad Length

Объем
8 бит (необязательно)

E (Exclusive)

1 бит

Stream Dependency 31 бит

Weight

8 бит

Header Block
Fragment

Объем фрейма
за вычетом других полей
в этой таблице
Определяется полем Pad
Length (необязательно)

Padding

Описание
Необязательное поле, указывающее длину поля Padding
(устанавливается, только если для этого фрейма установлен
флажок Padded)
Указывает, является ли поток эксклюзивным
(устанавливается, только если для этого фрейма установлен
флажок Priority)
Указывает поток, от которого зависит этот заголовок
(устанавливается, только если для этого фрейма установлен
флажок Priority)
Вес этого потока (устанавливается, только если для этого
фрейма установлен флажок Priority)
Заголовки запроса (включая псевдозаголовки)
Устанавливает значение 0 для каждого заполняемого байта
(устанавливается, только если для этого фрейма установлен
флажок Padded)

Поля E, Stream Dependency и Weight мы обсудим в главе 7. Поля Pad Length
и Padding добавлены по соображениям безопасности. При необходимости

Фреймы HTTP/2

163

они позволяют скрыть истинную длину сообщения. В поле Header Block
Fragment отправляются все заголовки (включая псевдозаголовки). Как
можно судить по выводу nghhtp, это поле не является открытым текстом.
В главе 8 мы рассмотрим формат сжатия заголовков HPACK, поэтому сейчас вам не следует беспокоиться об этом, к тому же, такие инструменты,
как nghhtp, автоматически распаковывают заголовки HTTP за вас.
Фрейм HEADERS использует четыре флажка, которые могут быть установлены в общем заголовке фрейма:
„„ END_STREAM (0x1) устанавливается при условии, что за фреймом HEADERS не следуют никакие другие фреймы (например, фрейм DATA для
запроса POST). Парадоксально, но фреймы CONTINUATION (которые мы
обсудим далее в этой главе) не ограничиваются как HEADERS; они
считаются продолжением фрейма HEADERS, а не дополнительными
фреймами, и управляются флажком END_HEADERS;
„„ END_HEADERS (0x4) указывает, что все заголовки HTTP включены во
фрейм и за ними не следует фрейм CONTINUATION, в котором могут
содержаться дополнительные заголовки;
„„ PADDED (0x8) устанавливается, когда используется заполнение. Она
означает, что первые 8 бит фрейма DATA указывают, насколько был
заполнен фрейм HEADERS;
„„ PRIORITY (0x20) указывает, что во фрейме установлены поля E, Stream
Dependency и Weight.
Если HTTP-заголовок состоит из более чем одного фрейма, вторым
фреймом станет не дополнительный фрейм HEADERS, а фрейм CONINTUATION,
следующий сразу за HEADERS. В сравнении с телами HTTP, которые используют столько фреймов DATA, сколько потребуется, данный процесс может
показаться чрезмерно усложненным. Но другие поля, представленные
в табл. 4.5, можно использовать только один раз. Если устанавливать их
в последующих фреймах HEADERS для одного и того же запроса, могут возникнуть некоторые проблемы. Поэтому мультиплексированный характер HTTP/2 ограничивается требованием, согласно которому фреймы
CONTINUATION должны следовать сразу за HEADERS, а их чередование запрещено. Именно поэтому были рассмотрены некоторые альтернативы1.
Сейчас фрейм CONTINUATION используется довольно редко, и большинство
запросов умещается в один фрейм HEADERS.
Теперь, владея вышеизложенной информацией, вы сможете лучше
понять первую часть сообщения:
[ 0.107] send HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /
:scheme: https
:authority: www.facebook.com
1

https://github.com/http2/http2-spec/wiki/ContinuationProposals.

164

Глава 4

Основы протокола HTTP/2

accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.28.0

Каждому новому запросу присваивается уникальный идентификатор
потока, значение которого больше, чем у идентификатора последнего используемого потока (в данном случае идентификатор последнего
фрейма PRIORITY, созданного ngix, имеет значение 11, а новому фрейму
присваивается значение 13, поскольку четное значение зарезервировано
за сервером). Также устанавливаются различные метки, которые в совокупности образуют шестнадцатеричное значение 0x25, а nghttp2 помогает отображать их в строке ниже. Метки END_STREAM (0x1) и END_HEADERS (0x4)
указывают на то, что фрейм содержит полный запрос и не имеет фрейма
DATA (что встречается у запросов POST). Метка PRIORITY (0x20) указывает на
приоритизацию. Сложив все эти шестнадцатеричные значения (0x1 + 0x4
+ 0x20), вы получите 0x25, как и указано в заголовке фрейма. Данный поток зависит от идентификатора потока 11, поэтому ему присваивается
соответствующий приоритет и вес 16 в рамках этого приоритета. Я вновь
прошу вас не особо задумываться над этим сейчас, поскольку о приоритизации мы подробнее поговорим в главе 7. Инструмент nghttp отмечает,
что этот поток новый (Open new stream), а затем следует перечисление различных псевдозаголовков HTTP и заголовков запросов HTTP.
HTTP-ответы содержат фрейм HEADERS в том же потоке, как вы можете
увидеть в следующем примере:
[ 0.257] recv (stream_id=13) :status: 200
[ 0.257] recv (stream_id=13) x-xss-protection: 0
[ 0.257] recv (stream_id=13) pragma: no-cache
[ 0.257] recv (stream_id=13) cache-control: private, no-cache, no-store,
must-revalidate
[ 0.257] recv (stream_id=13) x-frame-options: DENY
[ 0.257] recv (stream_id=13) strict-transport-security: max-age=15552000;
preload
[ 0.257] recv (stream_id=13) x-content-type-options: nosniff
[ 0.257] recv (stream_id=13) expires: Sat, 01 Jan 2000 00:00:00 GMT
[ 0.257] recv (stream_id=13) set-cookie: fr=0m7urZrTka6WQuSGa..BaQ4Ay.61.A
AA.0.0.BaQ42y.12345678; expires=Tue, 27-Mar-2018 12:10:26 GMT; Max-Age=7776
000; path=/; domain=.facebook.com; secu
re; httponly
[ 0.257] recv (stream_id=13) set-cookie: sb=so11234567890TZ7e-i5S2To; expi
res=Fri, 27-Dec-2019 12:10:26 GMT; Max-Age=63072000; path=/; domain=.facebo
ok.com; secure; httponly
[ 0.257] recv (stream_id=13) vary: Accept-Encoding
[ 0.257] recv (stream_id=13) content-encoding: gzip
[ 0.257] recv (stream_id=13) content-type: text/html; charset=UTF-8
[ 0.257] recv (stream_id=13) x-fb-debug: yrE7eqv05dkxF8R1+1234567890nVI+AP
DyG7HCW6t7NCEtGkIIRqJadLwj87Hmhk6z/N3O212zTPFXkT2GnSw==
[ 0.257] recv (stream_id=13) date: Wed, 27 Dec 2017 12:10:26 GMT
[ 0.257] recv HEADERS frame
; END_HEADERS
(padlen=0)
; First response header

Фреймы HTTP/2

165

В данном примере сначала идет псевдозаголовок (:status: 200), который, в отличие от того же процесса в HTTP/1.1, вместо текстового представления кода состояния (например, 200 ОК) дает только трехзначное
числовое значение (200). За ним следуют различные HTTP-заголовки,
которые отправляются немного иначе (мы рассмотрим это при обзоре
HPACK в главе 8). Затем nghttp перечисляет параметры фрейма HEADERS.
Как ни странно, nghttp предоставляет параметры фрейма после информационного наполнения фрейма, а не до него, что было бы предпочтительнее1. Параметры включают в себя флажок END_HEADERS (0x04), который сигнализирует, что весь заголовок HTTP-ответа помещается в один
фрейм HEADERS.

Завершающие заголовки
В HTTP/1.1 была впервые представлена концепция завершающих заголовков, которые можно отправлять после тела запроса. Они позволяют использовать метаинформацию, которая не может быть рассчитана заранее. Например, для потоковых данных можно вычислить контрольную сумму или
цифровую подпись контента и включить ее в завершающий HTTP-заголовок.
В реальности завершающие заголовки не поддерживаются в большинстве
реализаций и используются крайне редко. Но в HTTP/2 они поддерживаются, поэтому фрейм HEADERS (или фрейм HEADERS, за которым следует один или
несколько фреймов CONTINUATION) появляется до и (необязательно) после
всех фреймов DATA для этого потока.

Фрейм DATA
За фреймом HEADERS следует фрейм DATA (0x0), который используется
для отправки тела сообщения. В HTTP/1.0 тело сообщения следовало после заголовков и двух символов разрыва строки (сигнализирующих об
окончании заголовка). В HTTP/2 данные оформлены в отдельный тип сообщения. Вы можете отправлять заголовки, за которыми следует часть
тела, затем часть другого потока, снова часть тела и т. д. Разделив ответы
HTTP/2 на один или несколько фреймов, вы можете мультиплексировать
потоки через одно и то же соединение.
Фреймы DATA в HTTP/2 довольно просты и содержат различные данные, которые могут понадобиться на странице: текст в кодировке UTF-8,
сжатые с помощью gzip файлы, код HTML, байты, составляющие изображение JPEG – что угодно. Заголовок основного фрейма содержит длину,
поэтому данное значение не требуется для фрейма DATA. Как и фрейм
HEADERS, фрейм DATA позволяет использовать заполняющие данные (паддинг), чтобы скрыть размер сообщения по соображениям безопасности,
поэтому в начале может присутствовать поле Pad Length для указания
длины. Насколько проста структура фрейма DATA, вы можете увидеть
в табл. 4.6.
1

https://github.com/nghttp2/nghttp2/issues/1163.

Глава 4

166

Основы протокола HTTP/2

Таблица 4.6 Формат фрейма DATA
Поле
Объем
Pad Length 8 бит (необязательно)
Data
Padding

Описание
Необязательное поле, указывающее длину поля Padding
(присутствует, только если установлен флажок PADDED)
Объем фрейма за вычетом полей Данные
Padding
Определяется полем Pad Length
Установка значения 0 для каждого заполняемого байта
(необязательно)
(присутствует, только если установлен флажок PADDED)

Фрейм DATA определяет два флажка, которые могут быть установлены
в заголовке фрейма:
„„ END_STREAM (0x1) устанавливается, если кадр является последним
в потоке;
„„ PADDED (0x8) устанавливается при использовании заполнения. Это
означает, что первые 8 бит кадра DATA используются для обозначения степени заполнения в конце кадра.
В следующем примере большая часть содержимого вырезана из соображений экономии места; обычно строки ect… заполняются соответствующими данными:



…и т. д.
[ 0.243] recv DATA frame
…и т. д.
[ 0.243] recv DATA frame frame
…и т. д.
[ 0.264] recv DATA frame
…и т. д.
[ 0.267] send WINDOW_UPDATE frame
(window_size_increment=33706)
[ 0.267] send WINDOW_UPDATE frame
(window_size_increment=33706)
…и т. д.
[416.688] recv DATA frame

Здесь мы видим, как HTML-код отправляется в различных фреймах
DATA (nghttp помогает вам распаковать данные), и, когда клиент обрабатывает эти фреймы, он в ответах отправляет фреймы WINDOW_UPDATE, позволяя серверу продолжать отправлять больше данных. Интересно, что
Facebook по умолчанию отправляет относительно небольшие кадры DATA
(1122 октета, 2589 октетов и т. д.), несмотря на то что клиент может обрабатывать кадры гораздо большего размера (до 65 535 октетов). Я не
уверен, что такой способ выбран преднамеренно (чтобы как можно быстрее передать как можно больше данных клиенту). Возможно, так происходит, поскольку окно перегрузки TCP изначально невелико или по
какой-либо другой причине.
Поскольку фреймы DATA в HTTP/2 по умолчанию могут быть разделены
на части, нет необходимости в специальном механизме фрагментиро-

Фреймы HTTP/2

167

ванной передачи (обсуждается в разделе 4.1.1). В спецификации HTTP/2
сказано: «Механизм фрагментированной передачи ... НЕ ДОЛЖЕН использоваться в HTTP/2».

Фрейм GOAWAY
Фрейм GOAWAY (0x7) выглядит следующим образом:
[417.226] send GOAWAY frame
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

Этот фрейм с несколько грубым названием (go away – «убирайся
прочь») используется для закрытия соединений по двум причинам:
либо потому, что сообщений, которые могут быть отправлены, больше
нет, либо потому, что возникла серьезная ошибка. В табл. 4.7 приведен
формат фрейма GOAWAY.
Таблица 4.7 Формат фрейма GOAWAY
Поле
Объем
Reserved Bit
1 бит
Last-Stream-ID 31 бит
Error Code

Additional
Debug Data

Описание
Не используется
ID последнего обработанного входящего потока, чтобы клиент мог
знать, был ли пропущен недавно инициированный поток
32 бита
Код ошибки, если фрейм GOAWAY был отправлен из-за ошибки:
 NO_ERROR(0x0);
 PROTOCOL_ERROR(0x1);
 INTERNAL_ERROR(0x2);
 FLOW_CONTROL_ERROR(0x3);
 SETTINGS_TIMEOUT(0x4);
 STREAM_CLOSED(0x5);
 FRAME_SIZE_ERROR(0x6);
 REFUSED_STREAM(0x7);
 CANCEL(0x8);
 COMPRESSION_ERROR(0x9);
 CONNECT_ERROR(0xa);
 ENHANCE_YOUR_CALM(0xb);
 INADEQUATE_SECURITY(0xc);
 HTTP_1_1_REQUIRED(0xd)
Остаток длины кадра Неопределенный, зависящий от реализации формат
(необязательно)

Фрейм GOAWAY не определяет флажки.
Посмотрев на последнее сообщение в предыдущем выводе nghttp, вы
можете увидеть пример фрейма GOAWAY:
[417.226] send GOAWAY frame
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

Клиент nghttp сам отправляет фрейм GOAWAY, а не получает его от сервера. В этом примере nghttp получает HTML-код домашней страницы
и не запрашивает все зависимые ресурсы (CSS, Java­Script и т. д.), которые
запрашивал бы обычный браузер. После того как клиент получает и обрабатывает все данные, он отправляет этот фрейм для закрытия соединения HTTP/2. Веб-браузеры в большинстве случаев оставляют соединение открытым на случай возникновения последующих запросов, однако

Глава 4

168

Основы протокола HTTP/2

использование nghttp завершается после получения одного такого ответа, поэтому перед выходом закрывается и соединение. При закрытии
сеанса браузера то же самое может происходить с любыми открытыми
соединениями.
Фрейм был отправлен с минимальной длиной в 8 октетов (1 бит +
31 бит + 32 бита); флажки не устанавливались; фрейм отправлен в потоке
0. Последний идентификатор потока, полученный от сервера, имел значение 0, поэтому потоков, инициированных сервером, не было. Также не
было кодов ошибок (NO_ERROR [0x00]) и дополнительных отладочных данных. Таким образом, этот пример представляет собой стандартный способ аккуратно закрыть соединение, когда в нем уже нет необходимости.

4.3.4 Дополнительные фреймы
На примере Facebook мы рассмотрели многие типы фреймовHTTP/2, однако существует еще несколько типов, не вошедших в простой поток из
примера. Кроме того, протокол HTTP/2 предусматривает возможность
добавления в него новых фреймов. Недавно были добавлены три новых типа фреймов – ALTSVC, ORIGIN и CACHE_DIGEST, – которые мы обсудим
в конце данного раздела. На момент написания этой книги формально
стандартизированы только два типа, а последний, возможно, будет стандартизирован к моменту публикации. Каждый новый фрейм, настройка
HTTP/2 или код ошибки должны быть зарегистрированы в Internet Assigned Numbers Authority (IANA)1.

Фрейм CONTINUATION
Фрейм CONTINUATION (0x9) используется для больших HTTP-заголовков
и следует сразу за фреймом HEADERS или PUSH_PROMISE. Поскольку весь заголовок должен быть доступен до того, как запрос может быть обработан,
а также для контроля словаря HPACK (см. главу 8), фрейм CONTINUATION
должен следовать сразу за фреймом HEADERS, нуждающимся в продолжении. Как уже упоминалось ранее при обсуждении фрейма HEADERS,
данное требование ограничивает возможности мультиплексирования
потока HTTP/2. Возникало много споров о необходимости использования фрейма CONTINUATION и о том, следует ли разрешить больший размер фреймов HEADERS. Пока CONTINUATION еще используется, однако вряд
ли у него есть хорошие перспективы. Фрейм CONTINUATION проще, чем
фреймы HEADER или PUSH_PROMISE, продолжением которых он является. Он
содержит дополнительные данные заголовка. Формат данного фрейма
приведен в табл. 4.8.
Таблица 4.8 Формат фрейма CONTINUATION
Поле
Header Block Fragment
1

Длина
Длина кадра без этого поля

Описание
Информация

https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml.

169

Фреймы HTTP/2

Фрейм CONTINUATION определяет только один флажок, который может
быть установлен в общем заголовке фрейма. Флажок END_HEADERS (0x4),
если он установлен, указывает, что в данном фрейме завершены все заголовки и за ним не следует фрейм CONTINUATION с дополнительными заголовками.
Фрейм CONTINUATION не использует флажок END_STREAM для указания на
отсутствие тела, поскольку является продолжением исходного фрейма
HEADERS.

Фрейм PING
Фрейм PING (0x6) используется для приблизительного измерения объема
данных, проступающих со стороны отправителя, а также может использоваться для поддержания активности неиспользуемого соединения.
Получив этот фрейм, принимающая сторона должна немедленно ответить аналогичным фреймом PING. Оба фрейма должны быть отправлены в потоке управления (идентификатор потока 0). Формат фрейма PING
приведен в табл. 4.9.
Таблица 4.9 Формат фрейма PING
Поле
Opaque Data

Длина
64 бита (8 октетов)

Описание
Данные, которые будут отправлены в ответном запросе PING

Фрейм PING определяет одну метку, которая может быть установлена
в общем заголовке фрейма. ACK (0x1) не следует устанавливать в отправляемом фрейме PING, но он должен быть в возвращаемом фрейме PING.

Фрейм PUSH_PROMISE
Фрейм PUSH_PROMISE (0x5) используется сервером, чтобы сообщить клиенту, что сервер собирается отправить ресурс, который клиент не запрашивал. Фрейм PUSH_PROMISE должен предоставить клиенту информацию
о ресурсе, который будет отправлен, поэтому он включает все заголовки
HTTP, которые обычно включаются в запрос фрейма HEADERS (и аналогично может сопровождаться фреймом CONTINUATION для push-запросов с заголовками, превышающими размер одного фрейма). Формат фрейма
PUSH_PROMISE приведен в табл. 4.10.
Таблица 4.10 Формат фрейма PUSH_PROMISE
Поле
Pad Length

Длина
8 бит (необязательно)

Reserved Bit
Promised Stream ID

1 бит
31 бит

Header Block Fragment Длина фрейма за вычетом других полей
в этой таблице
Padding
Определяется полем Pad Length
(необязательно)

Описание
Необязательное поле, указывающее
длину поля Padding
Не используется
Указывает поток, в котором будет
отправлено push-обещание
Заголовки HTTP отправленного ресурса
Устанавливает в 0 все заполняющие
байты

Глава 4

170

Основы протокола HTTP/2

Фрейм PUSH_PROMISE определяет два флажка, которые могут быть установлены в общем заголовке фрейма:
END_HEADERS (0x4) указывает, что все заголовки содержатся в этом
фрейме и за ними не следует фрейм CONTINUATION с дополнительными заголовками;
„„ PADDED (0x8) устанавливается при использовании заполнения. Это
означает, что первые 8 бит фрейма DATA используются для обозначения объема заполнения, которое было добавлено в конец фрейма
PUSH_PROMISE.

„„

Мы обсудим механизм server push протокола HTTP/2 в главе 5.

Фрейм RST_STREAM
Последний фрейм, определенный в исходной спецификации HTTP/2, –
это фрейм RST_STREAM (0x3), который используется для немедленной отмены (или сброса) потока. Необходимость в отмене может возникнуть
ввиду ошибки или ввиду того, что запрос уже не требуется. Также она
возникает, когда клиент покидает соединение, отменяет загрузку или
ему больше не нужен выгружаемый сервером ресурс.
HTTP/1.1 не поддерживает эту функцию. Если вы начнете загружать
объемный ресурс на какой-либо странице, вы не сможете отключить его
загрузку, даже покидая страницу. Единственным выходом является закрытие соединения. У вас нет возможности отменить запрос, если он
находится в активном состоянии. Данная функция – это еще одно преимущество HTTP/2 перед HTTP/1.1. Формат фрейма RST_STREAM приведен
в табл. 4.11.
Таблица 4.11 Формат фрейма RST_STREAM
Поле
Error Code

Длина
32 бита

Описание
Код ошибки, указывающий на причину прерывания потока:
 NO_ERROR (0x0);
 PROTOCOL_ERROR (0x1);
 INTERNAL_ERROR (0x2);
 FLOW_CONTROL_ERROR (0x3);
 SETTINGS_TIMEOUT (0x4);
 STREAM_CLOSED (0x5);
 FRAME_SIZE_ERROR (0x6);
 REFUSED_STREAM (0x7);
 CANCEL (0x8);
 COMPRESSION_ERROR (0x9);
 CONNECT_ERROR (0xa);
 ENHANCE_YOUR_CALM (0xb);
 INADEQUATE_SECURITY (0xc);
 HTTP_1_1_REQUIRED (0xd)

Фрейм RST_STREAM не определяет флажки.
В спецификации содержится мало информации о данных кодах ошибок, и даже если она есть, то не всегда вполне ясна. Например, в спецификации сказано следующее насчет того, что один из двух кодов ошибок
может использоваться для отмены отправленных ответов:

171

Фреймы HTTP/2

Если по какой-либо причине клиент отказывается от получения
уже отправленного ответа от сервера или если серверу требуется слишком много времени на отправку обещанного ответа, клиент может отправить фрейм RST_STREAM и указать код CANCEL или
REFUSED_STREAM, а также ссылку на идентификатор отправляе­
мого потока.
В конечном итоге разработчики сами решают, какие коды ошибок использовать и когда. Реализации не всегда поддерживают использование
выбранных вами кодов.

Фрейм ALTSVC
Фрейм ALTSVC (0xa) был первым фреймом, добавленным к HTTP/2 после
утверждения спецификации. Он подробно описан в отдельной спецификации1 и позволяет серверу объявлять об использовании альтернативных служб, доступных для получения этого ресурса, как описано в разделе 4.2.4. Данный фрейм можно использовать для обновления протокола
(например, до соединения h2c с h2) или для направления трафика на
другую версию (см. табл. 4.12).
Таблица 4.12 Формат фрейма ALTSVC
Поле
Origin-Len
Origin
Alt-Svc-Field-Value

Длина
16 бит
Определяется полем Origin-Len (необязательно)
Длина фрейма за вычетом других полей в этой таблице

Описание
Длина поля Origin
Альтернативный URL
Альтернативный вид сервиса

Фрейм ALTSVC не определяет флажки.

Фрейм ORIGIN
Фрейм ORIGIN (0xc) – это новый фрейм, введенный в спецификацию
в марте 2018 года2. Он позволяет серверу указывать, исходя из какой
начальной точки (например, имя домена) этот сервер будет отвечать.
Данный фрейм полезен для клиента, поскольку помогает решить, объединять ли уже установленные соединения с этим соединением HTTP/2.
Формат фрейма ORIGIN представлен в табл. 4.13.
Таблица 4.13 Формат фрейма ORIGIN
Поле
Origin-Len
Origin

Длина
16 бит
Обозначается полем Origin-Len (необязательно)

Описание
Длина поля Origin
Альтернативный URL

Во всю длину фрейма можно включить несколько пар Origin-Len/Origin. Фрейм ORIGIN не определяет флажки. При обсуждении объединения
соединений в главе 6 мы еще вернемся к фрейму ORIGIN.
1
2

https://tools.ietf.org/html/rfc7838.
https://tools.ietf.org/html/rfc8336.

Глава 4

172

Основы протокола HTTP/2

Фрейм CACHE_DIGEST
Фрейм CACHE_DIGEST(0xd) на момент написания книги имел статус нового предложения, находящегося на утверждении1. Он позволяет клиенту указать, какие ресурсы он кешировал. Это означает, что серверу не
следует отправлять эти ресурсы, так как они уже есть у клиента. Формат
фрейма CACHE_DIGEST на момент написания книги (он может измениться)
приведен в табл. 4.14.
Таблица 4.14 Формат фрейма CACHE_DIGEST
Поле
Origin-Len
Origin
Digest-Value

Длина
16 бит
Определяется полем Origin-Len
(необязательно)
Длина кадра за вычетом других полей
в этой таблице (необязательно)

Описание
Длина поля Origin
То, к чему относится адрес начала краткого
изложения
Cache-Digest (обсуждается в главе 5)

Фрейм CACHE_DIGEST определяет следующие флажки:
„„ RESET (0x1) дает указание серверу сбросить любую текущую информацию CACHE_DIGEST;
„„ COMPLETE (0x2) указывает, что включенные дайджесты являются не
сегментом кеша, а его полным представлением.
При обсуждении механизма push-загрузки протокола HTTP/2 в главе 5
мы еще вернемся к фрейму CACHE_DIGEST.

Резюме
HTTP/2 – это двоичный протокол, в котором сообщения имеют особый
формат и структуру.
„„ По этой причине перед отправкой любых HTTP-сообщений клиент
и сервер должны подтвердить использование HTTP/2.
„„ Веб-браузеры согласовывают этот аспект посредством установки
HTTPS-соединения с использованием нового расширения под названием ALPN.
„„ В HTTP/2 запросы и ответы отправляются и принимаются в формате
фреймов.
„„ Например, запрос HTTP/2 GET обычно отправляется в фрейме HEADERS,
а ответ обычно выглядит как фрейм HEADERS, за которым следуют фреймы DATA.
„„ У большинства веб-разработчиков и администраторов веб-серверов
нет необходимости просматривать фреймы HTTP/2, хотя существуют
инструменты, с помощью которых это можно сделать.
„„ Уже утвержден довольно обширный набор фреймов HTTP/2 и, кроме
того, можно добавлять новые.
„„

1

https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest.

5

Реализация HTTP/2 push

В этой главе мы рассмотрим:
что такое HTTP/2 push;
различные способы запроса HTTP/2 push;
„„ работа HTTP/2 push со стороны сервера и клиента;
„„ где HTTP/2 push применяется, а где нет;
„„ устранение неполадок HTTP/2 push;
„„ некоторые риски HTTP/2 push.
„„
„„

5.1

Что такое HTTP/2 server push?
Концепция push-загрузки в HTTP/2 (далее HTTP/2 push) позволяет серверам вместе с ответами отправлять дополнительные ресурсы, которые не были запрошены клиентом. До появления версии HTTP/2 HTTP
был простым протоколом запроса и ответа; браузер запрашивал ресурс,
и сервер отвечал ему этим ресурсом. Если требовались дополнительные
ресурсы (такие как CSS, Java­Script, шрифты, изображения и т. д.), браузеру приходилось загружать начальную страницу, проверять наличие
ссылок на дополнительные ресурсы, а затем запрашивать их. В случае
изображений выполнение дополнительных запросов не представляло
серьезной проблемы, так как их загрузка обычно не задерживает визуализацию страницы, поскольку вместо них во время старта рендеринга
может быть просто пустое место. Однако некоторые ресурсы критически
важны для рендеринга страницы (например, CSS и Java­Script), и браузер
даже не будет пытаться отобразить страницу, пока эти ресурсы не загру-

Глава 5

174

Реализация HTTP/2 push

жены. Такой процесс добавляет по крайней мере один дополнительный
цикл приема и передачи, и поэтому замедляет просмотр веб-страниц.
Мультиплексирование HTTP/2 позволяет запрашивать все ресурсы параллельно в одном соединении, что, в отличие от HTTP/1, помогает
сократить очереди. Без HTTP/2 push браузеру пришлось бы совершать
дополнительные запросы только после загрузки начальной страницы.
Следовательно, в лучшем случае для большинства запросов веб-страниц
требовалось бы не менее двух циклов приема-передачи. На рис. 5.1 показано, что файл CSS и файл Java­Script загружаются одновременно во
втором наборе запросов.
Веб-браузер

1. GET /index.html

Веб-сервер

2. /index.html
3. GET /styles.css
GET /script.js
4. /styles.css
/script.js

Рис. 5.1 Дополнительный цикл приема-передачи при загрузке
критических ресурсов

На рис. 5.2 мы видим, что для начала визуализации страницы требуется два цикла. Обратите внимание, загрузка ресурсов styles.css и script.js
происходит в разное время из-за ограничений сети или обработки; они
загружаются не параллельно.

Рис. 5.2

Задержка приема-передачи критических ресурсов в виде каскадной диаграммы

По причине этой задержки появилась необходимость в оптимизации
производительности; например, теперь таблицы стилей встраиваются
непосредственно в HTML-страницу с помощью тегов , а в Java­
Script подобные процессы совершаются с помощью тегов . Таким
образом, браузеры могут запускать процесс рендеринга сразу после загрузки и анализа начальной страницы и не ждать загрузки дополнительных критических ресурсов.
Однако встраивание ресурсов также имеет некоторые недостатки.
Для CSS встраиваются только критически важные части стиля (те, что
необходимы для запуска рендеринга страницы), а полный файл стилей
загружается позже, что позволяет минимизировать количество встроенного кода и сократить размер страницы. Извлечь необходимые стили из

175

Что такое HTTP/2 server push?

ресурсов CSS и встроить их в HTML-файл довольно сложно, однако и для
этой задачи существуют свои инструменты. Данный процесс не только
сложный, но еще и довольно ресурсоемкий; критический CSS хранится
не в одном CSS-файле, который можно кешировать и использовать на
других страницах, а дублируется на каждой странице веб-сайта. Кроме того, встраиваемый критический CSS находится и в основном CSSфай­ле, который загружается позже. Таким образом, он дублируется на
каждой странице! Также процесс встраивания требует использовать
Java­Script для загрузки некритических файлов CSS. Для этого используется стандартный тег , что
приводит к приостановке рендеринга до загрузки файлов, так как для
тегов CSS link нет атрибута async. Кроме того, если вы захотите внести
изменения в критический CSS (например, при редизайне сайта), вместо одного общего CSS-файла вам нужно будет вносить изменения на
каждую страницу. В общем и целом встраивание обеспечивает неплохой
прирост производительности при первом посещении сайта, однако это
все же своего рода ухищрение. HTTP/2 push является другим (и лучшим)
способом решения данной проблемы.
HTTP/2 push исключает парадигму «один запрос – один ответ», согласно которой всегда работал протокол HTTP. Push-загрузка позволяет серверу на один запрос дать несколько ответов. На вопрос «Скажите, можно
ли мне получить эту страницу?» вполне может последовать ответ: «Да,
конечно, и вот вам еще несколько ресурсов, которые могут пригодиться
для этой страницы». Рисунок 5.3 показывает, что для получения страницы и критических ресурсов, необходимых для старта рендеринга, можно
обойтись одним циклом «запрос–ответ».
Веб-браузер

Веб-сервер
1. GET /index.html
2. /index.html
/styles.css
/script.js

Рис. 5.3 Использование HTTP/2 push может устранить задержку приема-передачи
для критических ресурсов

Данный процесс можно представить и в виде каскадной диаграммы,
как на рис. 5.4. На нем видно, что получение ресурсов происходит не одновременно, о чем свидетельствуют короткие промежутки между ними.
Однако на это требуется времени лишь немного больше одного цикла,
а не два полноценных цикла «запрос–ответ».
Также экономию времени можно увидеть на диаграммах типа «запрос–ответ», представленных в главе 2. На рис. 5.5 видно, что время значительно экономится за счет того, что все критические ресурсы отправляются вместе с начальной страницей.

Глава 5

176

Реализация HTTP/2 push

Рис. 5.4 Каскадная диаграмма использования HTTP/2 push
для получения всех запросов за один цикл приема-передачи
Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270

Время
(мс)
Запрос веб-страницы

Получение запроса веб-страницы
Отправка HTML-страницы

Получение HTML-страницы
Запрос CSS
Запрос JS

Получение запроса CSS
Получение запроса JS
Отправка CSS
Отправка JS

Запрос CSS
Запрос JS
Прорисовка страницы

0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270

Запрос веб-страницы

Получение запроса веб-страницы
Отправка HTML-страницы
Отправка CSS
Отправка JS

Получение HTML-страницы
Запрос CSS
Запрос JS
Прорисовка страницы

Рис. 5.5 Поток запросов для базовой веб-страницы без HTTP/2 push (слева)
и с HTTP/2 push (справа)

HTTP/2 push помогает сократить время загрузки, но только при условии правильного использования. Загрузка слишком большого количества ресурсов (перегрузка), которые клиент не будет использовать или
которые уже есть в его кеше, может привести к задержкам. Вместо того
чтобы загрузить действительно нужный вам ресурс, пропускная способность соединения будет использоваться для загрузки не столь важных
ресурсов. HTTP/2 push следует использовать вдумчиво и с осторож­
ностью, о чем мы и поговорим в этой главе.

Как отправлять push-сообщения

177

Может ли HTTP/2 push заменить WebSockets или SSE?
Следует отметить один важный момент: ресурсы, отправленные с помощью
push, отсылаются только в ответ на первоначальный запрос. Загрузка ресурсов с помощью HTTP/2 push не осуществляется только на основании решения сервера, что клиент может нуждаться в них. Такие технологии, как
WebSockets и server-sent events (SSE), допускают создание двустороннего
потока, однако HTTP/2 не является двунаправленным; все действия инициируются запросом со стороны клиента. Ресурсы, отправленные с помощью
push, представляют собой дополнения к ответу на первоначальный запрос.
Когда первоначальный запрос завершается, закрывается и поток, и поэтому
отправка других ресурсов невозможна, пока клиент не совершит еще один
запрос. Таким образом, HTTP/2 push не является полноценной заменой WebSockets или SSE в том виде, в каком он определен в настоящее время, хотя,
возможно, он станет таковым после внедрения некоторых расширений (см.
раздел 5.9).

5.2

Как отправлять push-сообщения
Способ отправки зависит от используемого вами веб-сервера, так как на
момент написания данной книги не все серверы поддерживают HTTP/2
push. Некоторые веб-серверы могут отправлять push-сообщения, в которых задаются HTTP-заголовок link или настройки конфигурации. Другие веб-серверы (например, IIS) требуют для этого написания отдельного
кода, поэтому они могут отправлять push-сообщения только с динамически сгенерированных страниц, а не со статических файлов HTML. В документации вашего веб-сервера содержится информация о том, поддерживает ли он HTTP/2 push, а также рекомендации по его использованию.
Далее в этой главе используются в основном примеры Apache, nginx
и NodeJS. Данные концепции применимы к большинству веб-серверов
HTTP/2, даже если в реализации детали немного различаются. Если ваш
веб-сервер не поддерживает HTTP/2 push, вы можете применить обходной путь: использовать сети доставки содержимого и уже с их помощью
отправлять push-сообщения.

5.2.1 Отправка push-сообщений с помощью HTTP-заголовка
ссылки
Многие веб-серверы (например, Apache, nginx и H2O) и некоторые CDN
(например, Cloudflare и Fastly) уведомляют веб-сервер о необходимости
использования push-технологии с помощью HTTP-заголовка link. Когда
сервер видит данные заголовки, он отправляет указанные в нем ресурсы
с помощью push. Для Apache вы можете добавить такой заголовок с помощью следующей конфигурации:
Header add Link ";as=style;rel=preload"

Глава 5

178

Реализация HTTP/2 push

Для ngix вы можете использовать похожий синтаксис:
add_header Link ";as=style;rel=preload"

Заголовки push-ссылок часто заключаются в условные операторы, что
позволяет применять push только для определенных маршрутов или типов файлов. Например, в Apache, для загрузки с помощью push таблицы
стилей CSS только с файлами index.html, а не со всеми ресурсами сразу
вы можете использовать следующую конфигурацию:

Header add Link ";as=style;rel=preload"


Для других веб-серверов HTTP-заголовки добавляются аналогичным образом, хотя не каждый из них использует метод HTTP-заголовка
ссылки для отправки ресурсов с помощью push. Если сервер все же поддерживает такой метод, при ответе клиенту он считывает данные заголовки, запрашивает соответствующий ресурс и отправляет его. Атрибут
rel=preload позволяет указать серверу на необходимый для отправки ресурс, а часть конфигурации as=style (которая указывает на тип ресурса)
может быть необязательна. Атрибут as может использоваться для расстановки приоритета, однако он требуется не для всех серверов: например,
Apache расставляет приоритет с помощью определения типа содержимого.

HTTP-заголовок предварительной загрузки и HTTP/2 push
Заголовки ссылок предварительной загрузки использовались еще до создания HTTP/2 и изначально задуманы как подсказка для клиента (см. главу 6).
Данный заголовок позволяет браузерам получать ресурсы, не дожидаясь загрузки, прочтения и анализа всей страницы, благодаря чему у них есть время
на то, чтобы решить, необходимо ли загружать эти ресурсы. Заголовки предварительной загрузки позволяют владельцу веб-сайта сказать: «Этот ресурс
обязательно понадобится, поэтому я предлагаю вам запросить его как можно
скорее, если он еще не у вас в кеше».
Во многих реализациях HTTP/2 заголовок ссылки предварительной загрузки
получил иное предназначение с целью внедрения концепции push-загрузки,
и поэтому данная подсказка вышла на несколько иной уровень. В случае,
если вы собираетесь использовать предварительную загрузку, а не отправить
ресурс с помощью push, вы можете использовать атрибут nopush:
Header add Link ";as=style;rel=preload;nopush"
На сегодняшний день не существует какого-либо стандартизированного
способа, позволяющего осуществить обратный процесс (т. е. заявить, что мы
хотим отправить заголовок ссылки с помощью push и на самом деле он не
является заголовком предварительной загрузки). Однако веб-сервер H2O
(и CDN Fastly, которую он используетa) добавил для таких случаев атрибут

x-http2-pushonly:

Как отправлять push-сообщения

179

link: ;as=script;rel=preload;x-http2-push-only
Заголовок предварительной загрузки также можно внедрить в сам HTML
в теге HEAD следующим образом:

Однако для HTTP/2 push обычно работает только использование HTTPзаголовка. Метод внедрения предварительной загрузки в HTML не подходит
для HTTP/2 push, поскольку серверам сложно анализировать весь код и извлекать из него заголовки. Веб-браузерам же в любом случае приходится
анализировать HTML, поэтому они поддерживают оба вышеописанных метода.
Для клиентских подсказок необходимо указывать атрибут as, однако при использовании HTTP/2 push он может и не требоваться. Во избежание путаницы я рекомендую указывать данный атрибут всегда. На вебсайте w3.orgb вы
можете найти полный набор атрибутов as, а также script, style, font, image,
и fetch. Обратите внимание, что для некоторых из этих атрибутов (особенно
для font) также требуется атрибут crossoriginc.
Повторное использование заголовков предварительной загрузки для HTTP/2
push немного сбивает с толку. Многие говорят, что применение существующих функций для новых целейd – это плохая идея, и предлагают найти новые
способы. Несмотря на это беспокойство, использование заголовков, похоже,
растет. Еще одним преимуществом использования заголовка предварительной загрузки в качестве подсказки как для клиента, так и для push-сервера
является то, что, в случае если и клиент, и сервер не поддерживают HTTP/2
push, они все же смогут использовать заголовок для предварительной загрузки ресурса с высоким приоритетом, получая таким образом прирост производительности. В разделе 5.8 я еще затрону тему директив предварительной загрузки и рассмотрю различные варианты их использования в HTTP/2
push. Вся эта сноска предназначена для тех читателей, которые понимают,
что предварительная загрузка может быть подсказкой для клиентов.
___________________________________
a
https://www.fastly.com/blog/optimizing-http2-server-push-fastly.
b
https://www.w3.org/TR/preload/#as-attribute.
c
https://drafts.csswg.org/css-fonts/#font-fetching-requirements.
d
https://github.com/w3c/preload/issues/99.

При тестировании страниц в Apache вам следует отключить PushDiary,
который будет пытаться предотвратить повторную отправку одних и тех
же ресурсов по одному и тому же соединению (подробнее мы поговорим
об этом в разделе 5.4.4):
H2PushDiarySize 0

Явный запрос обновления страницы в браузере (F5) заставит Apache
игнорировать PushDiary, но во время тестирования его все же лучше отключить; в противном случае вы увидите противоречивые результаты.
Для других серверов ситуация может обстоять подобным образом, и от-

180

Глава 5

Реализация HTTP/2 push

слеживание push для них также следует отключать. Также вы можете отправить несколько заголовков, используя два ссылочных заголовка:
Header add Link ";rel=preload;as=style"
Header add Link ";rel=preload;as=script"

или объединив их в один заголовок и разделив запятыми:
Header add Link " ;rel=preload;as=style,
;rel=preload;as=script"

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

5.2.2 Просмотр ресурсов, отправленных
с помощью HTTP/2 push
Ресурсы, загруженные с помощью push, отображаются на панели инструментов разработчика Chrome в столбце Initiator (инициатор обмена), как
показано на рис. 5.6.
На нем вы можете увидеть, что второй ресурс (common.css) отправлен
сервером. Также вы видите, что для данного запроса, в отличие от всех
последующих, загрузка ресурса начинается сразу, и на каскадной диаграмме нет элемента Waiting (TTFB), отмеченного зеленым цветом.
Выгруженный ресурс, как
показано в столбце Initiator
(Инициатор)

Отсутствует время ожидания

Рис. 5.6 Ресурс, отправленный с помощью HTTP/2 push во вкладке Network (Сеть) панели
инструментов разработчика Chrome.

На рис. 5.7 изображена загрузка той же страницы без push (запрос common.css переместился со второй позиции на третью и не был отправлен
с помощью push).
Ресурс больше
не выгружается

Большое время ожидания

Рис. 5.7 Загрузка той же страницы, что и на рис. 5.6, без HTTP/2 push

Глава 5

182
[
[
[
[
[
[
[

0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]

Реализация HTTP/2 push

recv (stream_id=13) :path: /assets/css/common.css
recv (stream_id=13) :method: GET
recv (stream_id=13) accept: */*
recv (stream_id=13) accept-encoding: gzip, deflate
recv (stream_id=13) user-agent: nghttp2/1.28.0
recv (stream_id=13) host: www.tunetheweb.com
recv PUSH_PROMISE frame
; END_HEADERS
(padlen=0, promised_stream_id=2)

Фрейм PUSH_PROMISE похож на фрейм HEADERS, отправляемый браузером
для получения исходного ресурса, но все же имеет два важных отличия:
„„ данный фрейм отправляется сервером в браузер, а не наоборот. Он
выступает предупреждением от сервера клиенту «Я собираюсь отправить вам ресурс»;
„„ он включает в себя promised_stream_id, который является идентификатором потока для отправленного ресурса, как показано в последней строке, и указывает, что ресурс будет отправлен с помощью push с идентификатором потока 2. Потокам, инициированным
сервером (на данный момент только push-потоки), присваиваются
четные номера.
После этого сервер возвращает первоначально запрошенный ресурс
в потоке запроса (13), используя фрейм HEADERS, за которым следуют
фреймы DATA. Затем он отправляет ресурс с помощью push в соответствующий поток (2), опять же используя фрейм HEADERS, за которым следуют
фреймы DATA:
[
[
[
[
[
[
[
[
[
[
[
[
[
[

0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]
0.017]

[
[
[
[
[
[

0.017]
0.017]
0.018]
0.018]
0.018]
0.018]

recv (stream_id=13) :status: 200
recv (stream_id=13) date: Sun, 04 Feb 2018 12:28:07 GMT
recv (stream_id=13) server: Apache
recv (stream_id=13) last-modified: Thu, 18 Jan 2018 21:52:14 GMT
recv (stream_id=13) accept-ranges: bytes
recv (stream_id=13) cache-control: max-age=10800, public
recv (stream_id=13) expires: Sun, 04 Feb 2018 15:28:07 GMT
recv (stream_id=13) vary: Accept-Encoding,User-Agent
recv (stream_id=13) content-encoding: gzip
recv (stream_id=13) link: ;rel=preload
recv (stream_id=13) content-length: 6755
recv (stream_id=13) content-type: text/html; charset=utf-8
recv (stream_id=13) push-policy: default
recv HEADERS frame
; END_HEADERS
(padlen=0)
; First response header
recv DATA frame
recv DATA frame
recv DATA frame
recv DATA frame
recv DATA frame
recv DATA frame
; END_STREAM

183

Как отправлять push-сообщения
[
[
[
[
[
[
[
[
[
[
[
[

0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]
0.018]

[
[
[
[
[

0.018]
0.018]
0.018]
0.018]
0.018]

recv (stream_id=2) :status: 200
recv (stream_id=2) date: Sun, 04 Feb 2018 12:28:07 GMT
recv (stream_id=2) server: Apache
recv (stream_id=2) last-modified: Sun, 07 Jan 2018 14:57:44 GMT
recv (stream_id=2) accept-ranges: bytes
recv (stream_id=2) cache-control: max-age=10800, public
recv (stream_id=2) expires: Sun, 04 Feb 2018 15:28:07 GMT
recv (stream_id=2) vary: Accept-Encoding,User-Agent
recv (stream_id=2) content-encoding: gzip
recv (stream_id=2) content-length: 5723
recv (stream_id=2) content-type: text/css; charset=utf-8
recv HEADERS frame
; END_HEADERS
(padlen=0)
; First push response header
recv DATA frame
recv DATA frame
recv DATA frame
recv DATA frame
recv DATA frame
; END_STREAM

5.2.3 Загрузка ресурсов посредством push из нисходящих
систем с помощью заголовков ссылок
В случае, если для указания на необходимые ресурсы вы используете
ссылочные заголовки, вам не нужно настраивать их в конфигурации вебсервера. Как было сказано в главе 3, обычно – по соображениям производительности и безопасности – перед целевым приложением стоит вебсервер, например Apache (это может быть сервер приложений, такой как
Tomcat, NodeJS или обработчик PHP). Если данные серверы приложений
проксируются через веб-сервер, поддерживающий HTTP/2 push с использованием заголовков ссылок (как Apache и ngix) при условии, что
у вас есть возможность устанавливать заголовки ответов, сервер приложений может отправлять запросы на отправку ресурсов от веб-сервера,
как показано на рис. 5.9.
Веб-браузер

Веб-сервер
1. GET /index.html

Сервер приложений
2. GET /index.html

5. /index.html
+ /styles.css
+ /script.js

3. /index.html
Link: ;rel=preload
Link: ;rel=preload

4. GET /styles.css
GET /script.js

Рис. 5.9 Отправка заголовков ссылок HTTP/2 push с серверов целевых приложений

Глава 5

184

Реализация HTTP/2 push

Использование ссылочных заголовков HTTP позволяет приложению
сообщать веб-серверу, что нужно отправить, поэтому вся логическая
часть может быть в одном месте, а каждый раз менять конфигурацию
веб-сервера и код приложения необходимости нет. Данный процесс работает, даже если серверные соединения используют HTTP/1. В использовании HTTP/2 на внутренних соединениях нет необходимости, даже
если вы хотите отправить ресурсы именно оттуда. Учитывая все возможные сложности (упомянутые в главе 3), это настоящий подарок судьбы!
На рис. 5.10 изображено, как выглядит такой поток на диаграмме запросов и ответов.
Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170

Клиент

Сервер

Внутренний сервер приложений

Запрос веб-страницы

Получение запроса веб-страницы

Получение запроса веб-страницы
Отправка HTML-страницы

Отправка HTML-страницы
Отправка CSS
Отправка JS
Получение HTML-страницы
Запрос CSS
Запрос JS
Прорисовка страницы

Рис. 5.10 Использование ссылочных заголовков для push-загрузки ресурсов
с внутреннего сервера приложений

Для того чтобы увидеть пример подобного потока, создайте простой
сервер с поддержкой HTTP/1.1, как показано в следующем листинге:
Листинг 5.1

Код службы HTTP/1.1 c ссылочным заголовком HTTP

var http = require('http')
const port = 3000
const requestHandler = (request, response) => {
console.log(request.url)
response.setHeader('Link',';rel=preload');
response.writeHead(200, {"Content-Type": "text/html"});
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('Test\n')
response.write('\n')
response.write('\n')

Как отправлять push-сообщения

185

response.end();
}
var server = http.createServer(requestHandler)
server.listen(port)
console.log('Server is listening on ' + port)

Поместите этот код в файл с именем app.js, а затем запустите его с помощью следующей команды:
node app.js

Вы должны увидеть строку c таким содержанием:
Server is listening on 3000

Данный код прослушивает порт 3000 и отправляет в ответ простую
жестко вписанную в код веб-страницу, которая ссылается на таблицу
стилей в теге HEAD и включает ее в заголовок ссылки. Для того чтобы проверить результат в другом окне, вы можете использовать curl:
$ curl -v http://localhost:3000
* Rebuilt URL to: http://localhost:3000/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.56.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Link: ;rel=preload;as=style
< Content-Type: text/html
< Date: Sun, 04 Feb 2018 15:46:12 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<






Test


* Connection #0 to host localhost left intact

Для того чтобы разрешить вызов этого сервера через Apache, добавьте
в конфигурацию Apache следующую строку. Необходимо включить mod_
proxy и mod_proxy_http:
ProxyPass /testnodeservice/ http://localhost:3000/

186

Глава 5

Реализация HTTP/2 push

Затем через Apache код вызовет необходимую службу, прослушивая
HTTPS через порт 443. Этот код позволяет вам вызывать службу через
HTTP/2 в браузере, даже если приложение NodeJS не настроено для
поддержки HTTP/2 или HTTPS; Apache позаботится об этом за вас. На
рис. 5.11 вы можете увидеть, как Apache отправляет через push таблицу
стилей. В данном примере загруженный с помощью push ресурс (common.css) обслуживается Apache. Связанный ресурс может обслуживаться
самим Apache, целевым приложением (в данном случае NodeJS) или другой целевой системой. Пока Apache может запрашивать ресурс, он может
и отправлять любые такие ресурсы.

Рис. 5.11 Ресурсы, указанные в ссылочном заголовке, могут допускать отправку
push-сообщений из нижестоящих систем

Веб-сервер не может отправить ресурс для другого домена. Если вы
загружаете страницу example.com, которая нуждается в изображениях,
например с google.com, вы не сможете загрузить их с помощью push; сделать это может только google.com. В разделе 5.5.1 мы обсудим эту тему
подробнее.
Предыдущий пример, где таблица стилей всегда загружается с по­мощью
push, довольно прост, однако вы можете создавать и более сложные примеры на любом языке, используемом в нижестоящем сервере, проксируемом через веб-сервер (или CND) с использованием ссылочных заголовков
HTTP. Приложение может само решать, что и когда отправлять, на основе того, что ему известно о запросе или пользовательском сеансе, но при
этом оно переносит функцию фактической отправки на веб-сервер.

5.2.4 Предварительная push-загрузка ресурсов
Помимо использования ссылочных HTTP-заголовков, вы можете загружать ресурсы с помощью push и другими способами. Выбор способа зависит от вашего веб-сервера, так как процесс определяется конкретной
реализацией. К примеру, Apache использует директиву H2PushResource:
H2PushResource add /assets/css/common.css

Как отправлять push-сообщения

187

nginx предлагает схожий синтаксис:
http2_push /assets/css/common.css;

Неиспользованные push-ресурсы не отображаются
в инструментах разработчика Chrome
Push-ресурсы, используемые на странице, отображаются во вкладке Network
панели инструментов разработчика Chrome. Предзагрузчик информирует
пользователя и дает ему подсказки касаемо предварительной загрузки. Благодаря этому отображаются все ресурсы, загруженные с помощью метода
ссылочных заголовков (при условии, что включен атрибут as). Однако, если
вы пользуетесь другим методом, а страница не использует загруженный ресурс, ресурсы будут загружаться в фоновом режиме и поэтому не отображаются на панели инструментов разработчика Chrome.
Если какой-либо push-ресурс не отображается в инструментах разработчика,
проверьте, действительно ли он нужен странице. Если он не является обязательным, то его нет смысла загружать.

Метод прямой push-загрузки лучше, нежели загрузка с помощью заголовков HTTP. Его преимущество заключается в том, что серверу не
нужно дожидаться заголовков, вместо этого он может сразу начать загрузку ресурса. Зависимые ресурсы могут начать загружаться уже во
время обработки сервером исходного ресурса. Для простых статических
ресурсов, генерация которых происходит довольно быстро (так как они
извлекаются непосредственно с диска), вышеописанное преимущество
может не иметь столь большого значения, как для ресурсов, которые генерируются медленнее1. На рис. 5.12 изображена диаграмма «запроса–
ответа», аналогичная той, что была на рис. 5.5 в этой же главе. Однако
здесь веб-страница генерируется за 100 мс. Возможно, это происходит
из-за того, что для загрузки требуется поиск в базе данных или другая
динамическая обработка.
На данном рисунке отмечен большой промежуток времени, в течение
которого через соединение HTTP/2 ни отправка, ни прием ресурсов не
происходит. Подобная задержка – это очень расточительно. Более того,
все это напоминает проблемы блокировки заголовка строки, которые
протокол HTTP/2 был призван решить. CSS и Java­Script обычно генерируются быстрее, поскольку они могут быть статическими, и сервер извлекает их с локального диска (или даже из собственного кеша). Освободившееся время может быть отведено для push-загрузки некоторых
ресурсов, чтобы к моменту завершения генерации страницы зависимый
ресурс уже был загружен (см. рис. 5.13).
1

https://icing.github.io/mod_h2/earlier.html.

Глава 5

188
Клиент

Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260

Сервер

Внутренний сервер приложений

Запрос веб-страницы

Получение запроса веб-страницы

Отправка HTML
Отправка CSS
Отправка JS

Получение запроса веб-страницы

Отправка HTML

Получение HTML-страницы
Запрос CSS
Запрос JS
Прорисовка страницы

Рис. 5.12

Загрузка веб-страницы с учетом времени, затраченного на внутреннюю обработку

Клиент

Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240

Реализация HTTP/2 push

Сервер

Внутренний сервер приложений

Получение запроса веб-страницы
Отправка CSS
Отправка JS

Получение запроса веб-страницы

Запрос веб-страницы

Запрос CSS
Запрос JS

Отправка HTML

Отправка HTML

Получение HTML-страницы
Прорисовка страницы

Рис. 5.13

Заблаговременная push-загрузка, позволяющая использовать время эффективнее

Листинг ниже демонстрирует имитацию задержки в NodeJS. Обратите внимание, что код async/await поддерживается только версией NodeJS 7.10 или новее.

Как отправлять push-сообщения

189

Листинг 5.2 Код Node с ссылочными заголовками HTTP, задержка 10 мс
var http = require('http')
const port = 3000
async function requestHandler (request, response) {
console.log(request.url)
//Начало готовности ответа.
response.setHeader('Link',';rel=preload ')
//Пауза на 10 с для имитации медленного ресурса.
await sleep(10000)
//А теперь отправляем ресурс.
response.writeHead(200, {"Content-Type": "text/html"})
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('Test\n')
response.write('\n')
response.write('\n')
response.end();
}
function sleep(ms){
return new Promise(resolve=>{
setTimeout(resolve,ms)
})
}
var server = http.createServer(requestHandler)
server.listen(port)
console.log('Server is listening on ' + port)

При обращении к этому коду с помощью nghttp и последующем перенаправлении ответа в grep с целью выделения только строк фрейма recv
после установки соединения вы увидите 10-секундную задержку, необходимую для отправки фрейма PUSH-PROMISE (соответствует 10-секундному «засыпанию» в предыдущем коде):
$
[
[
[
[
[
[
[
[
[

nghttp -anv https://www.tunetheweb.com/testnodeservice/ | grep "recv.*frame"
0.209] recv SETTINGS frame
0.209] recv WINDOW_UPDATE frame
0.213] recv SETTINGS frame
10.225] recv PUSH_PROMISE frame
10.225] recv HEADERS frame
10.225] recv DATA frame
10.226] recv HEADERS frame
10.226] recv DATA frame
10.226] recv DATA frame

Глава 5

190

Реализация HTTP/2 push

[ 10.226] recv DATA frame
[ 10.226] recv DATA frame
[ 10.226] recv DATA frame

В Apache, если вместо конфигурации, ожидающей ссылочные заголовки, выбрать конфигурацию push-загрузки с помощью H2PushResource,
ресурсы будут загружены мгновенно, а 10-секундной задержки не возникнет, поскольку основной ресурс не будет препятствовать загрузке
остальных:
$
[
[
[
[
[
[
[
[
[
[
[
[

nghttp -anv https://www.tunetheweb.com/testnodeservice/ | grep "recv.*frame"
0.248] recv SETTINGS frame
0.248] recv WINDOW_UPDATE frame
0.253] recv SETTINGS frame
0.253] recv PUSH_PROMISE frame
0.253] recv HEADERS frame
0.253] recv DATA frame
0.253] recv DATA frame
0.253] recv DATA frame
0.253] recv DATA frame
0.253] recv DATA frame
10.262] recv HEADERS frame
10.262] recv DATA frame

Данное улучшение чрезвычайно полезно. В идеале при загрузке большинства ресурсов 10-секундной задержки происходить и не должно (она
добавлена здесь для наглядности). Однако в реальности чем раньше вы
приступите к push-загрузке, тем более эффективно вы сможете использовать доступную пропускную способность. Кроме того, загрузка других
ресурсов не будет конфликтовать с основным запросом, так как он все
равно будет готов позже.
Однако у данного способа есть и оборотная сторона. Одним из недостатков является то, что инициация push-уведомлений зависит уже
в большей степени от приложения, а не от пользователя. Код состояния
HTTP —103 Early Hints2—1 призван решить данную проблему. Он позволяет
указывать требования к ресурсу заранее с помощью предварительно загруженных ссылочных заголовков HTTP. Как и все коды состояния в диапазоне 100, данный код носит информационный формат, и приложение
может проигнорировать его. Он позволяет получить ответ заранее и отправить при этом только заголовки (включая заголовки ссылок, необходимых для HTTP 2 push), за которыми последует стандартный код ответа
200.
В мире HTTP/1.1 данный код выглядит как набор ответов, следующих
друг за другом:
HTTP/1.1 103 Early Hints
Link: ;rel=preload;as=style
HTTP/1.1 200 OK

1

https://tools.ietf.org/html/rfc8297.

191

Как отправлять push-сообщения
Content-Type: text/html
Link: ;rel=preload;as=style


...и т. д.

На рис. 5.14 представлена диаграмма запросов и ответов.
Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240

Клиент

Сервер

Внутренний сервер приложений

Запрос веб-страницы

Получение запроса веб-страницы
Отправка CSS
Отправка JS

Получение запроса веб-страницы
Отправка 103 Early Hints

Время обработки
Запрос CSS
Запрос JS
Отправка HTML

Отправка HTML

Получение HTML-страницы
Прорисовка страницы

Рис. 5.14 Код состояния 103 Early Hints сообщает веб-серверу о необходимости
предварительной push-загрузки ресурсов

На этой диаграмме сервер отправляет предварительный ответ 103, который говорит о том, что странице необходимы CSS и Java­Script. С по­мощью
HTTP/2 push сервер отправляет клиенту данные статические ресурсы. Это
происходит во время ожидания завершения генерации самой страницы.
После завершения генерации и отправки страницы клиенту последний
сразу же может воспользоваться push-ресурсами. Страница отобразится
сразу после того, как клиент получит ее, однако есть ограничения: pushзагрузка не распределяется между сервером приложений и веб-сервером,
а также не должны использоваться приемы встраивания данных.
Вышеописанный процесс может работать медленнее, чем при использовании веб-сервера, поскольку в этом случае необходимо решить, какие
ресурсы будут загружены с помощью push. На рис. 5.14 push-загрузка может начаться с отметки 60 мс, однако, если процесс осуществляется через
ссылочные заголовки, она начнется только после отметки 80 мс, но выгода от использования для push-загрузки сервераприложений во многих
сценариях перевешивает недостатки.
При использовании nghttp подобный сценарий выглядел бы следующим образом:

Глава 5

192

Реализация HTTP/2 push

$ nghttp -anv https://www.tunetheweb.com/testnodeservice/ | grep
"recv.*frame"
[ 0.307] recv SETTINGS frame
[ 0.307] recv WINDOW_UPDATE frame
[ 0.307] recv SETTINGS frame
[ 0.308] recv HEADERS frame
[ 0.308] recv PUSH_PROMISE frame
[ 0.309] recv HEADERS frame
[ 0.309] recv DATA frame
[ 0.310] recv DATA frame
[ 0.310] recv DATA frame
[ 0.310] recv DATA frame
[ 0.310] recv DATA frame
[ 10.317] recv HEADERS frame
[ 10.317] recv DATA frame
[ 10.317] recv DATA frame
[ 10.318] recv DATA frame
[ 10.318] recv DATA frame
[ 10.318] recv DATA frame
[ 10.318] recv DATA frame

После завершения начальной настройки вы увидите следующее:
на отметке 0,308 с во фрейме HEADERS в потоке 13 приходит ответ 103;
„„ фрейм PUSH-PROMISE (также в потоке 13) предупреждает клиента
о предстоящем процессе push-загрузки;
„„ ресурс помещается во фрейм HEADERS и несколько последующих
фреймов DATA, а затем на отметке 0,309 и 0,310 с все эти фреймы отправляются в потоке 2;
„„ после обработки реального ответа через 10,217 с он отправляется
обратно в виде фрейма HEADERS, за которым следуют один или несколько фреймов DATA.
На момент написания этой книги поддержка нового кода ответа 103
Early Hints ограничена. Например, Node по умолчанию не поддерживает его1, однако вы можете включить эту функцию с помощью сторонней
библиотеки2 или путем внесения в поток необработанного HTTP (что
и делает библиотека). Apache может обрабатывать ответы с кодом 103. Он
будет обрабатывать любые необходимые ссылочные заголовки. Однако
намеренно пересылать ответ 103 браузеру он не станет, так как некоторые браузеры не поддерживают такие ответы, и могут возникнуть ошибки. Пересылку можно включить с помощью директивы H2EarlyHints53.
Поддержка ограничена еще и потому, что она предполагает отправку
нескольких ответов на один запрос. Для ответов в диапазоне 100 такой
исход допустим, но для других ответов он не совсем подходит, так как является дополнительным ответом к основному. Не все реализации HTTP
„„

1

2
3

Код состояния добавлен, но в данный момент использовать его возможности
нет: https://github.com/nodejs/node/pull/16644.
https://www.npmjs.com/package/early-hints.
https://httpd.apache.org/docs/2.4/mod/mod_http2.html#h2earlyhints.

Как отправлять push-сообщения

193

могут обработать такой ответ, ведь обычно они ожидают только один ответ. Другие коды состояния в диапазоне 100 (например, 100 Continue, 101
Switching Protocols и 102 Processing) используются только для определенных сценариев, таких как переключение на WebSockets. Многие инструменты и библиотеки поддерживают разные коды состояния, даже те, которых не существовало на момент создания этих инструментов, однако
лишь немногие средства могут правильно обрабатывать два запроса для
установки кодов ответа вручную, как того требует код ответа 103. Со временем поддержка появится, и это неизбежно. Таким образом, в будущем
данный код ответа будет чрезвычайно полезен, и я надеюсь, что его будут поддерживать повсеместно.

5.2.5 Другие способы push-загрузки
Вы можете запустить push-загрузку не только с помощью веб-серверов;
некоторые внутренние серверы приложений также позволяют разработчикам запускать push программным путем. В листинге 5.3 представлен
способ создания простого сервера NodeJS с поддержкой push. Для этого
вам потребуется модуль http2 и версия NodeJS v9 или новее.
Листинг 5.3 Служба Node с поддержкой push
'use strict'
const fs = require('fs')
const http2 = require('http2')
const PORT=8443
//Создание сервера HTTP/2 с сертификатом HTTPS и ключом.
const server = http2.createSecureServer({
cert: fs.readFileSync('server.crt'),
key: fs.readFileSync('server.key')
})
//Обработка всех входящих потоков.
server.on('stream', (stream, headers) => {
//Проверка, поддерживает ли входящий поток push на уровне соединения.
if (stream.session.remoteSettings.enablePush) {
//Если он поддерживает push, отправляем файл CSS.
console.log('Push enabled. Pushing CSS file')
//Открываем файл для чтения.
const cssFile = fs.openSync('/www/htdocs/assets/css/common.css', 'r')
//Получаем параметры файла для включения в HTTP-заголовок ответа.
const cssStat = fs.fstatSync(cssFile)
const cssRespHeaders = {
'content-length': cssStat.size,
'last-modified': cssStat.mtime.toUTCString(),
'content-type': 'text/css'
}

Глава 5

194

Реализация HTTP/2 push

//Отправка Push Promise для файла
stream.pushStream({ ':path': '/assets/css/common.css' },
(err, pushStream, headers) => {
//Отправка файла во вновь созданный pushStream
pushStream.respondWithFD(cssFile, cssRespHeaders)
})
} else {
//Если push не поддерживается, заносим это в лог.
console.log('Push disabled.')
}
//Ответ на исходный запрос.
stream.respond({
'content-type': 'text/html',
':status': 200
})
stream.write('')
stream.write('')
stream.write('Test')
})
//Запуск сервера для прослушивания назначенного порта.
server.listen(PORT)
console.log(`Server listening on ${PORT}`)
})

Данный код позволяет NodeJS отправлять ресурсы в ваш браузер с помощью push. В этом простом примере поддержка HTTP/2 осуществляется только с помощью HTTPS. Для настоящего сервера вам, вероятно,
потребуется HTTP/1.1 и HTTP (вот почему перед сервером приложений
должен стоять веб-сервер, такой как Node). Другие языки программирования (такие как ASP.NET и Java) имеют аналогичные способы передачи
ресурсов с помощью push.

Поддержка push на всех уровнях
Как я уже упоминал ранее, точкой входа в систему является балансировщик
нагрузки или веб-сервер (который часто называют пограничным). После
этого осуществляются прокси-запросы к внутреннему серверу приложений
или службе. На самом деле я рекомендую всегда ставить веб-серверы перед
динамическим сервером приложений, поскольку они обеспечивают прирост производительности и, кроме того, гарантируют безопасность. Когда
речь заходит о HTTP/2 push, многие думают, что для всей сетевой инфраструктуры предпочтительнее использовать HTTP/2, чтобы сделать возможной push-отправку и загрузку между элементами инфраструктуры. Однако
зачастую при таком варианте использования возникают некоторые ошибки,
особенно когда задействован посредник. Что же делать, если сервер приложений и пограничный сервер поддерживают push, а клиент – нет, или
наоборот? Как отследить перемещение push-ресурсов между тремя (или
более) сторонами?

Как работает HTTP/2 push в браузере

195

Спецификация HTTP/2 гласитa:
Посредник может получать push-ресурсы с сервера, но не пересылать
их клиенту. Иными словами, именно он решает, как использовать pushинформацию. Точно так же посредник может отправить клиенту дополнительные ресурсы с помощью push, и в этом процессе не будет
задействован сервер.
На самом деле, гораздо проще разрешить обрабатывать push пограничному
серверу и делать это с помощью заголовков HTTP-ссылок (с кодом 103 Early
Hints или без него). Иногда случается так, что сервер приложений передает
веб-серверу информацию об отправке ресурса с помощью push, и это может выглядеть как некий окольный путь. Однако такой путь намного проще, поскольку он позволяет серверам приложений использовать push для
ресурсов, находящихся вне их контроля (это, например, статические файлы
и мультимедиа, которые хранятся на уровне веб-сервера).
На момент написания этой книги мне неизвестны веб-серверы, которые поддерживали бы HTTP/2 push на всех уровнях. Прокси-модуль HTTP/2 Apache
(mod_proxy_http2) – это одна из немногих реализаций серверных HTTP/2соединений. Он отключает использование push для внутренних соединений,
а для предотвращения возникновения неполадок использует фрейм SETTINGSb.
В главе 3 мы обсудили варианты настройки инфраструктуры для поддержки HTTP/2. Возвращаясь к этому, можно сказать, что еще одной причиной,
по которой поддержка HTTP/2 во всех элементах инфраструктуры, может
быть, не нужна или даже нецелесообразна, является то, что веб-серверы
и прокси-серверы зачастую не поддерживают HTTP/2. Такой исход будет
актуален вплоть до того момента, пока поддержка HTTP/2 не станет повсеместной и больше не будет причин для отказа от этой версии протокола на
всех уровнях.
___________________________________
a
https://httpwg.org/specs/rfc7540.html#PushResources.
b
https://github.com/icing/mod_h2/issues/154.

5.3

Как работает HTTP/2 push в браузере
Независимо от того, как вы используете push на стороне сервера, брау­
зер обрабатывает данный процесс по-своему. Браузер помещает pushресурс не на веб-страницу, а в кеш. Сама же веб-страница обрабатывается как обычно. Когда странице требуется какой-то ресурс, она проверяет
кеш и находит там нужный ресурс, вместо того чтобы запросить его
у сервера.
Детали этого процесса индивидуальны для каждого браузера, и поэтому в спецификации HTTP/2 они не описаны подробно. Большинство
брау­зеров создало специальный кеш для HTTP/2 push, который несколько
отличается от обычного знакомого всем веб-разработчикам кеша HTTP.

Глава 5

196

Реализация HTTP/2 push

На момент написания этой книги лучшей «документацией» является пуб­
ликация в блоге Джейка Арчибальда (Jake Archibald) (из команды Google
Chrome1) об экспериментах с HTTP/2 push, в которой он рассказывает,
как браузеры взаимодействуют с данным сервисом. В этой публикации
подробно освещаются аспекты работы HTTP/2 в теории и на практике,
а также описываются некоторые неожиданные способы использования.
Благодаря данной работе выявлены некоторые ошибки; некоторые из
них на момент написания книги уже исправлены, а некоторые нет.
HTTP/2 push – это новая концепция, и поэтому, для того чтобы устранить проблемы в ее реализациях на стороне браузеров (и, вероятно, серверов), необходимо проделать большую работу. Я постараюсь рассказать
об основных ошибках, однако впоследствии могут появиться и новые.

5.3.1 Как работает кеш push
Все ресурсы, полученные с помощью push, хранятся в отдельном блоке
памяти (кеш HTTP/2 push). Браузер может запросить их из кеша и загрузить на страницу. Как правило, кешированные заголовки (при наличии)
также сохраняются в HTTP-кеше браузера. Однако существуют и исключения из этого правила, например браузеры на базе Cromium (Chrome
и Opera) не кешируют ресурсы для неподтвержденных сертификатов
(в их число входят самоподписанные сертификаты; веб-страницы, где
они установлены, отмечены значком красного замка). При обнаружении
ошибки сертификата функция кеширования будет недоступна2. Вы можете использовать HTTP/2 push, только если страница отмечена знаком
зеленого замка, для чего необходимо наличие проверенного сертификата. В случае, если у вас самоподписанный сертификат, его необходимо
внести в хранилище доверенных сертификатов браузера; в противном
случае отправка или получение ресурсов с помощью push будет невозможным3.
Кеш push – это далеко не первый источник, к которому браузер обращается при поиске ресурсов. Вообще, данный процесс зависит от выбранного браузера, однако эксперименты показывают, что, если ресурс
можно запросить из обычного HTTP-кеша, браузер не станет обращаться
к кешу push. Даже если push-ресурс новее, чем тот, что находится в кеше,
браузер все же предпочтет второй (если исходя из заголовков управления кешем он решит, что данный ресурс приемлем). Таким же образом
проверяются Service workers (SW ). Push-загрузка ресурсов, которые не
будут использоваться впоследствии, нецелесообразна. На рис. 5.15 показан процесс загрузки ресурса, необходимого для страницы в браузере
Chrome, а также кеши, к которым обращается браузер при загрузке ресурсов.

1
2
3

https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/.
https://bugs.chromium.org/p/chromium/issues/detail?id=103875#c8.
https://bugs.chromium.org/p/chromium/issues/detail?id=824988.

197

Как работает HTTP/2 push в браузере
Веб-браузер

Веб-сервер
1. GET /index.html
2. /index.html

Кеш изображений

3. /styles.css

4.

Кеш предзагрузки
Кеш сервис-воркеров
HTTP-кеш
Кеш HTTP/2 Push

Ключ типов кеша

Рис. 5.15

Кеш страницы

Кеш домена

Кеш браузера

Кеш соединения

Взаимодействие браузера с HTTP/2 push

При запросе к веб-серверу (1) и возвращении обратно в браузер (2) все
push-ресурсы помещаются в push-кеш HTTP/2 (3). Перед отправкой запроса на веб-сервер кеши проверяются по порядку (4). Ниже приводится
краткое описание каждого вида кеша:
кеш изображений (image cache) – это временный кеш, который создается в памяти используемой страницы с целью предотвращения
получения одного и того же изображения дважды. Такая ситуация
может возникнуть, например, если на изображение ссылаются
дважды. Когда пользователь закрывает страницу, кеш стирается;
„„ кеш предварительной загрузки (preload caсhe) – это также временный кеш в памяти страницы, в котором хранятся предварительно
загруженные ресурсы (см. главу 6). Опять же, этот кеш полностью
зависит от конкретной страницы, поскольку в предварительной загрузке других страниц заранее необходимости нет;
„„ service workers (SW ) – это фоновые приложения, разработанные
сравнительно недавно. Они работают независимо от веб-страницы
и действуют как посредники между веб-страницей и веб-сайтом.
Они позволяют веб-сайту функционировать как автономное приложение, даже при потере сетевого соединения. У SW есть собственные кеши, связанные с доменом;
„„ HTTP-кеш – это основной кеш, хорошо известный большинству разработчиков. Он является постоянным и располагается на диске. Од„„

Глава 5

198

Реализация HTTP/2 push

новременно его может использовать и браузер, но тогда его размер
будет ограничен, чтобы хватило места для всех доменов;
„„ кеш HTTP/2 push – это временный кеш, хранящийся в памяти. Он
зависит от соединения, и браузер обращается к нему в последнюю
очередь.
Файл styles.css, отправленный сервером, помещается в push-кеш
HTTP/2. Если браузеру необходим этот файл, перед отправкой сетевого
запроса источнику он будет проверять все кеши по порядку, поскольку
он не владеет информацией о том, отправил ли сервер данный ресурс.
Если файл styles.css находится в основном кеше HTTP, браузер возьмет
его оттуда, даже если в кеше HTTP/2 push есть более новая копия. Инструмент chrome://net-export, описанный в разделе 4.3.1, может предоставить вам сводку невостребованных push-ресурсов для всех текущих
активных страниц, как показано на рис. 5.16.

В этом столбце
отображаются
невостребованные
push-ресурсы
для домена
(отправленные,
но не востребованные
страницей)

Рис. 5.16 Отслеживание невостребованных push-ресурсов в Chrome

Кеш HTTP/2 push зависит от соединения, поэтому если соединение уже
не используется, то и push ресурсы в нем тоже. В HTTP-кеше, с которым
привыкло работать большинство разработчиков, все происходит иначе.
Например, в первом случае при потере соединения теряется и push-кеш,
а также все загруженные ресурсы, которые не были использованы (что
очень затратно). Если используется другое соединение, push-ресурсы
из кеша предыдущего соединения не могут быть использованы. Однако
HTTP/2 использует только одно соединение, поэтому такие ситуации не
представляют серьезных проблем. Стоит помнить, что ситуации могут
отличаться в зависимости от используемых браузеров, которые реализуют функции по-разному. Ранее я рассказывал о запросах без идентификаторов, для обработки которых большинство браузеров использует
отдельное соединение. Однако сейчас рабочая группа по технологиям
веб-гипертекстовых приложений (WHATWG – Web Hypertext Application
Technology Working Group) разрабатывает изменения, которые позволят
использовать одно соединение как для запросов с идентификаторами,
так и без них1. Пока данная проблема не исправлена, вы не сможете от1

https://github.com/whatwg/fetch/issues/341.

Как работает HTTP/2 push в браузере

199

правлять шрифты из разных источников (загруженные из другого домена, включая сегментированные домены), так как такие запросы не содержат идентификаторов. Кроме того, отдельные вкладки или процессы
в браузере могут инициировать новые соединении. Так происходит, например, в Edge, а Chrome и Firefox используют общее соединение между
всеми вкладками. Что касается Safari, он открывает несколько соединений на одной вкладке1. Поскольку кеш HTTP/2 push работает на уровне
соединения, а не на уровне страницы, можно отправлять ресурсы для
будущей навигации по страницам, однако ввиду временного характера
этого кеша в сочетании с тем, что возможна угроза прерывания соединения, это нецелесообразно.
Если ресурс был «востребован» браузером из push-кеша, в конечном
итоге он удаляется и не может быть снова загружен, хотя, если заданы
заголовки HTTP cache-control, его можно «востребовать» из HTTP-кеша
брау­зера. Push-кеш также отличается от HTTP-кеша тем, что некешируемые ресурсы (те, которые установлены с HTTP-заголовками cachecontrol, no-cache и no-store) могут быть загружены и прочитаны из pushкеша. Таким образом, он не является кешем в традиционном понимании,
а скорее представляет собой зал ожидания для запросов. Йоав Вайс (Yoav
Weiss), архитектор веб-производительности, называет его «хранилищем
невостребованных push-потоков»2, но уточняет, что этот термин запомнить сложнее, чем «push-кеш».

5.3.2 Отказ от push с помощью RST_STREAM
Посредством отправки в push-поток фрейма RST_STREAM с кодом CANCEL
или REFUSED_STREAM клиент может остановить push-загрузку ресурса. К использованию этого фрейма клиент может прибегнуть в случае, если
у браузера уже есть данный ресурс или по какой-либо другой причине
(например, если пользователь покидает страницу во время ее загрузки,
и поэтому браузеру уже не нужно загружать этот ресурс).
Может показаться, что такой способ помогает избежать чрезмерного
использования ненужных браузеру ресурсов. Однако его недостаток состоит в том, что на отправку данного фрейма обратно на сервер требуется некоторое время, в течение которого сервер отправляет и фреймы
HEADERS и DATA, и получается, что браузер отбрасывает их. Фрейм RST_STREAM
является сигналом обратной связи, и поэтому он является не столь агрессивным фактором, как разрыв соединения, который в HTTP/2 невозможно осуществить без прерывания других потоков в соединении. На момент получения и обработки сервером фрейма RST_STREAM ресурс уже мог
быть загружен полностью.
Данный фрейм полезен только в том случае, если браузер принимает решение, что ему не требуется загружать ресурс именно с помощью
push. Если данный ресурс есть в HTTP-кеше, то браузер, скорее всего,
1
2

https://bugs.webkit.org/show_bug.cgi?id=172639.
https://blog.yoav.ws/tale-of-four-caches/.

200

Глава 5

Реализация HTTP/2 push

воспользуется фреймом RST_STREAM для того, чтобы остановить push-за­
груз­ку. Но что, если, например, изображение большого размера уже загружено с помощью push, однако на странице на него не ссылаются?
К примеру, страницу обновили, и данное изображение уже не используется, а инструкция push-загрузки еще не была обновлена. В таком случае браузер загрузит весь объемный ресурс, однако использовать его не
будет. В зависимости от того, какой браузер вы используете, вы можете
даже не знать о том, что происходит подобный процесс, так как в некоторых случаях он может не отображаться во вкладке Network на панели
инструментов разработчика.
Подводя итог изложенному выше, отметим следующее: фрейм
RST_STREAM – это хороший способ остановить поток, и особенно поток
push-загрузки. Однако полагаться на него как на способ управления
push-загрузкой не стоит. Избыточная push-загрузка – это пустая трата
ресурсов, и, даже если ваш сервер может с ней справиться, помните, что
пропускная способность бывает ограничена. В частности, пропускную
способность ограничивают, например, в мобильных соединениях. Таким образом, при отправке ненужных ресурсов, вы «платите» больше, не
говоря уже о том, что потраченную впустую пропускную способность вы
могли бы потратить на отправку действительно нужных ресурсов.

5.4

Условная push-загрузка
Push-загрузка ненужных ресурсов – это один из самых значительных рисков HTTP/2 push. Другие риски связаны с проблемами реализаций, о которых мы говорили ранее в этой главе (например, если соединение не
используется повторно). Однако основная проблема заключается в pushзагрузке ресурсов, которые уже есть в браузере.
Например, если вы загрузите таблицу стилей заранее с помощью push,
время загрузки страницы для первого запроса сократится. Однако, если
то же самое будет происходить каждый раз, когда посетитель совершает запросы на вашем сайте, получается, что ресурс, который уже есть
у пользователя (при условии, что заголовки управления кешем гарантируют кеширование ресурса), будет загружаться снова и снова, что нецелесообразно.
Итак, фрейм RST_STREAM не позволяет нам приостанавливать pushзагрузку файлов без излишних затрат ресурсов. Тогда какие методы мы
можем использовать, чтобы гарантированно не загружать ненужные
клиенту ресурсы?

5.4.1 Отслеживание push на стороне сервера
Сервер может отслеживать push-загрузку файлов в определенных клиентских соединениях. Способ отслеживания зависит от сервера, соединения или, возможно, от идентификатора сеанса. Например, если вы
загружаете ресурс с помощью push, сервер отмечает, что это соедине-

Условная push-загрузка

201

ние или сеанс не должны загружать его снова, даже в случае соответствующих запросов. Так работает, например, Apache, и именно поэтому
при тестировании сервера вам необходимо отключить настройку H2PushDiarySize. Данная функция может быть реализована в веб-приложении,
а не в самом программном обеспечении веб-сервера, что обеспечит вебразработчику больший контроль над соединением.
Недостатком является то, что сервер делает эмпирическое предположение относительно использования push. Например, после очистки кеша
браузера некоторые ресурсы могут быть недоступны, но сервер все же не
станет отправлять их. Кроме того, отслеживание push-ресурсов на перегруженных серверах может быть ограничено, а серверы с балансировкой
нагрузки, если они не работают постоянно с одним и тем же клиентом,
зачастую не обладают полной информацией о push-ресурсах.
В конечном счете данный процесс очень сложен. Более того, он является лишь грубой попыткой добавить отслеживание состояния к протоколу HTTP, который по умолчанию не предусматривает его. В HTTP/2
концепция состояния стала применима к некоторым частям протокола
(например, сжатие заголовка HPACK и состояния потока, как обсуждается в главах 7 и 8). Таким образом, эта проблема может (и должна) быть
решена на уровне протокола, что позволит оптимизировать его реализации. В разделе 5.4.4 я расскажу об одном из предложений, касающихся
этого вопроса (дайджесты кеша), но сейчас стоит рассмотреть другие методы, которые в настоящее время возможно воплотить в жизнь.

5.4.2 Условные HTTP-запросы
HTTP-заголовки if-modified-since или etag, полученные от клиента, сообщают серверу о том, что нужная страница уже есть в кеше браузера, но
срок сеанса уже истек. Если вы всегда загружаете CSS-файл с помощью
push, то при наличии в запросе таких заголовков вы можете не делать
этого, поскольку таблица стилей, вероятно, будет загружена из кеша (возможно, даже на более долгий срок, чем страница, которая ссылается на
нее, как это часто бывает). Использование условных заголовков проще,
чем отслеживание push-загрузки на стороне сервера. Однако недостатки
двух этих процессов схожи. Например, при использовании условных заголовков сервер также делает однозначное обоснованное предположение о ресурсах на стороне клиента, а также использует тот же сценарий
перехода на другую страницу, которая загружает таблицу стилей из кеша.

5.4.3 Push-загрузка с помощью куки-файлов
Еще одним способом является фиксация факта push-загрузки файла на
стороне клиентов. Для этого можно использовать куки, а также Local­
Storage или SessionStorage. Это работает следующим образом: когда вы
загружаете ресурс с помощью push, вы устанавливаете действительный
для данного сеанса или на момент push-загрузки ресурса (хранящегося
в кеше довольно долго) файл куки (ресурсы, хранящиеся в кеше не столь

Глава 5

202

Реализация HTTP/2 push

долгое время). Каждый запрос страницы должен быть проверен на наличие куки, и, если он есть, вы можете загрузить ресурс с помощью push.
Данный способ может быть реализован в любом клиентском приложении или даже в конфигурации сервера. Рассмотрим на примере Apache:
#Проверка, сообщает ли куки о наличии загруженного css
#Если нет, устанавливаем переменную окружения для использования позже
SetEnvIf Cookie "cssloaded=1" cssIsloaded
#Если куки нет, и это файл html, отправляем файл css
#и формируем куки уровня сеанса, поэтому в следующий раз отправки не будет

Header add Link ";as=style;rel=preload"
env=!cssIsloaded
Header add Set-Cookie "cssloaded=1; Path=/; Secure; HttpOnly"
env=!cssIsloaded


Подобный код может быть реализован на любом языке.
Данный метод является усовершенствованной версией двух предыдущих. Процесс значительно упрощается, ввиду того что нет необходимости отслеживать ресурсы на стороне сервера, а также вы отслеживаете
немного больше в зависимости от статуса браузера. Однако файлы куки
и HTTP-кеш – это не одно и то же. Несмотря на то что можете установить
одинаковое время истечения срока действия, файлы куки можно сбрасывать независимо (например, отключить их в браузере или запустить
сеанс в режиме инкогнито), хотя то же самое можно сказать и о любом
другом методе отслеживания ресурсов на стороне сервера.
На момент написания этой книги использование файлов куки является наилучшим способом отслеживания push-загрузки файлов и их состояния в кеше.

5.4.4 Дайджесты кеша
Дайджесты кеша позволяют1 предоставлять браузеру информацию о содержимом его кеша. После установки соединения браузер отправляет
фрейм CACHE_DIGEST, в котором перечислены все ресурсы, хранящиеся на
данный момент в HTTP-кеше определенного домена (или других доменов, которые используют это соединение; см. главу 6). Сервер получает
содержимое кеша в виде URL-адреса с заголовком etag, позволяющим
ему управлять версиями URL. Использование дайджестов эффективнее
вышеописанных «обходных» путей, в которых содержимое кеша приходилось «угадывать». Дайджесты позволяют браузеру предоставить серверу однозначную информацию о содержимом кеша. Сервер способен
запоминать содержимое кеша и даже обновлять его по мере отправки
дополнительных ресурсов. Браузер отправляет фрейм CACHE_DIGEST всего
один раз в начале соединения (желательно после первого запроса).
1

https://tools.ietf.org/html/draft-ietf-httpbis-cache-digest.

Условная push-загрузка

203

В кеше могут храниться большие объемы данных, поэтому вместо
отправки полных URL-адресов еще и с заголовками etag клиенту предлагается зашифровать их в дайджесте с помощью фильтра с кукушкой
(Cu­ckoo Filter1). Сейчас я не буду вдаваться в подробности данной концепции, но скажу, что она позволяет отправлять данные о содержимом
кеша без возникновения риска конфликтов (например, ложные предположения о содержимом кеша).
На момент написания этой книги дайджесты не имеют официального
стандарта, и многие браузеры не используют эту концепцию. Интересно,
что некоторые серверы (такие как Apache, http2server и H2O) добавили
поддержку текущего проекта стандарта (который возник из реализации
H2O). На сегодняшний день такие реализации используются только для
отслеживания запросов, отправленных сервером, поскольку браузеры не
пользуются фреймом CACHE_DIGEST для инициализации серверного кеша.
Таким образом, серверы могут отслеживать отправленные ресурсы,
и у них нет необходимости прибегать к HTTP/2 push (даже если это рекомендуется). Безусловно, такая функция очень полезна, однако она могла
бы быть еще полезнее, если бы позволяла инициализировать состояние
кеша браузера, для чего и требуется фрейм CACHE_DIGEST. Как я уже упоминал в начале данного раздела, эту функцию Apache вы отключили при
тестировании HTTP/2 push посредством следующей конфигурации:
H2PushDiarySize 0

В этой строке указывается, что максимальный размер push-журнала –
0, что означает, что его у вас попросту нет. Следовательно, вы можете
воспользоваться push, даже если ресурсы уже были отправлены. Если
вы протестируете страницу без установки значения 0, вы увидите, что
иногда push-загрузка будет применяться, а иногда нет, и это может вас
запутать. По окончании тестирования удалите данную конфигурацию
и установите конфигурацию по умолчанию H2PushDiarySize (256 записей
на соединение) или любую другую. Другие серверы могут поддерживать
реализации, схожие с теми, что используются в Apache, поэтому я настоятельно рекомендую вам ознакомиться с документацией сервера.
Локальные обработчики заданий в вашем веб-приложении позволят
реализовать дайджест кеша на стороне браузера, так как они способны
перехватывать HTTP-запросы и вносить в них изменения. Существует
несколько таких реализаций2. Фрейм CACHE_DIGEST не является стандартом и не реализован в браузерах или на серверах, вследствие чего использовать его нельзя. Обработчики заданий отправляют дайджест кеша
в HTTP-заголовке или куки. Вы можете использовать эту концепцию на
своем сервере для инициализации дайджеста кеша. Для этого ваше вебприложение должно отправить такой заголовок «вручную» (с помощью
обработчиков), а затем использовать его для инициализации серверной
части. Протестировать данную функцию очень интересно, однако было
1
2

https://www.cs.cmu.edu/~dga/papers/cuckoo-conext2014.pdf.
https://www.npmjs.com/package/cache-digest-immutable.

Глава 5

204

Реализация HTTP/2 push

бы гораздо лучше, если бы она была стандартизирована и работала бы
на стороне браузера. К сожалению, в январе 2019 года рабочая группа
HTTP объявила, что не планирует продолжать работу по стандартизации
дайджестов кеша1.
Безопасность работы с дайджестами требует отдельного обсуждения.
Кеш браузера может содержать конфиденциальную информацию, например посещенные ранее URL-адреса, также (даже без использования
куки) могут создаваться цифровые отпечатки пользователя и т. п. Вероятнее всего, серверу в любом случае будет предоставлен доступ к некоторым из этих данных (в какой-то момент запросы все же должны быть
отправлены на сервер), однако аспект безопасности все же вызывает некоторое беспокойство. Согласно текущей концепции, браузеры не могут
отправлять дайджесты кеша в режиме конфиденциальности или же если
пользователь отказывается от файлов куки. Проблемы безопасности
и конфиденциальности – это еще одна причина, по которой на сегодняшний день работа по стандартизации функции дайджеста кеша была
приостановлена.

5.5

К каким ресурсам применим HTTP/2 push
Сейчас вы имеете четкое представление, что такое HTTP/2 push и как он
работает на сторонах сервера и клиента. Однако, помимо этого, важно
знать, к каким файлам он может быть применим.

5.5.1 К чему может быть применим push?
Согласно спецификации HTTP/2 push, существует ряд основных правил
его использования2:
„„ клиент может отключить push посредством установки во фрейме
SETTINGS значения 0 для параметра SETTINGS_ENABLE_PUSH. В таком случае сервер не будет отправлять фрейм PUSH_PROMISE;
„„ push-запросы должны иметь возможность быть кешированными
(GET, HEAD и некоторые запросы POST);
„„ push-запросы должны быть безопасны (обычно GET или HEAD);
„„ push-запросы не должны включать в себя тело запроса (хотя обычно
они содержат тело ответа);
„„ push-запросы могут быть отправлены только в домены соответствующего сервера;
„„ использовать push может только сервер;
„„ ресурсы могут быть отправлены с помощью push только в ответ на
текущий запрос. Сервер не может инициировать push без запроса.
В реальности push-запросами могут быть только запросы GET. Выше­
описанные правила касаются по большей части того, в каких случаях вы
1
2

https://lists.w3.org/Archives/Public/ietf-http-wg/2019JanMar/0033.html.
https://httpwg.org/specs/rfc7540.html#PushResources.

К каким ресурсам применим HTTP/2 push

205

можете использовать push, однако гораздо важнее знать, в каких случаях вам следует его использовать.
Ограничение, согласно которому push-запросы могут быть отправлены только на домены соответствующего сервера, запрещает применять
push к ресурсам, которые сервер не обслуживает (прямо или косвенно).
Использование Bootstrap, загруженного с getbootstrap.com (или jQuery,
размещенный на jquery.com) ограничивает использование вашим сервером push. Вы все же можете проксировать запросы через свой сервер,
однако для этого вам нужно будет обновить все ссылки. На этом этапе вы
можете организовать локальный хостинг страницы и устранить сложности с ее проксированием.
Signed HTTP Exchanges (официальное название – Web Packaging)1 позволит вам обслуживать ресурсы из вашего домена ровно так же, как
если бы они поступали из исходного домена, что позволит вам эффективно использовать push относительно других доменов. Однако данный
механизм на момент написания этой книги находится на этапе разработки. Пока что он не используется в браузерах и серверах, но на него все
же следует обратить внимание.

5.5.2 К чему должен быть применим push?
Ключевой вопрос, на который должны знать ответ владельцы веб-сайтов,
желающие использовать HTTP/2 push, – к каким ресурсам может быть
применим push, а к каким, что возможно даже более важно, нет. HTTP/2
push предназначен для оптимизации производительности, но он может
и тормозить обмен, в случаях если вы бездумно используете push и тратите пропускную способность на push-загрузку ресурсов, которые не понадобятся клиенту.
В идеале применять push-загрузку следует только к критически важным для страницы ресурсам. Push-загрузка неиспользуемых ресурсов –
это пустая трата пропускной способности. В число таких ресурсов входят,
например, ненужные файлы (ресурсы, на которые нет ссылок), файлы,
которые клиент не может использовать (например, изображения, формат
которых не поддерживает браузер клиента), а также ресурсы, которые не
следует использовать по решению самого клиента (например, изображения, которые отображаются только для определенных размеров экрана).
Как я уже говорил, push-загрузку следует применять только к критически важным ресурсам. Хотя иногда возникает соблазн применить ее ко
всем элементам, которые требует страница, однако это может замедлить
получение критически важных ресурсов (в зависимости от приоритетов
загрузки ресурсов, запрошенных клиентом).
Кроме того, необходимо учитывать наличие файла в кеше клиента (см.
раздел 5.3). Загружать ресурсы с помощью push следует только в том случае, если высока вероятность, что данного ресурса еще нет в кеше.
HTTP/2 push должен применяться для того, чтобы использовать время
простоя сети максимально эффективно. Следовательно, push-загрузка
1

https://tools.ietf.org/html/draft-yasskin-http-origin-signed-responses.

Глава 5

206

Реализация HTTP/2 push

всех необходимых странице ресурсов вряд ли повысит производительность, поскольку в таком случае вы будете игнорировать любые приоритеты, которые расставит браузер. Команда Chrome опубликовала
подробный документ, содержащий информацию о том, к каким файлам
применима push-загрузка1. Одна из основных рекомендаций в нем гласит, что необходимо загружать минимальное количество нужных ресурсов только «для заполнения времени простоя сети, и не более». Другое
исследование2 подтверждает использование push именно таким образом. Именно поэтому предварительная push-загрузка и код состояния
103 являются важными дополнениями к базовой стратегии push.
Короче говоря, лучше загрузить с помощью push меньше ресурсов, чем
больше. Худшее, что может случиться, если ресурс не получится загрузить с помощью push, – это то, что его все равно нужно будет запросить,
а условия для этого будут не совсем оптимальными. С другой стороны,
злоупотребление push-загрузкой может привести к загрузке ненужных
ресурсов, что будет очень затратно для клиента, сети и сервера, а также замедлит загрузку страницы. Однако даже при чрезмерной pushзагрузке HTTP/2 push не должен кардинально нарушить загрузку страницы. Производительность может значительно снизиться, однако спустя
какое-то время страница все же загрузится.

5.5.3 Автоматизация push-загрузки
Также необходимо выработать стратегию push-загрузки и того, какие
именно ресурсы вы будете загружать. Должен ли это делать владелец
веб-сайта или разработчик, или все же этот процесс можно автоматизировать? Jetty3 – это механизм Java, с помощью которого вы можете
автоматизировать push4. Он может отслеживать запросы с заголовоком
Referer (а также другие запросы, следующие за ними). Результаты отслеживания механизм применяет для опережающего создания предполагаемых push-ответов на вероятные аналогичные запросы от других
клиентов. Безусловно, он значительно упрощает процесс выбора ресурсов, которые вы будете загружать с помощью push, однако все зависит
от того, согласны ли вы с тем, что механизм предлагает, и подходит ли
это для вашего веб-сайта. Таким образом, разработка стратегии довольно сложна, а процесс автоматизации push должен быть индивидуален
для каждого сервера. Реализация Jetty довольно интересна, и в сочетании с некоторыми формами дайджестов кеша ее может быть достаточно
для предотвращения нежелательной push-загрузки. В качестве альтернативы владельцам веб-сайтов может потребоваться прямой контроль
за данным процессом, так как они должны знать свой сайт лучше его
1

2
3
4

https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYH
yLEXIxYMv0/, также доступно по адресу https://goo.gl/89RLGQ.
https://calendar.perfplanet.com/2016/http2-push-the-details/.
https://www.eclipse.org/jetty/.
https://www.eclipse.org/jetty/documentation/current/http2-configuring-push.html.

Решение проблем HTTP/2 push

207

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

5.6

Решение проблем HTTP/2 push
Легче всего контролировать HTTP/2 push в столбце Initiator вкладки Network в инструментах разработчика Chrome (аналогичным образом это
происходит и в других браузерах на основе Chromium, таких как Opera).
Но что делать, если в этом столбце вы не видите соответствующий ресурс? Этому может быть несколько причин, рассмотрим самые распространенные:
„„ точно ли вы используете HTTP/2? Если нет, то push-загрузка будет
невозможна. Для того чтобы убедиться, что вы используете нужную
версию протокола, добавьте столбец Protocol. В главе 3 представлены советы по устранению неполадок, возникающих при переходе
на HTTP/2;
„„ поддерживает ли ваш сервер HTTP/2 push? На момент написания
этой книги некоторые серверы и CDN все еще не поддерживают
его. Узнать, поддерживает ли клиент push, можно только благодаря
фрейму SETTINGS. Однако увидеть, поддерживает ли его сервер, просто взглянув на фрейм SETTINGS с помощью nghttp или страницу netexport в Chrome, невозможно;
„„ находится ли ваш сервер позади остальной инфраструктуры? Если
ваш сервер находится за балансировщиком нагрузки или другими элементами инфраструктуры, которые завершают соединение
HTTP/2, существует вероятность что он не будет поддерживать
HTTP/2 push, даже если его поддерживает ваш сервер. Даже при наличии поддержки HTTP/2 push ваш сервер не сможет передавать
push-ресурсы, которые уже были обработаны вышеупомянутыми
элементами инфраструктуры.
Если перед внутренним сервером приложений (например, Node
и Jetty) размещен Apache, то он не позволит ему загружать ресурсы
с помощью push. Для того чтобы это исправить, внутренний сервер
должен использовать заголовки HTTP-ссылок;
„„ загружается ли файл именно с помощью push? Для того чтобы просмотреть наличие фрейма PUSH_PROMISE и, соответственно, сам файл,
воспользуйтесь nghttp. Таким образом, вы сможете выяснить, связана ли эта проблема с браузером;
„„ нужны ли эти файлы странице? Если для загрузки странице не нужны ресурсы, браузер не станет использовать их, а Chrome не отобра­
зит их во вкладке Network. Для того чтобы просмотреть сводку
невостребованных push-ресурсов, воспользуйтесь инструментом
chrome net-export, как показано на рис. 5.16 в разделе 5.3.1.
Если вы осуществляете push-загрузку с помощью заголовка
HTTP-ссылки с атрибутом rel=preload и as, Chrome сочтет ресурсы
необходимыми для страницы (благодаря подсказке предваритель-

Глава 5

208

Реализация HTTP/2 push

ной загрузки) и отобразит их на вкладке Network. С одной стороны,
это очень полезно, а с другой стороны, это, напротив, может нас запутать.
Один из способов отладки – удалить атрибут as (например,
as=style) из заголовка ссылки. Chrome не будет использовать ресурсы в качестве подсказки для предварительной загрузки, но ваш
веб-сервер все же должен их отправлять (поскольку атрибут as не
является обязательным для push, в зависимости от реализации).
Если ресурсы не отображаются на вкладке Network, но отображаются там при наличии атрибута as, это означает, что вы загружаете
с помощью push ресурс, который странице не нужен;
„„ правильно ли вы используете push для своего сервера? Использование
HTTP/2 push зависит от вашего сервера. Многие (но не все) серверы используют заголовки HTTP-ссылок, поэтому логично предположить, что для push-загрузки они будут использовать именно этот
способ. Изучите документацию сервера или интерактивные руководства, чтобы узнать, как правильно использовать push на вашем
сервере;
„„ точно ли сервер решил загружать ресурс не с помощью push? При
определенных обстоятельствах push-загрузка ресурса может не
произойти, если вы реализовали ее процесс с поддержкой кеширования (см. раздел 5.4) или каким-либо другим методом. Если после обновления страницы или перезагрузки браузера (или даже
сервера) некоторое время файлы отображаются как push-ресурсы,
проверьте, установили ли вы их отправку с помощью push. Если вы
пользуетесь Apache, вы можете отключить функцию push-журнала,
которая пытается предотвратить push-загрузку ресурсов, которые,
по мнению сервера, у клиента уже есть, посредством присвоения
H2PushDiarySize значения 0. Такой способ может быть полезен при
отладке, когда вам необходимо, чтобы сервер загрузил один и тот
же push-ресурс несколько раз;
„„ существует ли файл, который вы хотите загрузить с помощью push?
Во время указания ресурса, к которому вы хотите применить pushзагрузку, легко сделать опечатку, а если ресурса не существует, его
невозможно будет отправить. В таком случае в журналах вашего
веб-сервера появится код состояния 404 (Not Found). Если вы используете nghttp, такой код появится в полученном в ответ фрейме.
Если вы пользуетесь методом заголовка HTTP-ссылки rel=preload
с атрибутом as, код 404 отобразится во вкладке Network или запрос
войдет в число отмененных1;
„„ возможно, вы осуществляете push-загрузку по другому соединению,
отличному от ожидаемого браузером? Как обсуждалось в разделе 5.2.1, HTTP/2 push напрямую связан с соединением. Если при
push-загрузке вы пользуетесь одним соединением, а браузер ожидает другого, он не будет использовать загруженный ресурс. Такая
1

https://bugs.chromium.org/p/chromium/issues/detail?id=811077.

Влияние HTTP/2 push на производительность

209

проблема часто возникает при загрузке шрифтов, поскольку они
должны загружаться по несертифицированным соединениям, однако некоторые проблемы и особенности браузера могут привести
к тому, что они будут загружаться по другому соединению. Наилучшим способом увидеть эту ситуацию является просмотр подключения в WebPagetest;
„„ возможно, вы используете самозаверенный или другой ненадежный
сертификат? Chrome игнорирует push-запросы для ненадежных
сертификатов HTTPS (включая самозаверенные фиктивные сертификаты, созданные для локального хоста1). Для того чтобы избежать
этого, вам необходимо добавить свой сертификат в хранилище доверенных сертификатоввашего компьютера, тогда для всех pushзапросов будет отображаться значок зеленого замка. Кроме того,
Chrome требует наличия у сертификата действительного альтернативного имени субъекта (Subject Alternative Name – SAN). Во многих
руководствах по созданию самозаверенных сертификатов рассказывается только о поле Subjects, которое на данный момент является
устаревшим, и поэтому даже после добавления таких сертификатов
в хранилище они не распознаются. Для того чтобы предотвратить
эту проблему и получить возможность пользоваться HTTP/2 push,
необходимо заменить их на сертификаты, содержащие и Subject,
и SAN.

5.7

Влияние HTTP/2 push на производительность
На разных веб-сайтах степень влияния HTTP/2 push на производительность может быть различна, она зависит от времени, которое занимает
цикл приема-передачи (время, необходимое на обслуживание ресурсов),
а также от оптимизации веб-сайта. На сегодняшний день HTTP/2 push используют лишь немногие сайты, ввиду чего какая-либо значимая информация о его влиянии на производительность практически отсутствует.
Ключом к эффективному использованию HTTP/2 push является эффективное использование «промежутков» пропускной способности в то
время, когда соединение не используется. Особое значение этот аспект
имеет для страниц, процесс генерации которых на стороне сервера сложен и долог. Для статических страниц это значение несколько меньше.
Однако для них также существует потенциальная возможность экономии ресурсов в циклах приема-передачи, поскольку из-за ограничений
пропускной способности и обработки push-ресурсы, вероятнее всего,
так или иначе будут стоять в очереди позади основных ресурсов с более
высоким приоритетом, что приведет к задержке (половина цикла приема-передачи). Вы можете увидеть этот процесс на рис. 5.17. На обеих
диаграммах первые четыре ресурса выглядят одинаково, поэтому от использования push здесь мало пользы.
1

https://bugs.chromium.org/p/chromium/issues/detail?id=824988.

Глава 5

210

Реализация HTTP/2 push

HTTP / 2 с push-загрузкой ресурсов 2–5

Ресурсы 2–4 показывают
небольшую разницу
при включенном push
HTTP/2 без push

HTTP/2 push
идет на пользу
ресурсу 5,
т. к. он больше
не зависит
от файла CSS

Рис. 5.17 Ресурсы, запрошенные с помощью HTTP/2 push и другие запрошенные ресурсы,
поступают в разное время

Однако push-загрузка пятого ресурса уже оправданна. Таким образом,
не нужно дожидаться загрузки таблицы стилей, чтобы проверить нужна
ли она. (Позже в этой главе я расскажу о предварительной загрузке, которая позволяет получить аналогичные преимущества.) В данном примере push используется несколько нестандартно, так как благодаря этому
исчезает необходимость встраивания ресурсов. Однако при правильном
использовании вы все же сможете извлечь необходимые вам преимущества.
На 102-й встрече IETF в Монреале в июле 2018 года Akamai1 и команда
Chrome2 представили свои наблюдения о влиянии HTTP/2 push на производительность. Akamai отметили значительные улучшения при применении push к критически важным ресурсам. Chrome также отметили
небольшие улучшения, однако они проводили несколько другой эксперимент: они отключали push и анализировали разницу. Ввиду того, что
исследования проводились разными способами, возникают некоторые
вопросы. Являются ли клиенты Akamai более репрезентативными для
интернета, ведь Chrome исследовал только те сайты, которые уже используют push (а их немного и, вероятно, их владельцы – сторонники
HTTP/2)? Отвечает ли исследование Akamai на вопрос, к каким ресурсам
необходимо применять push, и чье решение является более правильным – их или Google, где сайты выбирали эти ресурсы сами?
В ходе исследования были выделены две основные проблемы (особенно командой Chrome): HTTP/2 push использовали лишь немногие
сайты (0,04 % сеансов HTTP/2, по данным Chrome), вследствие чего потенциально может возникнуть снижение производительности. Команда
Chrome даже задалась вопросом, заметят ли пользователи отключение
1

2

https://github.com/httpwg/wg-materials/blob/gh-pages/ietf102/akamai-serverpush.pdf.
https://github.com/httpwg/wg-materials/blob/gh-pages/ietf102/chrome_push.pdf.

Push или предварительная загрузка?

211

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

5.8

Push или предварительная загрузка?
Даже при должной работе (что далеко не всегда удается реализовать)
HTTP/2 push имеет множество нюансов. При его использовании сущест­
вуют явные риски, такие как снижение пропускной способности и скорости работы сайта (несмотря на то что HTTP/2 push призван ее ускорить).
Я упоминал ранее, что сущность этих рисков заключается не в том, что
владельцы сайтов приведут свои страницы в негодность, а в том, что они
впустую потратят ресурсы, которые следовало бы использовать в другом
месте. Одной из основных проблем является неосведомленность сервера о содержимом HTTP-кеша браузера. Возможно, что дайджесты кеша
смогут решить эту проблему, если, конечно, для них разработают стандарт. Пока этого не произошло, многие люди задаются вопросом, готов
ли HTTP/2 push к переходу в массовое использование, или все же пока
что следует довольствоваться методом предварительной загрузки.
Предварительная загрузка1 помогает заранее указать браузеру, что для
загрузки страницы необходим определенный ресурс. Как упоминалось
в разделе 5.1.1, вы можете реализовать предварительную загрузку посредством использования заголовка HTTP-ссылки (с атрибутом nopush,
для того чтобы избежать push-загрузки ресурсов):
Link: ";rel=preload;as=style;nopush"

В HTML это выглядит следующим образом:


Независимо от выбранного метода браузер должен воспринимать эту
строку как индикатор того, что заявленный ресурс обладает высоким
приоритетом. Как я уже говорил ранее, разница HTTP/2 push и предварительной загрузки заключается в том, что для последней особенно важен атрибут as; если его исключить, браузер или не сможет распознать
необходимость предварительной загрузки, или нужный ресурс загрузится дважды.
В аспекте push-загрузки ресурса до его запроса предварительная загрузка уступает в скорости HTTP/2 push. Однако она является запросом,
инициируемым браузером, и имеет также ряд преимуществ:
„„ браузер знает, что находится в его кеше, и на основе этой информации принимает решение о необходимости запроса того или иного
ресурса. В отличие от HTTP/2 push при предварительной загрузке
ресурсы, которые уже есть у клиента, не будут загружаться повторно. Если в кеше браузера уже имеется необходимый ресурс, браузер
1

https://w3c.github.io/preload/.

Глава 5

212

Реализация HTTP/2 push

проигнорирует запрос предварительной загрузки. Однако многие
серверы HTTP/2 для push-загрузки ресурсов используют заголовки
HTTP-ссылок, поэтому, если вы не хотите применять push-загрузку,
вам следует добавить атрибут nopush;
„„ при использовании метода предварительной загрузки возникает
меньше проблем и сложностей с push-кешем, поскольку в данном
случае задействован только HTTP-кеш. Если предварительно загруженный ресурс не используется, вы все равно потратите время на
его загрузку, однако это происходит независимо от того, используете ли вы предварительную загрузку, или HTTP/2 push;
„„ с помощью предварительной загрузки вы можете загружать ресурсы из других доменов, в то время как HTTP/2 push вы можете использовать только в собственном домене;
„„ на панели инструментов разработчика Chrome отображаются все
предварительно загруженные ресурсы независимо от того, были
они использованы или нет. Однако, что касается push-ресурсов,
то отображаются только использованные при загрузке страницы
(хотя есть и обходной путь: вы можете отправлять заголовок HTTPссылки предварительной загрузки для каждого push-ресурса).
На сегодняшний день ряда этих преимуществ может быть достаточно, и поэтому некоторые люди советуют пользоваться именно менее
рискованным методом предварительной загрузки. Согласно анализу,
проведенному Хуманом Бехешти (Hooman Beheshti) из Fastly, в феврале
2018 го­да HTTP/2 push использовали всего лишь 0,02 % сайтов1 (и это
через три года после официального выхода HTTP/2). Схожие результаты
получила и команда Chrome, чей эксперимент описан в разделе 5.7. По
ряду веских причин некоторые люди не решаются использовать эту технологию. Особенно если учесть, что существует метод предварительной
загрузки, который обеспечивает те же преимущества, но со значительно
меньшими рисками.
Предварительная загрузка с указанием кода состояния 103 еще больше
приближает ее к HTTP/2 в плане прироста производительности. Данный
код может быть отправлен вместе с заголовками HTTP-ссылок предварительной загрузки, что будет указывать браузеру на необходимость
начала запроса ресурсов, для загрузки которых требуется относительно
много времени. Таким образом, к нужному моменту, в зависимости от
страницы и времени, которое требуется на ее генерацию, ресурсы уже
могут быть доступны браузеру. В разделе 5.2.4 я рассказал, как код состояния 103 совместно с HTTP/2 push позволяет оптимизировать время обработки страницы. На рис. 5.18 представлена диаграмма, повторяющая
уже имеющуюся в этой главе, поэтому, если вы не хотите возвращаться,
можете просто взглянуть на нее.
На этом рисунке видно, что внутренний сервер приложений, для того
чтобы сообщить веб-серверу о необходимости начала push-загрузки
во время обработки запрошенной страницы, использует ответ 103 Early
1

https://www.youtube.com/watch?v=wR1gF5Lhcq0.

213

Push или предварительная загрузка?

Hints. Здесь данный ответ используется только для указания веб-серверу
о необходимости push, и поэтому он может быть «проглочен» веб-сер­
вером, так как отправлять его клиенту бесполезно (и, как говорилось ранее, некоторые браузеры не в состоянии должным образом обработать
ответ 103).
Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240

Клиент

Сервер

Внутренний сервер приложений

Запрос веб-страницы

Получение запроса веб-страницы
Отправка CSS
Отправка JS

Получение запроса веб-страницы
Отправка 103 Early Hints

Время обработки
Запрос CSS
Запрос JS
Отправка HTML

Отправка HTML

Получение HTML-страницы
Прорисовка страницы

Рис. 5.18 103 Early Hints сообщает веб-серверу о необходимости начала
push-загрузки заранее

Если такой вариант реализации push вас не устраивает, по всем причинам, обсуждаемым в этой главе, вы можете воспользоваться менее
рискованным вариантом и отказаться от использования HTTP/2 push.
Альтернативным вариантом является отправка браузеру ответа 103 (при
условии, что браузер его поддерживает). Таким образом, браузер сможет
использовать предварительно загруженные заголовки ссылок HTTP для
предварительной загрузки ресурсов, как показано на рис. 5.19.
Конечно, этот процесс несколько медленнее, чем HTTP/2 push, поскольку браузеру все так же необходимо запрашивать ресурсы, однако
теперь он может начать делать это заранее. В приведенном выше примере вы можете увидеть, что запрос дополнительных ответов накладывается на процесс отправки главной страницы. Такой «нахлест» несколько
усложняет ситуацию, однако на практике все обычно происходит именно так. Преимущество предварительной загрузки заключается в том, что
вы с меньшей вероятностью потеряете пропускную способность вашего соединения. Если в кеше браузера уже есть необходимые ресурсы, он
не станет запрашивать их. В зависимости от времени предварительно
загруженные ресурсы могут быть получены до того, как будет получена
страница, для генерации которой они нужны. Таким образом, данный
процесс становится таким же быстрым, как и использование HTTP/2 push.

Глава 5

214

Реализация HTTP/2 push

Даже если в реальности он будет происходит несколько медленнее, чем
на рис. 5.19, предварительная загрузка все же может быть рассмотрена
в качестве безопасного промежуточного звена до тех пор, пока разработчики не найдут более эффективного способа безопасного использования
HTTP/2 push, при котором не возникнет риск потери ресурсов.
Время
(мс)
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290

Клиент

Сервер

Внутренний сервер приложений

Запрос веб-страницы

Получение запроса веб-страницы
Получение 103 Early Hints

Получение запроса веб-страницы
Отправка 103 Early Hints

Время обработки
Получение 103 Early Hints
Запрос CSS
Запрос JS
Отправка HTML

Получение HTML-страницы

Отправка HTML

Получение запроса CSS
Получение запроса JS
Отправка CSS
Отправка JS

Получение CSS
Получение JS
Прорисовка страницы

Рис. 5.19 103 с заголовками предварительной загрузки вместо HTTP/2 push

На момент написания этой книги ни один браузер не поддерживает
обработку 103 Early Hints (хотя Chrome1 и Firefox2 работают над этим).
Также не все браузеры поддерживают заголовки ссылок предварительной загрузки3. Возможно, к тому времени, когда заголовок 103 Early Hints
будет поддерживаться повсеместно, проблема излишней push-загрузки
решится, и, возможно, с помощью дайджестов или подобных методов;
в таком случае у разработчиков будет два варианта.

5.9

Другие варианты использования HTTP/2 push
Сегодня у HTTP/2 push есть конкретное предназначение: заблаговременная отправка критически важных ресурсов для ускорения загрузки
страницы без необходимости встраивания ресурсов. Однако многие
1
2
3

https://crbug.com/671310.
https://bugzilla.mozilla.org/show_bug.cgi?id=1407355.
https://caniuse.com/#feat=link-rel-preload.

Другие варианты использования HTTP/2 push

215

люди обсуждают и другие варианты его использования1. Среди них рассматриваются следующие:
„„ может ли HTTP/2 push заменить WebSockets или SSE, если требование использовать push только в ответ на определенные запросы было
смягчено? WebSockets и SSE обеспечивают двустороннюю связь
между клиентом и сервером (например, она важна при обновлении
веб-страницы или появлении новой информации на сервере). Или,
может быть, достаточно использовать эти две технологии совместно с HTTP/22? Как уже было сказано в начале главы, HTTP/2 push
не может стать полноценной заменой, однако при условии внесения некоторых изменений, которые сделают связь между клиентом
и сервером действительно двусторонней, у него есть потенциал
(однако это может быть более накладно, по сравнению с исходным
форматом, скажем, WebSockets). В связи с этим отдел исследований
и разработок BBC рассмотрел использование HTTP/2 push в качестве метода широковещательной рассылки и опубликовал довольно
интересную статью3;
„„ можно ли использовать HTTP/2 push для обновления кешей браузера
при изменении ресурсов? На сегодняшний день кеширование и очистка кеша – это довольно сложные процессы4. Но если бы HTTP/2 push
позволял вам помещать ресурсы непосредственно в HTTP-кеш (что
невозможно сейчас), появились бы новые возможности обработки
изменений на веб-сайтах;
„„ можно ли применить HTTP/2 push для концепции прогрессивных JPEGфайлов? Прогрессивные JPEG-файлы загружаются браузером постепенно, т. е. по мере загрузки большего количества файлов эти изображения становятся четче. Такая концепция позволяет загружать
несколько изображений параллельно. Если бы с помощью HTTP/2
push мы могли бы изменить приоритет этих файлов, было бы еще
интереснее5. Таким образом, сервер мог бы отправлять предпросмотр изображения с высоким приоритетом, а затем уже с низким
приоритетом отправлять основное изображение. В главе 7 мы обсудим веб-сервер Shimmercat, который как раз-таки использует эту
технологию;
„„ можно ли использовать HTTP/2 push для API? Один из разработчиков
API предположил, что HTTP/2 push можно использовать для отправки дополнительной информации и при этом сохранять разделение
ресурсов, вследствие чего может возникнуть множество интересных
вариантов использования, которые, возможно, не будут ограниченны push-кешами браузеров. Протокол позволяет использовать push
1
2
3
4
5

https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/.
https://www.infoq.com/articles/websocket-and-http2-coexist.
https://www.bbc.co.uk/rd/publications/whitepaper336.
https://css-tricks.com/strategies-for-cache-busting-css/.
https://calendar.perfplanet.com/2016/even-faster-images-using-http2-and-progressive-jpegs/.

Глава 5

216

Реализация HTTP/2 push

в ответ на любой запрос. Однако во многих отношениях браузеры
ограничивают использование push и уведомляют об этом страницу,
если впоследствии она не запрашивает ресурсы. Клиент HTTP/2, основанный не на браузере, может снять такие ограничения;
„„ может ли добавление уведомлений способствовать появлению других
вариантов использования HTTP/2 push? Добавление push-уве­дом­
лений HTTP/2 или API в браузеры может привести к другим интересным вариантам использования. Например, новостной веб-сайт
или веб-сайт социальной сети может проверить сервер на наличие
обновлений с помощью одного короткого запроса «Есть ли обновления?». Если они есть (например, на сайт выложили новости), любые
необходимые ресурсы могут быть переданы как стандартные ресурсы HTTP (HTML, данные JSON, изображения и т. д.); затем в вебприложение должно быть отправлено событие, сообщающее клиенту о необходимости извлечения и последующего отображения
этих ресурсов по их получении. Такая техника позволит загружать
веб-страницы мгновенно. Преимущества этого метода перед WebSockets или SSE – это преимущества HTTP (например, кеширование,
форматы файлов и простота).
Таким образом, у HTTP/2 push есть и другие полезные функции. Я думаю, что, несмотря на то что он не использовался по своему первоначальному предназначению (по веским причинам, обсуждаемым в этой
главе), он имеет большой потенциал, который станет основой для развития новых идей (упомянутых в этой главе и, возможно, каких-либо
новых1). Инженерная группа Internet (IETF) начала разработку информационного RFC, который будет включать в себя варианты использования
HTTP/2 push2.
Не слишком ли мы ограничиваем себя, пытаясь закрепить за HTTP
старую концепцию протокола запроса–ответа? WebSockets и SSE демонстрируют потребность в двустороннем протоколе на основе HTTP,
и, возможно, нам следует это реализовать. На этот счет уже поступали
предложения3, и уровень двоичных фреймов, представленный в HTTP/2,
позволяет реализации такого рода. В главе 10 я еще вернусь к обсуждению этого тезиса.
Так или иначе, HTTP/2 push является новинкой, и владельцы сайтов
должны подходить к ней с осторожностью. У этой технологии есть большой потенциал, и в дальнейшем эксперименты покажут, действительно
ли HTTP/2 push обеспечивает прирост производительности, или же, наоборот, слишком усложняет процесс и в принципе не стоит усилий.
Я надеюсь, что после прочтения этой главы вы увидели, что, несмотря
на то что потенциал HTTP/2 push действительно огромен, торопиться
с его реализацией не стоит. На самом деле вы можете не рассматривать
1

2
3

https://groups.google.com/a/chromium.org/forum/#!msg/net-dev/yfkW4mkWIPU/
5RckmfktJgAJ; https://goo.gl/gTJrwC.
https://tools.ietf.org/html/draft-bishop-httpbis-push-cases.
https://tools.ietf.org/html/draft-benfield-http2-p2p.

Резюме

217

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

Резюме
HTTP/2 push – это новая концепция HTTP/2, которая позволяет отправлять несколько ответов на один HTTP-запрос.
„„ HTTP/2 push может стать альтернативой концепции встраивания критических ресурсов.
„„ Многие серверы и сети CDN реализуют HTTP/2 push посредством заголовков HTTP-ссылок.
„„ Новый код состояния 103 Early Hints может помочь заблаговременно
предоставлять заголовки ссылок.
„„ HTTP/2 push может быть реализован в браузере довольно неочевидными способами.
„„ Зачастую возникающая избыточная push-загрузка ресурсов может отрицательно сказаться на производительности веб-сайта.
„„ Прирост производительности от HTTP/2 push может быть не велик,
а риски высоки.
„„ Возможно, лучше пользоваться технологией предварительной загрузки (с 103 Early Hints, если вы хотите пользоваться push).
„„ HTTP/2 push можно использовать для других целей, хотя некоторые из
них потребуют изменений в протоколе.
„„

6

Оптимизация в HTTP/2

В этой главе мы рассмотрим:
значение HTTP/2 для веб-разработчиков;
методы повышения веб-производительности HTTP/1.1
и являются ли они антипаттернами для HTTP/2;
„„ другие методы повышения производительности
и их актуальность для HTTP/2;
„„ оптимизацию в HTTP/1 и HTTP/2;
„„ метод слияния соединений.
„„
„„

Итак, вы имеете четкое представление о HTTP/2: вы знаете, для чего он
предназначен и как работает, а также знаете новые функции и новые
возможности, которые открываются благодаря нему. В третьей части
этой книги я расскажу о более сложных аспектах протокола, однако сейчас ваших знаний уже достаточно, для того чтобы понять, что HTTP/2
значит для ваших веб-сайтов и как вы можете их оптимизировать. Как
следует изменить методы разработки? Возможно ли отказаться от некоторых техник исполнения? Какие новые техники вы можете использовать? Как оптимизировать работу сайта для тех пользователей, которые
не поддерживают HTTP/2? Данная глава призвана ответить на все эти
вопросы.

Значение HTTP/2 для веб-разработчиков

6.1

219

Значение HTTP/2 для веб-разработчиков
Я думаю, вы заметили, что HTTP/2 в корне меняет способ отправки
HTTP-сообщений на сервер, ввиду чего и повышается производительность. Но нужно ли разработчикам менять свои языки и методы разработки? Следует ли использовать определенные фреймворки Java­Script
с целью получить все преимущества HTTP/2? Ответить на эти вопросы
можно следующим образом: в целом никаких изменений не требуется,
хотя некоторые из них все же могут быть полезны.
Протокол HTTP/2 имеет обратную совместимость. Если ваш сервер
поддерживает HTTP/2, у вас будет возможность включить его и в большинстве случаев вы сразу увидите прирост производительности, и при
этом ни одна строчка кода не поменяется. Изменений требуют некоторые новые функции (такие как HTTP/2 push). Вы сможете реализовать
эти изменения после тщательного изучения HTTP/2, в чем вам может
помочь в том числе и эта глава. Однако HTTP/2 не будет требовать внесения подобных изменений сразу же после перехода на него. По большей
части изменения являются необязательными дополнениями, которые
могут обеспечить еще больший прирост производительности.
Конечно же, переход на HTTP/2 может оказаться не таким простым,
как хотелось бы (об этом мы говорим в главе 3 и в приложении). Возможно, при переходе вам потребуется обновить свою инфраструктуру
или даже добавить новые ее элементы, например разместить перед вашим веб-сервером обратный прокси-сервер или сеть доставки контента.
Такие новшества могут повлечь за собой новые возможности, которыми
вы сможете воспользоваться в будущем или при ближайшем обновлении. Однако данная тема требует отдельного рассмотрения. В этой главе
я попытаюсь объяснить, каково значение HTTP/2 для веб-разработчиков
(если предположить, что они имеют доступ к нему).

HTTP/2-запросы от браузера к серверу
Одно из главных преимуществ HTTP/2 заключается в том, что, если сервер уже перешел на поддержку этой версии, на стороне клиента не требуется внесения никаких изменений; браузер сам позаботится об этом. Вам
не нужно обновлять версию JQuery, использовать другой синтаксис AJAX
или переключаться с Angular на React (или наоборот). С точки зрения вебразработчика, HTTP-запросы и ответы с клиентской стороны работают точно
так же, как и раньше. Единственным изменением здесь в идеале должна являться скорость: ввиду отсутствия очередей, все происходит намного быст­
рее. Библиотеки и инструменты позволяют браузеру обрабатывать детали
совершения запроса на низком уровне. Таким образом, знать о HTTP/2 достаточно только браузеру.
На данный момент разработчики пользовательских интерфейсов не могут
дать однозначный ответ на вопрос, следует ли использовать HTTP/1.1 или
HTTP/2, как и не могут точно сказать, следует ли использовать HTTP/1.1

220

Глава 6

Оптимизация в HTTP/2

или HTTP/1.0 (или даже HTTP/0.9). В будущем ситуация может измениться, поскольку, например, возможность расстановки приоритетов запросов
в HTML или AJAXa,b,c или реализация обратных вызовов HTTP/2 pushd – чрезвычайно полезные функции.
___________________________________
a
https://bugzilla.mozilla.org/show_bug.cgi?id=559092.
b
https://bugs.chromium.org/p/chromium/issues/detail?id=41501.
c
https://github.com/WICG/priority-hints.
d
https://github.com/whatwg/fetch/issues/65.

6.2

Оптимизация HTTP/1.1 мешает HTTP/2?
Целью HTTP/2 является решение фундаментальных проблем производительности HTTP/1.1. Именно из-за этих проблем запрос отдельных ресурсов в HTTP/1.1 был очень затратным, ввиду чего и появились
многочисленные методы, связанные с увеличением количества HTTPсоединений или минимизацией количества запрашиваемых ресурсов.
В первом случае браузеры открывали несколько соединений, а иногда
даже размещали ресурсы в нескольких доменах (сегментирование). Во
втором случае для объединения файлов CSS и Java­Script применялась
технология конкатенации, вследствие чего они объединялись в один
большой файл. Изображения небольших размеров (такие как иконки
социальных сетей или другие маленькие значки) также объединялись
в спрайт-файлы, из которых элементы можно было извлекать посредством CSS. Оба типа оптимизации предполагали передачу одних и тех же
данных (или, по крайней мере, похожих данных) посредством выполнения меньшего количества HTTP-запросов.
Несмотря на то что вышеописанные обходные пути устраняют некоторые недостатки HTTP/1.1, в то же время они создают и проблемы
(описанные в главе 2). HTTP/2 призван решить многие из них на уровне
протокола. В новой версии выполнение запросов почти не требует затрат ввиду внедрения технологии двоичного фрейминга. Встает вопрос,
актуальны ли в таком случае обходные пути? На самом деле, многие говорили, что для HTTP/2 они становятся помехами. Но не все так просто;
я сказал, что запросы HTTP/2 почти не требуют затрат и только на уровне протокола.

6.2.1 Запросы HTTP/2 по-прежнему затратны
Когда веб-страница ссылается на ресурс, происходит множество разных
процессов; некоторые из них оптимизированы в HTTP/2, а некоторые –
нет. На рис. 6.1 подробно описаны некоторые из множества решений
и процессов, которые браузер должен осуществить, когда веб-страница
запрашивает ресурс.

221

Оптимизация HTTP/1.1 мешает HTTP/2?
Начало

Конец

1. Проверка кешей
Да
Есть ли ресурс в памяти (например, в краткосрочном кеше изображений или предзагрузки)?

Используйте ресурс

Нет
Да

Используем ли мы сервис-воркеры
и есть ли у них копия?
Нет

Да

Находится ли она в HTTP-кеше?
Нет

Она
еще пригодна?

Да

Нет
Да

Она есть в кеше HTTP/2 Push?
Нет
2. Отправка HTTP-запроса

Проверьте, нужно ли
кешировать ответ

Решите, какие файлы куки (если есть)
отправлять с запросом
Да

Есть ли у нас подходящее соединение?
Нет
Разрешено ли нам открывать новое соединение
(при минимальном количестве соединений для этого домена)?

Нет

Да
Нет

Знаем ли мы IP-адрес сервера?
Да

Поиск в DNS

Установка TCP-соединения

Согласование HTTPS
Используем ли мы HTTP/2 и достигнуто ли
максимальное количество потоков?
Да

Рис. 6.1

Отправка HTTP-запроса

Нет

Решения и процессы браузера, связанные с HTTP-ресурсом

На высоком уровне браузеру сначала необходимо проверить наличие
актуальной копии в кешах (которые мы обсуждали в главе 5), и если она
есть, то он уже не будет выполнять для этого ресурса новый запрос. В за-

Глава 6

222

Оптимизация в HTTP/2

висимости от используемого домена и типа запроса, HTTP-запрос может
выполняться по уже существующему соединению или же по-новому. После загрузки ресурса клиенту необходимо просмотреть заголовки кеширования, чтобы решить, сохранять ли ресурс в кеш для потенциального
повторного использования. Даже после всех этих шагов браузер должен
обработать ресурс (проанализировать CSS или Java­Script, обработать
изображение JPEG и т. д.).
Множество процессов происходит еще до того, как вы доходите до стадии отправки и получения HTTP-запросов, что уж говорить о том, когда
дело доходит до получения ресурса. Все эти процессы требуют времени,
но зачастую совсем немного. Однако, если вы отойдете от ограничения
HTTP/1.1, разрешающее только шесть параллельных подключений, и получите сотни ресурсов, вы заметите, что возникли новые интересные
проблемы.
В 2016 году, когда использование HTTP/2 набирало обороты, разработчики Chrome заметили значительные задержки при использовании
HTTP/2 для большого количества ресурсов1. Например, даже при отсутствии запросов некоторые сайты зависали на 400–500 мс. Однако эта
проблема не имела ничего общего с протоколом HTTP/2 как таковым.
В главе 7 вы увидите, что приоритизация потоков должна позволять
параллельным запросам распределять соответствующие приоритеты
на уровне протокола. Проблема заключалась исключительно в том, что
«узким местом» системы была именно отправка ресурсов. В это время
браузер был занят выполнением всех задач, упомянутых в предыдущем
разделе. Благодаря HTTP/2 множество ресурсов теперь могут работать
одновременно, и при этом проблем с производительностью, связанных
с ожиданием свободного соединения, не возникает; однако эта возможность перемещает «узкое место» в другие части системы.
Чтобы исключить эту задержку, команде Chrome пришлось ограничить количество ресурсов, которые могут быть поставлены в очередь
даже при использовании HTTP/2, вернувшись к шести подключениям,
как в HTTP/1. Так продолжалось до тех пор, пока команда не оптимизировала код. Ограничение применялось только к несущественным ресурсам, поэтому в теории HTTP/2 ничем не уступал HTTP/1.1. Однако возник интересный сценарий: ограничение распространялось на ресурсы
Java­Script с атрибутами async или defer (обычно добавляемыми из соображений производительности, так как они предотвращают блокировку
ресурсов), а ресурсы Java­Script без этих атрибутов не ограничивались.
Веб-сайты, которые использовали передовые методы повышения производительности (например, неблокируемые Java­Script), были искусственно ограничены и загружались медленнее, чем сайты, не использующие их. В конечном счете веб-сайты, использующие async или defer,
загружались точно так же, как запросы HTTP/1.1 (см. рис. 6.2).
Вопрос о значении этой проблемы остается открытым; если ресурс
Java­Script был помечен атрибутами async или defer, значит, он не являет1

https://bugs.chromium.org/p/chromium/issues/detail?id=655585.

Оптимизация HTTP/1.1 мешает HTTP/2?

223

ся критическим для загрузки. Однако для владельцев веб-сайтов, реализовавших HTTP/2.2, такие исходы, свойственные HTTP/1, стали большим
сюрпризом1.
HTTP/2 – Async/Defer не используется

HTTP/2 – Async/Defer используется

Рис. 6.2

Ресурс Java­Script, не блокирующий рендеринг, искусственно замедляется в Chrome

В более поздних версиях Chrome проблема ограничения была решена.
Она стала ярким примером того, что при устранении «узких мест» раз1

https://stackoverflow.com/questions/45384243/google-chrome-does-not-do-multiplexing-withhttp2/45775288#45775288.

Глава 6

224

Оптимизация в HTTP/2

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

6.2.2 Возможности HTTP/2 не безграничны
Следует также отметить, что в HTTP/2 ограничения касательно количества соединений сняли не полностью. В главе 5 мы увидели, что, несмотря на то что по умолчанию SETTINGS_MAX_CONCURRENT_STREAMS имеет значение unlimited, во многих реализациях все же существуют ограничения
(см. табл. 6.1 и 6.2).
Таблица 6.1 Ограничения количества параллельных потоков в популярных
серверных реализациях HTTP/2
Программное обеспечение
Apache HTTPD (v2.4.35)
nginx (v1.14.0)
H2O (2.3.0)
IIS (v10)
Jetty (9.4.12)
Apache Tomcat (9.0)
Node (10.11.0)
Akamai
Amazon CloudFront и S3
Cloudflare
MaxCDN

Тип
Веб-сервер
Веб-сервер
Веб-сервер
Веб-сервер
Веб-контейнеры сервлетов Java
Веб-контейнеры сервлетов Java
Операционная среда Java­Script
Сеть CDN
Сеть CDN
Сеть CDN
Сеть CDN

Допустимое по умолчанию количество
параллельных потоков
100a
128b
100c
100
128d
200e
100f
100
128
128
128

a

https://httpd.apache.org/docs/2.4/mod/mod_http2.html#h2maxsessionstreams.
http://nginx.org/en/docs/http/ngx_http_v2_module.html#http2_max_concurrent_streams.
c
 https://h2o.examp1e.net/configure/http2_directives.html#http2-max-concurrent-requestsper-connection.
d
https://www.eclipse.org/jetty/documentation/9.4.x/http2-configuring.html.
e
https://tomcat.apache.org/tomcat-9.0-doc/config/http2.html.
f
https://nodejs.org/api/http2.html.
b

Таблица 6.2 Ограничения количества параллельных потоков в популярных
браузерах с поддержкой HTTP/2
Программное обеспечение
Chrome (v69)
Firefox (v62)
Safari (v12)
Opera (v56
Edge (v17)
Internet Explorer 11

Допустимое по умолчанию количество параллельных потоков
1000
Не задано (HTTP/2 используется по умолчанию без ограничений)
1000
1000
1024
1024

Такие значения были приняты по одной из двух причин: либо они рекомендованы в документации, либо их выявили в ходе экспериментов.
Сети CDN я исследовал с помощью Wireshark, посредством которой пере-

Оптимизация HTTP/1.1 мешает HTTP/2?

225

хватывал запросы на домашнюю или другую страницу этой CDN, и просматривал фрейм SETTINGS, как описано в главе 4 (раздел 4.3.3). С браузерами я работал с помощью сервера nghttp, настроив его на подробное
ведение журнала; далее я также просматривал фреймы SETTINGS, отправляемые браузерами при установке соединения HTTP/2.
Сразу бросается в глаза один факт: значения на стороне сервера намного меньше значений для веб-браузеров. В Firefox ограничение в прин­ципе
не установлено, и по умолчанию здесь может быть открыто неограниченное количество параллельных соединений. Это имеет смысл, поскольку
в конечном итоге браузер получает полный контроль над отправляемыми запросами, ввиду чего он может добавлять ограничения вне протокола (как изначально было у Chrome, который мы обсуждали в предыдущем
разделе). Кроме того, количество запросов, обслуживаемых браузером,
должно быть гораздо меньше, в отличие от сервера, который, как правило, одновременно взаимодействует сразу с множеством пользователями.
Несмотря на все это, учитывая проблемы с производительностью, с которыми сталкивался Chrome при параллельном запросе большого количества ресурсов, все же разумнее было бы ввести ограничения, хотя бы до
тех пор, пока HTTP/2 не будет полностью адаптирован. Сайты, состоящие
из более 200 ресурсов, встречаются редко. Однако после полной адаптации HTTP/2 и при условии прекращения объединения веб-сайтами
ресурсов в спрайт-файлы количество таких сайтов непременно возрастет. Например, если веб-сайту требуется 500 ресурсов и они не объединены в спрайт-файлы, вполне вероятно, что сначала начнут загружаться
100–128 ресурсов, а остальные будут поставлены в очередь, как это было
в HTTP/1.1. Мы уже встречались с подобной ситуацией в главе 2 (раздел
2.6.1), когда обсуждали превышение предела сервера в 100 потоков. Существует множество примеров, когда сайты отказывались от объединения файлов, выходя за пределы этих ограничений1.

6.2.3 Для больших ресурсов эффективнее сжатие
Все веб-ресурсы перед отправкой по сети должны быть сжаты. У некоторых типов ресурсов (например, JPEG- и PNG-изображения и WOFF,
и WOFF2 для шрифтов) сжатие встроено в формат. Для текстовых форматов, таких как HTML, CSS и Java­Script, используется сжатие, такое как
gzip (или более новый вариант brotli2), которое выполняется «на лету».
Общей чертой вышеперечисленных алгоритмов является то, что файлы большего размера они сжимают более эффективно, чем файлы меньшего размера. В разделе 7.3.4 описана работа каждого алгоритма. Если
сказать кратко, большинство из них работает по следующему принципу:
они ищут повторяющиеся последовательности данных и заменяют их
ссылками на версию этих данных. В больших файлах обычно встречает1

2

http://engineering.khanacademy.org/posts/js-packaging-http2.htm#http-2-0-hasservice-issues.
https://tools.ietf.org/html/rfc7932.

Глава 6

226

Оптимизация в HTTP/2

ся больше дубликатов, а степень сжатия обычно больше. Однако лучше
сжимать один большой файл размером 100 Кб, чем несколько файлов
по 10 Кб по отдельности, даже если в общей сумме объемы несжатых
данных одинаковы. Отказавшись от объединения ресурсов (как в случае с HTTP/1.1), вы можете потерять некоторые преимущества сжатия,
и в конечном итоге по протоколу HTTP/2 вы будете отправлять по сети
больше данных даже при одинаковом объеме файлов.
Также ситуация зависит и от типа сжимаемых файлов. Рассмотрим на
примере загрузки популярных файлов jQuery:
curl -OL https://code.jquery.com/jquery-3.3.1.min.js
curl -OL https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js
curl -OL https://code.jquery.com/ui/1.12.1/jquery-ui.min.js

Затем объединим эти файлы с помощью команды Linux cat:
cat jquery-3.3.1.min.js jquery.mobile-1.4.5.min.js jquery-ui.min.js >
jquery_combined.js

Взглянув на список файлов, мы можем увидеть их размеры:
-rw-r--r--rw-r--r--rw-r--r--rw-r--r--

1
1
1
1

barry
barry
barry
barry

p
p
p
p

86927
253668
200143
540738

19
19
19
19

May
May
May
May

19:31
19:31
19:31
19:31

jquery-3.3.1.min.js
jquery-ui.min.js
jquery.mobile-1.4.5.min.js
jquery_combined.js

Общий объем файла составляет 86 927 + 200 143 + 253 668 = 540 738 Кб.
Затем мы сжимаем все файлы с помощью gzip со стандартными настройками:
gzip jquery*

Таким образом, мы получаем файлы следующих размеров:
-rw-r--r--rw-r--r--rw-r--r--rw-r--r--

1
1
1
1

barry
barry
barry
barry

p
p
p
p

30371
68058
55649
152652

19
19
19
19

May
May
May
May

19:31
19:31
19:31
19:31

jquery-3.3.1.min.js.gz
jquery-ui.min.js.gz
jquery.mobile-1.4.5.min.js.gz
jquery_combined.js.gz

Сложив значения (30 371 Кб + 68 058 Кб + 55 649 Кб), мы видим, что
файл сжимается до 152 652 Кб. Объем становится меньше всего на 1 %, но
разница все же есть, и для неоптимизированных файлов (которые я обсуждаю в разделе 6.3.1) она может быть значительнее.
С другой стороны, отказ от объединения файлов может позволить разработчикам более направленно выбирать содержимое страницы. Например, раньше некоторые разработчики могли использовать один большой
объединенный файл Java­Script для каждой страницы сайта. Некоторым
страницам требовались одни части этого файла, а некоторым другие; поскольку работа в HTTP/1.1 с несколькими файлами неэффективна, отправлять один большой файл для каждой страницы было удобнее, чем
создавать отдельные пакеты. С появлением HTTP/2 одновременная загрузка множества файлов стала менее проблематичной. Сейчас для каждой страницы можно создать отдельный файл только с тем Java­Script,

Оптимизация HTTP/1.1 мешает HTTP/2?

227

который ей потребуется; таким образом, объем данных, передаваемых
для каждой страницы уменьшается.
Компенсирует ли сокращение передаваемых данных потерю степени
сжатия? Это зависит от вашего сайта и от того, разделяете ли вы файлы и насколько меньше ресурсов вы стали отправлять. В ходе тематических исследований1 наблюдалось небольшое увеличение объема данных,
передаваемых по сети даже при удалении ненужного кода, хотя огромное количество передаваемого кода Java­Script вызывает более заметные
проблемы. Даже если объем ресурсов несколько больше, прирост производительности ввиду обработки меньшего количества данных на некоторых страницах может компенсировать потерю производительности
из-за отправки большего количества необработанных байтов при использовании разделения. Также HTTP/2 уже не требует такого большого
количества пакетов, как это было HTTP/1.1, но в любом случае вам стоит
учитывать потери степени сжатия перед разделением.

6.2.4 Ограничение пропускной способности и конкуренция
ресурсов
HTTP/1.1 передает запросы независимо от их приоритета, вследствие
чего возникают сложности, ведь самые разные запросы могут быть инициированы в самое разное время. Именно поэтому разработчики веббраузеров начали активную работу над концепцией приоритетности запросов, с помощью которой наиболее важные из них отправлялись бы
в первую очередь. Обходными путями для этой проблемы были объединение и сегментирование ресурсов, однако эти решения имели множество ограничений. Сегодня, когда ограничения минимальны (или даже
вовсе устранены), может возникать конкуренция ресурсов за пропускную способность соединения.
На сайте-примере из главы 2 при загрузке 360 изображений по HTTP/2
100 из них загружались параллельно. Здесь мы видим явные отличия от
HTTP/1.1 и его ограничение в шесть параллельно загружаемых ресурсов.
Поскольку 100 загрузок происходили одновременно, загрузка каждого
ресурса по протоколу HTTP/2 занимала больше времени, как показано
на рис. 6.3.
Вообще, по HTTP/2 изображения загружаются намного быстрее. Однако было бы лучше, если вместо постепенной загрузки каждого изображения (как в HTTP/2, если не используется приоритизация) сначала
осуществлялась бы полная загрузка некоторых ресурсов (как это происходит в HTTP/1.1).
С вышеописанными проблемами при переходе на HTTP/2 столкнулся
веб-сайт, посвященный дизайну, 99design2. После перехода у него появи1

2

http://engineering.khanacademy.org/posts/js-packaging-http2.htm#bundling-improves-compression.
https://99designs.com/tech-blog/blog/2016/07/14/real-world-http-2-400gb-of-images-per-day/.

228

Глава 6

Оптимизация в HTTP/2

лась возможность загружать все необходимые веб-странице ресурсы параллельно, в том числе и большие изображения высокого качества (что
очень важно для веб-сайта о дизайне). Однако некоторые изображения
из верхней части страницы загружались медленнее, поскольку в это же
время загружались и изображения из нижней части страницы, занимая
при этом пропускную способность. В HTTP/1.1 браузер отдавал приоритет изображениям, которые должны отобразиться на экране первыми,
а ограничение в шесть подключений гарантировало, что их загрузка
не будет замедляться загрузкой множества последующих изображений
с более низким приоритетом.

Рис. 6.3 Загрузка отдельных ресурсов по HTTP/1.1 (сверху) происходит быстрее,
чем по HTTP/2 (снизу)

Оптимизация HTTP/1.1 мешает HTTP/2?

229

Данные проблемы решаются правильной установкой приоритетов запросов. До этого я рассказывал о приоритизации лишь кратко, но в главе 7 я рассмотрю этот аспект подробно. Приоритизация не зависит от
владельца сайта (поэтому я оставил ее детальное обсуждение для следующей главы), но она может оказаться ключевым фактором, влияющим
на производительность как браузеров, так и серверов. Данный способ
наиболее эффективен, если браузер может предложить соответствующий
приоритет (например, загрузка изображений в области просмотра с приоритетом выше, чем у изображений, расположенных дальше по странице) и если сервер может ответить на это предложение. В качестве альтернативы изображения могут загружаться полностью, но строго в порядке
следования запросов. Так, например, делает Chrome (см. главу 7).
Следовательно, эта проблема была скорее проблемой ранней реализации, чем проблемой самого протокола HTTP/2. Однако, учитывая, что
разработчики имеют 20-летний опыт борьбы с проблемами в HTTP/1,
маловероятно, что это станет последней подобной проблемой при переходе к HTTP/2.

6.2.5Сегментирование данных
Сегментирование данных использовалось для преодоления накладываемого браузерами на домен ограничения в шесть параллельных соединений. Количество параллельных загрузок увеличивали за счет размещения медиафайлов на поддоменах или в отдельных доменах. На мой
взгляд, полезный эффект от сегментирования был преувеличен, за исключением ситуаций, где необходимо загрузить большое количество ресурсов, и в этом случае целесообразно было бы применять объединение
или спрайтинг. Исследования показали, что зачастую дополнительные
соединения создаются только для одного или двух ресурсов, поэтому
время, затраченное на установку этих соединений, может свести на нет
любой возможный прирост производительности при их эффективном
использовании. И как всегда, разработчикам необходимо оценить степень влияния таких технологий, как сегментирование, на их сайт, а не
слепо применять их, предполагая, что они обязательно обеспечат прирост производительности.
С появлением HTTP/2 сегментирование утратило смысл, а попытки
создания отдельной инфраструктуры и управления ей теперь приносят
ограниченные результаты. Кроме того, некоторые технологии HTTP/2
(например, HTTP/2 push и сжатие заголовков HPACK) лучше работают
при наличии только одного соединения, поэтому сегментирование приведет здесь к снижению производительности. Я считаю, что, в силу того
что популярность HTTP/2 набирает стремительные обороты, с течением
времени сегментирование будет использоваться все реже. В разделе 6.4.4
вы увидите, что для HTTP/2 существует метод обратного сегментирования, объединяющий лучшие черты двух версий протокола. При некоторых конкретных сценариях, где потери очень высоки, сегментирование все еще может быть полезно (см. главу 9). Однако по большей части

Глава 6

230

Оптимизация в HTTP/2

сегментирование уже утратило свою ценность. Даже в случае подобных
сценариев, браузерам лучше открывать несколько соединений только
в случае необходимости, а не оставлять установку сегментирования для
всех подключений (независимо от того, с потерями они или нет) на усмотрение сайтов.

6.2.6 Встраивание ресурсов
Я всегда считал встраивание критически важного CSS или скриптов чемто вроде хакерства1 – эффективного, но тем не менее хакерства. Встраи­
вание кода CSS в заголовок страниц ускоряет загрузку первой страницы, но затем этот код либо дублирует содержимое реального CSS, либо
блокирует кеширование для последующих загрузок страниц на том же
сайте. Кроме того, встраивание исключительно критического CSS может
быть сложным осуществить, так как по умолчанию уже может быть предусмотрен определенный способ загрузки CSS с целью предотвращения
блокировки их рендеринга.
Предполагалось, что HTTP/2 push устранит необходимость встраивания ресурсов, но, как мы увидели в главе 5, эффективное использование
данной технологии оказалось довольно сложным, и поэтому она не получила должного распространения. Я предполагаю, что еще какое-то время
встраивание будет оставаться неплохим способом повышения производительности для тех веб-сайтов, которые готовы его использовать и хотят выжать из загрузки первой страницы все до последней капли.

6.2.7 Заключение
Одной из основных целей HTTP/2 было решение проблемы повышенной
ресурсоемкости запросов в HTTP/1.1. HTTP/2 ввел значительные улучшения, однако HTTP-запросы по-прежнему требуют затрат. Часто эта
проблема возникает из-за проблем вне протокола (таких как высокая
ресурсоемкость при множественных запросах к браузерам), но другие,
по крайней мере частично, связаны именно с HTTP/2. Например, при
поступлении слишком большого количества одновременных запросов
может произойти замедление некоторых показателей (таких как первичная отрисовка), если для критических запросов ресурсов не хватает.
Также необходимо учитывать факт того, что многие реализации
HTTP/2 еще не созрели; но, несомненно, некоторые недоработанные реализации со временем будут оптимизированы. Кроме того, могут быть
обнаружены ошибки или протокол может использоваться неэффективно
(например, при неправильной приоритизации запросов). Я настоятельно рекомендую обновить программное обеспечение HTTP/2 (серверам
и браузерам) именно сейчас.
Повторяя предыдущее высказывание, я могу сказать, что по прошествии 20 лет разработчики отлично разбираются в HTTP/1 и зрелых
1

https://www.tunetheweb.com/blog/inlining-css-is-not-for-me/.

Методы повышения веб-производительности все еще актуальны для HTTP/2

231

технологических стеках, но HTTP/2 все еще находится в зачаточном состоянии. Может показаться, что у новой версии вероятность возникновения проблем, затрудняющих показ страницы, не так высока, однако
существует множество «узких мест» и других примеров, где HTTP/2 не
так эффективен, как мог бы быть.
В HTTP/2 резко увеличились лимиты параллельной загрузки (по сравнению с былым ограничением в шесть соединений), однако отойти от
них полностью все еще не удается. Пока что я рекомендую параллельно
загружать не более 100 ресурсов на один домен. При этом не отказывайтесь от концепции объединения ресурсов, даже если она приведет к созданию сотен файлов; установите повышенные ограничения для создания соединения должным образом.
Потребность в оптимизации производительности, идея которой преобладала в HTTP/1.1, сейчас намного меньше; однако до тех пор, пока
разработчики не привыкнут к работе с новой версией протокола, полностью отказываться от подобных мер не стоит. Вместо этого вы можете
просто сократить их использование до подходящего вам уровня (например, просто объединять меньше ресурсов, а не отказываться от их объединения вовсе). Разработчикам лучше сгруппировать код в наборы файлов, которые с большей вероятностью будут использоваться совместно,
а не объединять все в один большой файл, как они, возможно, делали
в прошлом. Сокращение использования методов HTTP/1.1, а не их искоренение, является консенсусом, к которому рано или поздно приходят
исследователи и владельцы веб-сайтов1. После появления HTTP/2 многие считали, что старые методы стали неуместны, но это не совсем так.
Ключевой вывод – не стоит предполагать, что HTTP/2 являет собой
чудодейственное средство, которое может избавить вас от всех возникающих проблем. Новая версия может подойти для одних сайтов, но не
подойти для других. Вам не обойтись без тщательного изучения и разбора протокола.

6.3

Методы повышения веб-производительности
все еще актуальны для HTTP/2
В этой главе мы рассмотрели, что HTTP/2 действительно устраняет некоторые недостатки более ранних версий протокола, вследствие чего некоторые методы повышения производительности становятся для него менее актуальными. Оптимизация протокола HTTP – это не единственный
путь повышения производительности веб-сайтов, но, в соответствии
с природой Internet, которая предполагает взаимодействие клиента
и сервера на расстоянии, большая часть работы веб-сайта должна быть
оптимизирована именно на сетевом уровне. Стоит рассмотреть также
1

https://uhdspace.uhasselt.be/dspace/bitstream/1942/23909/1/h2bestpractices_
RobinMarx_WEBIST2017.pdf.

Глава 6

232

Оптимизация в HTTP/2

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

6.3.1 Уменьшение объема передаваемых данных
Независимо от того, насколько быстро HTTP/2 позволяет обрабатывать
запросы и ответы, предпочтительнее отправлять меньшее количество
данных. Он не способен чудесным образом ускорить соединение, он
просто делает его более эффективным. Может показаться, что веб-сайты
стали загружаться быстрее, однако количество загружаемых данных не
изменилось (возможно, их стало даже немного больше, если сжатие не
эффективно, как показано в разделе 6.2.3). Следовательно, объем данных
необходимо сокращать настолько, насколько это возможно. Все прежние
методы актуальны и для HTTP/2.

Использование подходящих форматов и размеров файлов
Пользователям всегда интереснее посещать веб-страницы с большим
количеством мультимедиа, однако для загрузки и отображения медиа­
файлов требуется некоторое время. Согласно данным HTTP Archive,
около 80 % размера страницы приходится на изображения и видео1 (см.
рис. 6.4), поэтому чрезвычайно важно учитывать целесообразность формата и размера отправляемых медиафайлов.

Шрифты

CSS

HTML

Другое

JavaScript
Изображения

Видео

Рис. 6.4 Среднее количество байтов на страницу по типу контента

1

https://httparchive.org/reports/page-weight.

Методы повышения веб-производительности все еще актуальны для HTTP/2

233

Видео и аудио – это несколько специфический тип данных, поэтому
рассматривать их здесь мы не будем, а вместо этого обсудим изображения1. Обычно фотографии имеют формат JPEG (он же JPG), а другие графические элементы – формат PNG. Google разработала особый формат
сжатия изображений WebP, однако за несколько лет существования он
так и не прижился2. Также часто используется формат SVG, однако над
ним еще предстоит проделать работу. Почти все веб-сайты пользуются JPEG и PNG3, поскольку эти форматы поддерживаются повсеместно
и обеспечивают баланс между размером и качеством.
Формат JPEG несколько снижает качество изображений, однако он позволяет настроить баланс между качеством и уровнем сжатия. Вы можете снизить качество изображения и получить изображения гораздо
меньшего размера, но без видимого падения качества для человеческого
глаза. Изображения также могут содержать значительный объем метаданных (информация о том, когда была сделана фотография, на какую
камеру она была сделана, какие настройки ISO использовались и т. д.).
Большая часть этих метаданных не важна тем, кто просматривает вашу
веб-страницу, и их следует удалять; но будьте осторожны с изображениями, которые вам не принадлежат, их изменения могут нарушить
авторские права. Для изменения качества и метаданных изображения
(что позволит вам уменьшить их размер) можно использовать различные инструменты и другое программное обеспечение для редактирования изображений, однако я советую пользоваться tinypng.com4, так как
он прост в использовании и сжимает JPEG и PNG быстро и без проблем.
Также вы можете использовать tinyjpg.com. Данные сайты работают примерно одинаково и могут обрабатывать оба вышеуказанных формата. На
рис. 6.5 показано, насколько уменьшается размер большинства изображений.
Следует учитывать не только уровень качества изображения, но и его
размер. Например, бессмысленно отправлять изображения размером
5120×2880 пикселей для отображения его с шириной 100 пикселей, поскольку затрачивается слишком много времени для загрузки и обработки браузером. Никогда не размещайте на веб-страницах изображения
большого размера с качеством или размером как для печати. Если необходимо сделать такие типы изображений доступными для пользователя,
лучше оставьте на странице ссылки на их скачивание.
Для мобильных и полных версий вашего сайта (а также для разных
размеров экрана) следует использовать изображения разных размеров.
Конечно, посетители, у которых есть возможность просмотреть ваш сайт
на большом экране смогут насладиться высококачественными изображениями, однако, если вы предложите такой же размер и качество посетителям мобильной версии, они, вероятнее всего будут неприятно удив1
2
3
4

https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats.
https://caniuse.com/#feat=webp.
https://w3techs.com/technologies/overview/image_format/all.
https://tinypng.com/.

Глава 6

234

Оптимизация в HTTP/2

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

Рис. 6.5

Tinypng может значительно уменьшить размер файла

Средний размер сайта (Kб)

2000
1800
1600
1400
1200
1000
800
600
400
200
0
16 мая 2011

16 мая 2012

16 мая 2013

16 мая 2014

16 мая 2015

Стационарный компьютер

16 мая 2016

16 мая 2017

Мобильный телефон

Рис. 6.6 Размеры сайтов для мобильных и настольных устройств постепенно
уравниваются

Словом, HTTP/2 не меняет формат отправляемых файлов и переда­
ваемых (или используемых) на стороне клиента данных. Используйте

Методы повышения веб-производительности все еще актуальны для HTTP/2

235

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

Сжатие текстовых данных
Несмотря на то что сжатие используется преимущественно для изображений, фундаментальные технологии Internet – HTML, CSS и Java­Script –
основаны на тексте, и размер таких ресурсов также следует сокращать
(настолько, насколько это возможно). В HTTP/1.1 тела HTTP сжимали
с помощью gzip или аналогичных инструментов, что позволяло уменьшить объем отправляемых данных. В HTTP/2 вы можете придерживаться
того же принципа. Ведь от версии протокола не меняется формат отправляемых данных, а меняется только метод их отправки. Таким образом,
сжатие текстовых ответов в HTTP/2 так же актуально, как и в HTTP/1.1.
Текстовые файлы легко поддаются сжатию, и вы можете уменьшить их
объем до 90 %. В табл. 6.3 представлены коэффициенты сжатия распространенных библиотек Java­Script и CSS1.
Таблица 6.3 Степень сжатия общих библиотек с использованием gzip
Библиотека
jquery-1.11.0.js
angular-1.2.15.js
bootstrap-3.1.1.css
foundation-5.css

Размер исходного файла
276 Kб
729 Kб
118 Kб
186 Kб

Размер сжатого файла
82 Kб
182 Kб
18 Kб
22 Kб

Степень сжатия
70 %
75 %
85 %
88 %

Единственным недостатком концепции сжатия данных перед их отправкой является то, что для сжатия на стороне сервера и распаковки
на стороне браузера нужны время и вычислительные ресурсы, но для
современного оборудования это мизерные затраты. На сетевом уровне преимущества отправки меньшего количества байтов почти всегда
намного выше, чем затраты времени и вычислительной мощности на
сжатие.
Самой распространенной технологией сжатия на сегодня остается
gzip2, однако популярность набирают и другие, такие как Brotli3. Иногда
Brotli обеспечивает даже лучшие показатели (в зависимости от настроек4) и, следовательно, еще большую экономию ресурсов. Подобные ему
инструменты можно использовать в дополнение к gzip для браузеров,
которые еще не поддерживают Brotli5. HTTP/2 обрабатывает различные
кодировки контента так же, как HTTP/1.1, поэтому переход на HTTP/2
не отменяет необходимости сжатия тел ответов и использования под1

2
3

4
5

https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimizeencoding-and-transfer?hl=en#text-compression-with-gzip.
https://w3techs.com/technologies/details/ce-compression/all/all.
https://opensource.googleblog.com/2015/09/introducing-brotli-new-compression.html.
https://blogs.akamai.com/2016/02/understanding-brotlis-potential.html.
https://caniuse.com/#feat=brotli.

Глава 6

236

Оптимизация в HTTP/2

ходящего формата (это важно!). Единственное незначительное отличие
состоит в том, что для некоторых новых форматов, таких как Brotli, требуется HTTPS и, возможно, более поздние версии вашего веб-сервера.
Если вы перешли на HTTPS и HTTP/2 одновременно или вам пришлось
обновить программное обеспечение вашего веб-сервера, у вас есть возможность использовать новые форматы, хотя, возможно, лучше всего
сначала выполнить переход и только потом применять новые форматы
в сочетании со старыми.
Одним из главных преимуществ сжатия является то, что, за исключением необходимости небольшой настройки сервера, при его использовании не возникает других проблем. При правильной настройке
веб-серверы сживают ресурсы буквально на лету, веб-браузеры автоматически распаковывают ресурсы, и большинство владельцев веб-сайтов
может не думать о сжатии после его включения. Некоторые веб-серверы
позволяют обслуживать предварительно сжатый контент, что снижает
нагрузку на веб-сервер. Такой метод требует дополнительных усилий
при добавлении нового контента (чтобы предварительно сжать его перед загрузкой), однако того же требует не только HTTP/1, но и HTTP/2.
Независимо от используемой версии HTTP, вам необходимо использовать сжатие тел HTTP-сообщений. Кодировка содержимого передается
браузеру в HTTP-заголовке content-encoding, как это было в HTTP/1.1.

Сжатие HTTP-заголовков
Следует поговорить еще об одной вещи, которая действительно меняется
при переходе на HTTP/2, – это сжатие заголовков. В HTTP/1 возможно сжимать только тела запросов и ответов, а HTTP/2 позволяет проделывать то же
самое и с заголовками, используя при этом формат HPACK. В том числе с помощью него HTTP/2 снижает расходы производительности при выполнении
нескольких запросов одновременно. Без HPACK отправка одного и того же
содержимого по двум запросам приведет к увеличению количества данных
заголовка в два раза. На самом деле такое нововведение очень важно, поскольку использование HTTP заголовков продолжает расти, а для многих
небольших запросов заголовки могут составлять пропорционально большую
часть как запроса, так и ответа.
Поскольку сжатие заголовков осуществляется непосредственно браузером
и владельцам веб-сайтов и разработчикам не требуется прикладывать для
этого много усилий, сейчас я не буду останавливаться на этой теме, однако
в главе 8 мы все же это обсудим.

Минификация кода
Минификация кода является еще одним способом сокращения объема
данных, будь то код HTML, CSS или Java­Script. Она заключается в удалении пробелов и комментариев из кода, и иногда даже его в переписывании в целях уменьшения размеров имен локальных переменных
и удаления ненужных разделителей. Подобно сжатию, минификация со-

Методы повышения веб-производительности все еще актуальны для HTTP/2

237

хранила актуальность в HTTP/2, поэтому при переходе на новую версию
протокола я настоятельно рекомендую вам ее использовать.
Потенциально HTTP/2 позволяет применять код и без конкатенации
строк (или, по крайней мере, сократить ее применение), поэтому этап
окончательной сборки кода теперь может быть пропущен. Возможно, вы
захотите использовать эту возможность, чтобы привести развернутый
код в большее соответствие с исходным кодом, ведь необходимости в их
различии уже нет. В таком случае, скорее всего, вы захотите отказаться
от минификации кода. Отказ от нее может немного снизить производительность, но, на мой взгляд, минификация поверх сжатия только зря
повышает ресурсоемкость, поскольку сжатие само по себе эффективно
удаляет повторяющиеся символы (например, пробелы). С другой стороны, минифицированный код труднее читать, и такой вариант доставит
трудности, если вы пытаетесь устранить проблемы в производственной
среде. К сожалению, в средах разработки далеко не все ошибки легко
поддаются устранению. Вы можете добавить карты кода, которые позволят вам сократить негативное влияние минификации на ваш рабочий
код, однако в ходе этого процесса не исключено возникновение некоторых сложностей1.
Рассмотрим преимущества минификации на реальном примере: популярная библиотека Bootstrap v4.0.0 имеет две версии кода CSS и Java­
Script. Сравним минифицированные и оригинальные версии. Для этого
откройте инструменты разработчика в Chrome, найдите столбец Size,
а затем загрузите файл Bootstrap CSS с https://stackpath.bootstrapcdn.
com/bootstrap/4.1.3/css/bootstrap.min.css, как показано на рис. 6.7.

Кнопка Use Large Requests
(Использовать большие
запросы)

Размер передачи
и размер несжатого
ресурса

Рис. 6.7 Просмотр размеров сжатых и несжатых запросов

Если в столбце Size не отображаются оба варианта, нажмите кнопку
Use Large Request Rows. В данном примере в Size показаны два значения:
размер сжатого (21,0 Кб) и несжатого (138 Кб) кода. Однако технически
эти числа несопоставимы: первое значение – это размер переданного
1

https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/.

238

Глава 6

Оптимизация в HTTP/2

сообщения, поэтому оно включает в себя HTTP-заголовки (запроса и ответа), а второе –это исходный размер отправляемого обратно тела. Сами
заголовки имели размер примерно 0,5 Кб (без сжатия). Таким образом,
различия имеют лишь незначительный эффект и могут быть проигнорированы. Если вы загрузите и сожмете файлы по отдельности (как в разделе 6.2.3), значение будет более точным. Однако здесь необходимо знать,
какие настройки gzip используются в командной строке и веб-сервере.
В табл. 6.4 приведены результаты для неминифицированной версии.
Таблица 6.4 Сжатие gzip и минификация CSS-файла Bootstrap (v4.1.3)
Тип сжатия
Bootstrap.css
Bootstrap.min.css
Исходный файл 170 Kб
138 Kб (81 % от размера исходного файла)
gzip
22,8 Kб (13 % от размера исходного файла) 21,0 Kб (12 % от размера исходного файла)

Как видите, неминифицированная версия сжимается до 13 % от размера исходного файла, минифицированная версия – до 12 %. Мы видим
разницу, но она не велика, всего 1 %, или 1,8 Кб. Безусловно, для таких
популярных инструментов, как Bootstrap, на счету каждый байт, и здесь
имеет смысл предоставить пользователям уменьшенную версию библиотеки. Однако для вашего кода экономия может быть не столь значительной.
Экономия зависит от конкретного кода. Например, если для CSS-файла
Bootstrap сжатие не дало значительного результата, то файл Java­Script
того же Bootstrap действительно уменьшился в объеме (см. табл. 6.5).
Таблица 6.5 Сжатие gzip и минификация файла Java­Script Bootstrap (v4.1.3)
Тип сжатия
Bootstrap.js
Bootstrap.min.js
Исходный файл 121 Kб
49,8 Kб (41 % от размера исходного файла)
gzip
20,9 Kб (17 % от размера исходного файла) 14,2 Kб (12 % от размера исходного файла)

Здесь мы наблюдаем большую степень сжатия (полученный в результате файл составляет 17 % от исходного только с помощью gzip, 12 %
с помощью gzip и минификации) и, соответственно, большую эффективность: экономия составляет 5 %, или 6,7 Кб. Зачастую Java­Script содержит
больше комментариев к коду, пробелов и имен переменных, чем CSS; отсюда и полученные нами результаты. Таким образом, за счет минификаиции (без сжатия) размер файла Java­Script сокращается до 41 %, а размер
CSS составляет 81 % от исходного размера. К слову, минифицированные
файлы намного больше файлов, сжатых с помощью gzip, что подтверждает мою точку зрения о том, что gzip (или аналогичный тип сжатия) является лучшим методом оптимизации; конечно, минификацию можно
использовать, ведь она тоже дает дополнительные преимущества.
Минификация намного полезнее для крупных сайтов, владельцы которых обладают необходимым опытом. В табл. 6.6, как и в 6.5, приведены примеры сжатия кода распространенных библиотек веб-разработки.
Особенно сильно разница заметна в минифицированных версиях некоторых библиотек, в частности jQuery и Angular.

Методы повышения веб-производительности все еще актуальны для HTTP/2

239

Таблица 6.6 Степень сжатия некоторых распространенных библиотек
Библиотека
jquery-3.3.1.js
jquery-3.3.1.min.js
angular-1.7.2.js
angular-1.7.2.min.js
bootstrap-4.1.3.css
bootstrap-4.1.3.min.css
foundation-6.4.3.css
foundation-6.4.3.min.css

Размер исходного файла
265 Kб
84,9 Kб
960 Kб
168 Kб
121 Kб
49,8 Kб
158 Kб
118 Kб

Размер сжатого файла
78,9 Kб
30,0 Kб
297 Kб
56,6 Kб
20,9 Kб
14,2 Kб
18,8 Kб
14,7 Kб

Степень сжатия
30 %
35 %
31 %
34 %
17 %
29 %
12 %
12 %

Обфускация кода – это еще одна причина минификации. Однако попытка использовать обфускацию с целью затруднения анализа кода нецелесообразна, так как неминифицированный код довольно тривиален.
Тем не менее удаление комментариев может предотвратить утечку каких-либо неловких вставок от разработчиков (например, комментарии
вроде «Когда-нибудь я исправлю этот ужасный код»). В теории минифицированный код помогает браузеру работать быстрее и эффективнее, хотя уже на первом шаге любого синтаксического анализа код и так
минифицируется, поэтому значительных преимуществ вы можете и не
получить.
Подводя итог, можно сказать, что решение применять минифицацию
кода или нет не зависит напрямую от HTTP/2. Однако, если после перехода на HTTP/2 изменились методы разработки (например, вы стали
меньше применять конкатенацию), возможно, стоит пересмотреть отношение к минификации кода и проследить, актуально ли ее использование в данный момент. Минификация сложнее, чем сжатие файлов
веб-сервером, а при их совместном использовании ее эффективность
становится намного меньше.

6.3.2 Предотвращение повторной отправки данных
с помощью кеширования
«Самый быстрый HTTP-запрос – тот, который не был сделан», так гласит
самая известная цитата о веб-производительности (хотя, я думаю, никто
не помнит, кто сказал это первым1). Целью HTTP/2 является улучшение
производительности отправки HTTP-запросов. Применение кеширования (настолько часто, насколько это возможно) поможет значительно
ускорить данный процесс. В этом случае, если пользователям понадобится какой-либо ресурс, уже используемый ранее, они смогут взять
его из кеша HTTP, что намного быстрее, чем выполнение полного сетевого запроса, независимо от того, используете ли вы протокол HTTP/1,
HTTP/2, или какую-либо будущую версию.
1

Наиболее вероятный кандидат, Стив Содерс, отрицает, что это был он, несмот­
ря на то что цитата часто приписывается именно ему: https://www.stevesouders.com/blog/2012/03/22/cache-them-if-you-can/.

Глава 6

240

Оптимизация в HTTP/2

В HTTP/1 необходимо было использовать кеширование настолько,
насколько это представлялось возможным. Принцип остается таковым
и для HTTP/2. Актуальны и HTTP-заголовки cache-control и expires (хотя
некоторые люди небезосновательно утверждают, что заголовок expires
утратил актуальность, ввиду того, что почти все клиенты сейчас понимают HTTP/1.1, а HTTP/1.0 используется крайне редко, особенно теми
приложениями, которым необходимо кеширование1.
Кеширование может быть сложным. Рациональное и постоянное использование кеширования – это сложная тема, требующая размышлений
и использования методов очистки кеша2. Чаще всего сайты, использующие кеширование общих ресурсов (таблиц стилей, Java­Script, логотипов
и т. д.) более быстрые и отзывчивые, а те, которые его не используют, загружаются медленно, вследствие чего становятся неудобными в использовании. Поскольку кеширование является важной темой, необходимо
разобраться в HTTP-кешировании и том, как работает код ответа 304 (Not
Modified), о котором я кратко упомянул в главе 1.
Полученный HTTP-ответ может включать в себя HTTP-заголовок
cache-control (или устаревший expires), который указывает на то, как
долго ресурс должен считаться действительным. Рассмотрим пример из
реальной практики. Если вы загрузите Wikipedia в браузере, вы увидите
заголовок, как на рис. 6.8.
Снимите флажок Disable cache
(Отключить кеш)

Заголовок
cache-control

Рис. 6.8

Заголовок cache-control в Wikipedia

На этом рисунке показано, что домашнюю страницу Wikipedia можно
кешировать в течение 3600 с (max-age=3600), или 1 ч, после чего ее необходимо повторно проверить перед использованием (mustrevalidate). Также
здесь указано, что промежуточные кеши, такие как прокси, могут кешировать страницу в течение 86 400 с, или 1 дня (s-maxage=86400). Однако
зачастую для поддержки своей актуальности (тема, выходящая за рамки
этой книги) они используют другие методы, поэтому в этом плане данную настройку можно проигнорировать.
1
2

https://www.fastly.com/blog/headers-we-dont-want.
https://css-tricks.com/strategies-for-cache-busting-css/.

Методы повышения веб-производительности все еще актуальны для HTTP/2

241

Убедитесь, что флажок Отключить кеш, показанный на рисунке, снят.
Затем пролистайте страницу назад и вперед, после чего вы увидите чтото похожее на рис. 6.9.

Рис. 6.9

Wikipedia загружается из дискового кеша

Как и ожидалось, веб-сайт был загружен из кеша диска, и это отражено в столбце Size. Если вы видите надпись from memory cache, а не from disk
cache, вероятно, вы перешли с другой страницы Wikipedia, а не с другого
сайта, и поэтому эти ресурсы находятся в свежем кеше памяти, однако
принцип остается неизменным. На рис. 6.9, также представлен старый
кешированный ответ с кодом состояния 200 (выделен темным цветом,
чтобы показать, что это кешированный ответ, который трудно увидеть
на рисунке).
Ради интереса подождите час, пока истечет срок действия кеша,
а затем повторите эксперимент. Чтобы сэкономить время, перезагрузите страницу (что даст тот же эффект). Вы увидите что-то похожее на
рис. 6.10.

Рис. 6.10

Перезагрузка Wikipedia из устаревшего кеша

На этом рисунке вместо кода ответа 200 мы видим код 304. Если вы
перезагрузите это окно, вы также увидите, что Chrome использовал кеш

Глава 6

242

Оптимизация в HTTP/2

изображений в памяти для изображений, а не HTTP-кеш диска, как было
на рис. 6.9. Ответ 304 появляется потому, что браузер выполнил условный
запрос GET, как показано на рис. 6.11.

Рис. 6.11 Условный запрос GET

Браузер нашел домашнюю страницу в кеше, обнаружил, что она устарела, и отправил новый запрос: «Отправьте мне новую версию страницы
(if-modified-Since) или значение eTag (if-none-match), которое вы отправляли со страницей в последний раз». Значение eTag позволяет проанализировать актуальность кешированной страницы не только по дате. Оно зависит от реализации и может быть, например, хешем содержимого. Если
указаны оба значения (как на рис. 6.11), приоритет имеет значение eTag,
указанное в заголовке if-none-match. Сервер совершает проверку и видит,
что страница не изменилась, затем отправляет ответ 304, чтобы сказать,
что копия, имеющаяся в веб-браузере, все еще актуальна. Ответ 304 не
имеет тела, поэтому он загружается быстрее, чем полноценный ресурс.
Ответы 304 так и остались медленными; на протяжении всего своего
пути они будут требовать сетевых запросов. В HTTP/2 цикл прием-передачи стал менее затратным (но не полностью!). Однако при использовании HTTP/1 затраты на отправку ответа 304 были почти такие же, как
отправка полного ответа 200, поэтому ответы 304, возможно, использовались не так часто, ведь HTTP-запросы были относительно ресурсоемкими. Многие веб-сайты вообще не кешируют свои HTML-страницы.
В таких случаях каждый раз, когда вы переходите на домашнюю страницу, браузер заново загружает ее, а затем использует кеш только для доступа к необходимым ему ресурсам. Отсутствие кеширования ресурсов
веб-страницы может замедлить просмотр веб-сайта. Если вы находитесь
на домашней странице и нажимаете на другую страницу, а затем хотите
вернуться обратно на домашнюю страницу, перезагрузка должна быть
мгновенной, но часто случается небольшая задержка. Директива cache
control на всех веб-страницах сайта сделает его более отзывчивым (по-

Методы повышения веб-производительности все еще актуальны для HTTP/2

243

скольку они будут загружаться из кеша), а также поможет сэкономить
пропускную способность (за счет использования ответов 304 даже после
истечения срока действия кеша).
Важно понимать, что отправка ответов 304 по-прежнему требует затрат, поэтому я не предлагаю вам использовать их как замену кешированию. Однако можно применять их как замену некоторых ресурсов,
которые вы не кешировали. Когда я делал скриншоты для предыдущих
рисунков, я изо всех сил пытался найти веб-сайт, который кешировал
бы саму веб-страницу, как это делает Wikipedia. Новостным сайтам и социальным сетям очень важно выдавать пользователю только актуальные
страницы. Для этого есть и другие, более эффективные способы загрузки контента на страницу, чем использование предварительно сгенерированного HTML, который не следует кешировать с помощью Java­Script
AJAX. Я считаю, что веб-сайты должны кешировать страницы на недолгий период, по крайней мере при использовании HTTP/2, потому что ответ 304 менее затратен, чем ответ 200.
Согласно многим рекомендациям по HTTP-кешированию, необходимо использовать именно долговременное кеширование, чтобы сэкономить ресурсы при последующих входах на сайт. Данный аспект, возможно, не так важен, как использование кеширования для просмотра
сайта в текущем сеансе, где сайт будет отзывчивым для пользователя.
Использование кеширования для оптимизации просмотра сайта непосредственно во время сеанса хорошо работает и с краткосрочным кешированием. Кроме того, краткосрочные кеши помогут избежать частого
проведения процедуры очистки кеша. Точно так же использование кеширования на стороне сервера для предотвращения запросов пограничного сервера к внутреннему серверу может привести к значительным
улучшениям, даже при краткосрочном кешировании1.
Одним из других недостатков краткосрочного кеширования является
то, что ресурсы могут быть удалены из кеша, ввиду того что браузер счел
их недействительными. Лучше «использовать долгосрочное кеширование и время от времени перепроверять и подтверждать актуальность ресурсов», но такой возможности у нас нет. Марк Ноттингем (Mark Nottingham), сопредседатель рабочей группы HTTP2, предложил использовать
опцию Stale-While-Revalidate3, которая делает такой сценарий возможным, однако пока что ни один браузер не воспользовался этим предложением.
В заключение стоит сказать, что HTTP/2 не меняет параметры кеширования напрямую, но снижение затрат на осуществление сетевых запросов может привести к переоценке стратегий кеширования. Кроме того,
было предложено внести изменения в HTTP/2 push, которые позволили
бы обновлять кеши (см. главу 5), однако в настоящее время осуществить
такое невозможно.
1
2
3

https://www.nginx.com/blog/benefits-of-microcaching-nginx/.
https://httpwg.org/.
https://www.mnot.net/blog/2014/06/01/chrome_and_stale-while-revalidate.

Глава 6

244

Оптимизация в HTTP/2

6.3.3 Снижение нагрузки на сеть посредством
сервис-воркеров
Сервис-воркеры1 (Сценарии, выполняемые в фоновом режиме в браузере
пользователя. – Прим. ред.) появились относительно недавно, но они уже
доступны во всех современных браузерах, кроме Internet Explorer 112. Они
являются одним из способов запуска прокси-сервера Java­Script, который
находится между веб-страницей и сетью, как показано на рис. 6.12.
Веб-страница

Сервис-воркер

Internet

Веб-сервер

Веб-браузер

Рис. 6.12 Сервис-воркеры

Сервис-воркеры могут просматривать и менять HTTP-запросы, а также отвечать на них. Также с их помощью можно обеспечить аналогичные возможности в нативных мобильных приложениях, в том числе
в автономном режиме. Даже если кешируется сама веб-страница, при
перезагрузке она будет пытаться подключиться к веб-сайту, чтобы
проверить, действительна ли кешированная версия и перезагрузка не
удастся, если нет сетевого подключения. В мобильных приложениях, которые не позволяют производить обновление в офлайн-режиме, такого
обычно не происходит. Если сервис-воркер используется на веб-сайте,
он может прерывать запросы и в автономном режиме возвращать ранее
кешированную версию ресурса. Данный метод позволяет кешированному сайту загружаться даже в автономном режиме, как в мобильных
приложениях.
Сервис-воркеры предоставляют много интересных возможностей для
оптимизации HTTP на стороне веб-разработки. Вы можете использовать кратковременное кеширование, но при этом не удалять элементы
из кеша сервис-воркера даже после истечения срока его актуальности.
Они позволят вам использовать ответ 304 чаще и не прибегать к долгосрочному кешированию. Использование сервис-воркеров не меняется
при переходе на HTTP/2, и вообще, им стоит посвятить отдельную книгу,
поэтому я не думаю, что сейчас нужно рассказывать о них что-то еще.
Однако мне бы очень хотелось, чтобы в течении следующих нескольких
лет они стали более популярными, поскольку сервис-воркеры предоставляют мощные возможности для обработки HTTP-запросов.

1
2

https://developers.google.com/web/fundamentals/primers/service-workers/.
https://caniuse.com/#feat=serviceworkers.

Методы повышения веб-производительности все еще актуальны для HTTP/2

245

6.3.4 Не отправляйте то, что вам не нужно
Продолжая тему отказа от ненужных HTTP-запросов, отмечу, что еще одним способом оптимизации производительности улучшением является
отправка только действительно необходимых данных. Хотя этот момент
может показаться очевидным, существует множество причин, ввиду которых вы можете отправлять данные, которые на самом деле не используются.
Методы, упомянутые в разделе 6.2 – объединение файлов и спрайтинг
изображений, – зачастую инициируют отправку большего количества
данных, что может быть остро необходимо для уменьшения количества
HTTP-запросов. Поскольку в HTTP/2 необходимость в сокращении количества запросов меньше, вы можете пересмотреть эти методы с точки
зрения уменьшения объема данных. Возможно, вам будет удобнее продолжать использовать их, если вы интегрировали их в процесс сборки
версий веб-сайтов и, следовательно, не видите острой необходимости
их удалять. Однако, возможно, используя эту технику, вы отправляете
больше файлов, чем нужно на самом деле.
Ненужные ресурсы могут загружаться и во время многих других
процессов. Например, существует вероятность, что вы загружаете изображения, которые не отображаются в мобильной версии, но на самом
деле равно присутствуют на веб-сайте. HTTP/2 не вносит ограничение
на отправку ненужных ресурсов. Фактически он лишь добавляет новый
способ отправки (HTTP/2 push), который требует особого внимания, как
обсуждалось в главе 5.
Может казаться, что в HTTP/2 загрузка ресурсов происходит очень
быст­ро, но на самом деле этот протокол никак не решает проблему отправки большого количества данных (не считая сжатия HTTP-заго­лов­
ков). Именно поэтому сайтам необходимо продолжать контролировать
загрузку данных и загружать только самое необходимое.

6.3.5 Подсказки для ресурсов HTTP
В главе 5 мы говорили о подсказке, предлагающей серверу осуществить
предварительную загрузку. Она является лишь одной из многих подобных
подсказок1, касающихся ресурсов, которые могут быть полезны для дальнейшей оптимизации использования HTTP. Данные подсказки актуальны
как для HTTP/2, так и для HTTP/1. Для HTTP они реализованы в виде заголовков, а для HTML в виде элементов . Несмотря на то что подсказки
существуют уже довольно давно, поддержку они получили лишь недавно.
Они представляют собой новые способы дополнить HTTP/2.

Предварительная выборка DNS
Данная подсказка использовалась сайтом Amazon, как показано в примерах из главы 2. Она находится в разделе HEAD кода домашней страницы:
1

https://w3c.github.io/resource-hints/.

Глава 6

246

Оптимизация в HTTP/2



Как следует из самого названия подсказки, она побуждает сервер выполнять поиск соединения в DNS еще до того, как оно потребуется, что
значительно экономит время (см. строку 17 на рис. 6.13).

Рис. 6.13

Предварительная выборка DNS на практике

Пусть предварительная выборка DNS и экономит совсем немного
времени, но зато для ее реализации требуется меньше строк кода. Также ее поддерживают все основные браузеры (однако время поддержки
ограничено)1. DNS-запросы имеют ограниченное время жизни (TTL –
Time to Live), поэтому иногда веб-сайтам не следует искать домен раньше
времени (например, тот, который будет использоваться исключительно
для дальнейшего перехода на другие страницы веб-сайта), так как поиск
может повториться, если TTL истечет. Обычно TTL составляет 300 или
больше секунд, и в идеале загрузка ваших страниц занимает не более
5 мин, поэтому использовать подсказки для текущей страницы безопасно. Выполнять предварительную выборку DNS перед ссылкой на ресурс
нет смысла, так как эта ссылка все равно будет выполнять поиск DNS,
поэтому подсказки следует выполнять только для ресурсов, обнаруженных позднее. Данный метод наиболее полезен для соединения, необходимого зависимому ресурсу, который не будет отображен после синтаксического анализа HTML. Большинство веб-сайтов загружает контент из
разных доменов, поэтому использование этого заголовка обеспечивает
хорошие результаты.

Предварительная установка соединения
Предварительная установка соединения продвигает концепцию предыдущей подсказки на один шаг вперед. Она не только выполняет поиск
DNS, но и устанавливает соединение, что может сэкономить затраты на
согласование TCP и HTTPS, связанные с установкой новых подключений.
Данную подсказку поддерживает большинство современных браузеров2.
Однако ее следует использовать в определенное время; если вы воспользуетесь ей слишком рано, алгоритм медленного запуска TCP сработает
после периода бездействия, или, что еще хуже, соединение будет разор­
вано (см. главу 9).
Как и предварительная выборка DNS, предварительная установка
соединения полезна при загрузке критических ресурсов из других доменов.

1
2

https://caniuse.com/#feat=link-rel-dns-prefetch.
https://caniuse.com/#feat=link-rel-preconnect.

Методы повышения веб-производительности все еще актуальны для HTTP/2

247

Предварительная выборка
Предварительная выборка осуществляет запрос ресурсов с низким приоритетом. В отличие от предварительной загрузки (которую мы обсудим
далее), которая нацелена на ускорение загрузки текущей страницы, предварительная выборка обычно используется для последующих переходов
по веб-страницам; поскольку ресурсы получены с низким приоритетом,
они не будут использоваться, пока не загрузится текущая страница. Загружаемые с помощью этой подсказки ресурсы хранятся в кеше и уже
готовы к использованию. Подсказка поддерживается большинством современных браузеров1, кроме Safari (на момент написания этой книги).
Я считаю, что из-за HTTP/2 в подсказку предварительной выборки не
будет внесено каких-либо изменений.

Предварительная загрузка
Метод предварительной загрузки подсказывает браузеру загрузить ресурс для конкретной страницы с высоким приоритетом. Данная подсказка является следующим локальным шагом после предварительной
установки соединения, но, в отличие от нее, он предназначен именно
для ресурсов на странице. Веб-браузеры довольно хорошо умеют сканировать HTML и загружать все необходимые ресурсы, но предварительная
загрузка позволяет загрузить ресурсы, которые не включены непосредственно на страницу (например, шрифты, указанные в файлах CSS) заранее. Распространение данной подсказки заняло долгое время, однако
сейчас ее поддерживает большинство современных браузеров2.
Особенно актуальна предварительная загрузка для HTTP/2 push (как
обсуждалось в главе 5). По большей части так произошло, потому что
данная подсказка была создана именно для этой цели, а не выступала
предлагаемым вариантом использования. Учитывая сложность HTTP/2
push, подсказка предварительной загрузки (без HTTP/2 push) может оказаться наиболее простым вариантом. Сегодня многие люди рекомендуют
использовать предварительную загрузку вместо HTTP/2 push, и в этом
случае обязательно используйте атрибут nopush при использовании заголовка ссылки (нет необходимости использовать его с версией HTML, поскольку веб-серверы не используютего как указание отправить ресурс).
Предварительная загрузка может принести еще большую пользу, если
код HTTP-ответа 103 Early Hints (обсуждаемый в главе 5) станет более распространенным, поскольку он может содержать заголовки ссылок предварительной загрузки HTTP (даже в HTTP/1.1).

Предварительная визуализация
Предварительная визуализация является самой затратной подсказкой.
Она позволяет загружать и выполнять предварительную визуализацию
1
2

https://caniuse.com/#feat=link-rel-prefetch.
https://caniuse.com/#feat=link-rel-preload.

Глава 6

248

Оптимизация в HTTP/2

целых страниц (включая все необходимые ресурсы). Ключевой идеей
этой подсказки является то, что, если можно надежно оценить навигацию по следующей странице, страница может быть загружена мгновенно. На момент написания этой книги предварительную визуализацию
поддерживают только Chrome и IE 111, хотя Chrome стремится отказаться от использования данной подсказки и, возможно, скоро сделает это2.
Риск чрезмерного использования предварительной визуализации довольно высок, что приводит к трате пропускной способности и увеличивает время обработки для клиента. Я считаю, что для HTTP/2 в подсказку предварительной визуализации не будут внесены изменения.
Кроме того, вряд ли она станет приоритетной для реализации в других
браузерах.

6.3.6 Сокращение задержки «последней мили»
Цель HTTP/2 – сократить влияние задержки за счет того, что во время выполнения HTTP-запроса TCP-соединение может использоваться
и для других запросов. Однако HTTP/2 не решил проблему задержки,
и нам все так же следует предпринимать различные меры для ее сокращения. Веб-серверы обычно подключены к Internet с помощью высокоскоростной инфраструктуры с широкой полосой пропускания, но
посетители веб-страниц часто подключаются через менее надежные
соединения, например через широкополосные и мобильные сети. «Последняя миля» – это канал, соединяющий конечное оборудование с инфраструктурой провайдера, и часто задержка здесь особенно сильно
влияет на соединение.
Самый простой способ решить эту проблему – расположиться как
можно ближе к браузеру, что для глобальных веб-сайтов обычно предполагает наличие сети локальных серверов рядом с вашей пользовательской базой. Такая сеть может быть локальной или (что встречается все
чаще) сетью CDN. Большинство CDN поддерживает HTTP/23. Учитывая
сложности обновления вашего веб-сервера для его поддержки и более
высокие требования HTTPS для него, CDN для вашего веб-сервера – простой вариант для перехода на HTTP/2 (как упоминалось в главе 3), а также гарантия сокращения задержки.

6.3.7 Оптимизация HTTPS
Во всем мире разработчики постепенно переходят на HTTPS. Новые
функциональные возможности, такие как протокол HTTP/2, требуют
поддержки HTTPS. Так происходит по ряду причин, как технических, так
и идеологических, поскольку люди, разрабатывающие ключевые компоненты Internet (создатели браузеров, рабочая группа HTTP и т. д.), счита1
2
3

https://caniuse.com/#feat=link-rel-prerender.
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/0nSxuuv9bBw.
http://cdncomparison.com/.

Методы повышения веб-производительности все еще актуальны для HTTP/2

249

Процент загрузок страниц через HTTPS
(скользящее среднее значение за 14 дней)

ют, что все в этом мире должно быть зашифровано. Изначально HTTPS
использовался только определенными веб-сайтами или на определенных веб-страницах этих сайтов, но сейчас веб-сайты переходят на использование HTTPS на всех страницах.
Популярность HTTPS за последние несколько лет значительно возросла ввиду появления множества бесплатных центров сертификации, таких как Let’s Encrypt1. Кроме того, HTTPS активно продвигают создатели
браузеров Chrome2 и Mozilla Firefox3. Динамика приведена на рис. 6.144.
Таким образом, HTTP/2 дает еще больше причин для перехода на такой
тип соединений.

%
80 %
%
70 %

Все пользователи
Германия
США
Япония

%
60 %
%
50 %
%
40 %
%
30 %
%
20 %
10 %
%
%
0%

Jan
Янв
2014

Jul
Июль
2014

Jan
Янв
2015
2015

Jul
Июль
2015
2015

Jan
Янв
2016
2016

Jul
Июль
2016
2016

Jan
Янв
2017
2017

Jul
Июль
2017
2017

Jan
Янв
2018
2018

Jul
Июль
2018
2018

Рис. 6.14 Рост популярности HTTPS за последние несколько лет по данным Let’s
Encrypt (на основе статистики телеметрии Firefox)

Во время установки HTTP-соединения при загрузке веб-страниц возникают дополнительные задержки. После установки сеанса задержки
минимальны, поскольку вычислительные ресурсы, необходимые для
шифрования и дешифрования трафика, незначительны даже для мобильных устройств, однако это влияет на начальное время соединения.
Чтобы попробовать подключиться заранее и уменьшить эффект для любых зависимых доменов, вы можете воспользоваться предварительной
установкой соединения (эту подсказку мы обсуждали в разделе 6.3.5).
Однако при первом подключении такой метод не сработает.
Для того чтобы сократить время, необходимое на установку HTTPSсоединения, обеспечить высокий уровень безопасности и предотвратить
появление предупреждений от браузера, важно убедиться, что настройка
HTTPS оптимизирована. Данный аспект важен для всех сайтов, исполь1
2
3

4

https://letsencrypt.org/.
https://blog.chromium.org/2018/02/a-secure-web-is-here-to-stay.html.
https://blog.mozilla.org/security/2017/01/20/communicating-the-dangers-ofnon-secure-http/.
https://letsencrypt.org/stats/#percent-pageloads.

Глава 6

250

Оптимизация в HTTP/2

зующих HTTPS (и, следовательно, всех сайтов, использующих HTTP/2),
поскольку он означает, что вы используете HTTPS из-за требований браузера. Ниже приведены рекомендации по обеспечению оптимального
использования HTTPS (с HTTP/1 или HTTP/2):
„„ во избежание предупреждений о смешанном содержимом убедитесь,
что вы загружаете исключительно HTTPS-ресурсы. В политике безопасности контента вы можете использовать конфигурацию upgradeinsecure-requests1, поскольку она поддерживается большинством
браузеров2;
„„ убедитесь, что ваш сертификат HTTPS вовремя обновляется. В случае, если у вашего сертификата истечет срок действия, доступ
к сайту будет закрыт. Раньше обновление выполнялось вручную,
но сейчас благодаря Let’s Encrypt процесс стал автоматическим.
Дело в том, что Let’s Encrypt допускает только 90-дневные кратковременные сертификаты, ввиду чего автоматизация просто неизбежна, ведь выполнение этого процесса вручную намного дольше
и требует больших затрат;
„„ регулярно проверяйте настройку HTTPS. Протоколы HTTPS, шифры и конфигурация часто меняются, при этом добавляются новые
параметры, а старые параметры становятся менее безопасными по
мере увеличения вычислительной мощности. С помощью онлайнинструмента SSLLabs Server Test3 вы можете всесторонне протестировать настройки HTTPS, а также узнать про известные уязвимости
и передовые методы в мире HTTPS. Оценка A гарантирует, что у вас
нет проблем с HTTPS, а регулярное сканирование (не реже одного
раза в квартал) для поддержания этой оценки позволит вам избежать неприятных сюрпризов;
4
„„ реализуйте возобновление сеанса защиты транспортного уровня
(TLS). Установка рукопожатия TLS требует значительного времени
и усилий, поэтому его оптимизация имеет решающее значение.
Один из лучших способов сделать это – разрешить возобновление
сеанса TLS, тогда для каждого нового соединения не нужно будет
устанавливать новое рукопожатие. В HTTP/2 следует использовать
меньше соединений, но для более поздних или дополнительных
соединений (например, сертифицированных и несертифицированных соединений) производительность может быть выше. Возобновление сеанса TLS может привести к некоторым проблемам
с безопасностью5, поскольку при последующем повторном подклю1
2
3
4

5

https://www.w3.org/TR/upgrade-insecure-requests/.
https://caniuse.com/#feat=upgradeinsecurerequests.
https://www.ssllabs.com/ssltest/.
https://calendar.perfplanet.com/2014/speeding-up-https-with-session-resumption/.
https://timtaubert.de/blog/2014/11/the-sad-state-of-server-side-tls-session-resumption-implementations/.

Методы повышения веб-производительности все еще актуальны для HTTP/2

251

чении соединение HTTPS может быть слабее (хотя TLSv1.3 решает
большинство этих проблем). Так или иначе большинство веб-сайтов
по-прежнему стремится реализовать возобновление TLS с целью
получить значительный прирост производительности;
„„ не переусердствуйте с безопасностью. Конечно, безопасность
важна, но тем не менее всегда должен быть баланс безопасности
и производительности. Если вы разрешите использовать только
последний протокол TLS и шифрование с самыми надежными настройками, ваш веб-сайт будет крайне медленным, и более того,
многие пользователи не смогут получить доступ к нему. На момент написания этой книги для обеспечения безопасности достаточно ключа RSA 2048, TLSv1.2 и TLS_ECDHE_RSA_WITH_AES_
128_GCM_SHA256; кроме того, их поддерживает большинство браузеров. С течением времени ситуация изменится, но одно правило остается неизменным – выбирайте разумный уровень безопасности. Для создания соответствующей конфигурации для общих
веб-серверов вы можете воспользоваться Mozilla Configuration
Generator1, а для сканирования настроек других сайтов инструментом SSLLabs. Я часто сканирую сайт ssllabs.com при помощи их
собственного инструмента и думаю, что SSLLabs лучше всех знает,
как его настроить;
„„ подумайте, какая настройка HTTPS подходит вам лучше всего. HTTPS –
это все же очень сложно. Зачастую, вместо того чтобы управлять его
настройкой самостоятельно, намного проще воспользоваться будет гарантировать пользователям стабильно безопасную настройку
HTTPS. Однако ключи своей настройки HTTPS можно передавать
только доверенной стороне; в противном случае весь смысл процесса теряется!
„„ когда TLSv1.3 станет доступен, воспользуйтесь им. Данный протокол был стандартизирован в августе 2018 года2, но на данный момент он может быть недоступен для многих читателей. Протокол
может обеспечить гораздо больший прирост производительности
(и безопасности) по сравнению с предыдущими версиями3.
Совсем скоро HTTPS войдет по всеобщее употребление. Его уже поддерживают многие сайты, и они могут пользоваться всеми приведенными выше советами. Одной из причин перехода большинства сайтов на
HTTPS является HTTP/2, требующий использования только безопасных
соединений. HTTPS требует настройки, решение относительно которой
следует принимать непосредственно владельцам веб-сайтов. Любому
создателю веб-сайта следует всерьез рассмотреть все упомянутые выше
аспекты независимо от того, использует ли он HTTP/2.
1
2
3

https://mozilla.github.io/server-side-tls/ssl-config-generator/.
https://tools.ietf.org/html/rfc8446.
https://blog.cloudflare.com/rfc-8446-aka-tls-1-3/48.

Глава 6

252

Оптимизация в HTTP/2

6.3.8 Методы повышения веб-производительности,
не связанные с HTTP
В этом разделе мы говорим преимущественно о советах и методах, связанных с передачей ресурсов через HTTP. Однако многие другие способы
улучшения веб-производительности не связаны с тем, как загружаются
данные. В частности, замедлить прорисовку веб-сайта может неэффективный Java­Script. Также загрузка множества рекламных сетей и трекеров может занять ресурсы, необходимые вашему сайту. А серверам
с низкими вычислительными характеристиками может быть сложно
справиться с объемом запросов, получаемых веб-сайтом. Все эти аспекты выходят далеко за рамки данной книги, но ошибочно полагать, что,
если вы оптимизируете использование HTTP посредством применения
некоторых из приведенных здесь советов и приемов, у вас никогда не
возникнет проблем с производительностью.
Технологии повышения производительности, связанные с HTTP, важны, и любые ресурсы о веб-производительности (книги, блоги или лекции) в значительной степени концентрируют на них свое внимание.
Однако данные технологии не являются конечной целью веб-про­из­во­
дительности. Поэтому оптимизируйте использование HTTP разумно,
ведь вы можете получить больше преимуществ посредством оптимизации других областей веб-сайта или веб-приложения.

6.4

Оптимизация и для HTTP/1.1, и для HTTP/2
Учитывая неплохую поддержку браузерами1, HTTP/2 должен быть доступен большинству пользователей. Однако некоторые пользователи не используют HTTP/2, так как пользуются старыми браузерами или старыми
устройствами, на которых обновление браузеров уже не поддерживается
(например, старые мобильные телефоны). Также понижать уровень соединения между браузером и сервером могут прокси (включая антивирусное программное обеспечение). Что же делать этим пользователям,
если на своем сайте вы используете какой-либо из методов, относящихся
к HTTP/2, описанных в этой главе (например, минимизация конкатенации)? В лучшем случае веб-сайт будет доступен для таких пользователей,
даже если вы начнете оптимизацию HTTP/2. В худшем случае при отказе
от оптимизации HTTP/1 сайт будет работать медленнее. Однако так быть
не должно. На самом деле вы можете оптимизировать свой веб-сайт как
для HTTP/1, так и для HTTP/2.

6.4.1 Измерение трафика HTTP/2
Первое, что вам необходимо сделать, – это измерить объем трафика, который использует каждый протокол. Я предполагаю, что на этом этапе
1

https://caniuse.com/#feat=http2.

Оптимизация и для HTTP/1.1, и для HTTP/2

253

вы уже перешли на HTTP/2, но еще не до конца поменяли свой сайт, поэтому оптимизация HTTP/1.1 все еще присутствует. Если подавляющая
часть вашего трафика уже направлена на HTTP/2, нет смысла беспокоиться о трафике HTTP/1. Веб-сайт по-прежнему будет работать, однако
уже немного медленнее, чем нужно.
Самый простой способ измерить трафик HTTP/2 – зарегистрировать
его в журналах вашего веб-сервера. В Apache вы можете добавить эти
данные в директиву LogFormat, обычно хранящуюся в основном файле
httpd.conf (или в некоторых дистрибутивах в файле apache2.conf). Для
этого в LogFormat необходимо добавить параметр %H:
LogFormat "%h %l %u %t %{ms}T %H \"%r\" %>s %b \"%{Referer}i\"
"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x
%{Content-Encoding}o %{H2_PUSHED}e" combined
CustomLog /usr/local/apache2/log/ssl_access_log combined

В журналах доступа вы увидите следующее:
78.17.12.1234 - - [11/Mar/2018:22:04:47 +0000] 3 HTTP/2.0 "GET / HTTP/2.0"
200 1847 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186
Safari/537.36" TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 br

Таким образом, мы видим, что протокол (HTTP/2.0) регистрируется еще
до запроса (GET / HTTP/2.0). Поскольку Apache печатает в журналах запрос
в формате HTTP/1, протокол можно получить в строке запроса (%r), но,
вероятно, проще указать его отдельно в файле журнала, используя %H, таким образом вам будет легче проводить анализ.
У nginx регистрация протокола происходит с помощью переменной
среды $server_protocol:
log_format my_log_format '$remote_addr - $remote_user [$time_local] '
'$server_protocol "$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /usr/local/nginx/nginx-access.log my_log_format;

Если вы пользуетесь каким-либо другим веб-сервером, найдите информацию об этом в его документации.

Каково значение пограничного сервера?
Если вы используете балансировщик нагрузки перед несколькими веб-сер­
верами, вам может потребоваться измерить трафик протокола в самом балансировщике. Процесс измерения зависит от типа вашего балансировщика
нагрузки.
Балансировщик нагрузки HTTP (также называемый балансировщиком нагрузки уровня 7, следуя модели OSI, упомянутой в главе 1) завершает HTTPсоединение и устанавливает другое HTTP-соединение от балансировщика
нагрузки к веб-серверу. Следовательно, если вы измеряете этот протокол на
веб-сервере, его журналы показывают протокол, используемый для соединения балансировщика нагрузки с веб-сервером, однако он может не совпа-

254

Глава 6

Оптимизация в HTTP/2

дать с основным соединением (клиент – балансировщик нагрузки), которое
вас, вероятно, интересует больше всего. В этом случае вам следует измерять
трафик протокола на балансировщике нагрузки, а не на веб-сервере.
Балансировщик нагрузки TCP (также известный как балансировщик нагрузки уровня 4) работает на уровне TCP и пересылает полезную нагрузку
пакетов TCP (HTTP-сообщения) на следующие веб-серверы. Следовательно,
HTTP-сообщения являются исходными сообщениями, и протокол можно измерить на уровне веб-сервера.
Так называемый пограничный сервер является точкой входа для пользователя интересующего вас протокола (в данном случае HTTP), поэтому, когда
вы пытаетесь измерить свой трафик, важно хорошо знать инфраструктуру
и выбрать соответствующее место для измерения.

При журналировании использования протоколов вы можете проанализировать журналы и увидеть процент трафика каждого из протоколов.
Если вы работаете в системе на базе Linux или UNIX, вы можете воспользоваться комбинациями grep, sort и uniq:
$ grep -oh 'HTTP\/[0-9]\.[0-9]*' ssl_access_log | sort | uniq -c
196 HTTP/1.0
1182 HTTP/1.1
5977 HTTP/2.0

Здесь мы видим наличие трафика HTTP/1.0 (который зачастую создается ботами). Трафик HTTP/1.1 составляет 16 %, а HTTP/2 – 81 %.

6.4.2 Отслеживание поддержки HTTP/2 на стороне сервера
Если вы предполагаете, что пользователи, использующие HTTP/1, попрежнему составляют значительную часть вашего веб-трафика, вы можете определить, выполняется ли текущее соединение через HTTP/1.1
или HTTP/2, и создать для каждого типа разные ответы. Пользователи
HTTP/1 могут иметь полностью объединенные ресурсы и даже загружать
ресурсы из сегментированных доменов, тогда как пользователи HTTP/2
могут получать меньше объединенных ресурсов и загружать все из основного домена.
Чтобы обрабатывать два протокола по-разному, необходимо знать,
какой протокол использует входящее соединение. Как и при измерении
использования протокола, вам необходимо измерить трафик протокола
на вашем пограничном сервере (см. сноску выше, «Каково значение пограничного сервера?»), что зависит непосредственно от его возможностей. Возможно, данную информацию необходимо будет передать далее
по соединению.
Большинство веб-серверов устанавливает переменные среды, которые можно использовать для принятия решений и изменения конфигурации. Сценарии CGI и PHP могут обращаться к переменной среды
SERVER_PROTOCOL, для которой необходимо установить значение HTTP/1.1
или HTTP/2.0 в зависимости от ситуации.

Оптимизация и для HTTP/1.1, и для HTTP/2

255

Некоторые веб-серверы устанавливают дополнительные переменные.
В табл. 6.7 приведены переменные среды1, которые можно использовать
на сервере Apache.
Таблица 6.7 Переменные среды Apache HTTP/2
Имя переменной
HTTP2
H2PUSH
H2_PUSH
H2_PUSHED
H2_PUSHED_ON
H2_STREAM_ID
H2_STREAM_TAG

Тип значения Описание
Флажок
HTTP/2 уже используется
Флажок
Для этого соединения включен push-сервер HTTP/2, который также
поддерживается клиентом
Флажок
Альтернативное название H2PUSH
Строка
Переменная пуста или имеет значение PUSHED для запроса,
отправляемого сервером
Число
Определяет номер потока HTTP/2, который инициировал отправку
запроса
Число
Номер потока HTTP/2 запроса
Строка
Представляет собой уникальный идентификатор потока процесса
HTTP/2, состоящий из идентификатора соединения и идентификатора
потока, разделенных дефисом

Данные переменные среды доступны для конфигурации Apache, а также для сценариев CGI и PHP. Их также можно использовать в директивах
LogFormat, но тогда нужно добавить часть %{H2_ PUSHED}e в пользовательский формат журнала, как я сделал ранее, для отслеживания push- ресурсов в файлах журнала.
Веб-сервер nginx имеет переменную $http22, для которой установлено
значение h2, если HTTP/2.0 используется через HTTPS (как и все соединения браузера), или h2c, когда используются незашифрованные соединения HTTP/2. На момент написания этой книги nginx не предоставляет
дополнительных переменных среды, например функцию push.
Многие также используют Apache и nginx в качестве обратных проксисерверов и для осуществления прокси-запросов к нижестоящему приложению (например, Node или серверу приложений на основе Java, например Tomcat). В Apache используют директиву ProxyPass:
ProxyPass /webapplication/ http://localhost:3000/

В этом сценарии из-за этого у систем ниже по потоку не будет доступа
к переменным среды Apache. Однако можно установить дополнительные HTTP-заголовки для информирования нижестоящей системы с помощью директивы RequestHeader:
#Определение переменной HTTP_VERSION, так как в Apache ее нет
#(SERVER_PROTOCOL и Request_Protocol не являются переменными полного окружения)
SetEnvIf Request_Protocol "(.*)" HTTP_VERSION=$1
#Затем используем эту переменную для установки заголовка HTTP для нижестоящих систем
RequestHeader set protocol "%{HTTP_VERSION}e"
#Добавляем некоторые преднастроенные переменные HTTP2
1
2

https://httpd.apache.org/docs/2.4/mod/mod_http2.html#envvars.
http://nginx.org/en/docs/http/ngx_http_v2_module.html#variables.

Глава 6

256

Оптимизация в HTTP/2

RequestHeader set http2 %{HTTP2}e
RequestHeader set h2push %{H2PUSH}e
ProxyPass /webapplication/ http://localhost:3000/

Приложения ниже по потоку могут читать эти HTTP-заголовки, как
и любые другие.
Аналогичный синтаксис для nginx:
location /webapplication/ {
proxy_set_header protocol $server_protocol;
proxy_set_header http2 $http2;
proxy_pass http://localhost:3000;
}

Используя сервер Node в качестве примера, вы можете вернуться
к простому серверу из главы 5 и добавить две дополнительные строки
для регистрации поддержки HTTP/2, как показано в листинге 6.1.
Листинг 6.1 Сетевой узел с проверкой HTTP-заголовка
'use strict'
var http = require('http')
const port = 3000
const requestHandler = (request, response) => {
const { headers } = request;
console.log('HTTP Version: ' + headers['protocol'])
console.log('HTTP2 Support: ' + headers['http2'])
console.log('HTTP2 Push Support: ' + headers['h2push'])
response.setHeader('Link',';rel=preload')
response.writeHead(200, {"Content-Type": "text/html"})
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('\n')
response.write('Test\n')
response.write('\n')
response.write('\n')
response.end();
}
var server = http.createServer(requestHandler)
server.listen(port)
console.log('Server is listening on ' + port)

При посещении /webapplication/ из браузера через правильно настроенный сервер Apache в журналы узла выводится следующее:
HTTP Version: HTTP/2.0
HTTP2 Support: on
HTTP2 Push Support:on

Оптимизация и для HTTP/1.1, и для HTTP/2

257

Браузеры HTTP/1 выводят следующее:
HTTP Version: HTTP/1.1
HTTP2 Support: (null)
HTTP2 Push Support: (null)

В nginx мы видим несколько другое:
HTTP Version: HTTP/2.0
HTTP2 Support: h2
HTTP2 Push Support: undefined

Запрос HTTP/1.1 через nginx выглядит так:
HTTP Version:HTTP/1.1
HTTP2 Support:undefined
HTTP2 Push Support:undefined

Альтернативным методом является передача деталей в виде параметров запроса, а не HTTP-заголовков. Однако я считаю, что HTTP-за­го­
ловки понятнее и их легче добавлять как для запросов GET, так и для POST.
Тем не менее вы видите, что можно узнать версию протокола, и в зависимости от нее ваше приложение будет реагировать по-разному.

6.4.3 Отслеживание поддержки HTTP/2
на стороне клиента
Клиентским приложениям также может понадобиться информация об
используемом вами протоколе (HTTP/1 или HTTP/2). На сегодняшний
день стандартизированного способа получить ее не существует, однако
API-интерфейс Resource Timing Level 2 включает атрибут nextHopProtocol1, который может вам помочь решить эту задачу.
Однако понять, что именно клиент считает доступным, сложно изза наличия промежуточных прокси. Возможно, веб-браузер ограничен
HTTP/1.1, но прокси-сервер подключается через HTTP/2 к серверу (хотя,
скорее всего, происходит наоборот, браузер поддерживает HTTP/2, но
ограничен HTTP/1.1 из-за прокси). По этой причине я считаю, что сначала лучше понять, какой протокол используется на стороне сервера,
а затем отправить результаты обратно клиенту. Вы можете отправить
результаты несколькими способами, например посредством HTTPзаголовка или переменной Java­Script. Единственное предостережение –
вам следует учитывать, как кеширование ресурса, указывающего эту
информацию, может повлиять на последующие соединения. Если вы
запускаете соединение HTTP/2 и кешируете ресурс, который клиентская
сторона использует для обозначения соединения, а затем клиент переключается на соединение HTTP/1, возможно, вы выполняете оптимизацию неправильно.
1

https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetimingnext­hopprotocol.

Глава 6

258

Оптимизация в HTTP/2

6.4.4 Объединение соединений
Согласно спецификации HTTP/2 одно и то же соединение можно использовать для нескольких доменов, если они являются доверенными1,
т. е. если домены разрешаются на один и тот же IP-адрес, а сертификат
HTTPS охватывает оба домена. Делается это для того, чтобы максимизировать единое соединение и разрешить автоматическое объединение
доменов, размещенных на одном сервере (также известное как объединение соединений).
Предположим, что сайт www.example.com использует для размещения
изображений домен images.example.com. Если оба этих домена размещены на одном сервере и представляют собой отдельные виртуальные
хосты, в HTTP/2 они могут обслуживаться одним и тем же соединением
при условии установки соответствующего псевдозаголовка :authority. Такой сценарий обычно происходит, если сегментированные домены создаются исключительно по причине неэффективности HTTP/1 и для них
не используется отдельный сервер, как показано на рис. 6.15.
Веб-сервер

Веб-браузер
HTTP/1.1
www.example.com
images.example.com

Веб-сервер

Веб-браузер
HTTP/2

www.example.com
images.example.com

Рис. 6.15 Объединение соединений по HTTP/2

На более высоком уровне (например, в инструментах разработчика браузера) обычные HTTP-запросы выглядят точно так же, как и сегментированные; клиент может решить объединить их только на уровне соединения. У многих веб-сайтов могут по-прежнему существовать
сегментированные домены, в которых HTTP/1.1-соединения будут использоваться автоматически, а HTTP/2-соединения будут объединяться автоматически, действуя так, как если бы они были не доверенными
доменами, и все это будет обслуживаться через одно соединение. Казалось бы, такая ситуация – это идеальный сценарий, который не требу1

https://httpwg.org/specs/rfc7540.html#reuse.

Оптимизация и для HTTP/1.1, и для HTTP/2

259

ет дополнительных усилий для продолжения поддержки пользователей
HTTP/1 или оптимизации для пользователей HTTP/2. Однако в реальности, как всегда, все немного сложнее.
Начнем с того, что данный сценарий становится возможным, только
если все домены расположены на одном сервере. Если домены расположены на разных серверах, их должны обслуживать отдельные соединения. Также в браузере должно быть реализовано повторное использование соединения (что есть сейчас не во всех браузерах)1. В спецификации
говорится, что соединение может быть использовано повторно, но это
необязательно. На момент написания этой книги Safari и Edge, в отличие
от Chrome и Firefox, не поддерживают данную функцию.
Кроме того, при определенных реализациях, объединение соединений может вызвать ряд проблем. Ввиду наличия нескольких IP-адресов
для доменов (некоторые из которых могут использоваться совместно
с другими доменами) браузер может решить использовать соединение
повторно (или объединить несколько соединений), когда на самом деле
это не представляется возможным. Рассмотрим IP-адреса, указанные
в табл. 6.8.
Таблица 6.8

Пример объединения соединений

Домен
www.example.com
images.example.com

IP-адреса
1.2.3.4
1.2.3.5
1.2.3.4
1.2.3.6

В этом случае по желанию клиента любое HTTP/2-соединение по IPадресу 1.2.3.4 может обслуживать запросы как www.example.com, так
и images.example.com, но соединение по другим IP-адресам (1.2.3.5
и 1.2.3.6) они осуществить не могут. Firefox реализовал так называемое
агрессивное объединение соединений. Согласно этому методу браузер,
если он будет информирован об объединении, будет пытаться использовать любое соединение для обоих доменов независимо от того, какой из
трех IP-адресов используется для соединения. Подобные ситуации приводят к ошибкам, как при переходе на HTTP/2 заметила компания BBC2.
Код состояния 421 был создан, чтобы позволить серверу вежливо сообщать браузеру, что он использовал неправильное соединение, и заставить его обратить внимание на то, куда он должен отправлять эти запросы. Однако, как выяснила BBC, поддержка этого кода все еще ограничена.
В качестве альтернативного решения существует фрейм ORIGIN3, который
позволяет серверу информировать клиента о том, для каких доменов он
является доверенным, а не действовать наугад. На момент написания этой
1
2

3

https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/.
https://medium.com/bbc-design-engineering/http-2-is-easy-just-turn-it-on34baad2d1fb1.
https://tools.ietf.org/html/rfc8336.

Глава 6

260

Оптимизация в HTTP/2

книги данный фрейм – новинка, однако его уже поддерживают некоторые серверы1,2, а другие имеют открытые запросы на его отслеживание3,4.
Со стороны браузеров фрейм поддерживает Firefox5, и ожидается, что за
ним последуют другие браузеры и впоследствии фрейм будет включен
в рекомендуемый стандарт. Как правило, ORIGIN отправляется в начале
соединения (в идеале после SETTINGS), что также должно предотвратить
отправку неверных запросов. В связи с этим проверка подлинности вторичного сертификата в HTTP/2 – это еще одно предложение, которое позволит объединить соединения, даже если на одном IP-адресе используются разные сертификаты.
Иными словами, объединение соединений – это очень сложно, поэтому полагаться на этот метод я не советую. Вместо этого обратите внимание на то, требуется ли вашим доменам сегментирование и позволит ли
это получить прирост производительности. Если сегментирование вам
все же требуется, возможно, лучше разместить такие домены на отдельных серверах, чтобы предотвратить проблемы с объединением.

6.4.5 Сколько времени занимает оптимизация
для пользователей HTTP/1.1
Еще одна вещь, которую нужно учитывать при оптимизации для всех
групп пользователей, – это время. Оптимизация требует дополнительных усилий, поэтому подумайте, готовы ли вы выполнять дополнительную работу, и если да, то сколько времени вы готовы этому уделить. Конечно, существуют и альтернативы, такие как отказ от отмены обходных
путей HTTP/1.1, поскольку при HTTP/2 в них теряется необходимость,
однако работают они не менее эффективно. Также, если большая часть
ваших посетителей уже использует HTTP/2, лучше оптимизировать для
большинства за счет меньшинства и позволить тем, кто использует соединения HTTP/1.1, загружать страницы медленнее, чем необходимо.
Каждый владелец веб-сайта должен принять взвешенное решение, исходя из своей базы и ожидаемых последствий изменений. Поскольку
браузер поддерживает протокол HTTP/2, пользователи, которые не могут использовать HTTP/2, делятся на несколько категорий:
„„ пользователи с устаревшими версиями программного обеспечения
(в зависимости от того, насколько вы поддерживаете старые версии,
в любом случае некоторые функции для таких пользователей будут
недоступны);
„„ пользователи, выходящие в Internet с помощью корпоративных
прокси (вероятно, при более быстром подключении);
1
2
3
4
5

https://github.com/nghttp2/nghttp2/pull/901.
https://github.com/h2o/h2o/pull/1199.
https://github.com/icing/mod_h2/issues/96.
https://trac.nginx.org/nginx/ticket/1530.
https://bugzilla.mozilla.org/show_bug.cgi?id=1337791.

Резюме

261

пользователи, применяющие антивирусные прокси (скорее всего,
это пользователи стационарных компьютеров, в основном пользующиеся широкополосным доступом);
„„ те, кто пользуются непопулярными браузерами (у которых могут
быть другие проблемы с отображением вашего сайта);
„„ боты и программы автоматического сбора информации (которые,
вероятно, наименее важны для вас).
В конце концов, стоит оценить количество посетителей вашего сайта, все еще использующих HTTP/1, а затем решить, стоит ли проводить
меры по оптимизации для обоих протоколов. Для крупных сайтов ответ,
вероятно, будет положительным, но для небольших оптимизация может
быть нецелесообразной. Даже если вы не произведете оптимизацию,
сайт по-прежнему будет работать (хотя и медленнее). Подобным образом постепенное сокращение возможностей используется в дизайне
веб-сайтов с целью создать работающий, но неоптимизированный сайт
для браузеров, которые не поддерживают функции, необходимые для
оптимального взаимодействия с ним.
„„

Резюме
HTTP/2 был разработан для устранения неэффективности HTTP/1.1.
Многие надеялись, что оптимизация производительности HTTP/1.1,
которая требует усилий и имеет свои недостатки, больше не потребуется, но их ожидания сбылись лишь частично. Сейчас необходимость
в таких методах меньше, однако отказываться от них пока что не стоит.
„„ Другие методы повышения веб-производительности в основном остаются актуальными для HTTP/2, но, возможно, стоит пересмотреть их
для вашего сайта при переходе на HTTP/2.
„„ Возможна оптимизация как для соединений HTTP/1.1, так и для
HTTP/2.
„„ Объединение соединений позволяет браузеру автоматически из­
бавляться от сегментированных доменов, однако этот процесс очень
сложен.
„„
„„

Часть III
Продвинутый уровень
использования HTTP/2

В

первой части этой книги мы познакомились с протоколом HTTP/2.
Во второй части представлены детали работы протокола и примеры его использования на практике. Основы HTTP/2 и принцип его
работы описаны в главе 4; глава 5 посвящена абсолютно новой для протокола концепции HTTP/2 push; а в главе 6 рассказывается о реализации
HTTP/2 на практике, а также о его влиянии на процесс разработки.
В третьей части я копну несколько глубже и расскажу о некоторых
сложных аспектах протокола, доступных лишь продвинутым пользователям. В главе 7 мы рассмотрим ранее упущенные части спецификации,
а глава 8 в отдельном порядке расскажет нам о спецификации сжатия
HTTP-заголовка HPACK. Вышеупомянутые главы помогут вам перейти
с уровня «прочной основы» на уровень экспертного понимания протокола HTTP/2. Благодаря этому вы сможете решить любую соответствующую проблему на своем сайте и, возможно, даже внести свой вклад
в дальнейшее развитие протокола!

7

Расширенные
возможности HTTP/2

В этой главе мы рассмотрим:
концепцию состояний HTTP/2-потока;
„„ управление потоками информации;
„„ приоритизацию в HTTP/2;
„„ проверку на совместимость с HTTP/2.
„„

Данная глава посвящена упущенным мною ранее частям протокола
HTTP/2. Я расскажу о них в порядке, примерно соответствующему их
описанию в спецификации1. Многие аспекты, рассматриваемые здесь,
сложны постольку, поскольку зачастую они не контролируются напрямую ни веб-разработчиками, ни администраторами отдельных серверов
(за исключением случаев, когда те сами пишут HTTP/2-сервер с нуля).
Однако понимание этих аспектов обеспечит вам и более глубокое понимание самого протокола, а если вы захотите реализовать свой собственный HTTP/2-сервер, эта информация может помочь вам при его отладке. Кроме того, возможно, что в будущем разработчики или, по крайней
мере, администраторы веб-серверов все же получат контроль над этими
частями протокола. В главе 8 я расскажу вам о протоколе HPACK, для которого существует отдельная спецификация.

1

https://httpwg.org/specs/rfc7540.html.

Глава 7

264

Состояния HTTP/2-потока
Отдельный HTTP/2-поток создается при каждой загрузке, а по ее завершении отбрасывается. В этом состоит основное различие соединений
HTTP/1 и потоков HTTP/2, ввиду чего вторые не могут быть аналогом
первых. Я привожу именно такой пример с целью наиболее доступно
представить данные концепции и разницу между ними. На многих диаграммах (например, как в главе 2 этой книги, которые я продублирую
на рис. 7.1) между потоками HTTP/2 и соединениями HTTP/1 проведены
параллели, однако это не совсем верно, поскольку потоки, в отличие от
соединений, не используются повторно.
HTTP/1.1

Клиент
(веб-браузер)
Запрос 2
GET /styles.css

Сервер
(веб-сервер)

TCP-соединение 1
Запрос

Запрос 2
GET /styles.css

Ответ
TCP-соединение 2

Запрос 3
GET /script.js

Запрос

Запрос 3
GET /script.js

Ответ
TCP-соединение 3

Запрос 4
GET /image.jpg

Запрос

Запрос 4
GET /image.jpg

Ответ

HTTP/2

Клиент
(веб-браузер)

Сервер
(веб-сервер)

Одиночное TCP-соединение
Поток 5

Запрос 2

GET /styles.css

GET /styles.css

Запрос 3
GET /script.js

Запрос 4

Поток 7

Поток 9

Уровень фрейминга HTTP/2

Запрос 2
Уровень фрейминга HTTP/2

7.1

Расширенные возможности HTTP/2

GET /image.jpg

Рис. 7.1 Соединения HTTP/1.1 и потоки HTTP/2 могут быть похожи,
однако на самом деле они абсолютно разные

Запрос 3
GET /script.js

Запрос 4
GET /image.jpg

Состояния HTTP/2-потока

265

По завершении доставки ресурса потоки закрываются, а при поступлении запроса на новый ресурс запускается новый поток. Потоки – это
виртуальная концепция, представляющая собой не что иное, как номер,
которым помечен каждый фрейм; такие номера называются идентификаторами потока. Закрытие и открытие потока – процесс гораздо менее
ресурсоемкий, нежели закрытие и открытие соединения HTTP/1 (которое включает в себя трехстороннее рукопожатие TCP и иногда согласование протокола HTTPS перед отправкой запроса). HTTP/2-соединения
намного сложнее соединений HTTP/1, поскольку перед осуществлением
запроса они требуют «волшебной» преамбулы HTTP/2 и хотя бы одного
фрейма SETTINGS. Таким образом, намного проще использовать именно
HTTP/2-потоки.
Потоки HTTP/2 имеют свой жизненный цикл, на протяжении которого они принимают различные состояния. Фрейм HEADERS, отправленный
от клиента, начинается как HTTP-запрос (такой, как GET) и проходит несколько состояний:
неактивный поток. В неактивном состоянии поток находится сразу
после его создания или же когда на него ссылаются. На самом деле
большинство потоков не остается в этом состоянии надолго. Объясняется это тем, что мало кто ссылается на потоки, если в будущем
не собирается их использовать. Именно поэтому незанятые потоки
немедленно входят в оборот и становятся активными, после чего
сразу же переходят к следующей фазе: открытию;
„„ открытый поток. Поток считается открытым и становится доступным обеим сторонам после отправки запроса фрейма HEADERS. Он
остается в этой фазе, пока клиент не отправит все необходимые
данные. Поскольку в HTTP/2 в одном фрейме HEADERS может быть
отправлено большинство запросов, почти всегда после этого этапа
поток сразу же переходит в следующую фазу (полузакрытый поток);
„„ полузакрытый поток. Поток становится полузакрытым, когда с помощью флажка END_STREAM клиент указывает, что фрейм HEADERS содержит все необходимые данные. После этого поток может быть использован только в одностороннем порядке: для отправки ответов
клиенту; таким образом, его не следует использовать для отправки
данных от клиента (за исключением управляющих фреймов, таких
как WINDOW_UPDATE);
„„ закрытый поток. После отправки сервером последнего фрейма
с флажком END_STREAM поток считается закрытым и становится непригодным для дальнейшего использования.
„„

Несмотря на то что в списке выше приведены переходы между состояниями обычного HTTP-запроса, инициированного клиентом, тот же
путь может проходить и запрос, инициированный сервером. На сегодняшний день в число таких запросов входит только HTTP/2 push, однако
в будущем их список может пополниться. Для запросов, инициированных сервером, запускаются отдельные потоки (обещанный идентификатор потока), которые проходят аналогичный цикл состояний:

Глава 7

266

Расширенные возможности HTTP/2

неактивное. В этом состоянии обещанный поток находится сразу
после его создания или на него ссылается фрейм PUSH_ PROMISE, отправленный в другом потоке;
„„ зарезервированное. Далее поток переходит в состояние зарезервированного, в котором он будет находиться до тех пор, пока сервер
не будет готов отправить ресурс. Таким образом, вы знаете о существовании этого потока (и о том, что он простаивает) и о его назначении (поэтому скорее не простаивает, а является зарезервированным для определенного ресурса, откуда и пришло название), но
при этом не имеете полного представления о ресурсе, для которого
этот поток предназначен. Такую ситуацию мы можем наблюдать
после получения фрейма HEADERS в первом примере. Поток не может находиться в открытом состоянии, поскольку он предназначен
для push-ресурса и клиент априори не может отправить данные по
этому потоку. Таким образом, он находится в зарезервированном
состоянии, а затем при отправке фрейма HEADERS (после отправки
фрейма PUSH_PROMISE в исходном потоке) переходит в полузакрытое
состояние; и такой порядок фаз неслучаен;
„„ полузакрытое. Поток переходит в полузакрытое состояние, когда
сервер начинает push-загрузку ответа. В это время он может быть
использован только для отправки соответствующего push-ресурса;
„„ закрытое. Поток считается закрытым по завершении отправки
ресурса сервером, когда в последнем фрейме DATA он использует
флажок END_STREAM. После этого поток больше не должен быть использован.
„„

На рис. 7.2 представлена схема всех возможных состояний потока
HTTP/2. В ней приведены два вышеупомянутых цикла, а также другие
сценарии (например, когда фрейм RST_STREAM используется для прежде­
временного закрытия соединения).
В каждом из этих потоков информации у клиента и сервера разнится
представление о состоянии потока. Оно зависит от того, какая сторона
инициирует запрос, а какая переходит в это состояние на основании сообщения от другой. Именно поэтому у некоторых из этих состояний есть
локальный или удаленный индикатор (в зависимости от того, являетесь
ли вы инициатором или получателем потока соответственно). Кроме
того, для каждого состояния существуют переходы send и recv.
Итак, мы уже знаем, что запрос GET проходит через следующие состоя­
ния: неактивное, открытое, полузакрытое и закрытое. Уделим особое
внимание неоднозначному полузакрытому состоянию: для клиента оно
закрыто (ввиду чего он может только получать данные), а для сервера полузакрыто. Клиент видит поток как наполовину закрытый (локальный),
а сервер видит его как наполовину закрытый (удаленный). Следовательно, запрос для клиента и сервера проходит через разные циклы состояний; если вы посмотрите на него со стороны сервера, он будет проходить, согласно правой части схемы на представленном рисунке, если со
стороны клиента – по левой.

267

Состояния HTTP/2-потока
Отправка
фрейма PUSH_PROMISE

Зарезервировано
(локально)
Отправка
фрейма HEADERS Получение
флага END_STREAM

Полузакрытый
(локально)

Получение
фрейма PUSH_PROMISE

Неактивный

Отправка
фрейма HEADERS
или
получение
фрейма HEADERS

Открытый

Зарезервировано
(удаленно)
Получение
фрейма HEADERS
Отправка
флага END_STREAM

Отправка
фрейма RST_STREAM
или
получение
фрейма RST_STREAM

Отправка
фрейма END_STREAM,
или
получение
фрейма RST_STREAM,
или
отправка
фрейма RST_STREAM

Полузакрытый
(удаленно)
Получение
фрейма END_STREAM,
или
отправка
фрейма RST_STREAM,
или
получение
фрейма RST_STREAM

Закрытый
Отправка
фрейма RST_STREAM
или
получение
фрейма RST_STREAM

Отправка
фрейма RST_STREAM
или
получение
фрейма RST_STREAM

Рис. 7.2 Схема состояний потока HTTP/2

Также стоит отметить, что схема состояний отражает лишь переходы
между состояниями. Однако некоторые фреймы не влекут за собой его
смену. Например, фреймы CONTINUATION считаются расширениями предыдущих фреймов HEADERS, поэтому на схеме они входят в состав HEADERS.
Точно так же существуют фреймы (такие как PRIORITY, SETTINGS, PING и WINDOW_UPDATE), которые вообще не приводят к смене состояния, ипоэтому
они не отражены в схеме.
Откровенно говоря, данная схема состояний важна не столько для
пользователей, сколько для разработчиков HTTP/2-библиотек низкого
уровня. С ее помощью последние могут понять, какие фреймы необходимо отправлять при определенной фазе состояния. Схема составлена
согласно спецификации HTTP/21, где можно найти множество ссылок на
информацию о различных состояниях. Любые попытки смены состояния, отклоняющиеся от спецификации, приводят к появлению ответа
PROTOCOL_ERROR. Если вы хорошо разберетесь в схеме состояний, то без
1

https://httpwg.org/specs/rfc7540.html#StreamStates.

268

Глава 7

Расширенные возможности HTTP/2

труда сможете найти причину подобных ошибок (однако такие ошибки
присущи базовым реализациям HTTP/2, и исправить их самостоятельно
зачастую не представляется возможным).
Поначалу схема может казаться пугающей, особенно в сравнении
с концепциями, о которых я рассказывал ранее. Ничего подобного вы не
найдете в инструментах разработчика браузера или в каких-либо других
инструментах, описанных в этой книге (например, nghttp и Wireshark).
Состояния потока – это внутренний аспект протокола, а от реализаций
HTTP/2 требуется только его отслеживание и поддержка. Учитывая данный факт, ситуация становится еще сложнее. Как правило, все упрощает
один простой шаг – возврат к основному варианту использования (запрос ресурса HTTP); этот процесс я описывал ранее.

7.2

Управление потоками информации
Управление потоками информации является важным аспектом сетевых
протоколов. С его помощью получатель может запретить отправку ему
ресурсов, если он еще не готов к их обработке (например, если он слишком загружен для обработки дополнительных данных). Ввиду того, что
клиенты обрабатывают данные с разной скоростью, управление потоками – чрезвычайно полезный механизм. Высокоскоростные серверы
отправляют данные очень быстро, но если они работают с клиентом, чья
скорость значительно меньше (например, мобильное устройство), то последний вряд ли справится со столь объемным потоком данных. Из-за
этого клиент начнет буферизировать данные в памяти, а когда буфер заполнится, он будет отбрасывать пакеты и впоследствии запрашивать их
повторную отправку. В результате такой сценарий становится слишком
ресурсоемким и для серверной, и для клиентской стороны, а также для
самой сети.
В HTTP/1.1 управление потоками не требовалось, поскольку в соединении всегда находилось только одно сообщение. Таким образом, на
уровне соединения можно было использовать управление передачей TCP.
Если получатель прекращает прием TCP-пакетов, то вторая сторона прекращает отправлять их ввиду того, что его окно загрузки (CWND) увеличивается до максимально возможного размера (см. главу 2).
HTTP/2 позволяет нам объединять независимые потоки в одно мультиплексированное соединение, поэтому управления потоком информации только на уровне соединения становится недостаточно. Оно должно осуществляться в том числе и на уровне потока, поскольку для вас,
например, может быть важнее получить больше данных именно от какого-либо определенном потока. В главе 4 приведен пример веб-сайта
с размещенным на нем видео, воспроизведение которого было приостановлено пользователем. В подобном случае сайту может быть необходимо остановить дальнейшую загрузку медиафайла на одном потоке, но
при этом разрешить функционирование других потоков в HTTP/2-со­
еди­нении.

Управление потоками информации

269

Управление потоком информации в HTTP/2 схоже с работой TCP. В начале соединения (с помощью фрейма SETTINGS) определяется размер окна
управления потоком (если размер не указан, используется значение по
умолчанию 65 535 октетов). Затем каждая отправленная часть данных
вычитается из этой суммы, после чего обратно добавляется каждый бит
подтвержденных данных с помощью фрейма WINDOW_UPDATE. На уровне соединения существует окно управления потоком, которое как бы дублирует окно управления потоком TCP. Также такие окна существуют и для
каждого потока в отдельности. Размер отправляемого ресурса не должен
превышать максимальный размер наименьшего окна управления потоком (на одном из уровней). Когда размер этого окна достигает нуля,
отправителю следует прекратить передачу данных до тех пор, пока он
не получит соответствующее подтверждение. Если вы реализуете клиент или сервер на HTTP/2 и забудете реализовать фреймы WINDOW_UPDATE,
совсем скоро вы заметите, что третья сторона прекратит «общаться»
с вами.
Управление потоком необходимо преимущественно для фреймов DATA
(хотя другие типы фреймов HTTP/2 также могут попадать под в зону его
влияния). Фреймы управления (и в частности WINDOW_UPDATE, необходимые
для управления потоком) могут быть отправлены даже тогда, когда клиент предупреждает о превышении максимального размера окна управления.

7.2.1 Пример управления потоками информации
В качестве примера управления потоком вернемся к инструменту nghttp.
В этом разделе мы инициируем запрос домашней страницы Facebook
и всех необходимых ей ресурсов, а затем с помощью утилиты grep мы
выделим и просмотрим только нужные нам строки кода.
$ nghttp -anv https://www.facebook.com | grep -E "frame
127 which isn't handled by this
program\n";
}
#Установите самый старший бит (128), чтобы указать, что используется кодировка
#Хаффмана.
#Укажите длину
#(опять же, эта простая версия предполагает длину в 7 бит.)
printf("Huffman Encoding Flag + Length: %x\n",128+$string_length);
#Пройдитесь по каждому 4-битному значению и преобразуйте их в шестнадцатеричный
#формат.
for(my $count=0;$count /proc/sys/net/ipv4/tcp_fastopen

Глава 9

340

TCP, QUIC и HTTP/3

Помимо настройки этого параметра в операционной системе, для его
использования вам следует настроить еще и серверное программное обеспечение. Например, nginx допускает наличие этого параметра на стороне веб-сервера1, но для этого требуются параметры и конфигурации
компиляции, поэтому по умолчанию параметр не включен. Windows IIS2
поддерживает этот параметр. В документации Apache нет информации
об этом параметре, поэтому, по-видимому, сервер его не поддерживает.
Другие, менее распространенные серверы также могут не поддерживать
его. На стороне клиента параметр можно включить в Edge3 и Chrome на
Android. Однако на момент написания этой книги в Chrome для Windows
или macOS4 он не поддерживается, а также отключен в Firefox5.
Функция TCP Fast Open действительно очень полезна. Google заявила6,
что «на основе анализа трафика и эмуляции сети мы увидели, что TCP
Fast Open снижает задержку в сети транзакций HTTP на 15 % и время загрузки всей страницы в среднем более чем на 10 %, а в некоторых случаях
процент возрастает до 40 %». Однако поддержку относительно нового
дополнения к TCP (RFC был опубликован в 2014 году)7 серверы и клиенты реализовывали достаточно медленно. Учитывая все эти сложности,
функция TCP Fast Open будет полезна скорее в перспективе, чем сейчас.

Использование алгоритмов управления перегрузкой, PRR и BBR
У TCP существует несколько разных алгоритмы управления перегрузкой,
которые контролируют состояние TCP-соединения при потере пакетов.
Большинство реализаций TCP использует алгоритм CUBIC8 (по умолчанию, начиная с ядра Linux 2.6.19). Для него существует расширение,
именуемое Proportional Rate Reduction (PRR)9, которое при потере пакетов
уменьшает размер окна управления перегрузкой вдвое (по умолчанию
расширение устанавливается с версии 3.2)10. Подробное описание различий между всеми алгоритмами выходит за рамки этой книги. Однако
стоит отметить, что правильно подобранный алгоритм может значительно повысить производительность. Для того чтобы узнать, какой алгоритм используется сейчас, воспользуйтесь следующей командой:
$ cat /proc/sys/net/ipv4/tcp_congestion_control
cubic
1
2

3

4
5
6
7
8
9
10

https://nginx.org/en/docs/http/ngx_http_core_module.html#listen.
https://blogs.technet.microsoft.com/networking/2016/07/18/announcing-newtransport-advancementsin-the-anniversary-update-for-windows-10-and-windows-server-2016/.
https://www.windowscentral.com/enable-tcp-fast-open-microsoft-edge-fasterpage-load-times.
https://bugs.chromium.org/p/chromium/issues/detail?id=635080.
https://bugzilla.mozilla.org/show_bug.cgi?id=1398201.
https://ai.google/research/pubs/pub37517.
https://tools.ietf.org/html/rfc7413.
https://tools.ietf.org/html/rfc831.
https://tools.ietf.org/html/rfc6937.
https://ai.google/research/pubs/pub37486.

HTTP и слабые стороны TCP

341

Список доступных алгоритмов управления перегрузкой можно получить при помщи команды:
$ cat /proc/sys/net/ipv4/tcp_available_congestion_control
reno cubic

Согласно проведенным исследованием, более новый алгоритм Bottleneck Bandwidth and Round-trip propagation time (BBR) обеспечивает еще
больший прирост производительности1, особенно для HTTP/2-со­еди­не­
ний2. BBR был создан Google и доступен в ядре Linux 4.9; изменений на
стороне клиента он не требует. Для его включения в Linux (версии 4.9 или
новее) используйте следующие команды:
#Динамически загружать модуль tcp_bbr, если он еще не загружен.
sudo modprobe tcp_bbr#Add
#Добавить правило управления трафиком Fair Queue, с которым BBR лучше работает.
sudo echo "net.core.default_qdisc=fq" > /etc/sysctl.conf
#Изменить алгоритм перегрузки TCP на BBR.
sudo echo "net.ipv4.tcp_congestion_control=bbr" > /etc/sysctl.conf
#Перезагрузить настройки.
sudo sysctl –p

Вопреки всем положительным результатам экспериментов, некоторые исследователи3 отмечают, что BBR плохо согласовывается с другими
типами трафика и может отнимать слишком много сетевых ресурсов.

9.1.5 Будущее TCP и HTTP
Итак, я рассказал вам о некоторых сложностях TCP-протокола, который
на первый взгляд кажется довольно простым, но на деле оказывается намного сложнее. Как и HTTP/1.1, TCP имеет некоторые врожденные недостатки, с которыми пользователи могут начать сталкиваться только
сейчас, поскольку проблемы протоколов более высокого уровня, таких
как HTTP, устранены, а требования к сетям продолжают расти.
На данный момент TCP находится в стадии развития, хотя и очень
медленного. Несмотря на то что для него постоянно создаются новые
параметры и алгоритмы управления перегрузкой, а браузеры регулярно
обновляются, для поддержки этих функций, для того, чтобы все эти новшества вошли в сетевые стеки серверов, требуется некоторое время. Новые функции обычно связаны с фундаментальными частями операционной системы, поэтому они требуют полного ее обновления. Если в вашей
операционной системе доступны новые функции, их можно включить
вручную, но зачастую лучшим вариантом остается обновление. Все изменения в этой области лучше доверить разработчикам операционной
системы, которые обладают необходимыми навыками и знаниями. Од1

2
3

https://cloudplatform.googleblog.com/2017/07/TCP-BBR-congestion-controlcomes-to-GCP-your-Internetjust-got-faster.html.
https://blog.cloudflare.com/http-2-prioritization-with-nginx/.
https://doc.tm.uka.de/2017-kit-icnp-bbr-authors-copy.pdf.

Глава 9

342

TCP, QUIC и HTTP/3

нако, все равно стоит иметь представление обо всех упомянутых мной
нововведениях, ведь в будущем они могут стать чрезвычайно полезны.
Кроме того, я коснулся далеко не всех сетевых магистралей. Даже если
обе стороны поддерживают некоторые из этих функций TCP, но какиелибо промежуточные элементы инфраструктуры не поддерживают их,
существует риск возникновения проблем. Подобно тому, как прокси-серверы HTTP меняют протокол соединений на HTTP/1.1, даже если оба конца поддерживают HTTP/2, инновации в области TCP могут сдерживаться
так называемыми промежуточными устройствами. Поскольку TCP – это
старый алгоритм, у некоторых из этих промежуточных устройств есть
определенные ожидания относительно того использования, и при применении новых, неожиданных для них методов, возникают ошибки.
По этим и другим причинам некоторые люди начали задумываться,
является ли TCP приемлемым для HTTP, или же стоит с нуля разработать
другой, независимый от операционной сети, протокол, который будет
удовлетворять потребности HTTP (текущие и будущие). Одним из таких
протоколов стал QUIC.

9.2

QUIC
QUIC (произносится как «квик») – это новый протокол на основе UDP от
компании Google (снова от Google!), призванный заменить TCP и другие
части традиционного стека HTTP и тем самым устранить многие недостатки, упомянутые в этой главе. QUIC выводит концепции, представленные в HTTP/2 (например, пакеты и управление потоком) на новый
уровень и заменяет TCP.

Что означает название QUIC?
Первоначально название QUIC было акронимом Quick UDP Internet Connections. Изначально под этим именем его можно было встретить в большей
части документации Google Chromiuma,b,c. При официальном оформлении
рабочая группа QUIC решила отказаться от этого вариантаd, и теперь в спецификации протокола поясняется: «QUIC – это имя, а не акроним»e. Однако
во многих источниках его все еще можно встретить под первоначальным
названием. Один из членов рабочей группы забавно заметил: «QUIC – это не
акроним. Скорее, это ваш удивленный возглас ;)»f.
___________________________________
a
https://www.chromium.org/quic.
b
 https://docs.google.com/document/d/1gY9-YNDNAB1eip-RTPbqphgySwSNSDHLq9D5Bty4FSU/.
c
 https://docs.google.com/document/d/1RNHkx_VvKWyWg6Lr8SZ-saqsQx7rFVev2jRFUoVD34/.
d
https://github.com/quicwg/base-drafts/pull/1282.
e
https://tools.ietf.org/html/draft-ietf-quic-transport#section-2.
f
https://www.ietf.org/mail-archive/web/quic/current/msg03844.html.

QUIC

343

По задумке разработчиков QUIC должен был выполнять следующие
функции1:
„„ значительное сокращение времени установления соединения;
„„ улучшенный контроль перегрузки;
„„ преодоление HOL-блокировки;
„„ прямую коррекцию ошибок;
„„ миграцию соединения.
Первые три функции – это как раз то, чего недоставало TCP (и HTTPS).
А следующие две – это интересные дополнения, созданные для дальнейшего решения всех этих проблем.
Прямая коррекция ошибок (Forward error correction, FEC) позволяет сократить потребность в повторной передаче пакетов за счет включения
части пакета QUIC в соседние пакеты. Таким образом, если один пакет
теряется, появляется возможность восстановить его по частям из доставленных пакетов. Данный метод называли «RAID 5 на сетевом уровне»2.
Ранее я говорил, что пакеты могут теряться случайным образом, но не
факт, что это следует считать причиной для ограничения соединения,
и FEC стремится исправить проблему потери пакетов. Безусловно, вместе
с QUIC возрастает избыточность оборудования и расходы. Однако, учитывая тот факт, что HTTP требует гарантированной доставки (в отличие,
скажем, от протоколов видеопотока, где потеря пакетов обычно не приводит к каким-либо последствиям), польза от QUIC намного превышает
затраты на него. На момент написания этой статьи FEC все еще является
экспериментальной3 функцией. Также она не будет включена в первоначальную версию QUIC, поскольку она не рассматривается в QUIC-WG4.
Функция миграции соединения позволяет соединению перемещаться между сетями, благодаря чему снижаются расходы на установку соединения. В TCP с обеих сторон соединение было связано с IP-адресом
и портом. Для изменения IP-адреса необходимо было установить новое
TCP-соединение. Во времена TCP такая практика считалась приемлемой,
поскольку считалось, что изменение IP-адреса в течение времени существования сеанса маловероятно. Теперь, когда существует несколько типов сетей (проводные, беспроводные и мобильные), такой метод устарел.
QUIC делает возможным начать сеанс через Wi-Fi дома, а затем перейти
в мобильную сеть, избежав при этом перезапуска сеанса. Также благодаря новому протоколу возможно использовать Wi-Fi и мобильную сеть
одновременно для одного соединения. Такая возможность появилась
благодаря методу многопутевых передач, увеличивающему пропускную
способность. Хотя этот метод не будет представлен в первоначальной
версии QUIC, функция миграции соединения останется.

1
2
3

4

https://www.chromium.org/quic.
https://ma.ttias.be/googles-quic-protocol-moving-web-tcp-udp/.
https://docs.google.com/document/d/1Hg1SaLEl6T4rEU9j-isovCo8VEjjnuCPTcLNJewj7Nk.
https://datatracker.ietf.org/wg/quic/about/.

Глава 9

344

TCP, QUIC и HTTP/3

9.2.1 Преимущества QUIC в производительности
В апреле 2015 года Google опубликовала в своем блоге пост1, посвященный преимуществам производительности QUIC. Он содержал следующую информацию:
„„ 75 % соединений теперь устанавливаются быстрее;
„„ в Google Search среднее время загрузки страницы сократилось на
3 %, а в медленных сетях на одну секунду. Данные цифры могут показаться небольшими, но помните, что мы стараемся оптимизировать Google Search для широкого круга пользователей, и любое улучшение является значимым;
„„ благодаря QUIC количество прерываний видеороликов из-за повторной буферизации на YouTube сократилось на 30 %.
Я полагаю, что результаты сравнивались с HTTP/2 и SPDY. В то время
в Google QUIC использовали лишь 50 % трафика Chrome; с тех пор этот
процент значительно возрос. До недавнего времени QUIC поддерживался только Chrome и Google (см. раздел 9.2.6), поэтому его использование ограничено. Согласно W3Tech на момент написания этой книги
QUIC используют чуть больше 1 % сайтов2, хотя по другим оценкам эта
цифра составляет 7,8 % от объема трафика3, из которых 98 % приходится
на Google.

9.2.2 Место QUIC в стеке Internet
QUIC является альтернативой не только для TCP. На рис. 9.18 показано
место QUIC в традиционном стеке технологий HTTP.
HTTP/3

HTTP
HTTPS

QUIC

TCP

UDP
IP

Рис. 9.18

Место QUIC в стеке технологии HTTP

Как видите, QUIC заменяет большую часть функций TCP (части настройки, надежности и управления перегрузкой), полностью заменяет
HTTPS (сокращает задержки настройки) и даже часть HTTP/2 (управление потоком и части сжатия заголовка).
1
2
3

https://blog.chromium.org/2015/04/a-quic-update-on-googles-experimental.html.
https://w3techs.com/technologies/details/ce-quic/all/all.
https://blog.apnic.net/2018/05/15/how-much-of-the-internet-is-using-quic/.

QUIC

345

QUIC призван обеспечить возможность установки соединений с помощью одного цикла приема-передачи. Он функционирует на уровне
соединения (традиционно это был TCP) и на уровне шифрования (TLS).
Для этого QUIC применяет совершенно новые концепции, а также методы, которые были добавлены еще в TCP (например, Fast Open) и TLS
(например, TLSv1.3).
Что касается более высокого уровня, QUIC не заменяет HTTP/2 полностью, однако он берет на себя некоторые части транспортного уровня,
оставляя HTTP/2 более легкие части. Аналогично переходу от HTTP/1.1
к HTTP/2 основной синтаксис HTTP (с которым работают профессиональные разработчики) в QUIC остается неизменным; концепции, представленные в HTTP/2 (например, мультиплексирование потоков, сжатие заголовков и server push) также остались почти в неизменном виде.
QUIC заботится о некоторых деталях более низкого уровня. Переход от
HTTP/1.1 к HTTP/2 влечет за собой большие изменения для разработчиков, но QUIC не вносит каких-либо новых изменений, поэтому все, что вы
подчерпнули в книге, не пропадет зря! Протокол по-прежнему является
мультиплексированным двоичным протоколом на основе потоков, и некоторые особенности, используемые для достижения этого изменения
на более низком уровне, теперь относятся к QUIC, а не к HTTP/2. Чтобы
разграничить HTTP/2 и QUIC и показать преимущества нового протокола, было решено, что HTTP поверх QUIC будет называться HTTP/3 (более
подробно я расскажу об этом в разделе 10.3)1.

9.2.3 Что такое UDP и почему он является основой QUIC
QUIC основан на протоколе UDP (User Datagram Protocol), который,
в сравнении с TCP, является более простым; однако он аналогичным образом построен поверх Internet Protocol (IP). TCP обеспечивает сетевому
соединению надежность в IP, включая повторную передачу, перегрузку
и управление потоком. Обычно эти функции полезны и необходимы, но
для HTTP/2 они мало что значат. В HTTP/2 эти функции не обязательны на
сетевом уровне; к тому же в TCP они создают проблему HOL-блокировки.
UDP – это базовый протокол. Он имеет концепцию портов, аналогичную концепции TCP, поэтому на одном компьютере может работать несколько служб на основе UDP. Также он имеет дополнительную функцию
контрольной суммы, которая позволяет проверить целостность UDPпакетов. За исключением этих двух функций, в протоколе нет ничего
особенного. Надежности, упорядоченности и контроля перегрузки он не
гарантирует, а если эти функции вам понадобятся, то они должны обеспечиваться приложением. Если UDP-пакет потерян, он не будет отправлен повторно автоматически. Если пакеты UDP приходит не по порядку,
приложение более высокого уровня все равно сможет их просмотреть.
Первоначально UDP использовался для приложений, которым не требовалась гарантированная доставка (например, видео, в котором неко1

https://lists.w3.org/Archives/Public/ietf-http-wg/2018OctDec/0065.html.

346

Глава 9

TCP, QUIC и HTTP/3

торые фреймы могли быть отброшены без особых потерь). UDP также
идеально подходит для мультиплексированного протокола, такого как
HTTP/2. Он обеспечит лучшее решения проблем, чем TCP.

Почему бы просто не улучшить TCP?
Самый очевидный вопрос: почему бы просто бы не улучшить TCP? TCP
все еще совершенствуется, и проблемы в нем можно решить путем дальнейших улучшений. Проблема здесь заключается в скорости внедрения
подобных улучшений. TCP встроен в операционную систему, и, хотя некоторые изменения могут быть внесены на стороне сервера, предпочтительнее делать это с помощью обновлений системы. Проблема не в том,
что операционные системы не могут внедрять инновации, а в том, что
для этого требуется очень много времени. TCP Fast Open – яркий тому
пример; он предлагает значительные преимущества, но пока не получил
широкой популярности у браузеров и серверов.
Затяжной процесс внедрения инноваций усугубляется инфраструктурой
Internet. Ей свойственно делать определенные предположения о протоколах, таких как TCP, и, когда в реальности все происходит по-другому, возникают ошибки. Такая проблема известна как окостенение протокола. Отказавшись от TCP, QUIC получает больше свободы и меньше ограничений.

Почему бы не использовать SCTP?
Вместо того чтобы создавать новый транспортный протокол на базе UDP
или ждать, пока инновации в TCP станут более широко распространенными, QUIC мог бы использовать протокол передачи с управлением потоком (Stream Control Transmission Protocol, SCTP). Данный протокол имеет
много общих характеристик с QUIC, например потоковую передачу надежных сообщений. С 2007 года SCTP существует как самостоятельный
стандарт.
К сожалению, статус официального стандарта не гарантирует популярности протокола. SCTP был принят не слишком тепло, поскольку
существовал TCP, который прекрасно справлялся с его задачами. Следовательно, переход на SCTP может занять столько же времени, сколько
и обновление TCP. И даже после такого шага совершенствование протокола, скорее всего, прекратится. QUIC стремится улучшить функцию
контроля перегрузки на уровне потока и решить другие проблемы, влияющие на работу всего Internet, такие как установка HTTPS-соединений,
ограниченная потеря пакетов и миграция соединения.

Почему бы не использовать IP напрямую?
Другой вариант, который могли бы использовать разработчики QUIC, –
это построение его на основе IP, поскольку требования транспортного
уровня обычно невелики. IP – это не что иное, как IP-адрес источника
и места назначения; поверх него можно построить все, что угодно.
Однако в таких случаях возникают те же проблемы, что и при использовании SCTP. Протокол должен быть реализован на уровне операцион-

QUIC

347

ной системы, поскольку лишь немногие приложения получают прямой
доступ к IP-пакетам. Кроме того, QUIC должен быть направлен на конкретное приложение, поэтому ему нужны порты, которые есть у UDP.
Многие клиенты могут открывать отдельные HTTP-соединения через
QUIC, например, для одновременного запуска Chrome и Firefox, а также,
возможно, еще какой-нибудь программы, использующей HTTP. Без этой
функции приложение, контролирующее QUIC, должно было бы читать
все пакеты QUIC и при необходимости направлять их в каждое приложение.

Преимущества UDP
UDP – это базовый протокол, который встроен в ядро операционной системы. Все, что построено на его основе, должно быть построено на уровне пользовательского пространства. Находясь за пределами ядра, можно быстро внедрять инновации за счет развертывания приложения на
любой стороне. Google использует QUIC во всех своих сервисах браузера
Chrome. Просмотреть текущую используемую версию QUIC (версия 43 на
момент написания этой книги) вы можете, открыв инструменты разработчика и перейдя на сайт Google, как показано на рис. 9.19.

Рис. 9.19. Просмотр текущей версии QUIC на www.google.com

За несколько лет существования QUIC Google создал 43 его версии1.
Как и при развертывании SPDY, Google легко и незаметно для пользователей смог развернуть изменения в основном клиенте, используемом
для просмотра веб-страниц (Chrome), и некоторых из самых популярных
серверов. По состоянию на 2017 год около 7 % сервисов в Internet используют QUIC2, хотя эту цифру, скорее всего, составляют в основном сайты
Google.
Быстрая реализация QUIC была возможна только при использовании
в качестве основы протокола UDP. Принудительного изменение суще1

2

История версий подробно описана в исходном коде: https://chromium.googlesource.com/chromium/src/+/master/net/third_party/quic/core/quic_versions.h.
https://ai.google/research/pubs/pub46403.

Глава 9

348

TCP, QUIC и HTTP/3

ствующих протоколов потребовало бы слишком много времени, и, вероятно, было бы заблокировано большей частью существующей инфраструктуры Internet. Использование простого протокола UDP позволило
Google создавать и совершенствовать протокол по своему усмотрению,
поскольку компания могла контролировать обе стороны соединения.
Конечно, у UDP тоже есть свои проблемы. Данный протокол не такой распространенный, как TCP. Например, DNS работает через UDP
по причине его простоты (хотя есть шаги, позволяющие DNS работать
через HTTPS, как обсуждается в главе 10). Другие приложения (например, потоковое видео в реальном времени и онлайн-видеоигры) также
используют UDP, поэтому он поддерживается подавляющей частью сетевой инфраструктуры. Однако TCP гораздо более распространен, а UDP
часто по умолчанию блокируется межсетевыми экранами и трафиком
промежуточного программного обеспечения. В этом случае Chrome изящно возвращается к HTTP/2 через TCP. По началу это было серьезной
проблемой, но эксперименты Google показали, что 93 % UDP-трафика
обрабатывается нормально, и со временем этот показатель даже улучшился. Иногда инфраструктура все же блокирует трафик UDP для HTTP
(где также используется порт 443), однако в большинстве случаев этого не происходит. На UDP, если он станет стандартном (как, по крайней
мере, для сервисов Google), будет очень легко перейти.
Другая проблема UDP заключается в том, что пользовательское пространство бывает менее эффективным, чем оптимизированное пространство ядра. Измерения QUIC на раннем этапе показывают, что серверы используют в 3,5 раза больше ресурсов ЦП, чем эквивалентные
серверы на основе TLS/TCP1. Несмотря на оптимизацию, исследования
показывают, что UPD является более затратным протоколом и, вероятно,
останется таковым до тех пор, пока он будет находиться вне ядра.

Всегда ли QUIC будет использовать UDP?
В исходном FAQ, выпущенном при запуске QUICa, Google заявила: «Мы надеемся, что функции QUIC будут перенесены на TCP и TLS, если они окажутся
эффективными».
Так что вполне вероятно, что объектом экспериментов в ближайшее время
станет UDP, а TCP будет развиваться более медленными темпами. Вернется
ли QUIC к TCP? На этот вопрос сложно ответить, но я считаю, что отказаться
от свободы развития Google будет сложно. Internet, похоже, находится в фазе
инноваций транспортного уровня; сложно представить, что разработчики
протоколов захотят прекратить вводить новшества и перейти к фиксированному протоколу, который будет сложно обновить.
Кроме того, по сравнению с TCP, QUIC вносит фундаментальные изменения,
которые будет нелегко внедрить в TCP, даже если бы для этого существовал
стимул.

1

https://dl.acm.org/citation.cfm?id=3098842.

QUIC

349

Более вероятно, что HTTP по-прежнему будет доступен как для TCP (HTTP/2),
так и для UDP (QUIC и HTTP/3), но реализация TCP будет отставать от UDP
с точки зрения функций и производительности.
___________________________________
a
 https://docs.google.com/document/d/1lmL9EF6qKrk7gbazY8bIdvq3Pno2Xj_l_
YShP40GLQE/.

9.2.4 Стандартизация QUIC
QUIC был представлен компанией Google в июне 2013 года1. На протяжении следующих двух лет Google занималась доработкой протокола
и в июне 2015-го предложила его Инженерной группе Internet (IETF)
в качестве стандарта2. Google подала протокол на рассмотрения в крайне удачный момент, ведь предыдущий стандарт компании (SPDY) был
официально принят как HTTP/2; люди, связанные с разработкой HTTP/2
могли работать и над QUIC. Несколькими месяцами позже для разработки и оформления стандарта нового протокола была создана рабочая
группа IETF QUIC3.

Два варианта QUIC: gQUIC и iQUIC
Как и SPDY, QUIC развивался под руководством Google параллельно
с разработкой стандарта. Ввиду этого на момент написания этой книги
существует две реализации протокола gQUIC (Google QUIC) и iQUIC (IETF
QUIC). Рабочая среда Google использует gQUIC; компания продолжает
развивать и улучшать ее на свое усмотрение, без необходимости официального подтверждения каждого изменения.
Ожидается, что gQIUC, подобно SPDY, выйдет из использования после
стандартизации iQUIC (скорее всего, это произойдет в начале 2019 года).
Однако на данный момент gQUIC является единственной версией протокола, которую можно использовать в рабочих средах.
QUIC (а именно gQUIC) используется только Chrome и браузерами на
базе Chromium, например Opera. В свою очередь gQUIC претерпевает
частые изменения по мере внедрения в него нововведений командой
Google4. На серверной стороне его поддерживают все сервисы Google.
Другие реализации веб-серверов на момент написания этой книги используют Caddy5 и LiteSpeed6, но, поскольку они основаны на развивающемся нестандартизированном gQUIC, эти реализации меняются вместе
1
2
3
4

5
6

https://blog.chromium.org/2013/06/experimenting-with-quic.html.
https://datatracker.ietf.org/doc/draft-tsvwg-quic-protocol/00/.
https://datatracker.ietf.org/wg/quic/about/.
См. раздел «Последние изменения версии» на странице https://docs.google.
com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/.
https://github.com/mholt/caddy/wiki/QUIC.
https://blog.litespeedtech.com/2017/07/11/litespeed-announces-quic-support/.

Глава 9

350

TCP, QUIC и HTTP/3

с ним, и в случае, если обновления задерживаются, реализации могут
перестать работать в Chrome1.

Различия между gQUIC и iQUIC
По мере развития каждого из протоколов различия становятся все ощутимее, однако на момент написания этой книги одно из основных различий между gQUIC и iQUIC заключается в уровне шифрования. Google
применяет специализированный способ криптографии, а iQUIC работает с TLSv1.32. Выбор Google обусловлен тем, что на момент создания
QUIC TLSv1.3 попросту не существовало. Google заявила, что заменит
свой способ криптографии на TLSv1.3, когда тот будет признан официально3, что на данный момент уже произошло, поэтому gQUIC и iQUIC
постепенно сходятся к одной точке. Между этими двумя протоколами
существует несколько других различий, и в этих точках они несовместимы, но на концептуальном уровне, за исключением использования
TLSv1.3, они похожи.

Стандарты QUIC
На данный момент у QUIC нет одного общепринятого стандарта, зато
есть целых шесть отдельных. Как и HTTP/2, который состоит из двух
стандартов (HTTP/2 и HPACK), QUIC имеет отдельные стандарты для своих основных частей:
4
„„ QUIC Invariants – стандарт для неизменных частей протокола;
5
„„ QUIC Transport – основной транспортный протокол;
6
„„ QUIC Recovery – контроль потери пакетов и перегрузки;
7
„„ QUIC TLS – применение шифрования TLS в QUIC;
8
„„ HTTP/3 – протокол, в значительной степени основан на HTTP/2
с некоторыми изменениями;
9
„„ QUIC QPACK – сжатие HTTP-заголовков в QUIC.
Также был предложен еще один экспериментальный документ под названием QUIC Spinbit10, который добавлял бы отдельный бит, используемый при мониторинге зашифрованных соединений QUIC. Кроме того,
доступны два дополнительных информационных документа по исполь-

1
2
3

4
5
6
7
8
9
10

https://github.com/mholt/caddy/issues/2194.
https://tools.ietf.org/html/rfc8446.
https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g.
https://tools.ietf.org/html/draft-ietf-quic-invariants.
https://tools.ietf.org/html/draft-ietf-quic-transport.
https://tools.ietf.org/html/draft-ietf-quic-recovery.
https://tools.ietf.org/html/draft-ietf-quic-tls.
https://tools.ietf.org/html/draft-ietf-quic-http.
https://tools.ietf.org/html/draft-ietf-quic-qpack.
https://tools.ietf.org/html/draft-ietf-quic-spin-exp.

QUIC

351

зованию QUIC: для разработчиков приложений1 и для распространения
QUIC в сети2.
На данный момент IEFT еще работает над этими документами. Поскольку стандарт все еще находится в стадии разработки, эти спецификации могут претерпеть значительные изменения, а также может измениться их количество.
Следует отметить один важный момент: QUIC задуман как протокол
общего назначения; HTTP – это только одна из областей, где его можно
применить. На сегодняшний день HTTP является основным вариантом
использования QUIC (чем преимущественно и занимается рабочая группа), протокол все же разрабатывается с учетом других возможных вариантов использования.

9.2.5 Различия между HTTP/2 и QUIC
QUIC основан на HTTP/2, поэтому многие из основных концепций, представленных в этой книге, пригодятся, когда QUIC станет официальным
стандартом и выйдет за пределы серверов и браузеров Google. Однако
существуют и различия, например HTTP/2 и QUIC созданы на базе разных протоколов. В разделах ниже я расскажу и о других различиях.

QUIC и HTTPS
HTTPS встроен в QUIC и, в отличие от HTTP/2, это закрывает незашифрованным HTTP-соединениям доступ к QUIC. Такой вариант был
выбран по тем же идеологическим и практическим причинам, что
и в HTTP/2, просмотр страниц в котором доступен только через HTTPS
(см. главу 3).
С практической точки зрения шифрование данных гарантирует, что
сторонние элементы инфраструктуры не будут невольно вмешиваться
в его работу. Сейчас эта ситуация может показаться не такой уж серьезной (ведь ни один элемент инфраструктуры не будет работать с UPD через HTTP-трафик), однако она уже вызвала проблемы для QUIC, поскольку промежуточные устройства, в сравнении с QUIC, несколько устарели3.
По мере развития протокола становится еще более важным аспект предотвращения окостенения, что мы можем наблюдать в случае с TCP по
причине использования промежуточных устройств, управляющих трафиком TCP. QUIC стремится к шифровке наибольшего количества данных. Предложение разрешить использование одного незашифрованного
бита, позволяющего промежуточным устройствам отслеживать трафик4,
было принято негативно5, и на момент написания этой книги разработ1
2
3
4
5

https://tools.ietf.org/html/draft-ietf-quic-applicability.
https://tools.ietf.org/html/draft-ietf-quic-manageability.
https://datatracker.ietf.org/doc/draft-ietf-quic-spin-exp/.
https://news.ycombinator.com/item?id=16695816.
https://news.ycombinator.com/item?id=16695816.

Глава 9

352

TCP, QUIC и HTTP/3

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

Установка QUIC-соединения
В HTTP/2 существовало несколько методов согласования протокола:
ALPN, заголовок Upgrade, использование заранее известного протокола и HTTP-заголовок или HTTP/2-фрейм Alt-Svc. Все они предполагают
использование TCP. Поскольку QUIC основан на UDP, веб-браузер, подключающийся к веб-серверам, должен запускать соединение по TCP
и затем переходить до QUIC1. Такой процесс вводит QUIC в зависимость
от HTTP и, следовательно, сводит на нет одно из его ключевых преимуществ (значительное сокращение времени установки соединения). В качестве альтернативы можно использовать TCP и UDP одновременно или
же смириться со скачками производительности при установке соединения, возможно, вспомнив в следующий раз, что сервер использует QUIC.
В любом случае для HTTP/3 будет установлен идентификатор ALPN и AltSvc h3 (примечание: до того, как было принято имя HTTP/3, идентификатором для HTTP через QUIC был hq). Данный идентификатор следует
использовать только для стандартизированного iQUIC; существующие
реализации gQUIC не должны использовать это значение2.

QPACK
Алгоритм сжатия заголовков HPACK в силу характера TCP гарантирует упорядоченное получение фреймов HTTP-заголовков, чтобы динамическая
таблица поддерживалась обеими сторонами, как показано на рис. 9.20.
Запрос 2 использует индексы заголовка, определенные в запросе 1 (62
и 63). При потере части запроса 1 заголовок не может быть прочитан полностью, поскольку состояние динамической таблицы не известно. Таким
образом, запрос 2 не может быть обработан до тех пор, пока не будут
получены отсутствующие пакеты, иначе это может привести к использованию неверных ссылок. QUIC стремится устранить необходимость
в упорядоченной доставке пакетов на уровне соединения, чтобы потоки могли обрабатываться независимо. HPACK требует именно этого (по
крайней мере, для кадров заголовков), ввиду чего возникает проблема
HOL-блокировки, с которой призван бороться QUIC.
Именно поэтому HTTP/3 нуждался в обновленном варианте HPACK;
его создали и по понятным причинам назвали QPACK. Данный алгоритм
довольно сложен, и на момент написания этой книги его все еще дорабатывают. Однако именно он вводит концепцию подтвержденных заголовков. Если отправителю необходимо использовать неподтвержденный заголовок, он может использовать его (и тогда его, возможно, заблокируют
в этом потоке) или может отправить заголовок с литеральными константами (что предотвратит блокировку за счет менее эффективного сжатия
для этого значения заголовка).
1
2

https://tools.ietf.org/html/draft-ietf-quic-http-12#section-2.1.
https://github.com/w3c/navigation-timing/issues/71.

353

QUIC

QPACK отличается от HPACK еще по нескольким пунктам. В нем бит
определяет, используется ли статическая или динамическая таблица
(в то время как в HPACK отсчет начинается с 61). Кроме того, в QUIC заголовки можно легко и эффективно дублировать; это делается для того,
чтобы заголовки ключей (такие как Authority и user-agent) оставались
в верхней части динамической таблицы и передавались посредством
меньшего количества битов.
Статическая таблица HPACK
Значение
индекса
1
2
3
4

58

61
Запрос 1
Заголовок
:method
:authority

Значение
заголовка
GET
www.example.com

:path
/
user-agent Chrome-69

:method
:authority
:path

Значение
заголовка
GET
www.example.com
/styles.css

user-agent Chrome-69

Рис. 9.20

:authority
:method
:method
:path

user-agent

www-authenticate

Значение
заголовка
GET
POST
/



Сжатый запрос 1
Заголовок

Значение
заголовка

Индексировано 2
www.example.com
Литеральный
индекс 24
с индексацией
Индексировано 4
Chrome-69
Литеральный
индекс 56
с индексацией

Запрос 2
Заголовок

Имя заголовка

Сжатый запрос 2
Заголовок

Значение
заголовка

Индексировано 2
Индексировано 63 www.example.com
Литеральный
индекс 4
без индексации
Индексировано 62 Chrome-69

Динамическая таблица после запроса 1
Значение
индекса
62
63

Имя
заголовка
user-agent
:authority

Значение
заголовка
Chrome-62
www.example.com

Динамическая таблица после запроса 2
Значение
индекса
62
63

Имя
заголовка
user-agent
:authority

Значение
заголовка
Chrome-62
www.example.com

Пример сжатия HPACK

Другие различия
В QUICK были внесены еще несколько изменений касаемо функционирования фреймов и потоков1. От уровня HTTP/3 отделяют некоторые
фреймы протоколов транспортного уровня (например, фреймы PING
1

https://github.com/quicwg/wg-materials/blob/master/interim-18-06/HTTP.pdf.

Глава 9

354

TCP, QUIC и HTTP/3

и WINDOW_UPDATE). Они перемещаются на базовый уровень QUIC-Transport,
не связанный с HTTP (что имеет смысл, поскольку эти фреймы, вероятно, будут использоваться для протоколов, отличных от HTTP). Также разработчики отказались от фрейма CONTINUATION, который в HTTP/2
практически не использовался. Еще произошли некоторые изменения
в плане форматирования фреймов, однако, поскольку протокол еще находится в стадии развития, я считаю, что не стоит обсуждать их сейчас.
По сути, QUIC во многих аспектах похож на HTTP/2, поэтому, усвоив информацию о предыдущей версии, читатели будут иметь более или менее четкое представление о QUIC и HTTP/3, когда они будут официально
стандартизированы и станут доступными для клиентских и серверных
реализаций.

9.2.6 Инструменты QUIC
Поскольку QUIC еще не стандартизирован, для пользователей доступен
только gQUIC, хотя многие разработчики все же работают над реализациями iQUIC1. Наилучшим инструментом для работы с QUIC является
Chrome, когда он соединен с сервером Google. В QUICK, также как в HTTP/2,
существует страница net-export (см. раздел 4.3.1). Кликнув на строку сеанса QUICK, вы увидите окно наподобие показанного на рис. 9.21.

Рис. 9.21

Просмотр данных QUIC в Chrome

Другие инструменты, такие как Wireshark, поддерживаются gQUIC, как
видно на рис. 9.22.
1

https://github.com/quicwg/base-drafts/wiki/Implementations.

QUIC

Рис. 9.22

355

gQUIC в Wireshark

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

9.2.7 Реализации QUIC
При создании реализаций QUIC история повторяется. У Caddy была реа­
лизация gQUIC, основанная на реализации QUIC на языке программирования Go, однако на данный момент от нее отказались1. Она будет
доступна благодаря компиляции Caddy из исходного кода и должна появиться в следующей версии. Версия Go2 часто обновляется, поэтому,
если вы загрузите последнюю версию, Chrome должен иметь возможность взаимодействовать с ней по gQUIC. LiteSpeed также представил
реализацию QUIC3. Она была представлена в июне 2017 года и сейчас
1
2
3

https://github.com/mholt/caddy/issues/2190.
https://github.com/lucas-clemente/quic-go.
https://blog.litespeedtech.com/2017/06/26/litespeed-is-powered-by-quic/.

Глава 9

356

TCP, QUIC и HTTP/3

еще дорабатывается, однако версия с открытым исходным кодом еще
не поддерживает ее, поэтому это не лучший инструмент для экспериментов с QUIC, если вы не пользуетесь LiteSpeed. LiteSpeed также открыл
исходный код клиента QUIC1, который иногда может быть крайне полезен. Совсем недавно, в мае 2018 года, о поддержке gQUIC на своей сетевой платформе доставки контента объявила компания Akamai2, а в июне
2018 го­да Google объявила о поддержке gQUIC для своего балансировщика нагрузки Google Cloud Platform3, так что те, кто использует эту платформу, получили gQUIC буквально из первых рук.

9.2.8 Стоит ли переходить на QUIC?
В отличие от SPDY gQUIC не был тепло принят большей частью широкой публики. Вероятно, с iQUIC такого не произойдет. На данный момент
я не могу однозначно рекомендовать вам перейти на QUIC, за исключением случаев использования облачной платформы Google. Для всех,
кто хочет поэкспериментировать с QUIC, наилучшим вариантом будет
делать это с помощью Go, однако его не следует использовать в рабочей
среде для браузеров. Реализация браузера Chrome может немного измениться, ведь Chrome отключает старые версии gQUIC в браузерах сразу
после развертывания новых версий.
Я считаю, что после стандартизации iQUIC появятся новые реализации. На данный момент их намного меньше в сравнении с SPDY. Скорее
всего, развертывание QUIC и HTTP/3 займет больше времени, чем развертывание HTTP/2, поскольку в нем гораздо больше изменений, а также он основан на UDP, а не TCP. QUICK имеет серьезные перспективы.
Вероятно, что через несколько лет он будет использоваться так же широко, как HTTP/2. Благодаря Google, а также CND, обслуживающих большую часть трафика, повсеместный переход на QUIC может значительно
ускориться, однако какой-то процент небольшихкомпаний и серверов
останется на TCP и HTTP/2 (или даже HTTP/1.1).

Резюме
Сетевой стек HTTP имеет несколько недостатков на уровнях TCP
и HTTPS.
„„ Ввиду весьма осторожного алгоритма контроля перегрузки при установке TCP-соединения требуется много времени для достижения
максимальной пропускной способности, к тому же процесс замедляет
квитирование HTTPS.
„„

1
2

3

https://github.com/litespeedtech/lsquic-client.
https://community.akamai.com/customers/s/article/FAQ-QUIC-Native-PlatformSupport-for-Media-DeliveryProducts?language=en_US.
https://cloudplatform.googleblog.com/2018/06/Introducing-QUIC-support-forHTTPS-load-balancing.html.

Резюме

357

Существуют новые технологии, устраняющие эту проблему, однако их
внедрение на стороне TCP происходит слишком медленно.
„„ QUIC – это новый протокол, основанный на UDP.
„„ Благодаря UDP, в QUIC инновации внедряются намного быстрее, чем
TCP.
„„ QUIC построен на базе HTTP/2, он включает в себя как уже существующие концепции, так и совершенно новые.
„„ QUIC предназначен не только для HTTP; в будущем он может использоваться и для других протоколов.
„„ HTTP, реализованный через QUIC, будет называться HTTP/3.
„„ У QUIC есть две версии: Google Quic (gQUIC) с ограниченным доступом
и ETF QUIC (iQUIC), который в настоящее время находится в процессе
стандартизации.
„„ Ожидается, что gQUIC будет заменен iQUIC (по завершении разработки и оформления его стандарта), так же как HTTP/2 в свое время заменил SPDY.
„„

10

Дальнейшее развитие HTTP

В этой главе мы рассмотрим:

споры, связанные с HTTP/2;
работу с HTTP/2-сервером после его запуска;
„„ HTTP вне HTTP/2;
„„ HTTP как базовый транспортный уровень.
„„
„„

Спецификация HTTP/2 была опубликована в мае 2015 года, почти через
20 лет после выхода HTTP/1.0, который вскоре был заменен HTTP/1.1. За
это время Internet стал неотъемлемой частью жизни каждого человека,
и успех HTTP/1.1 многое говорит нам о качестве протокола. Однако постепенно этот протокол начал отставать от ритма современной жизни,
а все попытки его инноваций1 ограничивались лишь уточнением документации или введением новых функцией ограниченного характера, реализуемых посредством HTTP-заголовков.
На смену HTTP/1 пришел HTTP/2, которым сегодня активно пользуется большая часть пользователей2. Однако что ждет HTTP в будущем?
Что на самом деле представляет из себя работа с HTTP/2 на практике? До
конца ли решены проблемы HTTP? Сколько нам придется ждать появления новой версии: 20 лет или намного меньше? В этой главе я попытаюсь
ответить на все эти вопросы, а также поделюсь с читателями некоторыми обоснованными предположениями о развитии HTTP.
1
2

https://www.w3.org/Protocols/HTTP-NG/Activity.html.
https://w3techs.com/technologies/details/ce-http2/all/all.

Споры о HTTP/2 и его недостатках

359

10.1 Споры о HTTP/2 и его недостатках
На протяжении всего процесса стандартизации HTTP/2 возникало большое количество разногласий, особенно сильно ситуация обострилась,
когда процесс приближался к завершению и ратификации. Одни аргументы были убедительными, другие – не очень1. Одни считали, что
SPDY не следует использовать как основу для HTTP/2, а другие говорили
о нерешенных проблемах конфиденциальности. Кроме того, были высказаны аргументы за и против применения в протоколе техники принудительного шифрования. В последующих разделах я расскажу об этих
и многих других спорных моментах, касающихся HTTP/2.
Множество споров возникало в списке рассылки рабочей группы
HTTP (HTTP-WG) Инженерной группы Internet2, а также в более широком
Internet-сообществе на таких сайтах, как HackerNews3,4,5, SlashDot6 и The
Register7. В свою очередь разработчиками протокола было выдвинуто
множество контраргументов.
Сейчас, когда протокол приобрел устойчивое положение и обсуждается уже его будущее, стоит оглянуться назад и еще раз рассмотреть каждый из возникших в то время споров. Таким образом, мы сможем понять, какие проблемы на сегодняшний день остаются актуальными и что
полезного из этого могут извлечь разработчики.

10.1.1 Споры о SPDY
HTTP/2 основывается на протоколе SPDY, разработанном компанией
Google. SPDY – протокол прикладного уровня, который можно было внедрить и развернуть в Internet. Успех SPDY побудил IETF рассмотреть вопрос об обновлении HTTP8. Разумеется, IETF не ограничивался только
лишь SPDY, однако именно этот протокол послужил основой для HTTP/2.
Многих людей не устроило, что самому HTTP/2 уделялось несколько
меньше внимания, чем SPDY.

Был ли SPDY единственным вариантом для HTTP/2?
В отношении HTTP/2 в уставе рабочей группы HTTP9 сказано следующее:
Мы стремимся создать протокол, который сохранит семантику HTTP, но не унаследует структуру и синтаксис сообщений
1
2
3
4
5
6
7
8
9

https://lists.w3.org/Archives/Public/ietf-http-wg/2015JanMar/0043.html.
https://lists.w3.org/Archives/Public/ietf-http-wg/.
https://news.ycombinator.com/item?id=8850059.
https://news.ycombinator.com/item?id=9022470.
https://news.ycombinator.com/item?id=9066379.
https://tech.slashdot.org/story/15/01/09/0118226/http2---the-ietf-is-phoning-it-in.
https://www.theregister.co.uk/2015/02/18/http2_specification_approved/.
https://lists.w3.org/Archives/Public/ietf-http-wg/2012JanMar/0098.html.
https://datatracker.ietf.org/wg/httpbis/charter/.

Глава 10

360

Дальнейшее развитие HTTP

предыдущих версий. Опыт реализации прошлых версий показал,
что методы, используемые в них, снижают производительность
и приводят к неправильному функционированию лежащего в основе транспортного протокола.
Путем внедрения концепции упорядоченных двунаправленных потоков рабочая группа создаст абсолютно новую форму уже существующей семантики HTTP. Как и для предыдущих версий, приоритетной основой станет TCP, однако возможно использование
и других транспортных протоколов.
Отправной точкой работ стал draft-mbelshe-httpbis-spdy-00.
Данное заявление не оставляло сомнений в том, что в основу HTTP/2
должен был лечь SPDY. Несмотря на его успех, многие люди сомневались,
что он пригоден для того, чтобы стать основой новой версии. Некоторые
считали, что IETF следует расширить свои взгляды на совершенствование HTTP и отойти от старых структур. Также был предложен независимый план-график сроком на два года1, и многие верили, что только его
соблюдение сделает SPDY приемлемым вариантом.
Рабочей группой также были рассмотрены два других предложения:
протокол HTTP Speed and Mobility от Microsoft2 (основанный на SPDY
и WebSockets3) и Network Friendly HTTP Upgrade4. Во многом оба предложения были схожи с SPDY (что не удивительно, учитывая параметры,
в которых был определен HTTP/2). Также они оба были направлены на
введение уровня двоичного фрейминга и усовершенствование HTTPзаголовков.
Однако в конечном итоге SPDY все же превзошел всех потенциальных
конкурентов. Сегодня его поддерживает не только Google, но и другие
популярные веб-серверы и веб-браузеры; многие сайты уже перешли на
SPDY или находятся в процессе перехода на него.
Facebook представил предварительный анализ развития для каждого
предложения5 и выяснил, что SPDY все же является предпочтительным
вариантом. Таким образом, в процессе стандартизации взятый за основу
SPDY был изменен и улучшен. HTTP/2 – это не SPDY, однако они имеют
немало общего.
Основная проблема заключалась в том, что возможность выйти за рамки SPDY была упущена. SPDY был разработан для решения одной серьезной проблемы с производительностью HTTP/1 – и, хотя он справлялся
с этой задачей хорошо, он не решал другие проблемы, связанные с HTTP,
например касающиеся файлов куки. Учитывая, что предыдущие обновления, такие как HTTP-NG, потерпели неудачу в значительной степени
1
2
3

4
5

https://lists.w3.org/Archives/Public/ietf-http-wg/2012OctDec/0003.html.
https://tools.ietf.org/html/draft-montenegro-httpbis-speed-mobility.
https://blogs.msdn.microsoft.com/interoperability/2012/03/25/speed-and-mobility-an-approach-for-http2-0-to-make-mobile-apps-and-the-web-faster/.
https://tools.ietf.org/html/draft-tarreau-httpbis-network-friendly.
https://lists.w3.org/Archives/Public/ietf-http-wg/2012JulSep/0251.html.

Споры о HTTP/2 и его недостатках

361

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

SPDY и Google
Помимо прочего, многих беспокоило что SPDY является результатом деятельности одной компании: Google. Несмотря на то что им пользовались
различные веб-сайты разной величины (включая Yahoo!, Twitter и Facebook), он разрабатывался Google и принадлежал только этой компании.
Некоторые люди негодовали, что Google, уже имеющая значительное
влияние в сети, продвигает протокол собственного производства и, следовательно, хочет выйти на уровень более широкого веб-сообщества. Некоторые основания для этого недоверия имелись, поскольку компания
действительно является одной из главных в веб-индустрии. Кроме того,
большую часть дохода Google составляет веб-реклама, которая неблагоприятным образом может сказываться на конфиденциальности данных
и отслеживании пользователей. HTTP/2 отличился тем, что он не был нацелен на решение проблем конфиденциальности, которые многие люди
считали приоритетной. Однако я не считаю, что под отстранением от
этих проблем компания скрывала злонамеренные цели. Я думаю, что
в Google всего-навсего выбрали производительность приоритетной задачей.
Данный аргумент, однако, упускает из виду ключевое значение стандартизации SPDY для HTTP/2: отказ от зависимости от одной компании,
ввиду чего веб-сообщество и сообщество интернет-стандартов (IEFT)
смогут пересматривать и улучшать протокол. Google продолжает оставаться одним из ведущих новаторов в Internet. Компании принадлежит
множество других достижений в области веб-стандартов, о некоторых из
которых (например, QUIC; см. главу 9) я рассказываю в этой книге. Я считаю нецелесообразным игнорировать нововведения Google, поскольку
большинство из них крайне полезно.

10.1.2 Проблемы конфиденциальности и состояния в HTTP
Одним из объектов споров в отношении HTTP/2 была конфиденциальность, особенно это касалось файлов куки HTTP. Ввиду негативного
влияния на безопасность и конфиденциальности их зачастую называют одной из самых больших проблем HTTP. По задумке разработчиков,
HTTP – это протокол, не предусматривающий сохранение состояний.
В HTTP/2 этот аспект не изменился. Получается, что любой запрос к серверу не должен быть связан с предыдущими или будущими запросами.
Однако на практике выясняется, что многие современные приложения и веб-сайты требуют наличия в протоколе концепции состояния. Например, когда вы добавляете что-то в корзину на Amazon, покупка оста-

Глава 10

362

Дальнейшее развитие HTTP

ется там, несмотря на ваши последующие действия на странице. Также,
когда вы входите в систему интернет-банкинга, вам не нужно входить
в систему при каждом последующем действии (по крайней мере, во время этого сеанса). Следовательно, ввиду отсутствия в HTTP связей между
запросами возникла необходимость добавить этот аспект в протокол,
а поскольку соединения не всегда сохранялись или использовались повторно, данная функция не могла быть реализована на уровне соединения.
У этой проблемы есть несколько решений. Например, вы можете добавлять в URL-адреса параметры идентификатора сеанса (например,
http://www.example.com?SESSIONID=12345), однако они выглядят громоздко, могут запутать пользователя, а также несут в себе угрозу безопасности, поскольку такие URL могут быть использованы или добавлены в закладки другими пользователями. Решить эту проблему были
призваны файлы куки1. Они представляют собой небольшие фрагменты
информации, хранящиеся в браузере и отправляемые им при каждом
запросе. Файлы куки позволяют использовать в HTTP идентификаторы
сеансов или другие настройки. В последнее время репутация этих файлов в глазах пользователей сильно упала, однако на самом деле они носят нейтральный характер, а использование их альтернатив (например,
параметры URL-адреса) зачастую влечет за собой более серьезные проблемы.
Файлы куки заслужили дурную репутацию по нескольким причинам,
включая следующие:
„„ они позволяют отслеживать пользователя в целях внедрения рекламы (а иногда и более страшных вещей);
„„ по умолчанию они небезопасны;
„„ они отправляются с каждым запросом.

Куки и стороннее отслеживание
Файлы куки могут использоваться не только сайтом, но и любым ресурсом, который веб-браузер загружает для этого сайта. Использование так
называемых сторонних файлов куки возможно, когда веб-сайт (например, www.example.com) загружает контент с рекламного сайта (например, adwords.google.com) и устанавливает файл куки, который можно
использовать на других веб-сайтах (например, www.example2.com). Такой куки также ссылается на сторонний рекламный веб-сайт (adwords.
google.com), как показано на рис. 10.1.
На основе истории просмотров при посещении других веб-сайтов на
них может отображаться соответствующая реклама, и часто это происходит без ведома пользователя. В результате Европейский союз (ЕС) ввел
в действие так называемый закон о файлах куки, согласно которому вебсайты должны информировать пользователей о том, что они используют
файлы куки, как показано на рис. 10.2.
1

https://tools.ietf.org/html/rfc6265.

Споры о HTTP/2 и его недостатках

www.example.com
1. Запрос страницы
2. Страница с контентом
adwords.google.com

3. Запрос содержания
объявления

4. Объявление
плюс куки

adwords.google.com

www.example2.com
5. Запрос страницы
6. Страница с контентом
adwords.google.com

7. Запрос содержания
объявления (на этот
раз с куки)

8. Индивидуальная реклама на основе
других посещенных сайтов
(например, www.example.com)

adwords.google.com

Рис. 10.1 Отслеживание сторонними файлами куки

Рис. 10.2 Предупреждение об использовании куки на веб-сайте правительства
Великобритании

363

Глава 10

364

Дальнейшее развитие HTTP

Веб-пользователям (особенно в ЕС) приходилось скрывать сообщение «Этот веб-сайт использует файлы куки» на каждом сайте, поскольку
большинство из них использовало файлы куки. В 2018 году, благодаря
Общему регламенту по защите данных (General Data Protection Regulation, GDPR), в силу вступило еще более жесткое требование, согласно
которому эти предупреждения должны были быть намного больше. Однако для большинства пользователей предупреждения – это всего лишь
всплывающие окна, которые необходимо срочно закрыть, чтобы они не
мешали, и вряд ли они задумываются о том, что GDPR, который является
законом о защите прав потребителей и направлен на сохранение данных
пользователя, вообще существует.

HTTP-куки и безопасность
Другая проблема заключается в том, что файлы куки по умолчанию небезопасны. Атрибуты, призванные решить эту проблему (например,
флажки Secure1 и HttpOnly2), крайне неудобны в использовании, поскольку требуют, чтобы программисты устанавливали их при создании файлов куки. И даже в случае, если они это сделают, данный способ не гарантирует предотвращения, например, перезаписи безопасных файлов
куки небезопасными. На момент написания этой книги исследования
показывают, что эти параметры встроены примерно в 8 % файлов куки3,
что свидетельствует о том, что подавляющее большинство файлов куки
по умолчанию небезопасно.
Это плохо, потому что файлы куки, в которых хранятся идентификаторы сеанса, предлагают полный доступ к учетной записи, поэтому перехват файла куки практически аналогичен перехвату имени пользователя
и пароля. По этой причине вы могли подумать, что файлы куки будут
отключены (и их можно отключить), но по умолчанию это не так.
Файлы куки отправляются как с HTTP, так и с HTTPS-запросами (если
при создании куки не используется атрибут Secure). Ввиду затрат на установку HTTPS-соединений (что по большей части больше не проблема) их
используют лишь на страницах входа на сайт или на странице оформления заказа, а остальная часть сайта обслуживается HTTP. Таким образом,
файлы куки без атрибута Secure для отправки (и, возможно, чтения) передаются по незащищенному HTTP-трафику. Точно так же иметь доступ
к файлам куки (если не установлен флажок HttpOnly) может любой фрагмент Java­Script, загруженный на страницу (например, удобный скрипт,
который делает страницу более симпатичной, добавляющий систему
комментирования или классный виджет).
Даже если вы пользуетесь флажками, злоумышленники могут подделать HTTP-запрос и перезаписать безопасные файлы куки Secure
и HttpOnly. Для решения этой проблемы были добавлены префиксы фай1
2
3

https://tools.ietf.org/html/rfc6265#section-5.2.5.
https://tools.ietf.org/html/rfc6265#section-5.2.6.
https://github.com/mikewest/http-state-tokens#a-problem.

Споры о HTTP/2 и его недостатках

365

лов куки1. Согласно этой концепции файл куки с именем, начинающимся
с __Secure, должен иметь атрибут Secure. Опять же, это решение требует
участия владельцев веб-сайтов (для использования) и браузеров (для побуждения к использованию).

Файлы куки отправляются с каждым запросом
Файлы куки отправляются с каждым запросом. Безусловно, это очень
просто, однако в других аспектах это порождает множество проблем.
Конечно, запросы к вашей службе интернет-банкинга для проверки вашего баланса или перевода средств должны требовать отправки
информации о сеансе, однако запросы логотипов или других статических ресурсов не должны нуждаться в этой информации, но браузер
все равно отправляет ее. Все это приводит к возникновению проблем
безопасности, а также делает веб-сайт уязвимым для атак и подделки
межсайтовых запросов2, что может привести к утечке информации.
Для того чтобы отказаться от передачи куки при навигации на сайте,
был предложен атрибут SameSite3. Однако он не является значением по
умолчанию, и для его реализации необходимо обладать расширенными знаниями и навыками.
Другие типы методов отслеживания конфиденциальной информации часто называются куки, даже если они не являются HTTP-куки. Flash
cookie, которые реализованы во Flash, или супер куки, используют для отслеживания пользователей методы снятия цифрового отпечатка4. Также
существуют так называемые зомби-куки, которые невозможно удалить.
Все это сильно вредит репутации файлов куки, особенно в тех случаях,
когда вредоносные куки обходят любые средства контроля и позволяют
пользователям управлять обычными файлами куки.

Должен ли HTTP/2 решить проблему HTTP-куки?
Несмотря на то что в аспектах конфиденциальности и безопасности
у пользователей сложилось негативное отношение к файлам куки, не
было предложено никакой альтернативы. По свой сути, эти файлы не
являются вредоносными и опасными; однако таковым может быть их
использование. Альтернативы, которые позволяют добавить в HTTP состояния, например параметры URL и локальное хранилище5, имеют аналогичные недостатки и дают пользователям меньше контроля над конфиденциальностью и безопасностью.
Некоторые люди считают, что HTTP/2 должен был решить эту проблему с помощью введения концепции состояний, реализовав тем самым
более безопасное решение, которое не будет нарушать конфиденциаль1
2
3
4
5

https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis#section-4.1.3.
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF).
https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis#section-5.3.7.
https://nakedsecurity.sophos.com/2018/03/20/apple-burns-the-hsts-super-cookie/.
https://dev.to/rdegges/please-stop-using-local-storage-1i04.

Глава 10

366

Дальнейшее развитие HTTP

ность. Однако, несмотря на частые попытки решить эту проблему1, придумать что-то лучше файлов куки еще никому не удалось. Любое решение так или иначе должно также поддерживать традиционные файлы
куки HTTP, иначе возникает риск того, что веб-сайты просто не будут
поддерживать его. В HTTP нет концепции состояния, и даже несмотря на
то, что в HTTP/2 она была добавлена на сетевом уровне (например, состояния потока и состояние динамической таблицы HPACK), на уровне приложения эта концепция по-прежнему не реализована. HTTP-сообщение,
отправленное по одному соединению HTTP/2, может немного отличаться от сообщения, отправленного по другому соединению HTTP/2 на двоичном уровне (например, из-за сжатия заголовка), однако оно все еще
остается тем же самым HTTP-сообщением без сохранения состояния для
вышестоящего уровня.
Как уже было сказано в этой главе, в концепцию файлов куки были
внесены некоторые нововведения. Вполне возможно, что в HTTP/2 могли бы обеспечить их полное соблюдение, вместо того чтобы делать их
необязательными, как в HTTP/1. Однако в таком случае возникли бы проблемы с их принятием, поэтому разработчики HTTP/2 решили сохранить
семантику HTTP/1 и внести изменения только на транспортный уровень.
Возможно, в будущем они захотят реализовать нечто лучшее, чем
куки. Таким образом, не стоит отказываться от следующей версии HTTP,
даже если в текущей версии решены еще не все проблемы.

10.1.3 HTTP и шифрование
Как и состояние, шифрование изначально не входило в принципы разработки HTTP; эта концепция была добавлена постфактум с HTTPS. HTTPS
зашифровывает обычные HTTP-сообщения перед отправкой и разворачивает их после их получения, и в большинстве случаев это работает.
Ранее высказывались опасения относительно стоимости сертификатов
SSL/TLS и производительности шифрования и дешифрования, однако
первое решалось с помощью дешевых (или даже бесплатных) сертификатов2, а второе становилось лучше по мере увеличения вычислительной
мощности3. Однако установка первых соединений по-прежнему влечет
за собой снижение производительности, как описано в главе 9.

Нужно ли шифровать весь HTTP-трафик?
Большинство вопросов относительно HTTPS возникает на почве сложности начальной настройки и управления, зависимости от сторонних
центров сертификации (ЦС)4, а также многих беспокоит факт того, что
использование HTTPS не является повсеместным. Последний аспект
1
2
3
4

Например: https://github.com/mikewest/http-state-tokens.
https://letsencrypt.org/.
https://istlsfastyet.com/.
https://www.howtogeek.com/182425/5-serious-problems-with-https-and-ssl-security-on-the-web/.

Споры о HTTP/2 и его недостатках

367

стал яблоком раздора после стандартизации HTTP/2. Многие считали,
что следующая версия HTTP должна быть доступна только в защищенном формате (HTTPS) и что незащищенные HTTP-соединения должны
остаться в прошлом. Другие были убеждены, во многих случаях шифрование не требуется, и, следовательно, оно не должно быть обязательным
в протоколе. По иронии судьбы ко второй группе обычно относились те,
кто сетовал, что HTTP/2 недостаточно улучшил конфиденциальность изза файлов куки.

Вероятностное шифрование
Альтернативой шифрованию HTTPS, которое требует как шифрования,
так и аутентификации (с использованием сторонних центров сертификации), является вероятностное шифрование для URL-адресов HTTP.
Данный метод шифрует данные на транспортном уровне, но не требует
подтверждения, что пользователи обращаются к подлинному веб-сайту.
Безусловно, вероятностное шифрование – это шаг вперед в сравнении
с HTTP, но шаг назад в сравнении с HTTPS. Такой тип шифрования можно
без особых усилий развернуть на уровне протокола, не прибегая к обращению в сторонние центры сертификации.

HTTP/2 и шифрование
После длительных споров консенсус так и не был достигнут. В результате HTTP/2 был представлен с возможностью использовать как зашифрованное (h2), так и незашифрованное (h2c) соединение. Функция вероятностного шифрования не была реализована.
На практике все сложилось следующим образом: все веб-браузеры (основные клиенты HTTP) решили реализовать HTTP/2 через HTTPS (h2),
как обсуждалось в главе 3. Так произошло как по идеологическим причинам (ведущие производители браузеров заявили о своем намерении
перейти на зашифрованный Internet1), так и по техническим (в зашифрованных сеансах новый протокол может быть введен без использования
соответствующей сетевой инфраструктуры промежуточного программного обеспечения). Microsoft был единственным поставщиком браузеров, который выразил заинтересованность в разрешении использования
незашифрованного HTTP/2, однако в конце концов свет увидела только
зашифрованная версия; по всей видимости, в Microsoft пришли к этому
решению после обнаружения проблем с совместимостью, когда HTTP/2
не использовался поверх HTTPS.
Firefox позволяет использовать заголовок Alt-Svc для загрузки вебсайтов через альтернативные службы, благодаря чему теперь некоторые
поставщики предлагают HTTP/2 для HTTP-сайтов2. Такой метод работает
1

2

https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/
и https://blog.cloudflare.com/opportunistic-encryption-bringing-http-2-to-theunencrypted-web/.
https://blog.cloudflare.com/opportunistic-encryption-bringing-http-2-to-theunencrypted-web/.

Глава 10

368

Дальнейшее развитие HTTP

для сайтов, имеющих версию HTTPS, но не готовых к переходу на нее
(например, во избежание предупреждений о смешанном содержании).
В Firefox заголовок Alt-Svc позволяет получать сайт по HTTPS (и HTTP/2),
а представлять его как сайт HTTP; в каком-то смысле это представляет
собой вероятностное шифрование «с изюминкой».
Возможно, требования относительно доступности HTTP/2 только через HTTPS могли быть преждевременными. За последние несколько лет
использование HTTPS значительно возросло, но, к сожалению, до сих
пор не приняло повсеместный характер. В главе 6 я говорил, что Firefox
обслуживает более 70 % веб-трафика по HTTPS, однако сейчас статистика изменилась. Если смотреть на картину в целом, цифры менее оптимистичны. Топ-10 самых популярных сайтов обслуживает через HTTPS
лишь 40 % веб-трафика1 (см. рис. 10.3); однако эта цифра в перспективе
будет расти. В 2015 году, после того как HTTP/2 был одобрен, по умолчанию HTTPS использовали только около 5 % сайтов.

45
45
40
40
35
35
30
30

25
25
20
20
15
15
10
10
55
1 Sep

01.09.2018

1 Aug

01.08.2018

1 Jul

01.07.2018

1 Jun

01.06.2018

1 May

01.05.2018

1 Apr

01.04.2018

1 Mar

01.03.2018

01.02.2018

01.01.2018

1 Dec 1 Jan’18 1 Feb

01.12.2017

1 Nov

01.11.2017

01.10.2017

01.09.2017

00
1 Sep’17 1 Oct

Использование протокола HTTPS по умолчанию для веб-сайтов, 30 сентября 2018 года, W3Techs.com

Рис. 10.3

Использование HTTPS топ-10 популярных сайтов

Таким образом, можно предположить, что для крупных сайтов более
важны безопасность (и, раз уж на то пошло, производительность). Однако до момента, когда использование HTTPS станет обязательным, нам
предстоит пройти еще долгий путь. Возможно, обязательный характер
1

https://w3techs.com/technologies/details/ce-httpsdefault/all/all.

Споры о HTTP/2 и его недостатках

369

HTTPS для HTTP/2 при стандартизации не был введен именно поэтому.
Между стимулом для поощрения принятия хороших стандартов и появлением еще одного препятствия для принятия существует очень тонкая
грань.
Также следует помнить и о существовании внутреннего трафика, не
связанного с внешним Internet. Сюда входят сайты внутренней сети
и серверы внутренних приложений, обслуживаемые веб-сервером, на котором выполняется внешняя реализация HTTPS. Внутренние веб-сайты,
наряду с внешними, должны быть защищены с помощью HTTPS, однако
это можно реализовать далеко не для всех инфраструктур. Сегодня Internet находится только на стадии перехода на шифрование отчасти по
причине его открытого характера, ввиду которого возникают некоторые
риски. Закрытая внутренняя сеть сопряжена с меньшими рисками, поэтому шифрование таких сайтов зачастую оставляют на потом. Внедрение HTTPS во внутренние сети отстает от внедрения HTTPS во внешнюю
сеть; особенно это касается внутренних серверов, когда для отработки
HTTPS-соединений используется пользовательский интерфейсный вебсервер.
Если сайты, предназначенные только для внутреннего использования,
не открывают доступ к Internet или не используют групповой сертификат (что дороже и не так легко автоматизировать), они не могут использовать коммерческие центры сертификации (особенно автоматизированные бесплатные центры сертификации, такие как Let’s Encrypt).
Многие прибегают к запуску внутренних центров сертификации, однако
их крайне сложно настроить на автоматический выпуск и продление
сертификатов.
HTTP/2 не является обязательным требованием для веб-сайтов; его
можно рассматривать как вариант для тех, кто хочет увеличить скорость
веб-сайта, а такие пользователи, скорее всего, в любом случае будут использовать HTTPS. Во внутренних сайтах задержки не так страшны, как
во внешних, поэтому здесь преимуществ перехода на HTTP/2 становится несколько меньше. В предыдущих главах я уже говорил, что сегодня
нет необходимости поддерживать HTTP/2 на внутренних серверах, если
через HTTP/2 работает пограничный сервер, к которому подключаются
пользователи. Тем не менее нет смысла изобретать новую версию основного протокола, если большая часть пользователей не сможет ей пользоваться. На момент написания этой книги эта часть составляет 60 % внешнего Internet, а сразу после стандартизации HTTP/2 она составляла 95 %.
В частных внутренних сетях это число, вероятно, еще выше.
Помимо прочего, HTTP ограничен для распространения за пределы веб-сайтов и в доменах интернета вещей (Intrenet of Things, IoT),
где управление сертификатами и HTTPS, безусловно, намного сложнее. Даже на момент написания этой книги в достаточной степени этот
аспект не изучен. Однако, несмотря на это, были выдвинуты предложения относительно реализации HTTPS в локальной сети1. Устройства IoT
1

https://www.w3.org/community/httpslocal/.

Глава 10

370

Дальнейшее развитие HTTP

могут не нуждаться в HTTP/2, однако оставаться на HTTP/1 – сравнимо
с шагом назад.
В конечном итоге не удивительно, что HTTP/2 не был стандартизирован с требованием HTTPS. Если тогда было слишком рано, то сейчас
есть веские причины стремиться к этому. В протокол QUIC заложено согласование параметров HTTPS, что может иметь смысл, поскольку разница между протоколами составляет 3-4 года. Однако многие аргументы
в пользу реализаций HTTP, не предназначенного для публичного доступа, по-прежнему остаются, и к адекватному решению общественность
еще не пришла.

10.1.4 Проблемы транспортного протокола
Также одна из основных претензий касалось самого протокола на
транспортном уровне. Если до этого HTTP не функционировал на этом
уровне, за исключением отправки сообщений о том, что это был поток
данных, которому предшествовала строка запроса (или ответа) и HTTPзаголовков, то в HTTP/2 ситуация в корне изменилась благодаря добавлению уровня двоичного фрейминга.

Нарушения иерархических представлений
Как уже говорилось в главе 1, сетевые протоколы часто построены на отдельных уровнях. HTTP/2 вышел за рамки своего уровня и приобрел некоторые характеристики TCP. На рис. 10.4 представлен веб-стек и то, как
он (примерно) отображается на некоторых уровнях сетевой модели OSI
(и тут есть некоторые совпадения). Уровень 6, например, не сопоставляется напрямую ни с одной из представленных технологий, хотя HTTP
позволяет передавать информацию о форматах файлов.
HTTP/2 выходит за пределы уровня приложения, на котором так
и остался HTTP/1. HTTP/2 управляет большим количеством элементов, традиционно считающимися прерогативой транспортного уровня
(например, мультиплексирование и управление потоком). Поскольку
HTTP/2 повторяет, а не заменяет обработку этих концепций в TCP, он
как бы дополняет то, что традиционно считается HTTP. Таким образом,
новый уровень двоичного фрейминга охватывает несколько уровней,
как показано на рис. 10.5.
Многие люди считали «нарушение иерархических представлений» не
лучшей идеей, ведь иерархия уровней протокола позволила упростить
реализацию на каждом из уровней. На практике уровни не имеют между
собой столь четких границ, и пользователям нежелательно зацикливаться на этом1, поэтому предыдущий аргумент действительно может иметь
смысл. В частности, повторение концепций TCP (хотя и с ограничениями), может привести к возникновению некоторых проблем, как обсуждалось в главе 9.
1

https://tools.ietf.org/html/rfc3439#section-3.

371

Споры о HTTP/2 и его недостатках

7

Прикладной уровень
(Hypertext Transport Protocol – HTTP)

6

Уровень представления
(формат файла такой как ASCII, UTF-8, JPG, PNG ... и т. д.)

5

Сеансовый уровень
(Secure Sockets Layer – SSL / Transport Layer Security – TLS)

HTTPS

4

Транспортный и сеансовый уровень
(Transmission Control Protocol – TCP)

TCP

3

Сетевой уровень
(Internet-протокол – IP)

IP

Рис. 10.4

HTTP

Модель OSI и стек веб-сетей

7

Прикладной уровень
(Hypertext Transport Protocol – HTTP)

6

Уровень представления
(формат файла такой как ASCII, UTF-8, JPG, PNG ... и т. д.)

5

Сеансовый уровень
(Secure Sockets Layer – SSL / Transport Layer Security – TLS)

HTTPS

4

Транспортный и сеансовый уровень
(Transmission Control Protocol – TCP)

TCP

3

Сетевой уровень
(Internet-протокол – IP)

IP

HTTP/2

HTTP

Рис. 10.5

Модель OSI и сетевой сетевой стек HTTP/2

Глава 10

372

Дальнейшее развитие HTTP

QUIC нацелен на то, чтобы вернуть границу между транспортным
протоколом приложения (HTTP) и сетевым транспортным протоколом
(TCP и UDP), как показано на рис. 10.6. В этой модели HTTP/3 сопоставляет типы фреймов HTTP и показывает, как они должны использоваться для HTTP-трафика, но оставляет информацию транспортного уровня
(например, управление мультиплексными потоками) для QUIC. Однако
с другой стороны может показаться что, объединяя части уровней TCP
и HTTPS, QUIC еще больше размывает границы между ними; но на самом
деле это имеет смысл. HTTPS всегда был отделен от HTTP, поэтому он
больше относится к сессионному и транспортному уровням, чем к уровню приложения.
Прикладной уровень
(Hypertext Transport Protocol – HTTP)

6

Уровень представления
(формат файла такой, как ASCII, UTF-8, JPG, PNG ... и т. д.)

5

Сеансовый уровень
(Secure Sockets Layer – SSL / Transport Layer Security – TLS)

HTTP

HTTP/3

HTTP/2

7

HTTPS
QUIC

4

Транспортный и сеансовый уровень
(Transmission Control Protocol – TCP)

TCP

3

Сетевой уровень
(Internet-протокол – IP)

IP

Рис. 10.6

UDP

IP

Модель OSI и сетевой сетевой стек QUIC и HTTP/3

HOL-блокировка в TCP
В главе 9 я рассказывал о том, как мультиплексирование HTTP/2 решает
проблему блокировки заголовка HTTP (HOL) и переносит ее на уровень
TCP. Следовательно, в определенных условиях (при потерях) HTTP/2,
возможно, работает медленнее, чем HTTP/1. На эту проблему уже давно
обратили внимание. Она упоминалась в первом проекте HTTP/21 (в отличие от более поздних проектов), где говорилось, что преимущества
использования одного TCP-соединения перевешивают отрицательные
эффекты:
Использование нескольких соединений имеет свои плюсы. Например, SPDY мультиплексирует несколько независимых потоков
в один, что создает потенциальную возможность возникнове1

https://tools.ietf.org/html/draft-ietf-httpbis-http2-00#section-4.3.

Споры о HTTP/2 и его недостатках

373

ния проблем с блокировкой заголовка на транспортном уровне.
Согласно результатам проведенных тестов отрицательные
эффекты блокировки заголовка (особенно при потере пакетов)
перевешиваются преимуществами сжатия и приоритизации.
– Спецификация HTTP/2, версия 00
Согласно главе 9, QUIC стремится решить эту проблему, однако для
большинства соединений подходит и HTTP/2. Стремление к разработке
идеального решения привело бы к ненужной задержке разработки полезного обновления, не говоря уже о том, что переход с HTTP 1.1 на QUIC
и HTTP/3 сам по себе чрезвычайно грандиозен.

Внедрение в HTTP концепцию состояния
Также в базовый протокол HTTP впервые была заложена концепция состояния. Файлы куки HTTP, о которых я рассказывал ранее, позволяют
передавать состояние по HTTP, однако не добавляют состояние в сам протокол. Концепция состояния вводятся благодаря мультиплексированию
потоков и внедрению их идентификаторов (глава 4), машине состояний
HTTP/2 (глава 7) и сжатию заголовка HPACK (глава 8). Однако одно состояние все же не удалось ввести: это состояние сеанса, о котором я рассказывал в разделе 10.1.2. Данный выбор был осознанным, и он не так
противоречив, как кажется. Несмотря на то что состояние было заложено
в HTTP/2 на уровне подключения, на общем уровне HTTP это не имеет
никакого значения. Тот же HTTP-запрос по-прежнему может быть выполнен через отдельное HTTP/2-соединение либо в параллельном соединении или вообще намного позже, и он по-прежнему будет передавать
то же семантическое значение на уровне HTTP. Концепция состояний
была введена исключительно для облегчения обработки мультиплексированного протокола и сжатия заголовка. Если состояние каким-либо
образом собьется, соединение может быть разорвано и восстановлено,
а HTTP-запрос может быть выполнен через новое соединение с теми же
проблемами и ограничениями, что и в HTTP (например, когда повторная
отправка безопасна [индемпотентна])1. Части HTTP/2 с отслеживанием
состояния не важны ровно так же, как те же части TCP. Фактически состояние было добавлено только для того, чтобы работа потоков HTTP/2
больше походила на TCP.

Инструментальная поддержка HTTP/2
Бинарное разделение на слои и отказ от простого текстового формата
раздражали многих людей, поскольку перестали работать такие простые
инструменты, как Telnet (см. главу 1). Однако по большей части эти недовольства беспочвенны, поскольку зашифрованные HTTPS-соединения
страдают теми же проблемами, и поэтому HTTPS получает соответствующую инструментальную поддержку. Возможно, более серьезным аргу1

https://tools.ietf.org/html/rfc7231#section-4.2.2.

374

Глава 10

Дальнейшее развитие HTTP

ментом было бы то, что некоторым сетевым устройствам, поддерживающим HTTP (например, HTTP-кеши, такие как Varnish) теперь приходится
реализовать большее количество HTTP-трафика. Однако, даже несмотря
на это, не стоит отказываться от развития протокола, и запрещать инфраструктуре маршрутизировать HTTP-трафик без полного понимания
деталей HTTP-сообщения.

Действительно ли HTTP/2 является улучшением HTTP?
Таким образом, мы приходим к выводу, что большинство изменений
в HTTP/2 коснулось транспортного уровня, а не уровня HTTP. Достаточно ли улучшений на уровне HTTP получила новая версия? А может быть,
вместо HTTP/2 ее стоило назвать HTTP/1.2, TCP/2 или HTTPS/2? Дальнейшее развитие (а именно разработка QUIC) подтверждает, что многоуровневое распределение протокола не может быть рассмотрено как
часть HTTP. В любом случае на эти вопросы мы никогда не найдем ответ. Новый протокол содержит критические изменения, поэтому он по
определению отличается от предыдущей версии. Кроме того, разработчики редко присваивают обновлениям номера версий. Таким образом,
переход спустя 20 лет на версию 2, которая включает в себя множество
критических изменений и не имеет обратной совместимости, – это не
самый плохой вариант.

10.1.5 HTTP/2 слишком сложен
Еще один предмет споров – это сложность HTTP/2. Нет сомнений
в том, что это так, особенно по сравнению с простым на первый взгляд
HTTP/1.1. Уровень фрейминга, его двоичная природа и внедрение концепции состояния – это действительно очень сложно (особенно в сравнении с HTTP/1.1), не говоря уже о приоритизации и сжатии заголовков.
Чтобы разобраться в этих концепциях, вам нужна целая книга! Благодаря этому у авторов технической литературы появилось широкое поле
для творчества, однако для протокола – это серьезная проблема. Одна из
основных причин успеха HTTP – его простота. Главные детали HTTP умещаются на одну страницу спецификации (если так можно выразиться)
HTTP/0.9; несмотря на добавленные версии HTTP/1.0 и 1.1, концептуально HTTP все еще легко понять.
Однако перед вами сейчас складывается неполная картина. HTTP/1.1
был прост для людей, но его было сложно реализовать в программном
обеспечении. Для этого неформатированного, неструктурированного
текстового протокола нужна дополнительная проработка всех возможных крайних случаев. В свою очередь HTTP/2 кажется намного более
сложным, но все эти сложности легко поддаются автоматизации. Конечно, это не гарантирует, что все его реализации будут идеальны. Разработчикам все еще будет над чем поработать (еще одна причина для написания такой книги), однако стоит помнить, что такая ситуация может
возникнуть при внедрении любой новой технологии, и особенно это от-

Споры о HTTP/2 и его недостатках

375

носится к HTTP/1. Сложность HTTP/2, как и HTTP/1, касается в основном
разработчиков низких уровней, таких как разработчики веб-браузеров
и веб-серверов, и почти не затрагивает веб-разработчиков более высокого уровня; и большинство разработчиков низкого уровня утверждало,
что HTTP/2 реализовать проще, чем HTTP/1.
Возможно, количество реализаций является главным показателем
степени сложности. На домашней странице веб-сайта HTTP/2 представлено более 80 отдельных активных реализаций1. HTTP/2 поддерживает
большинство популярных веб-серверов и веб-браузеров; и многие из
них запустили поддержку сразу после выхода новой версии. Безусловно, во всех этих реализациях были свои ошибки и проблемы, некоторые
из них еще не устранены. Однако то, как быстро они приняли новую
версию, вероятно, указывает на то, что эти сложности не представляют
большой опасности. Впрочем, обратите внимание, что некоторые части
спецификации (например, HTTP/2 push и определение приоритетов)
ввиду их сложности заложены не во все реализации.
Трудно спорить с тем, что концепция HTTP/2 действительно сложна; однако это не такая серьезная проблема, как вы можете подумать.
В большинстве случаев простота должна быть приоритетней сложности (принцип Keep It Simple Stupid [KISS]), однако, например, HTTP/1 не
так прост, как кажется. Несмотря на все аргументы в пользу того, что
сложность не имеет значения, те, кто обнаруживают проблемы с HTTP/2
в своих реализациях, на решение которых у них уйдут дни, несомненно,
будут проклинать эту сложность. Я думаю, что все, кто сталкивались с таким, поймут насколько это неприятная ситуация.

10.1.6 HTTP/2 – временная мера
HTTP/2 не был нацелен на решение абсолютно всех проблемы HTTP. После нескольких лет застоя, имея на вооружении проверенный на деле
SPDY, группа HTTP-WG IETF стремилась не увязнуть в кроличьей норе,
поэтому многие проблемы не были решены полностью, а HTTP/2 был
одобрен, несмотря на отсутствие единого мнения касаемо некоторых
вопросов. Несомненно, эта ситуация разочаровала тех, кто считал, что
в HTTP/2 многие проблемы могли бы быть решены. Однако подход разработчиков выглядит весьма прагматичным.
HTTP/2 улучшил производительность протокола, устранив некоторые
из основных узких мест, поэтому его было удобно запускать и использовать на практике. Ничто не мешает будущей версии HTTP улучшить
то, что не успели сделать в этой версии. Сегодня возникает множество
предложений относительно улучшения протокола с помощью новых настроек и типов фреймов.
QUIC стремится решить некоторые проблемы, которые не удалось
решить в HTTP/2. К ним относится HOL-блокировка TCP, обеспечение
более полного шифрования, улучшенные процесса установки соедине1

https://github.com/http2/http2-spec/wiki/Implementations.

Глава 10

376

Дальнейшее развитие HTTP

ния и внедрение концепции миграции соединения. На появление этих
изменений в HTTP/2 могло потребоваться еще четыре года, но, по всей
видимости, в этом не было необходимости. Реализация QUIC, вероятно,
займет больше времени, потому что он сложнее HTTP/2. На аналогичном этапе процесса стандартизации у QUIC было меньше реализаций,
чем у HTTP/2, вероятно, из-за того, что реализации SPDY на HTTP/2
были намного проще. Однако gQUIC воспринимается несколькоиначе.
Для Internet, который почти полностью основан на TCP, переход на UDP
будет сопряжен с массой проблем. Учитывая эту сложность, тот факт, что
HTTP/2 является временным препятствием и в нем наблюдается некоторый прогресс, является скорее положительным, чем отрицательным.

10.2 HTTP/2 в реальном мире
Все эти аргументы были выдвинуты до официальной стандартизации
HTTP/2, и ни один из них не рассматривался как причина отменить или
отложить окончательную стандартизацию. С момента выпуска HTTP/2
и на момент написания этой книги HTTP/2 поддерживают более 30 % из
10 млн веб-сайтов1 (рис. 10.7).
35
35
30
30
25
20

15
10

5

Использование HTTP/2 веб-сайтами, 30 сентября 2018 года, W3Techs.com

Рис. 10.7 Рост поддержки HTTP/2 с сентября 2017 года по сентябрь 2018-го
1

https://w3techs.com/technologies/details/ce-http2/all/all.

01.09.2018

1 Aug 1 Sep

01.08.2018

1 Jul

01.07.2018

01.06.2018

01.05.2018

1 Apr 1 May 1 Jun

01.04.2018

01.03.2018

01.02.2018

01.01.2018

01.12.2017

01.11.2017

01.10.2017

01.09.2017

0
1 Sep’17 1 Oct 1 Nov 1 Dec 1 Jan’18 1 Feb 1 Mar

HTTP/2 в реальном мире

377

Кроме того, на HTTP/2 основывается более 55 % веб-трафика1, потому
что крупные сайты, такие как Google, YouTube и Facebook, поддерживают именно эту версию и генерируют пропорционально больше трафика,
чем небольшие сайты. Таким образом, согласно этой статистике, HTTP/2
уже зарекомендовал себя как успешный протокол.
Кроме того, в HTTP/2 уже был расширен и усовершенствован новыми
настройками и типами фреймов, такими как альтернативные службы2,
фрейм ORIGIN3 и WebSockets4. Существуют и другие предложения, направленные на дальнейшее расширение HTTP/2, в их число входит Cache Digests5, а также многие другие инновации, находящиеся в стадии разработки.
В целом HTTP/2 стал частью Internet и продолжает расти как в плане
использования, так и в плане функциональности. Все проблемы, обсуждаемые в этой главе, не лишены оснований, однако на практике они не
являются критическими.
Безусловно, разработчикам удалось далеко не все. В частности, особого влияния пока еще не приобрела технология HTTP/2 push, в основном
из-за сложностей правильного использования (см. главу 5), а отказ от
поддержки на стороне сервера не помог исправить ситуацию. Возможно, оглядываясь назад, HTTP/2 push и не был панацеей для повышения
производительности (как многие считали). Возможно, его следовало
исключить из первоначального предложения HTTP/2 и добавить позже
в качестве необязательного фрейма, использующегося при необходимости. Некоторые даже призывают исключить его из спецификации, хотя
мне кажется, что такие люди делают поспешные выводы.
Кроме того, сложности при реализации HTTP/2 показывают, что пе­
рейти на него не так просто, как вам хотелось бы (что неудивительно
для нового крупного протокола), и переход еще не гарантирует прироста
производительности. У большинства сайтов все же наблюдается улучшение производительности, но бывают случаи, что производительность
снижается. Чтобы получить максимальную отдачу от любой новой технологии, необходимо хорошо разобраться в ней. Я считаю, что HTTP/2
будет продолжать расти и развиваться, по крайней мере до тех пор, пока
QUIC не будет оформлен и принят повсеместно, но ни один из них не заменит HTTP/1 во многих реализациях в ближайшее время, если вообще
заменит.

1

2
3
4
5

https://telemetry.mozilla.org/newpipeline/dist.html#!cumulative=0&measure
=HTTP_RESPONSE_VERSION.
https://tools.ietf.org/html/rfc7838.
https://tools.ietf.org/html/rfc8336.
https://tools.ietf.org/html/rfc8441.
https://datatracker.ietf.org/doc/draft-ietf-httpbis-cache-digest/?include_text=1.

Глава 10

378

Дальнейшее развитие HTTP

10.3 Будущие версии HTTP/2 и возможности
HTTP/3 или HTTP/4
Теперь, когда набирает обороты HTTP/2, чего можно ожидать от следующей итерации HTTP? Будет ли это HTTP/2.1 или HTTP/3?

10.3.1 QUIC – это HTTP/3?
QUIC выводит концепции HTTP/2 на новый уровень, поэтому рассмат­
ривается как преемник, что по определению делает его HTTP/3. В июле
2018 года один из двух председателей рабочей группы HTTP заявил1:
«Я рассматриваю QUIC как [HTTP/3] во многих отношениях ... hq является
логическим преемником h2», и в ноябре 2018 года рабочая группа HTTP
согласилась, что HTTP-часть QUIC должна называться HTTP/3, и решила передать ее (и QPACK) рабочей группе HTTP после публикации2. Имя
HTTP/3 не будет официально зарегистрировано до тех пор, пока QUIC не
будет ратифицирован, поэтому нет гарантии, что новая версия будет называться именно так. Однако вероятность этого довольно высока.
QUIC позиционируется как более общий протокол транспортного
уровня, однако выполняющий больше функций, чем HTTP, а HTTP/3 –
это лишь один из вариантов использования QUIC. Во многих отношениях QUIC является преемником TCP, а не HTTP/2, но некоторые люди
не согласны с обозначением TCP/23. Еще и по этой причине, вероятнее
всего, будет принято имя HTTP/3, что позволит различать более крупный протокол QUIC и часть HTTP. Разумеется, все это не означает, что
функционирование и развитие HTTP/2 прекратится, точно так же после
появления HTTP/2 пользователи не отказались от HTTP/1.1. Таким образом, многие реализации HTTP, скорее всего, останутся на TCP, используя
HTTP/2 или HTTP/1.1. Однако, как только QUIC получит должное распространение, что займет некоторое время, HTTP/3 будет представлять
лучшую версию HTTP и по идее должен использоваться везде, где это
возможно (отсюда и название).

10.3.2 Дальнейшее развитие двоичного протокола HTTP
Если не брать во внимание различия и направленность версий QUIC (h3)
и TCP (h2/h2c), то как следует расширить новый двоичный и мультиплексированный протокол в будущем?
Раньше HTTP-заголовки позволяли расширять основной протокол
HTTP в зависимости от их использования клиентом и сервером. HTTP/2
добавляет новые возможности с новыми значениями настроек и новы1

2
3

Патрик Макманус (сопредседатель HTTP-WG), IETF 102 HTTPBIS, встреча II
(https://youtu.be/tQAfDmW0qlI?t=588).
Рабочая группа HTTP IETF 103 (https://youtu.be/uVf_yyMfIPQ?t=4956).
https://github.com/HTTPWorkshop/workshop2016/blob/master/talks/quic.pdf.

Будущие версии HTTP/2 и возможности HTTP/3 или HTTP/4

379

ми типами фреймов. Значения настроек1, в частности, позволяют обнаруживать новые возможности при установке соединения, что намного
лучше, чем отправка HTTP-заголовка в надежде, что другая сторона поймет его.
Протокол был расширен фреймами ALTSVC, ORIGIN и CACHE_DIGEST (предлагаемый). Также были и другие предложения, такие как вторичные сертификаты2, поэтому есть возможность расширения протокола благодаря
новым типам фреймов3. В настоящее время нет острой потребности
называть новую версию HTTP/2.1, поэтому рабочая группа HTTP отказалась от вспомогательного номера версии и назвала протокол HTTP/2,
а не HTTP/2.0.

10.3.3 Развитие HTTP над транспортным уровнем
Если HTTP/2 и QUIC функционируют на нижнем транспортном уровне
HTTP, то что происходит на более высоком уровне HTTP? Клиент и сервер управлялись устойчивым потоком новых заголовков HTTP, однако
семантика HTTP не сильно изменилась со времен HTTP/1.1, в HTTP/2
этот уровень также остался нетронутым.
Как я упоминал ранее, одна из насущных проблем – это поиск альтернативы HTTP-файлам куки. Однако до сих пор еще не было ни одного
подходящего предложения.
В других аспектах на высоком уровне HTTP оказался на удивление
надежным, ввиду чего потребовались в первую очередь не расширения,
а уточнения и настройки. Многочисленные расширения HTTP разрабатываются рабочей группой IETF4, Web Platform Incubator Community
Group (WICG)5 или сторонними разработчиками, например такими как
SPDY и QUIC, созданные Google. Большинство этих расширений можно
реализовать, избежав фундаментального изменения протокола, вместо
этого используя уже доступные методы расширения (заголовки HTTP,
настройки HTTP/2 или новые типы фреймов HTTP/2).

Новые HTTP-методы
Особенно удивляет тот факт, что список основных HTTP-методов (GET,
POST, PUT, DELETE и т. д.) не изменился со времен HTTP/1.1. Новые методы
были представлены в Web Distributed Authoring and Versioning (WebDAV)6
(такие как PROPFIND, COPY и LOCK), а также в некоторых RFC7. Последний до1

2
3

4
5
6
7

https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml#
settings.
https://tools.ietf.org/html/draft-ietf-httpbis-http2-secondary-certs.
https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml#
frame-type.
https://github.com/httpwg/http-extensions.
https://www.w3.org/blog/2015/07/wicg/.
https://tools.ietf.org/html/rfc4918.
https://www.iana.org/assignments/http-methods/http-methods.xhtml.

Глава 10

380

Дальнейшее развитие HTTP

бавленный метод был зарегистрирован в 2010 году (BIND). По большей
части фундаментальными методами HTTP являются представленные 20
лет назад GET и POST, а также отчасти PUT и DELETE.
Добавить новые HTTP-методы довольно легко, однако в этом нет необходимости, поскольку большинство требований можно проксировать
с помощью POST. В следующем примере переменная action позволяет передать необходимый метод, определяемый приложением (упорядочить
указанный элемент):
:method: POST
:path: /api/doaction
{
"action": "order",
"item": 12345,
."quantity": 1
}

В других реализациях дополнительная информации (включая рекомендуемые действия) передается с помощью HTTP-заголовков:
:method: POST
:path: /api/doaction
action: order
{
"item": 12345,
"quantity": 1
}

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

Новые HTTP-заголовки
С течением времени возникали все новые и новые HTTP-заголовки,
и я считаю, что их количество продолжит расти. HTTP/2 категорически
запрещает использование новых полей псевдозаголовков1, которые начинаются с двоеточия (:method, :scheme, :author, :path и :status). Однако эти
поля могут добавляться новыми спецификациями (например, в HTTP/2
RFC вместе с загрузочными веб-сокетами был добавлен псевдозаголовок
:protocol)2.
Также заголовки могут добавлять и приложения. В спецификацию
HTTP входят советы относительно новых полей заголовков3, что говорит
нам о том, что их список можно пополнять. Существует официальный ре-

1
2
3

https://httpwg.org/specs/rfc7540.html#PseudoHeaderFields.
https://tools.ietf.org/html/rfc8441.
https://tools.ietf.org/html/rfc7231#section-8.3.

Будущие версии HTTP/2 и возможности HTTP/3 или HTTP/4

381

естр заголовков сообщений1 (включая те, которые используются HTTP),
но многие приложения используют и незарегистрированные заголовки.
HTTP-заголовки позволяют реализовать новые функции независимо
от их типа; это может быть и дополнительная информация для сторон
(например, «Этот ответ использует формат XXX»), и подсказки (например, «К вашему сведению, я поддерживаю следующие форматы»), и информация для аутентификации (например, файлы куки), а также информация о маршрутизации («Переадресация была осуществлена с этого
IP-адреса») и многое другое. Приложения с обеих сторон могут работать
с заголовками, даже если промежуточная HTTP-инфраструктура (например, HTTP-сервер или веб-браузер) не может их передать.
Также последние несколько лет наблюдается тенденция к использованию HTTP-заголовков в целях безопасности. Зачастую такие заголовки отправляются с веб-сайта в браузер в ответах и содержат инструкции
по включению функций безопасности, таких как Content-Security-Policy (CSP)2 и HTTP Strict-Transport-Security (HSTS)3. Также с их помощью
можно предоставить серверу дополнительную информацию о клиенте
(веб-браузере или другом), например спецификацию Client Hints4, которая должна позволять доставлять различный контент в зависимости от
типов поддерживаемого клиентом содержимого.
Несомненно, по мере роста использования HTTP будет добавлено
много новых заголовков функций на стороне клиента и сервера. Однако
сейчас все это не нужно и не должно быть обязательным требованием
в новых версиях HTTP.

Новые форматы
После того как в HTTP/1.0 появились заголовки, HTTP начал поддерживать самые разные форматы файлов и позволял использовать различную кодировку содержимого (например, методы сжатия, такие как gzip
и br). Опять же, расширить HTTP в этом аспекте очень просто, и для этого
даже не нужно создавать новую версию протокола под новым номером.

Новые коды состояния
Коды состояния5 представляют собой еще один способ расширения функциональности HTTP. И для этого также не требуется разработка новой
версии, по крайней мере, для тех кодов, которые будут близки к группам
кодов, определенным основной спецификацией HTTP6, как представлено в табл. 10.1.
1
2
3
4
5

6

https://www.iana.org/assignments/message-headers/message-headers.xhtml.
https://w3c.github.io/webappsec-csp/.
https://tools.ietf.org/html/rfc6797.
https://tools.ietf.org/html/draft-ietf-httpbis-client-hints.
https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml#
http-status-codes-1.
https://tools.ietf.org/html/rfc7231#section-6.

Глава 10

382

Таблица 10.1

Дальнейшее развитие HTTP

Группы кодов состояния HTTP

Код Тип
1xx Информационный
2xx Свидетельствующий
об успешном завершении
3xx Перенаправление
4xx Ошибка клиента
5xx Ошибка сервера

Описание
Запрос был получен; процесс продолжается
Запрос был успешно получен, понят и принят
Для выполнения запроса необходимо предпринять дальнейшие действия
Запрос содержит неверный синтаксис или не может быть выполнен
Серверу не удалось выполнить действительный запрос

Например, код состояния HTTP 1031 был введен в конце 2017 года и не
потребовал создания новой версии HTTP. Однако при его первом использовании во многих реализациях HTTP (например, в веб-браузерах)
обнаружились проблемы. Реализации не ожидали получения более одного HTTP-ответа, так как до этого информационные ответы 1xx использовались нечасто (только при конкретных обстоятельствах). Технически
для этого изменения не требовалась новая версия. Однако на практике
этот код состояния функционировал несколько иначе, чем существующие, и для многих клиентов он стал критическим изменением до тех
пор, пока в реализации не внесли доработки.
Помимо прочего, существует возможность добавлять новые категории кодов состояний (6xx, 7xx, 8xx, 9xx), вместо того чтобы расширять
уже существующие группы. За 20 лет существования HTTP не было необходимости расширять категории кода состояния, однако в будущем это
возможно.

10.3.4 Какие расширения требуют создания новой версии
HTTP?
Учитывая наличие способов расширения HTTP, неудивительно, что версия HTTP за последние 20 лет не изменилась. Какие изменения потребуют создания новой версии? На данный момент этого никто не знает! Некоторые проблемы HTTP/2 (большинство из которых решается в рамках
QUIC и HTTP/3) не были решены даже в результате мозгового штурма
рабочей группа HTTP2. На сегодняшний день нет четкого представления
о том, какие изменения может повлечь за собой создание совершенно
новой версии (минуя HTTP/3).
Одно можно сказать наверняка: как и HTTP/2 и HTTP/3, следующая
основная версия (для того чтобы получить новый номер) должна содержать критические изменения, не имеющие обратной совместимости.
Будут ли это изменения в формате сети (как в HTTP/2), изменения на
транспортном уровне (как в HTTP/3, переходящем на QUIC через UDP)
или что-то еще, еще предстоит выяснить.

1
2

https://tools.ietf.org/html/rfc8297.
https://github.com/HTTPWorkshop/workshop2016/wiki/Future-of-HTTP#http3.

HTTP как базовый транспортный уровень

383

10.3.5 Как могут быть представлены будущие версии HTTP
Внедрение HTTP/2 предоставляет больше возможностей для расширения протокола, а также прокладывает дорогу для новой версии с использованием ALPN или других методов обновления, подробно описанных
в главе 4. Если в какой-то момент потребуется критическое изменение,
его внедрение должно стать проще. Таким образом, такая система должна привести к большему количеству инноваций, чем за последние 20 лет.
HTTP/3 стремится отойти от TCP, и в случае успеха в будущем это откроет нам путь для перехода к совершенно новым базовым технологиям. Будут ли разработчики использовать для доставки HTTP-сообщений
что-то другое, кроме TCP и UDP? Изменится ли IP? Ответов на эти вопросы нет. Однако совершенно точно ясно одно: настала новая эра развития
интернет-протоколов, и у разработчиков наконец-то есть методы для их
внедрения.

10.4 HTTP как базовый транспортный уровень
А что насчет изменений в использовании HTTP? Что ждет протокол в этом
аспекте? Первоначальным вариантом использования HTTP были вебстраницы, однако многие разработчики стремятся реализовать популярный HTTP за пределами этого уровня. HTTP – это довольно простой протокол, который поддерживается повсеместно, поэтому у него существует
множество реализаций и библиотек. Для HTTP/1.1, по крайней мере, легко
создать простой HTTP-сервер или клиент с любым программным обеспечением, которое позволяет читать или записывать TCP-канал (хотя, как
обсуждалось ранее, текстовая природа HTTP/1.1 может привести к множеству проблем, которые изначально скрыты этой кажущейся простотой).
HTTP – это гибкий протокол, который готов к реализации вне своего
изначального предназначения. HTTP обеспечивает связь между отдельными системами стандартизированным и понятным способом. От сложных приложений, использующих REST API или аналогичную технологию,
до устройств интернета вещей HTTP используется в веб-приложениях
и непосредственно вне веб-приложений.
Приложения могут использовать HTTP разными способами:
„„ использовать семантику и сообщения HTTP для доставки внутреннего трафика;
„„ использовать концепцию двоичного фрейминга HTTP/2;
„„ использовать HTTP для запуска другого протокола.
В оставшейся части главы мы подробнее рассмотрим эти варианты.

10.4.1 Использование семантики и сообщений HTTP
для доставки внутреннего трафика
Данный метод является наиболее популярным методом использования
HTTP за пределами веб-страниц. Сообщения API могут быть отправлены

384

Глава 10

Дальнейшее развитие HTTP

через HTTP в любом формате, который использует клиент и сервер (XML,
JSON или какой-либо проприетарный формат). Часто такие сообщения
для взаимодействия между службами используют распространенные
методы HTTP (GET, POST, PUT и DELETE). Например, архитектура микросервисов использует небольшие независимые службы, для которых HTTP – это
отличный вариант. Другие протоколы также можно легко смоделировать
с помощью HTTP (см. сноску ниже).

DNS через HTTPS (DoH)
Система доменных имен (Domain Name System, DNS) – это относительно
простой протокол, который обычно включает в себя выборку одного типа записи из центрального каталога. Традиционно DNS был проприетарным протоколом на отдельном порту (порт 53), а также его можно было реализовать
с помощью простого запроса GET.
Одной из причин перехода на HTTP стало использование шифрования, обеспечиваемого HTTPS. Раньше DNS был незашифрованной системой, и попытки внедрить шифрование породили более сложные протоколы, такие как
DNSSEC и DANE, которые имеют свои недостатки, выходящие за рамки этой
книги. HTTPS проверен, безопасен (при правильной настройке) и поддерживается повсеместно. Объединив интерфейс HTTPS с DNS, разработчики могут
решить проблему шифрования DNS, о которой они спорят десятилетиями.
Эта система известна как DNS over HTTPSa, или DoH.
Поверх DNS вместо HTTPS разработчики могут использовать TLS, и для этой
цели также существует отдельная спецификацияb. Однако HTTPS проще реализовать, а также этот путь позволяет использовать и другие преимущества HTTP. Они включают широко поддерживаемое, удобное для прокси использование HTTP/2 или QUIC в мультиплексных запросах и использование
HTTP/2 push для отправки нескольких ответов.
На момент написания этой книги на стороне сервера DoH поддерживают
Googlec и Cloudflared; также поддержку добавил Firefox и даже начал экспериментировать с этимe и, кроме того, получил интересные результатыf.
Они говорят нам о том, что HTTP является достаточно мощным инструментом для приложений, даже если у них уже есть собственный протокол
передачи!
___________________________________
a
https://tools.ietf.org/html/draft-ietf-doh-dns-over-https.
b
https://tools.ietf.org/html/rfc7858.
c
https://tools.ietf.org/html/rfc8484.
d
https://developers.cloudflare.com/1.1.1.1/dns-over-https/.
e
https://blog.nightly.mozilla.org/2018/06/01/improving-dns-privacy-in-firefox/.
f
https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/.
g
 https://blog.nightly.mozilla.org/2018/08/28/firefox-nightly-secure-dns-experimental-results/.

HTTP как базовый транспортный уровень

385

IETF опубликовал спецификацию «Об использовании HTTP в качестве
основы» (On the Use of HTTP as a Substrate)1, в которой перечислены рекомендации и передовые методы, позволяющие получить максимальную
отдачу от использования HTTP таким способом. Некоторые приложения
расходятся со стандартами основных спецификациями HTTP и используют только часть протокола. Однако документ это допускает, но все же
предупреждает, почему это может привести к потере некоторых преимуществ использования HTTP. HTTP можно использовать таким образом
по многим причинам, некоторые из которых могут быть не очевидны,
и в спецификации предпринимается попытка прояснить эти причины
для пользователей протокола.

10.4.2 Использование концепции двоичного фрейминга HTTP/2
Появление уровня мультиплексированного двоичного фрейминга представляет новые возможности и причины для использования HTTP. В то
время как HTTP предлагали отвергнуть ввиду его неэффективности
в пользу прямых TCP-соединений или, возможно, WebSockets, HTTP/2
решает многие из этих проблем и становится его прямым соперником.
Новый протокол gRPC от Google (сокращение от Google Remote Procedure Calls)2 использует семантику HTTP и уровень двоичного фрейминга HTTP/23 для реализации более эффективного API на основе буферов4
протокола вместо менее эффективных форматов, таких как JSON. Маловероятно, что Google не воспользовался бы преимуществами, предлагаемыми HTTP/2.
Многие пользователи могут захотеть применить концепцию двоичного фрейминга HTTP/2 для трафика, отличного от HTTP, в качестве полнодуплексного протокола, а не инициируемого клиентом протокола, как
HTTP/2, только с ограниченным HTTP/2 push для запросов от сервера
к клиенту. В настоящее время эта функция не поддерживается. Возможно, QUIC, созданный с нуля с учетом использования других протоколов,
лучше подходит для этой цели (или, возможно, любые такие реализации
будут перенесены в HTTP/2 через TCP). Сейчас разработчики могут использовать HTTP как способ запустить другой протокол, например полноценный двусторонний протокол.

10.4.3 Использование HTTP для запуска другого протокола
Еще один вариант нестандартного использования HTTP – использование
его для начального обмена с целью перехода на другой протокол. HTTP
идеально походит для этого, поскольку он поддерживается повсеместно.
1

2
3
4

https://tools.ietf.org/html/rfc3205 сейчас находится в процессе обновления, поэтому вскоре должен быть заменен.
https://grpc.io/faq/.
https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.
https://developers.google.com/protocol-buffers/docs/overview.

Глава 10

386

Дальнейшее развитие HTTP

Таким образом, другой протокол будет выглядеть как HTTP и поддерживаться так же широко, особенно через прокси и других посредников. Чтобы реализовать этот вариант, вы можете воспользоваться HTTP-методом
CONNECT или обновить соединение (например, на WebSockets). В обоих
случаях соединения будут устанавливаться как HTTP, а затем быстро перейдут на другой протокол. Однако для сторонних наблюдателей все это
будет выглядеть как HTTP-трафик.

HTTP-метод CONNECT
Данный метод появился еще в версии HTTP/1.1. Он позволяет использовать HTTP-соединение в качестве прокси-сервера для туннелирования
его к альтернативному серверу и порту. Метод часто использовался для
применения этой функции к HTTPS. Делалось это посредством следующего синтаксиса:
CONNECT example.com:443 HTTP/1.1

Для HTTP/2 синтаксис будет таким:
:method: CONNECT
:authority: example.com:443

Данный код создает новое соединение от HTTP-сервера к example.com
и позволяет сообщениям проходить от клиента к этому серверу, как показано на рис. 10.8.
Клиент

Прокси-сервер

:method: CONNECT
:authority: example.com:443

Открывает соединение
с example.com через порт
443 и позволяет
исходному клиенту
отправлять туда сообщения

HTTPS-запрос(ы)

HTTP-соединение

Сервер сайта example.com

HTTPS-запрос(ы)

HTTPS-соединение через HTTP

Рис. 10.8 Туннелирование HTTPS-соединения через HTTP-прокси-соединение
с помощью CONNECT

Обратите внимание, что такой прокси-сервер отличается от проксисервера типа с функцией посредника, который создает два отдельных
HTTP-соединения. В этом сценарии используется одно HTTPS-со­еди­
не­ние и как минимум два TCP-соединения, поэтому после начальной
настройки создается впечатление, что клиент подключается непосредственно к конечному серверу.

387

HTTP как базовый транспортный уровень

Прокси-серверы не получат доступа к HTTPS-соединению, поскольку
оно будет сквозным. Таким образом, с помощью этой функции мы можем устанавливать HTTP/2-соединения через прокси-сервер HTTP/1.1,
если клиент и сервер поддерживают HTTP/2 (см. рис. 10.9).
Клиент
(поддерживает HTTP/2)

Прокси-сервер
(поддерживает только HTTP/1.1)

Сервер сайта example.com
(поддерживает HTTP/2)

Открывает соединение
с example.com через порт
443 и позволяет
исходному клиенту
отправлять туда сообщения

CONNECT
example.com:443

HTTP/2-запрос(ы)

HTTP/2-запрос(ы)

HTTP/1.1-соединение

HTTP/2-соединение через HTTPS с использованием HTTP/1.1

Рис. 10.9. Туннелирование HTTP/2-соединения через прокси HTTP/1.1
с помощью CONNECT

Туннелированное соединение, за исключением начального сообщения, не требует передачи HTTP-сообщений, ввиду чего может использовать любой протокол. На рис. 10.10 HTTP используется для подключения
к example.com через порт 22 (используемый SSH). После этого любые сообщения, отправляемые по этому соединению, станут управляющими
сигналами SSH, а не HTTP.
Клиент

Прокси-сервер

:method: CONNECT
:authority: example.com:22

Сервер сайта example.com (SSH)

Открывает соединение
с example.com через порт
22 и позволяет исходному
клиенту отправлять туда
сообщения

SSH-запрос(ы)

SSH-запрос(ы)

HTTP-соединение

SSH-соединение через HTTP

Рис. 10.10 Туннелирование SSH-соединения через HTTP-соединение
с помощью CONNECT

Глава 10

388

Дальнейшее развитие HTTP

Данная настройка может быть полезна для того, чтобы разрешить использование недоступных непосредственно из системы альтернативных
протоколов (при условии, что они доступны на прокси-сервере). Все эти
примеры показывают, что с помощью HTTP можно внедрить в какуюлибо среду другие протоколы. Таким образом, вопрос об обработке этих
протоколов сетевой инфраструктурой отпадает сам собой, особенно когда для сокрытия деталей от самих прокси-серверов используется HTTPS.
Возможно, этот вариант даже интереснее, чем QUIC, перешедший от TCP
к более простому протоколу UDP. На данный момент для поддержки метода CONNECT HTTP/3 обязательно потребуется TCP, однако эта проблема
находится на стадии решения.1 Для того чтобы ее решить, требуется настройка поддержки прокси-серверов как со стороны клиента, так и со
стороны сервера.

Обновление HTTP-соединения (например, на WebSockets)
Еще один вариант – запустить HTTP-соединение, а затем обновить его до
альтернативного протокола. Именно так работает WebSockets. Ниже вы
можете увидеть такое рукопожатие с синтаксисом HTTP/1.1.
Запрос клиента (в целях упрощения другие поля заголовка WebSockets
не представлены):
GET /application HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade

Ответ сервера:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

Затем HTTP-соединение, которое все еще работает через TCP-порты
80 или 443, преобразуется в соединение WebSockets. Для небольших соединений WebSockets более эффективен, чем HTTP, поскольку отсутствуют накладные расходы для HTTP-заголовков (значительно уменьшенных в HTTP/2 благодаря HPACK, но все же затратных). Также WebSockets
обеспечивает полнодуплексную связь. Поэтому использование этого
протокола может быть полезно для приложений, обновляющихся в реальном времени, таких как чаты, финансовые приложения или спортивные новостные веб-сайты.
Данный метод нельзя реализовать в HTTP/2, поскольку он потребует обновления всего HTTP-соединения, что для мультиплексированного
соединения, в котором должен обновляться только поток, бессмысленно.
Следовательно, для HTTP/2 WebSockets должен использовать метод CONNECT 2 (с небольшими изменениями, позволяющими указать протокол).
1
2

https://tools.ietf.org/html/draft-pardue-httpbis-http-network-tunnelling.
https://tools.ietf.org/html/draft-ietf-httpbis-h2-websockets.

HTTP как базовый транспортный уровень

389

Запрос клиента (в целях упрощения некоторые поля заголовка WebSockets упущены):
:method = CONNECT
:protocol = websocket
:scheme = https
:path = /application
:authority = example.com

Ответ сервера:
:status = 200

После этих шагов в соединении данными WebSockets можно обмениваться в любом направлении. Другие потоки HTTP/2 в этом соединении
могут продолжать использовать HTTP, другие соединения WebSockets
или даже иной подобный протокол. WebSockets открывает множество
возможностей, которые затрудняет природа HTTP (а именно то, что это
протокол запроса и ответа). Тот же метод подходит для переключения на
любой другой протокол, и, таким образом, я завершаю главу еще одним
способом использования HTTP!
В последней главе я попытался заглянуть в будущее, чтобы увидеть,
что ждет HTTP дальше. Сегодня относительно этого есть много предположений и крайне мало подтвержденной информации, поэтому на данном этапе никто не знает, как на самом деле будет выглядеть следующая
версия HTTP. Однако вы можете быть уверены в двух вещах: HTTP останется важной частью Internet и его популярность будет расти. В начале
главы 1 я уточнил, что термин «Всемирная паутина» часто ошибочно отождествляется с Internet как комплексом физических и логических протоколов. Однако непрерывное развитие Всемирной сети (а также специально созданного для нее протокола HTTP) может означать, что очень
скоро такое смешение понятий окажется не столь далеким от истины,
как раньше. В последней главе я объясняю различные варианты, которые
позволяют расширить использование HTTP и будут способствовать его
дальнейшему росту.
Читатели, дошедшие до этого момента, уже должны хорошо разбираться в HTTP/2 и связанных с ним технологиях. Также теперь вы знаете,
что, хотя HTTP/2 и предлагает большое количество возможностей и методов для ускорения, он не гарантирует повышения производительности. Те, кто искал в этой книге быстрое решение, могут быть несколько
недовольны, однако все же HTTP/2 работает быстрее для большинства
сайтов; кроме того, не стоит забывать, что протокол находится в стадии
развития и повсеместного принятия. QUIC и HTTP/3 будут использовать
те же концепции, что и HTTP/2, однако выведут их на новый уровень.
Знания, полученные благодаря этой книге, помогут вам ощутить все
преимущества QUIC. Однако до этого вам еще предстоит многое узнать
о HTTP/2 и о том, как его лучше всего использовать. Я надеюсь, что эта
книга вызвала у вас интерес к HTTP/2. Я хочу, чтобы она стала отправной
точкой для дальнейших экспериментов и наблюдения за развитием этого интересного, широко используемого протокола. Желаю удачи!

Глава 10

390

Дальнейшее развитие HTTP

Резюме
В процессе стандартизации вокруг HTTP/2 возникли некоторые разногласия и появилось немало недоброжелателей. Однако это не помешало внедрению HTTP/2 в Internet, и в большинстве случаев многие
проблемы оказались не такими серьезными, как предполагалось.
„„ HTTP/2 получил широкое распространение и уже по этой причине может считаться успешным протоколом.
„„ HTTP теперь состоит из уровня двоичного фрейминга HTTP/2 и семантики HTTP, на которой строятся фреймы.
„„ QUIC обещает быть преемником HTTP/2, и когда он будет опубликован, HTTP-часть спецификации QUIC будет называться HTTP/3.
„„ Существует множество HTTP-методов для расширения вариантов использования протоколов, а благодаря HTTP/2 их становится еще больше.
„„ HTTP может использоваться для передачи других протоколов.
„„ HTTP ждет большое будущее!
„„

Приложение
Обновление популярных
веб-серверов до HTTP/2
В этом приложении описываются методы включения поддержки HTTP/2
на некоторых популярных веб-серверах напрямую (A1) или в качестве
обратного прокси (A2). Обратите внимание, что предложение не является исчерпывающим справочным материалом для всех веб-серверов,
поддерживающих HTTP/2; в первую очередь обратитесь к документации
своего веб-браузера.

A.1

Обновление вашего веб-сервера
для поддержки HTTP/2
В следующих разделах я расскажу, как установить совместимые с HTTP/2
версии обычных веб-серверов, а также как настроить их для поддержки
HTTP/2. Обратите внимание, что этот список далеко не полный и со временем будет меняться; он призван лишь показать некоторые примеры
настройки веб-серверов, совместимых с HTTP/2. Многие шаги в разделах повторяются, поэтому рекомендую сразу перейти к интересующему
вас разделу.

Самозаверенные сертификаты HTTPS и ошибки сертификата
В большинстве примеров в этом приложении используются самозаверенные
сертификаты SSL/TLS (также известные как сертификаты HTTPS). Такие сертификаты поставляются с веб-сервером или создаются с помощью команды
openssl и т. п. Для того чтобы сертификат распознавался браузером и был
надежным, он должен быть выпущен авторизованным центром сертифика-

Приложение

392

Обновление популярных веб-серверов до HTTP/2

ции (ЦС). Каждый браузер или операционная система содержит список центров спецификации. Фиктивные сертификаты, которые поставляются с вебсерверами (и те, которые представлены в этом приложении), не выдаются
центром сертификации, и они известны как самозаверенные сертификаты.
Они позволят сайту работать через HTTPS, но вы получите уведомление об
ошибке сертификата. Обычно ошибки такого рода можно проигнорировать
и легко получить доступ к сайту, однако это в некоторой степени опасно.
В случаях, если пользователь игнорирует ошибки сертификата, Chrome и Opera не используют кеширование HTTP по соображениям безопасностиa, поэтому нельзя использовать HTTP/2 push.
Можно добавить самозаверенный сертификат в хранилище доверенных
сертификатов компьютера, чтобы браузер распознавал сертификат, отображал зеленый замок и устранял эти ошибки. Данный метод рекомендуется для
локальной разработки (через localhost). Как это сделать, зависит от вашего
браузера и операционной системы, но обычно действие выполняется двойным кликом по сертификату.
Если вы используете сервер с реальным доменным именем, стоит все же
получить настоящий сертификат, тогда его будут распознавать все браузеры
и не требовать при этом дополнительных шагов для его регистрации. Данная
опция недоступна для localhost или любого другого локального IP-адреса
(например, 127.0.0.1 или :: 0).
___________________________________
a
https://bugs.chromium.org/p/chromium/issues/detail?id=103875#c8.

A.1.1 Apache
В версии 2.4.17 HTTP-сервер Apache (он же httpd) представил поддержку
HTTP/2 через модуль mod_http2 (именуемый ранее как отдельный модуль
mod_h21). Он считался экспериментальным вплоть до выхода версии 2.4.26.
Я рекомендую использовать последнюю версию Apache (доступную на
http://httpd.apache.org/), так как вышеупомянутый модуль получил в ней
значительное развитие.
Apache поддерживает HTTP/2 через HTTPS с использованием ALPN
и никогда не реализовывал старый метод NPN согласования SPDY/
HTTP/2. Следовательно, Apache требует как минимум OpenSSL 1.0.2 (или
эквивалент), чтобы включить поддержку HTTP/2 даже для браузеров, которые все еще поддерживают NPN. Apache разрешает HTTP/2 через HTTPсоединения с открытым текстом (известные как h2c), хотя эта функция
имеет ограниченное использование, поскольку ни один браузер не поддерживает ее. Apache использует библиотеку nghttp2 в качестве основы
своей функциональности HTTP/2 и требует версии 1.5.0 или более поздней для полной функциональности на момент написания книги.
HTTP/2 push поддерживается в Apache (см. главу 5). Также сервер поддерживает модуль mod_proxy_http2, который позволяет подключаться
1

https://github.com/icing/mod_h2.

Приложение

Обновление популярных веб-серверов до HTTP/2

393

к серверным системам через HTTP/2, хотя этот модуль все еще считается
экспериментальным. На вопрос «Нужно ли всегда использовать HTTP/2?»
есть ответ в главе 3, где объясняется, что использование HTTP/2 на всем
пути к внутреннему соединению чаще всего не является необходимым.

Apache на Windows
Хотя Apache на Windows в производственных системах, возможно, не
так распространен, как на Linux, версия для Windows часто используется разработчиками. Компиляция Apache из исходного кода для Windows
выходит за рамки этой книги. Но если вы хотите запустить Apache локально, чтобы поэкспериментировать с HTTP/2, вы можете использовать готовые версии Windows из различных источников (к сожалению,
не из Apache). Популярные варианты для версий Windows – Apache Haus1,
Apache Lounge2 и пакеты XAMPP. Все это вы можете найти на веб-сайте
Apache по адресу http://httpd.apache.org/docs/current/platform/windows.
html#down. Выбор подходящего вариант зависит от следующего:
„„ версии Visual C++. Возможно, вам придется установить Visual C++
Distributable (от Microsoft). Имейте в виду, что Microsoft в документации ссылается на эти файлы по-разному (по годам выхода или по
версии). Для удобства я соединю все в один список:
– Visual Studio 2008: VC++ 9;
– Visual Studio 2010: VC++ 10;
– Visual Studio 2012: VC++ 11;
– Visual Studio 2013: VC++ 12;
– Visual Studio 2015: VC++ 14;
– Visual Studio 2017: VC++ 15.
Вы можете просмотреть установленные вами версии в панели
управления вашей системы Windows во вкладке Установка и удаление программ. Установить новую версию несложно, и я рекомендую выбирать самую последнюю;
„„ архитектуры – выбор между 64-битной и 32-битной (также известной как x86). Если вы используете 64-битную операционную
систему, я рекомендую 64-битный Apache. Если вы не используете
64-разрядную операционную систему, задумайтесь о переходе на
нее (в наши то дни!). Вы можете просмотреть архитектуру, щелкнув
правой кнопкой мыши по Мой компьютер и выбрать Свойства
в контекстном меню. В появившемся окне вы должны увидеть архитектуру (Тип системы);
„„ версии OpenSSL. Вам потребуется версия 1.0.2 или новее, но некоторые сайты предлагают сборки для версии 1.1.0 или новее. Я рекомендую использовать последнюю версию везде, где это возможно.
Установить пакеты Apache Haus (и другие) с поддержкой HTTP/2 можно следующим образом.
1
2

https://www.apachehaus.com/cgi-bin/download.plx.
https://www.apachelounge.com/download/.

394

Приложение

Обновление популярных веб-серверов до HTTP/2

1 Загрузите

соответствующую версию на основе трех предыдущих
вариантов и распакуйте папку в нужное место (например, C:\Program Files\Apache\Apache24).
2 Отредактируйте файл conf\httpd.conf, чтобы изменить следующую
строку:
Define SRVROOT "/Apache24"

указав в ней расположение вашего сервера, например:
Define SRVROOT "C:\Program Files\Apache\Apache24"

Убедитесь, что в конце строки нет косой черты (C:\Program Files\
Apache\Apache24\), и не забудьте заключить путь в кавычки, если он
содержит пробел (например, Program Files).
3 Убедитесь, что модуль mod_http2 активирован и в нем нет комментариев (значка # перед ним):
LoadModule http2_module modules/mod_http2.so
4 Сохраните

изменения в httpd.conf. Обратите внимание, что, если
он находится в папке Program Files, могут потребоваться права администратора.
5 Запустите Apache, желательно из командной строки, чтобы увидеть
ошибки:
cd "c:\Program Files\Apache\Apache24\bin"
httpd.exe

Ниже приведены некоторые распространенные ошибки, которые могут помешать работе этой команды:
„„ сообщение об ошибке содержит предупреждение об отсутствии
VCRUNTIME140.dll или аналогичного файла, что указывает на то,
что вы не установили необходимый компонент Visual C++;
„„ файл журнала предназначен только для чтения, поэтому вы увидите сообщение о невозможности открыть журнал ошибок. Щелкните правой кнопкой мыши по папке C:\Program Files\Apache\
Apache24\Logs, выберите Свойства в контекстном меню и снимите флажок Только для чтения, если он выбран;
„„ всплывающее окно брандмауэра Windows просит вас предоставить ему доступ к этому веб-серверу. Вам нужно ответить «да»;
„„ в сообщении об ошибке говорится, что у вас нет доступа к портам
80 или 443. Скорее всего, этот порт использует другая программа. Наиболее часто это World Wide Web Publishing Service (она же
называется IIS), которую можно найти во вкладке Службы компьютера. (Остановите эту службу и установите для нее параметр
запуска Вручную вместо Автоматически, если вы не используете IIS, Skype или другой веб-сервер.)
6 После запуска Apache проверьте http://localhost. Там вы должны
увидеть страницу приветствия Apache Haus. Затем попробуйте

Приложение

Обновление популярных веб-серверов до HTTP/2

395

перейти на https://locahost, где вы получите ошибку сертификата,
если используете фиктивный сертификат по умолчанию. Пропустите ошибку сертификата, и вы увидите, что ваша страница работает на HTTP/2, если вы откроете инструменты разработчика и добавите столбец Protocol, как показано на рис. A.1.

Рис. A.1

Запуск Apache на HTTP/2
7 Если

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

cd "c:\Program Files\Apache\Apache24\bin"
httpd.exe -k install

Вы должны увидеть новую службу во вкладке Службы. Удалить
службу можно с помощью следующей команды:
cd "c:\Program Files\Apache\Apache24\bin"
httpd.exe -k uninstall

Приложение

396

Обновление популярных веб-серверов до HTTP/2

Apache на Linux
Переход Apache на HTTP/2 в серверах Linux сложнее ввиду устаревшей
версий Apache и необходимого программного обеспечения (обсуждаемого в главе 3). Часто приходится перед установкой компилировать их
из исходного кода. В этом разделе я расскажу про один из таких методов
для Red Hat/CentOS, но, если вы используете другую версию Linux, вам
необходимо будет адаптировать его соответствующим образом.
Red Hat Enterprise Linux (RHEL) и CentOS, на которой он основан, добавили поддержку OpenSSL 1.0.2 в версии 7.4, которая решает одну из проблем. Однако на момент написания этой книги они не включают версию
Apache с поддержкой HTTP/2; вместо этого они по умолчанию включают старую версию 2.4.6. Кроме того, HTTP/2 не поддерживается, когда
Apache использует префорк MPM (Multi-Processing Module)1, который часто устанавливается по умолчанию из соображений совместимости и не
может быть изменен без перекомпиляции.
Для Red Hat/CentOS доступны два полуофициальных дополнительных
репозитория:
репозиторий Extra Packages for Enterprise Linux (EPEL)2 разработан
целевой группой Fedora и позволяет легко устанавливать дополнительные пакеты для Red Hat/CentOS. К сожалению, он не включает
Apache, хотя включает более свежую версию nghttp2, необходимую
для включения поддержки HTTP/2 в Apache;
3
„„ репозиторий Red Hat Software Collections (RHSCL) – это репозиторий официально поддерживаемый Red Hat, который включает новые версии общего программного обеспечения. Версия 3.1 включает Apache 2.4.27, который имеет поддержку HTTP/2 при условии,
что вы используете RHEL/CentOS версии 7.4 или новее. Репозиторий
устанавливает дополнительную версию Apache (apache24) в отдельном месте (/opt/rh/httpd24/) с файлами конфигурации и местоположениями, отличными от обычных, поэтому может потребоваться
некоторое время, чтобы привыкнуть к этому. Кроме того, поскольку
HTTP/2 находится в активной разработке, к версии 2.4.27 были добавлены некоторые доработки. Они обеспечивают частично поддерживаемую версию, но могут быть не идеальными при работе
с более старыми версиями.
„„

Самую новуюверсию, вам, вероятно, придется устанавливать из неофициального источника или исходного кода. Ни один из методов не является идеальным, поэтому его рассмотрение не так важно (см. главу 3).
Если вы ознакомились с предупреждениями и по-прежнему хотите загружать файлы из репозитория, можем перейти непосредственно к са1

2
3

https://serverfault.com/questions/383526/how-do-i-select-which-apache-mpmto-use.
https://fedoraproject.org/wiki/EPEL.
https://developers.redhat.com/products/softwarecollections/overview/.

Приложение

Обновление популярных веб-серверов до HTTP/2

397

мому процессу. Процедура зависит от версии RHEL/CentOS, которую вы
используете. Для версий до 7.4 вам также необходимо установить OpenSSL из исходного кода, поскольку версия 1.0.2 с поддержкой ALPN была
недоступна. Для версии 7.4 и новее некоторые шаги проще, поскольку вы
можете использовать версию OpenSSL по умолчанию.
1

Установите все необходимые зависимости:
sudo
sudo
sudo
sudo
sudo
sudo
sudo

2

yum
yum
yum
yum
yum
yum
yum

-y
-y
-y
-y
-y
-y
-y

install
install
install
install
install
install
install

wget
perl
zlib-devel
gcc
pcre-devel
expat-devel
epel-release

Создайте каталог для исходного кода:
cd /tmp
mkdir sources
cd sources

Для OpenSSL у вас есть два варианта.
Если вы используете RHEL/CentOS 7.4 или новее, вы можете воспользоваться пакетной версией OpenSSL, которая включает поддержку ALPN:
sudo yum -y install openssl-devel

Если вы используете старую версию или хотите установить последнюю версию OpenSSL, вам необходимо установить ее из исходного кода:
#Загрузите ее с http://openssl.org/source/
#Например:
wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz
wget https://www.openssl.org/source/openssl-1.1.1a.tar.gz.asc
#Проверьте пакет после загрузки:
gpg --verify openssl-1.1.1a.tar.gz.asc
#Если вы получаете сообщение "Невозможно проверить подпись: нет открытого
#ключа", получите соответствующий открытый ключ и повторите проверку.
#Например:
gpg --recv-keys 0E604491
gpg --verify openssl-1.1.1a.tar.gz.asc
#Обратите внимание, что вы увидите ПРЕДУПРЕЖДЕНИЕ, что ключ не сертифицирован
#с доверенной подписью, что ожидаемо.
#Извлеките файл и скомпилируйте его:
tar -zxvf openssl-1.1.1a.tar.gz
cd openssl-1.1.1a
./config shared zlib-dynamic --prefix=/usr/local/ssl
make
sudo make install
cd ..

398

Приложение
3 Установите

версию:

Обновление популярных веб-серверов до HTTP/2

nghttp2. Опять же, вы можете использовать пакетную

sudo yum install -y libnghttp2-devel

Она подходит как для RHEL/CentOS 6, так и для 7. Если вы хотите
получить последнюю версию, поскольку в ней есть функции, которые вы хотите использовать, установите ее из исходного кода (но
обратите внимание, что ключ PGP не входит в пакет):
#Загрузите и установите nghttp2 (необходимо для mod_http2)
#с https://nghttp2.org/
#Последняя версия находится здесь: https://github.com/nghttp2/nghttp2/
#releases/
#Например:
wget https://github.com/nghttp2/nghttp2/releases/download/v1.34.0/nghttp21.34.0.tar.gz
tar -zxvf nghttp2-1.34.0.tar.gz
cd nghttp2-1.34.0
./configure
make
sudo make install
cd ..
4 Затем

получите ключи подписи, необходимые для проверки загрузки Apache:

#Загрузите и установите ключи PGP необходимые Apache
wget https://www.apache.org/dist/httpd/KEYS
gpg --import KEYS
wget https://people.apache.org/keys/group/apr.asc
gpg --import apr.asc
5 Теперь вам нужно установить библиотеки разработки apr и apr-util.

Если вы используете RHEL/CentOS 7.4 и пакетный OpenSSL (1.0.2),
вы можете также использовать пакетные версии:
sudo yum -y install apr-devel
sudo yum -y install apr-util-devel

Если вы хотите использовать Openssl 1.1.0 или выше или ваша
версия RHEL/CentOS ниже 7.4, вам следует произвести установку из
исходного кода:
#Загрузите и установите последнюю версию за апрель apr.
#Обратите внимание, при openssl версии 1.1.0 или выше, потребуется APR 1.6
#или выше.
#Загрузите с http://apr.apache.org/.
#Например:
wget http://mirrors.whoishostingthis.com/apache/apr/apr-1.6.5.tar.gz
wget https://www.apache.org/dist/apr/apr-1.6.5.tar.gz.asc
#Проверьте пакет после загрузки:
gpg --verify apr-1.6.5.tar.gz.asc
#Обратите внимание, вы увидите ПРЕДУПРЕЖДЕНИЕ о том, что ключ
#не сертифицирован доверенной подписью, что ожидаемо.

Приложение

Обновление популярных веб-серверов до HTTP/2

399

#Установите пакет:
tar -zxvf apr-1.6.5.tar.gz
cd apr-1.6.5
./configure
make
sudo make install
cd ..
#Загрузите и установите последнюю версию apr-util.
#Обратите внимание, при openssl 1.1.0 или выше потребуется APR-UTIL 1.6
#или выше.
#Загрузите с http://apr.apache.org/.
#Например:
wget http://mirrors.whoishostingthis.com/apache/apr/apr-util-1.6.1.tar.gz
wget https://www.apache.org/dist/apr/apr-util-1.6.1.tar.gz.asc
#Проверьте пакет после загрузки:
gpg --verify apr-util-1.6.1.tar.gz.asc
#Обратите внимание, вы увидите ПРЕДУПРЕЖДЕНИЕ о том, что ключ
#не сертифицирован доверенной подписью, что ожидаемо.
tar -zxvf apr-util-1.6.1.tar.gz
cd apr-util-1.6.1
./configure --with-apr=/usr/local/apr
make
sudo make install
cd ..
6

Наконец, установите Apache:
#Загрузите и установите apache.
#Например:
wget http://mirrors.whoishostingthis.com/apache/httpd/httpd2.4.37.tar.gz
wget https://www.apache.org/dist/httpd/httpd-2.4.37.tar.gz.asc
#Проверьте пакет после загрузки:
gpg --verify httpd-2.4.37.tar.gz.asc
#Обратите внимание, вы увидите ПРЕДУПРЕЖДЕНИЕ о том, что ключ
#не сертифицирован доверенной подписью, что ожидаемо.
#Извлеките исходный код.
tar -zxvf httpd-2.4.37.tar.gz
cd httpd-2.4.37

7 Данный

шаг зависит от того, скомпилировали ли вы openssl, nghttp,
apr и apr-util в предыдущих шагах. Если вы используете системные
версии всех этих библиотек, вы можете скомпилировать Apache
следующим образом:
./configure --enable-ssl --enable-so --enable-http2
make
sudo make install
cd ..

Если вы использовали локальные apr, apr-util и openssl, вам необходимо сделать следующее:
./configure --with-ssl=/usr/local/ssl \
--with-apr=/usr/local/apr/bin/apr-1-config \

Приложение

400

Обновление популярных веб-серверов до HTTP/2

--with-apr-util=/usr/local/apr/bin/apu-1-config \
--enable-ssl --enable-so --enable-http2make
sudo make install
cd ..

Данный код должен установить Apache в /usr/local/apache2.
Apache, чтобы проверить, работает ли он с базовым
HTTP:

8 Запустите

sudo /usr/local/apache2/bin/apachectl -k graceful

Если все установлено правильно, вы сможете посетить свой сайт
через HTTP и увидеть страницу It works (Это работает), открываемую по умолчанию. Чтобы запустить HTTP/2, требуется еще несколько шагов.
9 Если вы скомпилировали OpenSSL или nghttp2, вам необходимо отредактировать файл envvars в каталоге bin, чтобы загрузить пути
для локальных установок. Если вы используете стандартную установку (для этого требуется версия 7.4), перейдите к следующему
шагу:
if test "x$LD_LIBRARY_PATH" != "x" ; then
LD_LIBRARY_PATH="/usr/local/apache2/lib:/usr/local/lib/:
/usr/local/ssl/lib:$LD_LIBRARY_PATH"
else
LD_LIBRARY_PATH="/usr/local/apache2/lib:/usr/local/lib/:
/usr/local/ssl/lib"
fi
10 Удалите

комментарии из следующих модулей в файле httpd.conf,
чтобы загрузить модули SSL и HTTP, а также модуль socache, необходимый для SSL:
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule http2_module modules/mod_http2.so

11

Добавьте эту строку, чтобы включить конфигурацию SSL:
Include conf/extra/httpd-ssl.conf

12 Добавьте

эту строку, чтобы показать, что сервер сначала использует HTTP/2 (h2), а затем переходит HTTP/1, а также для включения
ведения журнала:

Protocols h2 http/1.1
LogLevel http2:info


Вы также можете добавить h2c в строку Protocols, если хотите
включить HTTP/2 через HTTP (и не требовать HTTPS), но эта функция не поддерживается ни одним браузером и имеет ограниченное
использование.

Приложение

Обновление популярных веб-серверов до HTTP/2

401

13 Установите сертификат HTTPS. Получение сертификата выходит за

рамки этой книги, поэтому я покажу вам, как использовать OpenSSL для создания базового самозаверенного сертификата. Браузер
не распознает этот сертификат, но он будет работать для базовых
тес­тов:
#Обратите внимание, что команду openssl нужно запускать под root.
sudo su –
cd /usr/local/apache2/conf
openssl req \
-newkey rsa:2048 \
-x509 \
-nodes \
-keyout server.key \
-new \
-out server.crt \
-subj /CN=server.domain.tld \
-reqexts SAN \
-extensions SAN \
-config