因此,我正在管理一家公司http服务器,并已被要求升级服务器以支持使用http 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());
} 我的服务器运行在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我的服务器是用来解析这个请求的,因为它是升级请求,它包含客户端的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编辑.
为了调试响应,我创建了一个运行在端口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));
}
}
}
}发布于 2022-04-30 16:20:49
显然,OpenJDK的HttpClient只能向代理发送HTTP/1.1请求。
即使您指定了HttpClient.Version.HTTP_2,因为有一个代理,OpenJDK的HttpClient忽略了您指定的版本,将请求格式化为HTTP/1.1并将其发送给代理。
OpenJDK的HttpClient代理功能非常有限(例如,您不能轻易地使用安全协议与代理通信),因此您可能需要查看不同的HTTP。
免责声明我是Jetty团队的一员。
Jetty的HttpClient支持更灵活的代理配置,因此您可以指定代理所使用的协议(以及它是否安全)。
请注意,您需要代理合作才能获得所需的东西,因为Connection是一个逐跳标头,因此它仅在单个跳(即从客户端到代理)上有效。
代理可以合法地将客户端的HTTP/1.1升级请求接收到HTTP/2,但随后使用HTTP/1.1与服务器对话,从服务器获取HTTP/1.1响应,然后生成一个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可能与upgradeB完全不同,因为Connection和Upgrade是逐跳报头,并且代理对于客户端可能有不同的HTTP/2配置(因此也发送不同的HTTP2-Settings报头)。
如果客户机和代理之间的通信是明文的,您必须事先知道代理是否支持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、明文和安全)。
https://stackoverflow.com/questions/72068831
复制相似问题