作者:傲慢的小草7_170 | 来源:互联网 | 2023-08-11 14:01
似乎Sun
Java内置的HttpsUrlConnection工具无法以服务器友好的方式(即没有溢出服务器SSL重新协商缓冲区)来处理带有客户端证书的大型HTTP
PUT。
我检查了curl所做的事情,以了解“服务器友好”的含义,结果发现有一个名为“ Expect”的HTTP 1.1标头,curl发送的值为“
100-continue”(请参阅规格http://www.w3 .org / Protocols / rfc2616 /
rfc2616-sec14.html#sec14.20)。该标头本质上说:“我有很大的负载,但是在发送之前,请告诉我是否可以处理”。这使端点有时间在发送有效负载之前重新协商客户端证书。
在SunHttpUrlConnection实现中,似乎不允许使用此标头,而实际上是在受限标头列表中。这意味着即使您使用HttpUrlConnection.setRequestProperty方法进行设置,标题也不会实际发送到服务器。您可以使用系统属性sun.net.http.allowRestrictedHeaders覆盖受限制的标头,但是由于Sun实现不知道如何处理协议的这一部分,因此客户端只会因套接字异常而崩溃。
有趣的是,似乎Java的OpenJDK实现确实支持此标头。另外,Apache HTTP
Client库支持此标头(http://hc.apache.org/);我已经用Apache
HTTP客户端库实现了一个测试程序,它可以使用客户端证书和Expect标头成功执行大文件的HTTP PUT请求。
概括地说,解决方案是:
- 将Apache SSLRenegBufferSize指令设置为一个很大的数字(例如64MB)。默认值为128K。此解决方案可能会导致拒绝服务风险
- 配置一台始终需要客户端证书的主机,而不是只需要几个目录的主机。这样可以避免重新谈判。在我的情况下,这不是一个好选择,因为大多数用户都是匿名的,或者已验证用户名/密码。只有一个上载目录用于程序上载文件。我们只需要为此一个目录创建一个具有自己的SSL证书的新虚拟主机。
- 使用支持HTTP 1.1 Expect标头的客户端。不幸的是,Sun Java不支持此功能。必须使用第三方(例如Apache HTTP Component Client库)或使用Java套接字API推出自己的解决方案。
- 通过最初发出没有大有效负载但导致重新协商的HTTP请求来利用HTTP 1.1持久连接(使用keep-alive进行流水线化),然后将连接重用于HTTP PUT。从理论上讲,客户端应该能够在上传目录上发出HTTP HEAD或OPTIONS,然后重用相同的连接来执行PUT。为了使它起作用,持久连接池可能只需要包含一个连接,以避免“启动”一个连接,然后为PUT发出另一个连接。但是,由于我一直无法使该解决方案正常工作,因此HttpUrlConnection类似乎不会保留/重用涉及客户端证书或SSL的持久连接。