fix(rustfs): 修 unexpected end of stream 根因——零长度请求体缺 Content-Length

抓包定位(90 秒内 515 次 HTTP 411 Length Required):OkHttp 对长度为 0 的请求体
不写 Content-Length,请求于是既无 Content-Length 也无 Transfer-Encoding——HTTP/1.1
不允许这样,RustFS 直接回 411;客户端读到不完整响应就报 unexpected end of stream
再重试。业务里上传空内容(content 为 null/空)是常态,所以每天上千次。

修复用 network interceptor 在协议层补 Content-Length: 0:普通 interceptor 里加的
头会被 BridgeInterceptor 按 body 长度覆盖,加了不生效。

实测:部署后同样 90 秒抓包,411 从 515 次降到 0,两节点「确定性错误不重试」归零。
此前排除的公网链路、keepAlive、连接复用三项确实都不是原因——抓包才是对的入口。
This commit is contained in:
2026-09-17 01:36:55 +08:00
parent 188aedec84
commit 7aea3a0a50
@@ -382,6 +382,22 @@ public class RustfsObjectStorageService {
Math.max(1L, properties.getConnectionPoolKeepAliveMillis()), Math.max(1L, properties.getConnectionPoolKeepAliveMillis()),
TimeUnit.MILLISECONDS)) TimeUnit.MILLISECONDS))
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.addNetworkInterceptor(chain -> {
okhttp3.Request request = chain.request();
okhttp3.RequestBody body = request.body();
// OkHttp 对「长度为 0 的请求体」不会写 Content-Length,于是请求既无
// Content-Length 也无 Transfer-EncodingHTTP/1.1 不允许这样)。
// RustFS 对此直接回 411 Length Required,客户端读到不完整响应就报
// unexpected end of stream,进而重试——线上抓包实测 90 秒内 515 次 411,
// 而上传空内容(content 为 null/空)在业务里是常态。
// 用 network interceptor 在协议层补上该头(普通 interceptor 会被
// BridgeInterceptor 按 body 长度覆盖掉,加了也不生效)。
if (body != null && body.contentLength() == 0L
&& request.header("Content-Length") == null) {
request = request.newBuilder().header("Content-Length", "0").build();
}
return chain.proceed(request);
})
.build(); .build();
} }
return httpClient; return httpClient;