【问题标题】:Force immediate insecure HTTP2 connection with Java HttpClient使用 Java HttpClient 强制立即不安全的 HTTP2 连接
【发布时间】:2019-03-19 10:35:02
【问题描述】:

仅使用标准 Java 库,作为客户端,不安全 HTTP/2 连接如何事先知道将使用该版本的协议? IE。无需先通过 HTTP/1.1 发送升级请求。

我已经尝试使用java.net.http 中的实用程序,根据请求和客户端构建器调用version(HttpClient.Version.HTTP_2),但是初始请求始终通过 HTTP/1.1 发送并带有升级标头。到目前为止,从一开始就强制使用第 2 版的唯一方法似乎是通过 https 使用安全连接(我想避免)。

我还想坚持只使用 OpenJDK 11 中包含的类(没有 netty 等)。

【问题讨论】:

  • 有什么好笑的,如果我理解正确的话,这样的功能被声明为相应JEP 110的目标: > 必须能够协商从 1.1 升级到 2(或不能),或者 从头开始选择 2.

标签: java http2


【解决方案1】:

恐怕这在 Java 11 http 客户端 API 中似乎是不可能的。

最相关的内部 JDK 类是Http2Connection.java。它列出了我们期望的 HTTP/2 连接的三种“创建”情况:

  1. 升级的 HTTP/1.1 普通 tcp 连接
  2. 先验知识直接创建普通tcp连接
  3. 直接创建使用 ALPN 的 HTTP/2 SSL 连接。

我们对案例 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; } );

似乎只在升级 HTTP 1.1 连接时才调用非 TLS 构造函数。

我也研究了许多其他课程。我看不到任何暗示可以协商明文 HTTP/2 连接而无需从 JDK 11 客户端中的 HTTP 1.1 升级。

我发现的内容与 HttpClient.Builder 的 javadocs 中的 API 描述非常匹配,该描述仅说明选择 HTTP/2 作为版本:

如果设置为 HTTP/2,则每个请求都会尝试升级到 HTTP/2。 如果升级成功,则对该请求的响应将使用 HTTP/2 以及对同一来源的所有后续请求和响应 服务器将使用 HTTP/2。如果升级失败,则响应将 使用 HTTP/1.1 处理

在 HTTP/2 连接重用中发生了很多有趣的事情。例如,在升级并发请求的情况下,只有一个保留在附近以供将来在 HTTP/2 连接“缓存”中使用。当流的数量达到最大值时,缓存的连接将过期,即 ~ 2^31-1,这样进一步的请求不会超过它,但现有的流将保持打开状态。那是很多流。我认为,如果你的用例和我的一样——一个运行时间很长的应用程序中的 Web 服务客户端——那么实际上只有第一个请求的升级成本可以忽略不计。我仍然想消除它,主要是为了简单起见,但我开始认为它不值得。

就像提问者一样,我想坚持使用 JDK 客户端。我只想提一下,Apache HttpClient 5 beta 似乎支持强制 HTTP/2 (HttpVersionPolicy.FORCE_HTTP_2)。我无法提供任何进一步的建议,因为 beta 没有太多文档,而且 cmets 比 JDK 实现要稀疏得多。但如果我尝试并取得成功,我会更新我的答案。

【讨论】:

  • 感谢分析实现!我有同样的问题,我认为性能可能不是使用它的原因——而是能够在本地开发环境(用于测试、实验、基准测试)中建立 HTTP/2 连接而无需管理密钥和建立 TLS连接,这也使 Wireshark 中的数据包分析变得复杂。在标准库中拥有这样的功能肯定会很有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-27
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
  • 2017-08-29
  • 2013-02-28
相关资源
最近更新 更多