在Android中如何明确禁用HTTP连接的分块传输编码模式?

11

我正在使用HttpsURLConnection从Android 4.0针对一个REST Web服务进行定位。 除非我尝试进行POST,否则这个工作正常。这是相关的代码部分:

   connection.setDoOutput(true);
   connection.setChunkedStreamingMode(0);

   ByteArrayOutputStream out = new ByteArrayOutputStream();
   serializeObjectToStream(out, object);
   byte[] array = out.toByteArray();
   connection.getOutputStream().write(array, 0, array.length);

这会抛出以下异常:

   java.net.HttpRetryException: Cannot retry streamed HTTP body

通过调试我发现,我通过connection.getOuputStream()获取的输出流类型是ChunkedOutputStream,并且从Android源代码中深入探究后我发现,如果请求需要重试(无论何种原因),它会出现上述异常。这是因为它发现自己没有使用想要的RetryableOutputStream

现在的问题是:我该如何让我的HttpsURLConnection返回这样一个RetryableOutputStream呢?或者说,我该如何防止分块请求正确编码?我之前认为我已经用setChunkedStreamingMode(0)做到了,但显然情况并非如此...

[编辑]

不,java.net.HTTPUrlConnection的实现会忽略流模式为0或更低的设置:

 public void setChunkedStreamingMode(int chunkLength) {
    [...]
    if (chunkLength <= 0) {
        this.chunkLength = HttpEngine.DEFAULT_CHUNK_LENGTH;
    } else {
        this.chunkLength = chunkLength;
    }
}

另外一件事:我认为它需要RetryableOutputStream的原因是因为REST服务器配置了基本身份验证保护,而Android的HttpURLConnectionImpl似乎在收到401未经授权的响应时会自动重试请求。 - Thomas Keller
2个回答

12

糟糕!解决方法是完全不要从客户端代码中调用setChunkedStreamingMode()(甚至是setFixedStreamingMode())!"-1"是fixedLength和chunkedLength的内部默认值,无法在客户端设置,因为将值设置为低于或等于"0"会使其默认为HttpEngine.DEFAULT_CHUNK_LENGTH(或在固定流模式下引发异常)。


1
解决方案是设置一个Content-Length头部(可以由下一部分设置),并使用您打算发送的POST消息的正确长度调用setFixedLengthStreamingMode
请参阅此SO FAQ中的“流模式”部分:

使用java.net.URLConnection触发和处理HTTP请求


在Java中,您不应该设置Content-length头。 setFixedLengthStreamingMode()会自动执行此操作,并覆盖已设置的任何内容。请注意,如果您调用setChunkedStreamingMode(),则没有 Content-length头,您绝对不应该设置它。如果您不调用这些方法之一,则HttpURLConnection将自行设置正确的值。 - user207421
@EJP,也许我的评论不够清晰,但我试图暗示setFixedLengthStreamingMode会为您设置标题。原始问题明确要求非分块请求,以便可以“重试”。原始帖子的作者还在6年前回复说,任何对set*StreamingMode的调用都会破坏此功能。因此,在某些情况下手动设置Content-Length是有一定道理的。 - Christopher Schultz

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