上传较大文件时,Apache HttpClient 4.x表现奇怪?

4

我正在使用Java(和Scala)开发和测试一个简单的客户端-服务器应用程序。

服务器基于com.sun.net.httpserver.HttpServer,通过基本的RESTful接口使用POST和PUT操作允许上传文件。上传操作使用我们自己实现的摘要身份验证进行限制,在浏览器、curl和Apache HttpClient中经过测试并且正常工作。

上传客户端包装了Apache HttpClient 4.1.2,通过http执行PUT操作以上传文件实体。文件的内容类型在头部中被指定为application/xml,一次只上传一个文件。

当上传不同大小的文件时,会出现奇怪的行为:

  • 大小小于或等于1,076,006字节的文件可以成功上传。
  • 大小大于或等于1,122,158字节的文件失败,显示java.net.SocketException: Broken pipe
确切的临界大小未知,因为我手动创建了不同大小的文件来近似最大工作大小
导致管道中断的原因是客户端在上传此类文件时某种方式忽略了www-authenticate响应,正如服务器日志所记录的那样。 “忽略”意味着它只发送了多个(4个)不含任何身份验证头的消息。 但是,更小的文件可以很好地工作,并且客户端会立即在www-authenticate响应之后正确地发送具有适当挑战-响应的身份验证请求。 使用curl上传各种大小的文件都没有问题,因此没有问题。 因此,此时可以说:“您的客户端存在一些错误。” 好吧,我有点希望如此,但是我也尝试过一个开源的java RESTclient(也包装了apache httpclient),它的行为完全相同!
我们尝试使用此客户端通过互联网进行测试,结果与描述的相同。因此,我希望我没有错过在Apache HttpClient中设置的重要内容,导致这种错误行为,并且开源RESTclient的开发人员也错过了它...有什么想法吗?谢谢!
1个回答

6
很可能是几个因素的组合导致了这种情况: (1) 可能您的客户端在发送不包含身份验证标头的大请求实体时没有使用“expect-continue”握手。 (2) 服务器早期检测到请求未达到其期望,并且不会读取和丢弃完整的请求正文,而是提前用401状态进行响应并关闭连接。在我看来,这是服务器违反HTTP协议的行为。 (3) 尽管一些HTTP代理可以处理早期响应,但Apache HttpClient不能,因为Java阻塞I/O的限制(执行线程只能从阻塞套接字中读取或写入,但不能同时进行)。
有多种方法可以解决此问题,“expect-continue”握手是最简单和自然的方法。或者,可以执行简单的HEAD或GET请求以在执行大型POST或PUT请求之前强制进行HTTP身份验证。 HttpClient能够在同一逻辑HTTP会话中重复使用身份验证数据。

感谢您的解释,我完全明白了!现在我选择“expect-continue”解决方案。在客户端中,只需翻转一个布尔值即可。服务器上的握手正在进行中,我相信这应该可以解决问题。 - mtsz
奇怪的是底层的sun httpserver总是在不涉及应用程序的情况下响应100-continue!在我看来,它似乎违反了协议(请参见RFC 2616“使用100(Continue)状态”,httpserver-source:http://www.docjar.com/html/api/sun/net/httpserver/ServerImpl.java.html)。但是,我已经实现了您的第二个解决方案,在发送大量数据之前使用第二个请求触发身份验证。这很有效,所以谢谢您! - mtsz
@mtsz 这不关我的事,但是现在有这么多体积小巧、可嵌入的HTTP服务器,为什么你还要使用Sun的ServerImpl呢? - ok2c
因为我正在大学做一个项目,这是我的教授选择和嵌入的...我注意到几乎没有人在使用它。但是我通过这种艰难的方式学到了一些有趣的东西,所以总体来说并不是完全糟糕的 ;) 至少我会更加珍惜好的服务器! - mtsz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接