如何避免来自 HttpURLConnection 的空 HTTP 响应出现 EOFException?

Posted

技术标签:

【中文标题】如何避免来自 HttpURLConnection 的空 HTTP 响应出现 EOFException?【英文标题】:How to avoid EOFException for an empty HTTP response from HttpURLConnection? 【发布时间】:2013-01-26 20:38:09 【问题描述】:

我正在向服务器发送一个 HTTP 请求,该服务器合法地返回一个 HTTP 响应代码 = 200(OK)的空白响应。

但这会导致下面的标记行抛出EOFException

InputStream responseStream = httpURLConnection.getInputStream();
final String contentEncoding = this.connection.getContentEncoding();
if ("gzip".equalsIgnoreCase(contentEncoding)) 
    responseStream = new GZIPInputStream(responseStream); // throws EOFException

可能有一种明显的方法可以防止这种情况,但我找不到。在创建 GZIPInputStream 之前,我是否应该检查 connection.getContentLength() > 0responseStream.available() > 0 之类的内容?这些似乎都不对,我在示例代码片段中没有遇到任何类似的东西......

【问题讨论】:

【参考方案1】:

我是否应该检查类似 connection.getContentLength() > 0

是的。

或 responseStream.available() > 0

绝对不是。 available() == 0 不是 EOF 的有效测试,Javadoc 明确指出。

【讨论】:

非常感谢 - 看起来就像 getContentLength() 那样。唯一有点担心的是我不想引入可能排除有效响应的检查 - 你知道服务器是否始终保证正确设置内容长度? @SteveChambers Content-length 可能是 HTTP 1.1 中最重要的标头。如果设置不正确,则服务器严重损坏,浏览器也无法使用它。【参考方案2】:

您的服务器不应该返回 HTTP 代码 204(无内容)而不是 200 吗?除了 HEAD 请求之外。见Http Status Code definition

除此之外,您确实可以检查以解决看起来不太合规的服务器实现,或者捕获异常,然后根据您发送的请求类型确定适当的操作。

【讨论】:

有趣的一点,我同意 204 会更好——但我无法控制这一点,并认为带有空白响应的 200 是有效的——尽管可能不是“标准”。如果可能的话,我宁愿首先避免发生异常——这违背了我的编程直觉,例如我喜欢在 Eclipse 中调试并捕获任何类型的异常作为断点——这通常应该指示错误而不是异常数据。【参考方案3】:

我从您的问题中了解到,每次您从服务器收到 200 响应时,都会始终抛出此异常。

如果responseStream.available() > 0 不是一个选项,则意味着流实际上包含某些内容,但该流似乎不完整或以任何其他方式提前结束,因为available 声明流中有可读字节。


更新

根据您的评论(并且出于纯粹的好奇心再次阅读InputStream 的文档后)我也相信connection.getContentLength() > 0 是要走的路。如果您使用的是 Java 7,那么,connection.getContentLengthLong() 是首选,因为它直接返回 long

鉴于文档对InputStream#available 的评价:

InputStream 类的可用方法总是返回 0。

请注意,虽然 InputStream 的某些实现会返回流中的总字节数,但很多不会。使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是不正确的。

这会放弃该检查作为一个选项。

【讨论】:

我并不总是得到例外 - 来自服务器的大量 200-OK 响应确实包含数据,但这个特定的没有。实际用例是作为异步数据流的一部分发送到服务器的 ACK。服务器回复 200(OK)作为回复,但没有更多要说的(即响应为空白)。 @SteveChambers 所以我的假设是错误的,对此我深表歉意。我也相信connection.getContentLength(或connectionlgetContentLengthLong,如果可能的话)是选项。 谢谢,这很有帮助。我使用 connection.getContentLength() 看到了很好的结果。【参考方案4】:

除非我遗漏了什么,否则内容编码为“gzip”的响应不能为空。

【讨论】:

不确定你是否打算扩展它,但目前它似乎更像是一个评论而不是一个答案 - 也许提供一个关于为什么它不能为空的参考(我假设这将是HTTP规范中的某处?)无论如何,有时规范所说的和实际可以做的不同 - 问题描述了所看到的,似乎其他人可能已经看到类似的东西,例如here 和 here. 如果服务器说“content-encoding: gzip”,那么payload必须被gzip压缩;根据定义,空有效负载不会被压缩。因此 HTTP 响应无效。 这听起来合乎逻辑,但 IIRC 的问题是关于收到的回复超出了我的控制范围,仍需要适当处理。猜猜第一句中的“合法”一词可能是错误的。

以上是关于如何避免来自 HttpURLConnection 的空 HTTP 响应出现 EOFException?的主要内容,如果未能解决你的问题,请参考以下文章

来自 inputStream 的内容的 HTTP PUT 请求,大小未知,无法在 HttpUrlConnection 中设置 ChunkedStreamingMode

来自JSON的最后一张图片没有显示

HttpURLConnection 411错误解决

如何避免记录来自 Java 异常的敏感信息?

一定要为HttpUrlConnection设置connectTimeout属性以防止连接被阻塞

使用 Java HttpUrlConnection 发送带有令牌的 GET 请求