当前位置: 首页 > 知识库问答 >
问题:

HttpClient:通过代理发送的http 2请求没有升级头

钱展
2023-03-14

所以,我正在管理一个公司超文本传输协议服务器,并被要求升级服务器以支持使用超文本传输协议2的代理请求

这是测试客户端。我正在jdk 17中使用HttpClient。下面是测试用例。

    public static void main(String[] args)throws Exception
     {
      HttpClient client=HttpClient.newBuilder()
                                  .proxy(ProxySelector.of(new InetSocketAddress("192.168.1.2",1000))) //proxy to 192.168.1.2:1000 
                                  .version(HttpClient.Version.HTTP_2)    //use http 2                      
                                  .build();
      
      //request to 192.168.1.2:2000
      HttpRequest request=HttpRequest.newBuilder(URI.create("http://192.168.1.2:2000/Test.txt"))
                                      .build();
      
      HttpResponse response=client.send(request,BodyHandlers.ofString());
      System.out.println("Status code: " + response.statusCode());                            
      System.out.println("Headers: " + response.headers().map());
      System.out.println("Body: " + response.body());
     } 

我的服务器运行在超文本传输协议2上,我没有得到任何响应。在调试我的服务器上收到的数据包时看起来像这样:

    GET http://192.168.1.2:2000/Test.txt HTTP/1.1
    Content-Length: 0
    Host: 192.168.1.2:2000
    User-Agent: Java-http-client/17.0.2

这既不是超文本传输协议2格式,也没有我的服务器被设计为解析的任何升级标头,在不使用代理的情况下通常看起来像这样:

    GET /Test.txt HTTP/1.1
    Connection: Upgrade, HTTP2-Settings
    Content-Length: 0
    Host: 192.168.1.2:2000
    HTTP2-Settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA
    Upgrade: h2c
    User-Agent: Java-http-client/17.0.2

我的服务器被设计来解析这个请求,因为它是包含客户端http 2设置的升级请求。

所以,当我收到这样的请求时,我的问题是:

     GET http://192.168.1.2:2000/Test.txt HTTP/1.1
     Content-Length: 0
     Host: 192.168.1.2:2000
     User-Agent: Java-http-client/17.0.2

>

有没有办法让HttpClient包含升级头,这样我就可以以HTTP 2格式回复?

编辑

为了调试响应,我创建了一个运行在端口1000上的简单TCP服务器,并通过它进行代理。我只需打印在这个代理上接收到的字节,就可以得到完全相同的输出

//Test Proxy server running on port 1000. No parsing data simply print and close for debugging purposes
public static void main(String[] args)throws Exception
 {
  try(ServerSocket server=new ServerSocket(1000))
  {
   try(Socket client=server.accept())
   {
    try(InputStream input=client.getInputStream())
    {
     byte[] data=new byte[8196];
     int length=input.read(data);
     System.out.println(new String(data,0,length));
    } 
   } 
  } 
 }

共有1个答案

楚灿
2023-03-14

显然,OpenJDK的HttpClient只能向代理发送HTTP/1.1请求。

即使您指定了HttpClient。版本HTTP_2,因为存在代理,OpenJDK的HttpClient会忽略您指定的版本,将请求格式化为HTTP/1.1并将其发送到代理。

OpenJDK的HttpClient代理功能非常有限(例如,您无法轻松使用安全协议与代理通信),因此您可能需要查看不同的HTTP客户端。

[免责声明,我是Jetty团队的一员。]

Jetty的HttpClient支持更灵活的代理配置,因此您可以指定代理所使用的协议(以及是否安全)。

请注意,您需要代理协作才能获得所需的内容,因为连接是一个逐跳标头,因此它仅在单跳上有效,即从客户端到代理。

代理可以合法地将客户端的HTTP/1.1升级请求获取到HTTP/2,但随后使用HTTP/1.1与服务器对话,从服务器获取HTTP/1.1响应并生成101 Upgrad响应,然后是获得的HTTP/2响应,将服务器的HTTP/1.1响应转换为HTTP/2:

client                    proxy               server
   | ----  h1+upgrade  ---> |                    |
   |                        | --  h1 request --> |
   |                        | <-- h1 response -- |
   | <-- h1 101 upgrade  -- |
   | <--- h2c response  --- |

支持h2c的代理可以执行您想要的操作:

client                     proxy                    server
   | ----  h1+upgradeA  ---> |                        |
   |                         | ---  h1+upgradeB  ---> |
   |                         | <--  h1 101 upgrade -- |
   |                         | <--- h2c response ---- |
   | <--  h1 101 upgrade  -- |
   | <--- h2c response  ---- |

然而,请注意,upgradeA可能与upgradeB不同,这正是因为ConnectionUpgrade都是逐跳的头,并且代理可能具有与客户端不同的HTTP/2配置(因此也发送不同的HTTP2设置头)。

如果客户端和代理之间的通信是明文的,您必须事先知道代理是否支持h2c,否则客户端将默认为HTTP/1.1(就像OpenJDK的HttpClient一样)。相比之下,Jetty的HttpClient可以配置您想要使用的协议来与代理对话。

或者,您必须通过TLS使用安全通信,以便客户端和代理可以协商他们想要说话的协议(例如h2)。

代理也是如此:如果与服务器的通信是明文的(您的案例),那么代理必须事先知道服务器支持h2c,因此您需要使用该信息配置代理,否则代理可能会默认为HTTP/1.1。

显然,代理必须同时支持HTTP/1.1和HTTP/2。

如果您正在实现代理,将Connection(和其他逐跳标头)复制到服务器在技术上是错误的,但它可能在受限、更简单的情况下工作。请参阅RFC 7230的本节,该节指定代理应如何删除逐跳标头。

FTR,该测试表明Jetty(客户端和服务器)可以支持客户端、代理和服务器之间的任何协议组合(HTTP/1.1和HTTP/2、明文和安全)。

 类似资料:
  • 需要通过代理执行REST请求。有几个HTTP代理服务器可用,受基本身份验证的保护。使用Java16。 访问HTTP资源时完全工作的请求示例:

  • 问题内容: 我正在尝试使用HttpsUrlConnection类将请求发送到服务器。服务器存在证书问题,因此我设置了一个可信任所有内容的TrustManager以及同样宽松的主机名验证程序。当我直接发出请求时,此管理器工作正常,但是当我通过代理发送请求时,似乎根本没有使用过。 我这样设置代理设置: 默认SSLSocketFactory的TrustManager的设置如下: 如果运行以下代码,则最终

  • 我正在用python创建一个基于BaseHTTPServer的代理服务器。它所做的是创建到squid代理的连接,识别浏览器请求(GET、CONNECT、POST等),并向其添加代理授权标头,然后将此请求转发给squid代理。 问题是,据我所知,当我发送连接请求时,我应该将所有相应的流量中继到squid代理。但是,正如我在wireshark中看到的,squid代理没有回复握手的“Client Hel

  • 我有一个WPF应用程序,需要从远程API检索配置数据。该API已经过测试,并且工作正常,返回我认为是正确的格式。我已经为响应等创建了一个数据类型,但是当我到达通过HttpClient获取响应的行时,它不会向API发送任何请求。 数据类型:- 我已经在API上运行了日志,我们没有看到请求,如果我们使用相同的代码模型调用另一个API控制器来调用Uri2(https://example.com/API/

  • 我的一个EC2实例上有一个graphql服务器正在运行。我也有AWS appsync运行,但目前它只与几个Lambda集成。 我想将我的Appsync连接到graphql服务器,这样Appsync将作为特定查询/变化的代理。 因此,从客户端来看,它将如下所示: 客户端将一个查询发送到APPESNC,让我们假设它看起来像这样: Appsync已经定义了一个查询,它被配置为在graphql服务器上代理

  • 我如何发送此信息: 到API: 使用cURL,我目前一直在使用: 其中我得到了结果(NGrok): GET/testDir/curlPost。php HTTP/1.1 接受:text/html、Application/xhtml xml、image/jxr、/ 接受语言:en-GB 用户代理:Mozilla/5.0(Windows NT 10.0;Win64;x64)AppleWebKit/537