openjdk.ruOpenJDK на русском

JEP 517: HTTP/3 for the HTTP Client API

HTTP/3 для HTTP Client API

ОтветственныйDaniel Fuchs
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск26
Компонентcore-libs / java.net
Обсуждениеnet dash dev at openjdk dot org
ТрудоёмкостьL
ДлительностьL
Связан сJEP 321: HTTP Client API
РецензентыAlan Bateman, Bradford Wetmore, Paul Sandoz
ОдобренPaul Sandoz
Создан2022/08/05 14:58
Обновлён2026/01/21 11:04
Задача8291976

Аннотация

Обновить HTTP Client API, чтобы он поддерживал протокол HTTP/3, и библиотеки и приложения могли взаимодействовать с HTTP/3-серверами при минимальных изменениях кода.

Цели

  • Обновить HTTP Client API, чтобы он отправлял и получал HTTP/3-запросы и HTTP/3-ответы.

  • Обойтись лишь небольшими изменениями в HTTP Client API и в коде приложений.

  • Не менять версию протокола по умолчанию с HTTP/2 на HTTP/3, а дать разработчикам возможность включать HTTP/3 явно.

Что не является целью

  • Предоставлять API для протокола QUIC, на котором основан HTTP/3, не является целью.

  • Поддерживать сторонних провайдеров защищённых сокетов не является целью.

  • Предоставлять серверную реализацию протокола HTTP/3 не является целью.

  • Обновлять обработчик протокола, который использует устаревший API java.net.URL, поддерживающий только HTTP/1.1, не является целью.

Мотивация

JEP 321 (JDK 11) добавил в платформу Java современный HTTP Client API. Этот API поддерживает HTTP/1.1 и HTTP/2 и был спроектирован так, чтобы поддерживать будущие версии протокола с минимальными изменениями. По умолчанию API предпочитает HTTP/2, но прозрачно переходит на HTTP/1.1, если целевой сервер не поддерживает HTTP/2.

С HTTP Client API легко писать код, который взаимодействует с HTTP-серверами. Например, чтобы отправить GET-запрос на https://openjdk.org/ и получить ответ в виде строки:

import java.net.http.*;

...

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
                         .GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assert response.statusCode() == 200;
String htmlText = response.body();

Здесь в использовании API нет ничего явного, что зависело бы от версии протокола HTTP. Код приложения не зависит от протокола.

К сожалению, HTTP Client API не поддерживает последнюю версию протокола HTTP. HTTP/3, преемник HTTP/2, был стандартизирован в 2022 году организацией Internet Engineering Task Force (IETF). HTTP/3 использует надёжный протокол транспортного уровня QUIC вместо TCP. QUIC защищается с помощью Transport Layer Security (TLS) версии 1.3.

Поддержка HTTP/3 позволила бы приложениям, использующим HTTP Client API, получить преимущества многих улучшений, которые даёт протокол HTTP/3, в том числе:

HTTP/3 уже поддерживается большинством веб-браузеров и развёрнут примерно на трети всех веб-сайтов. Платформа Java должна поддерживать его для использования на стороне клиента.

Описание

Чтобы отправить запрос по HTTP/3, вы должны явно включить его использование.

Для этого можно установить версию протокола объекта HttpClient в HTTP/3, и тогда все запросы, отправляемые этим клиентом, по умолчанию будут предпочитать HTTP/3:

var client = HttpClient.newBuilder()
                       .version(HttpClient.Version.HTTP_3)
                       .build();

Либо можно задать предпочтительную версию протокола для отдельного объекта HttpRequest:

var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
                         .version(HttpClient.Version.HTTP_3)
                         .GET().build();

Больше никаких изменений не требуется. Выбрав HTTP/3 в качестве предпочтительной версии в запросе или в клиенте, вы отправляете запрос обычным образом. Если целевой сервер не поддерживает HTTP/3, то запрос по умолчанию будет прозрачно переведён на HTTP/2 или даже на HTTP/1.1, в зависимости от ситуации.

Согласование версий протокола HTTP

Заранее определить, поддерживает ли целевой сервер HTTP/3, невозможно. Также невозможно перевести существующее соединение HTTP/1.1 или HTTP/2 на соединение HTTP/3, поскольку HTTP/1.1 и HTTP/2 построены поверх TCP-потоков, а QUIC, на котором работает HTTP/3, построен поверх UDP-датаграмм.

Как же тогда HTTP Client API определяет, может ли он использовать HTTP/3 для данного целевого сервера? Есть четыре основных подхода:

  1. Отправить первый запрос по HTTP/3. Если соединение HTTP/3 не удаётся установить за разумное время, откатиться на HTTP/2 или HTTP/1.1. (Так происходит, когда у объекта HttpRequest предпочтительная версия HTTP_3.)

  2. Отправить первый запрос, пытаясь параллельно установить и соединение HTTP/3, и соединение HTTP/2 или HTTP/1.1. Использовать протокол того соединения, которое установится первым. (Так происходит, когда у объекта HttpClient предпочтительная версия HTTP_3, а у объекта HttpRequest предпочтительная версия не задана.)

  3. Отправить первый запрос по HTTP/2 или HTTP/1.1. Если ответ сервера указывает, что HTTP/3 доступен как альтернативный сервис, использовать HTTP/3 для всех последующих запросов. (Так происходит, когда вы указываете [Http3DiscoveryMode.ALT_SVC] в качестве значения параметра запроса H3_DISCOVERY и хотя бы у одного из объектов HttpClient или HttpRequest предпочтительная версия HTTP_3.)

  4. Отправлять все запросы по HTTP/3. Если сервер не отвечает по HTTP/3, завершиться с ошибкой, не откатываясь на HTTP/1.1 или HTTP/2. (Так происходит, когда вы указываете Http3DiscoveryMode.HTTP_3_URI_ONLY в качестве значения параметра запроса H3_DISCOVERY и хотя бы у одного из объектов HttpClient или HttpRequest предпочтительная версия HTTP_3.)

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

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

Возможные дальнейшие улучшения

  • Дать возможность ограниченной настройки и оптимизации реализации HTTP/3, например с помощью специфичных для JDK системных свойств или, возможно, небольших новых API.

  • Определить новые подклассы IOException и SSLException для исключительных ситуаций, специфичных для HTTP/3.

  • Расширить интерфейс PushPromiseHandler, чтобы поддержать возможность HTTP/3 совместно использовать promise-ответы в разных потоках запросов/ответов в рамках одного соединения.

  • Добавить в API HttpClient.Builder и HttpRequest.Builder параметры конфигурации для управления обнаружением целевого сервера.

Тестирование

Мы проведём обширное модульное и интеграционное тестирование. Мы также проверим взаимодействие с серверными реализациями HTTP/3, включая Netty, quic-go, quiche, Neqo и nghttp3.

Риски и допущения

Эта первая реализация HTTP/3 не будет поддерживать провайдеры защищённых сокетов, кроме провайдера по умолчанию, SunJSSE. Для поддержки сторонних провайдеров защищённых сокетов потребовалось бы добавить методы в SPI провайдеров, а затем разработчикам, сопровождающим такие провайдеры, пришлось бы реализовать эти методы. Мы можем заняться этим в дальнейшей работе.

Зависимости

Протокол QUIC определяется следующими документами:

  • RFC 8999: Version-Independent Properties of QUIC
  • RFC 9000: A UDP-Based Multiplexed and Secure Transport
  • RFC 9001: Using TLS to Secure QUIC
  • RFC 9002: QUIC Loss Detection and Congestion Control

Протокол HTTP/3 определяется следующими документами:

Эти RFC представляют интерес для обнаружения конечных точек QUIC и измерения MTU сетевого пути. Первая реализация поддерживает HTTP Alternative Services, но Path MTU Discovery не реализован.

  • RFC 7838: HTTP Alternative Services
  • RFC 8899: Packetization Layer Path MTU Discovery for Datagram Transports
  • RFC 4821: Packetization Layer Path MTU Discovery

Интерес представляют ещё два RFC, которые также поддерживаются первой реализацией: