DEBUG.WIKI
晚安云;香港 CN2 ¥15/月
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

AnyIO 发送被取消后,其他请求也报 BAD_RECORD_MAC

AnyIO TLSStream 的发送取消后,连接可能已处于未知状态。说明加密记录丢失的路径、HTTP/2 共享连接影响,以及关闭旧连接而非重放写入的边界。

一次 TLS 发送被取消后,同一连接上的其他请求也开始报 SSLV3_ALERT_BAD_RECORD_MAC 或 DECRYPTION_FAILED_OR_BAD_RECORD_MAC,应先检查失败请求是否共用了那条连接。重新发一个请求偶尔恢复,可能只是连接池换了连接,不能据此认定原连接仍可使用。

AnyIO 4.15.1 的 TLSStream.send() 存在这样的取消边界;4.14.2 也有对应表现。HTTP/2 的多个请求共用一条 TLS 连接时,一个任务发送窗口更新或确认帧期间被取消,影响可能扩散到其他流。报错本身不能排除对端、代理或其他 TLS 故障,还要对照取消时间和连接标识。

取消时,加密记录可能已经离开缓冲区

TLS 写入不是“直到 await 返回才发生任何事”。SSLObject.write() 可以先生成加密记录并推进记录序号,随后代码从写 BIO 取出字节,再等待底层流发送。取消若落在取出字节之后、实际传输之前,那段记录可能没有交给对端;后续记录的序号却已经继续前进。

因此,发送抛出取消异常时,应用不能判断对端收到了全部、部分还是没有收到。TLS 连接随后也可能无法再使用。这与“只取消了一个业务请求,所以别的请求应该不受影响”的假设不同。

核对 AnyIO、httpcore、HTTP 客户端及 Python 版本,保存完整异常链;用已有诊断标识关联取消与底层连接,不记录令牌、请求正文或 TLS 密钥。不要只按 HTTP 请求 ID 分组,漏掉共享连接这一层。

淘汰旧连接,保留取消传播

自己管理 TLSStream 时,发送被取消后应让连接拥有者结束这条连接的生命周期,不继续向同一流发送新的应用数据。清理可以使用资源本来支持的关闭或强制关闭接口,并遵守现有有界取消清理流程;不要把清理失败重新解释成发送成功。

使用 HTTP 连接池时,重点核对客户端是否把受影响连接标为不可复用。业务层不能只捕获取消异常、把同一连接放回池,再让其他请求继续使用它。调用方取消仍须传播,不能为了隐藏 TLS 错误改成无限重试。

也不要给底层发送套一个没有截止时间的 CancelScope(shield=True)。这会改变超时与取消契约;网络停滞时,任务可能一直等着发送完成。保存待发加密字节再重送也没有解决部分发送的不确定性。

重新建立连接与重新执行业务操作是两件事。即使新连接能握手,也不能证明先前的支付、写入或消息发布没有生效。是否重试须由业务的幂等键、事务结果或其他已定义机制判断。

检查完整的连接退出路径

在隔离环境覆盖正常发送、发送前取消、发送中取消、对端提前断开,以及 HTTP/2 共享连接仍有其他请求的情况。受影响连接应退出复用路径,其他请求按客户端规定得到可追踪的失败或重建连接;不能以“不再看到 BAD_RECORD_MAC”代替业务结果核对。

更新 AnyIO 后,仍要检查取消是否正常传播、受影响连接是否退出复用,以及其他未完成请求如何结束。只是不再出现同一个 TLS 异常,还不能说明连接与业务结果都已恢复。