块末尾的意外内容 - 是什么构建了损坏的流?

Unexpected content at the end of chunk - what builds the corrupt stream?

我正在达到 returns 大约 XML 的 900k 的终点。时不时地(在今天的测试中不到 5000 分之一)我得到一个 MalformedChunkCodingException

这是一个相当老的 web 应用程序(约 10 年)发生的,它建立在 Spring 3 上。我转而使用 RestTemplate 而不是直接使用 httpclient,但这并没有解决它。今天在 运行 几个小时后,在 httpclient 上启用了线级日志记录,我设法捕获了一个。

Caused by: org.apache.http.MalformedChunkCodingException: Unexpected content at the end of chunk
    at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:259)
    at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:227)
    at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:186)
    at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.Reader.read(Reader.java:140)
    at org.springframework.util.StreamUtils.copyToString(StreamUtils.java:74)
    at org.springframework.http.converter.StringHttpMessageConverter.readInternal(StringHttpMessageConverter.java:85)
    at org.springframework.http.converter.StringHttpMessageConverter.readInternal(StringHttpMessageConverter.java:40)
    at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:103)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:724)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:709)

通常日志似乎是这样的:

DEBUG org.apache.http.wire - << "words words words"
DEBUG org.apache.http.wire - << "[\r][\n]"
DEBUG org.apache.http.wire - << "FAF[\r][\n]"
DEBUG org.apache.http.wire - << "words words words up to FAF bytes" 
DEBUG org.apache.http.wire - << "[\r][\n]"
DEBUG org.apache.http.wire - << "BAA[\r][\n]"
DEBUG org.apache.http.wire - << "words words words up to BAA bytes"

但是在那个出错的地方我有这个:

DEBUG org.apache.http.wire - << "words words words"
DEBUG org.apache.http.wire - << "[\r][\n]"
DEBUG org.apache.http.wire - << "B50[\r][\n]"
DEBUG org.apache.http.wire - << "words words words up to B50 bytes"
DEBUG org.apache.http.wire - << "3FC0[\r][\n]"

它缺少 B50 块末尾的 [\r][\n]。

因此,假设我可以信任 org.apache.http.wire 调试,那么我的应用程序正在 接收 那样的流,而且它的格式是错误的。它们之间的 haproxy 是否有可能破坏流?

幸运与否,对话的另一端也是我的应用之一,运行 in tomcat with Spring 4.2.4。

我从哪里开始寻找谁在构建无效响应? Spring? Tomcat?

看起来我可以禁用分块,但只能在构建响应时计算我的内容长度,我不太愿意这样做,因为那样我就必须手动序列化我的响应,而不是让 Spring 去做。

正如您所说,该应用程序很旧。您可能需要更新所有库版本,希望该问题已在某处检测到并修复。

但是找到坏演员会有所帮助,因为您只需修复那个。

根据我自己在 HTTP 工具中测试错误语法支持的经验,我很确定 Haproxy 是您列出的元素中更强大的元素。但这并不排除那里的问题。

发送端点和接收端点之间的每个 HTTP actor 都可以更改 HTTP 主体(修改块大小),因此您需要捕获所有 actor 的输入和输出(spring、tomcat , haproxy, any other proxy and/or reverse proxy, load balancer, ssl terminator) 来检测坏块。我将从消息发射器开始,即 XML 端点。我会使用 wireshark/pcap/httpdump,真正捕获 TCP 和 HTTP 流量的东西。但是你可能必须找到一种方法来快速丢弃捕获,直到你到达失败点,因为 1/5000 意味着你有很大的风险捕获大量数据。

我花了一个月的大部分时间 运行 对此进行测试。虽然我没有 100% 肯定的答案,但我发现:

当运行 haproxy版本1.7.x我无法触发错误

当运行haproxy版本2.0.x我偶尔会触发错误

它可能与此有关,也可能与此无关:

https://github.com/haproxy/haproxy/issues/171