Android端Charles代理gRPC失败 报Stream cancelled by CLIENT
问题原因
该问题是Charles v4.6.2版本自身HTTP/2协议解析的兼容缺陷导致,和Android端gRPC逻辑、服务端逻辑无关:
- 界面提示的
IO: Stream cancelled by CLIENT是反向误导信息:实际触发逻辑是Charles在接收服务端返回的gRPC响应HTTP/2帧时,内部帧处理模块抛出java.lang.IllegalStateException运行时崩溃,主动断开了和Android客户端的HTTP/2流连接;gRPC客户端检测到代理侧异常断流,按协议规范发送RST_STREAM帧取消流,这个取消动作被Charles捕获后记录为“客户端取消流”。 - Android模拟器Logcat能看到完整请求/响应的原因是:gRPC的okhttp/netty传输层在Charles断流前,已经完成了和服务端的全量数据交互,拿到了完整响应,代理层的解析崩溃不会影响客户端实际收包。
- 触发这个兼容bug的直接原因是你使用的gRPC 1.45.1版本,在传输gRPC响应trailers头时使用了带特定FLAG的HTTP/2 HEADERS帧格式,Charles 4.6.2的HTTP/2解析器没有适配该格式,在合并响应头帧和trailers帧时状态校验失败直接抛错,所以会出现在
Receiving response body阶段中断、Contents标签页只能看到部分响应内容、请求被判定为失败的现象。
修复方案
按优先级从高到低可选择以下方案:
- 升级Charles版本到v4.6.3及以上。Charles官方在4.6.3版本专门修复了HTTP/2帧处理时的IllegalStateException崩溃问题,适配了高版本gRPC发送的trailers帧格式,升级后无需修改任何客户端配置即可正常抓取gRPC流量,是最稳定的解决方案。
- 若暂时无法升级Charles,可临时修改Android端gRPC客户端配置,规避触发解析bug:
构建gRPC通信频道时,显式指定使用okhttp传输实现,同时强制设置头列表大小为Charles兼容的8KB阈值,示例配置代码如下:
注意:该配置为临时兼容方案,会限制gRPC请求的头列表大小,不要在正式发布版本中保留该限制。val channel = OkHttpChannelBuilder .forAddress("your-grpc-host", 443) .maxHeaderListSize(8192) .useTransportSecurity() .build() - 替代方案:如果不想调整Charles版本或客户端代码,可直接使用gRPC内置的日志拦截器打印原始流量数据,配合protoc工具解码查看内容,绕过Charles的解析缺陷。
内容的提问来源于stack exchange,提问作者Alexander Borodin
相关产品推荐
相关产品推荐

