Yesod接收大JSON请求时浏览器出现net::ERR_CONNECTION_RESET错误
我之前碰到过类似的场景,结合你描述的触发条件和现象,这个问题的核心原因大概率出在Warp(Yesod底层的HTTP服务器)对未消费请求体的处理逻辑上,下面给你拆解分析和可行的解决方案:
问题触发的关键边界
从你的测试结果来看,这个bug有非常明确的触发条件:
- 仅在发送3MB级的base64编码JSON请求时出现
- 当handler直接忽略请求体、直接返回响应时,触发
net::ERR_CONNECTION_RESET - 用
requireCheckJsonBody完整消费请求体后,问题完全消失 - curl请求或二进制文件表单提交不会触发(这类请求的处理逻辑会自动消费请求体)
背后的原因
Warp在处理HTTP请求时,如果应用层没有完整读取请求体就直接返回响应,服务器会直接关闭连接,而非正常发送响应并处理剩余的请求体数据。对于浏览器来说,这种突然的连接中断会被判定为ERR_CONNECTION_RESET;而curl这类工具对这种场景的容错性更强,所以不会报错。你的Yesod日志显示处理成功,是因为handler确实执行完成了,但后续Warp的连接收尾逻辑出了问题。
解决方案
根据触发原因,有几个可行的解决方向:
1. 显式消费请求体(最直接的临时修复)
即使你不需要使用请求体里的内容,也要在handler里显式读取并丢弃它,确保Warp能正确处理连接收尾:
postSendJsonR :: Handler Value postSendJsonR = do -- 读取整个请求体并丢弃 _ <- requestBody return $ object ["status" .= String "ok"]
如果本来就需要解析JSON,继续用requireCheckJsonBody即可,它会自动完整消费请求体,从根源避免触发问题。
2. 升级依赖版本(根治方案)
你使用的LTS 9.14对应的Warp版本比较老旧,这个“未消费请求体导致连接重置”的问题在后续的Warp版本中已经被修复。建议升级到较新的LTS Haskell版本(比如LTS 12及以上),更新Warp和WAI相关依赖后,这个问题应该会自然消失。
3. 检查Warp超时配置(辅助排查)
虽然你的日志显示处理耗时极短,但可以额外检查下Warp的settingsTimeout配置,确保大请求体的传输过程不会触发连接超时。不过这个大概率不是你当前问题的核心原因,仅作为额外的排查点参考。
内容的提问来源于stack exchange,提问作者MaxGabriel

