httpclient:http 2通过代理发送的请求没有升级标题

发布于 2025-01-25 05:43:41 字数 2724 浏览 1 评论 0原文

因此,我正在管理公司HTTP服务器,并被要求升级服务器以使用HTTP 2协议支持代理请求,

这是测试客户端。我正在使用 httpclient 在JDK 17中。这是测试案例。

    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());
     } 

我的服务器正在使用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

这既不是http 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

我的服务器旨在解析此请求。因为它是

因此,我的问题是当我收到这样的请求时:

     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
  1. 我是否在HTTP 1.1或HTTP 2格式中回复了客户?出现疑问是因为在我的客户端测试案例中,我明确指定了版本(HTTP_2),但是请求没有升级标题,所以我应该以HTTP 1.1格式回复吗?

  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));
    } 
   } 
  } 
 }

So, I am managing a company http server and have been asked to upgrade the server to support proxy requests using http 2 protocol

This is the test client. I am using HttpClient in jdk 17. Here is the test case.

    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());
     } 

My server is running on http 2 protocol and I wasn't getting any response. Upon debugging the packet received on my server looked like this:

    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

This is neither in http 2 format nor are there any upgrade headers which my server was designed to parse, which typically looks like this without using proxy:

    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

My server was designed to parse this request as it is the upgrade request which contains the http 2 settings for the client.

So, my questions are when I get a request like this:

     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
  1. Do I respond back to the client in HTTP 1.1 or HTTP 2 Format? The doubt arises because in my client test case I have clearly specified version(HTTP_2) but the request has no upgrade headers, so should I maybe respond back in HTTP 1.1 format?

  2. Is there any way to make the HttpClient include the Upgrade headers so I can respond back in HTTP 2 format?

EDIT.

To debug the response i created an simple TCP server running on port 1000 and proxied through it. I simply print the bytes received at this proxy and get the exact same output

//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));
    } 
   } 
  } 
 }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

淡墨 2025-02-01 05:43:41

显然,OpenJDK的HTTPCLIENT只能将HTTP/1.1请求发送到代理。

即使您指定httpclient.version.http_2,因为有一个代理,openjdk的httpclient忽略了您指定的版本,请将请求格式化为http/1.1,并将其发送到http/1.1代理人。

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

[免责声明,我是码头团队的一部分。]

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

请注意,您需要代理合作来获得所需的东西,因为连接 hop-by-hop标头,因此它仅在单个啤酒花上有效,即从客户端到代理。

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

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 upgradea < /code>可能与upgradeb完全不同,因为Connection升级是跳跃的标头,并且代理可能具有不同的http /2关于客户端的配置(因此,也发送不同的http2-settings标题)。

如果客户端和代理之间的通信是明确的文本,则必须先验了解代理是否支持H2C,否则客户端将默认为HTTP/1.1(例如OpenJDK的httpclient < /代码>做)。
相比之下,可以使用要使用的协议来配置Jetty的httpclient

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

代理也是如此:如果与服务器的通信是清晰文本(您的情况),则代理必须先验知道服务器支持H2C,因此您需要使用该服务器配置代理信息,否则代理可能默认为HTTP/1.1。

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

如果您要实现代理,则将连接(以及其他跳跃标题)复制到服务器上是错误的,但是它可能在受限制的,更简单的情况下起作用。
参见 RFC 7230的这一部分标题应由代理卸下。

ftr,此测试表明Jetty(客户端和服务器)可以支持协议的任何组合(http/1.1 and 1.1 and 1.1 and http/http/2,clear-text,clear-text,clear-text,并在客户端,代理和服务器之间进行安全)。

Evidently, OpenJDK's HttpClient is only able to send HTTP/1.1 requests to a proxy.

Even if you specified HttpClient.Version.HTTP_2, because there is a proxy, OpenJDK's HttpClient ignores the version you specified, formats the request as HTTP/1.1 and sends it to the proxy.

OpenJDK's HttpClient proxy capabilities are quite limited (for example, you cannot easily use a secure protocol to communicate to the proxy), so you may want to look into different HTTP clients.

[Disclaimer, I'm part of the Jetty team.]

Jetty's HttpClient supports a more flexible proxy configuration, so you can specify what protocol the proxy speaks (and whether it is secure).

Note that you need the proxy cooperation to obtain what you want, because Connection is a hop-by-hop header, so it is only valid on a single hop, i.e. from the client to the proxy.

The proxy can legitimately take the client's HTTP/1.1 upgrade request to HTTP/2, but then use HTTP/1.1 to talk to the server, get the HTTP/1.1 response back from the server and produce a 101 Upgrade response followed by the an HTTP/2 response obtained converting to HTTP/2 the server's HTTP/1.1 response:

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

A proxy that supports h2c could instead do what you want:

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

Note however that upgradeA could be different from upgradeB exactly because Connection and Upgrade are hop-by-hop headers, and the proxy may have a different HTTP/2 configuration with respect to the client (and therefore also send a different HTTP2-Settings header).

If the communication between the client and the proxy is clear-text, you must have prior knowledge of whether the proxy supports h2c, otherwise the client will default to HTTP/1.1 (like OpenJDK's HttpClient does).
In contrast, Jetty's HttpClient can be configured with the protocol you want to use to talk to the proxy.

Alternatively, you have to use a secure communication via TLS so that the client and the proxy can negotiate the protocol they want to speak (e.g. h2).

The same holds for the proxy: if the communication with the server is clear-text (your case), the proxy must have prior knowledge that the server supports h2c, so you need to configure the proxy with that information, otherwise the proxy will likely default to HTTP/1.1.

Obviously the proxy must support both HTTP/1.1 and HTTP/2.

If you are implementing the proxy, copying the Connection (and other hop-by-hop headers) towards the server is technically wrong, but it may work in restricted, simpler, cases.
See this section of RFC 7230 that specifies how hop-by-hop headers should be removed by proxies.

FTR, this test shows that Jetty (client and server) can support any combination of protocols (HTTP/1.1 and HTTP/2, clear-text and secure) between the client, the proxy and the server.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文