Принудительное немедленное небезопасное соединение HTTP2 с Java HttpClient

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

Я пытался использовать утилиты в java.net.http, вызывая version(HttpClient.Version.HTTP_2) по запросу и сборщики клиентов, однако первоначальный запрос всегда отправляется через HTTP/1.1 с заголовком обновления. Пока что единственный способ заставить версию 2 с самого начала, похоже, использовать безопасное соединение через https (чего я хотел бы избежать).

Я также хотел бы использовать только классы, включенные в OpenJDK 11 (без netty и т.п.).


person Jakob Odersky    schedule 26.09.2018    source источник
comment
Что забавно, такая фича, если я правильно понимаю, заявлена ​​как цель соответствующего JEP 110: › Должен иметь возможность согласовать обновление с 1.1 до 2 (или нет) или выбрать 2 с самого начала.   -  person Dmitry Timofeev    schedule 10.05.2019


Ответы (1)


Боюсь, это невозможно в клиентском API http Java 11.

Наиболее подходящим внутренним классом JDK является Http2Connection.java. В нем перечислены три случая «создания», которые мы ожидаем для соединений HTTP/2:

  1. обновленное HTTP/1.1 простое TCP-соединение
  2. предварительное знание напрямую создало простое TCP-соединение
  3. непосредственно созданное HTTP/2 SSL-соединение, которое использует ALPN.

Нас интересует случай 2. Для этого есть код:

/**
 * Cases 2) 3)
 *
 * request is request to be sent.
 */
private Http2Connection(HttpRequestImpl request,
                        Http2ClientImpl h2client,
                        HttpConnection connection)
    throws IOException
{
    ...
}

Увы, этот конструктор фактически используется только из метода для безопасных соединений:

// Requires TLS handshake. So, is really async
static CompletableFuture<Http2Connection> createAsync(HttpRequestImpl request,
                                                      Http2ClientImpl h2client,
                                                      Exchange<?> exchange) {
    assert request.secure();
    AbstractAsyncSSLConnection connection = (AbstractAsyncSSLConnection)
    ...
    return connection.connectAsync(exchange)
              .thenCompose(unused -> connection.finishConnect())
              .thenCompose(unused -> checkSSLConfig(connection))
              .thenCompose(notused-> {
                  CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
                  try {
                      Http2Connection hc = new Http2Connection(request, h2client, connection);
                      cf.complete(hc);
                  } catch (IOException e) {
                      cf.completeExceptionally(e);
                  }
                  return cf; } );

Конструкторы, отличные от TLS, вызываются только при обновлении соединения HTTP 1.1.

Я изучил и многие другие классы. Я не вижу ничего, что могло бы предположить, что соединения HTTP/2 с открытым текстом можно согласовать без обновления с HTTP 1.1 в клиенте JDK 11.

То, что я нашел, очень совпадает с описанием API в javadocs для HttpClient.Builder, в котором говорится только о выборе HTTP/2 в качестве версии:

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

В повторном использовании соединения HTTP/2 происходит много интересного. Например, когда одновременные запросы обновляются, только один сохраняется для будущего использования в «кэше» соединения HTTP/2. Кэшированное соединение истечет, когда количество потоков достигнет максимума, который составляет ~ 2^31-1, так что дальнейшие запросы не превысят его, но существующие потоки останутся открытыми. Это много потоков. Я думаю, что если ваш вариант использования подобен моему — клиент веб-службы в очень долго работающих приложениях — тогда стоимость обновления практически только по первому запросу незначительна. Я все еще хотел бы исключить его, в основном для простоты, но я начинаю думать, что это того не стоит.

Как и спрашивающий, я хотел бы придерживаться клиента JDK. Я просто упомяну, что бета-версия Apache HttpClient 5 поддерживает форсирование HTTP/2 (HttpVersionPolicy.FORCE_HTTP_2). Я не могу ничего посоветовать по этому поводу, потому что в бета-версии не так много документации, а комментарии гораздо реже, чем в реализации JDK. Но если я попробую и добьюсь успеха, я обновлю свой ответ.

person drrob    schedule 19.03.2019
comment
Спасибо за анализ реализации! У меня был тот же вопрос, и я думаю, что производительность, вероятно, не причина для его использования, а возможность установить соединение HTTP/2 в локальной среде разработки (для тестирования, экспериментов, бенчмаркинга) без управления ключами и установки TLS соединения, что также усложняет анализ пакетов, скажем, в Wireshark. Было бы определенно полезно иметь такую ​​функцию в стандартной библиотеке. - person Dmitry Timofeev; 10.05.2019