错误信息
curl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
也可能输出为
- curl: (56) OpenSSL SSL_read: OpenSSL/3.5.7: error:0A000126:SSL routines::unexpected eof while reading, errno 0
较新的 curl 版本会在“SSL_read: ”之后插入 TLS 库的版本(例如“OpenSSL/3.5.7: ”)。OpenSSL 1.1.1 不会这样报告这种情况;在 1.1.1 上,同样的截断通常显示为“curl: (18) transfer closed with N bytes remaining to read”。
curl (OpenSSL 3 backend) — CURLE_RECV_ERROR, SSL_R_UNEXPECTED_EOF_WHILE_READING
含义
TLS 连接已经建立,数据也在传输,随后对端在 curl 仍在等待更多数据时关闭了 TCP 连接,事先没有发送 TLS close_notify。OpenSSL 3 把这种突然的结束报告为错误,而不是正常的流结束,因为缺少 close_notify 正是截断攻击的表现。curl 将其报告为接收失败(退出码 56)。
原因
服务器进程在响应中途崩溃或超时
常见因超时或内存不足而被终止的应用工作进程、PHP-FPM 或 Gunicorn 工作进程重启,或者放弃处理的上游服务,都会在响应体仍在发送时断开套接字。
中间设备断开了连接
常见执行空闲超时或最长持续时间限制的防火墙、NAT 网关、企业代理和负载均衡器,可能会在 TCP 层面切断耗时较长的下载或缓慢的响应,而服务器并没有执行 TLS 关闭。
服务器结束响应时不发送 close_notify
偶见一些嵌入式 Web 服务器、旧版 IIS 和自行编写的 TLS 服务器会在不执行 TLS 关闭的情况下直接关闭套接字。如果 HTTP 响应以关闭连接作为结束标志,OpenSSL 3 客户端会把每一个这样的响应都报告为此错误。
消息体大小限制中止了传输
偶见上传限制(client_max_body_size、WAF 规则)或下载配额有时会直接重置连接,而不是返回规范的 413 或 403 响应。
修复方法
- 1.
找出是哪一跳关闭了连接
记下出错前传输了多少字节、经过了多少秒,然后直接向后端重发该请求。如果连接恰好在整数秒处被切断,说明是代理或负载均衡器中的超时。
bash # How much arrived, and for how long, before the connection died? curl -sv --trace-time https://api.example.com/export.csv -o out.csv 2>&1 | tail -20 # Same request directly to the backend, bypassing the proxy / load balancer curl -sv --resolve api.example.com:443:10.0.0.12 https://api.example.com/export.csv -o out.csv - 2.
调大被触发的超时限制
在切断连接的组件中,把限制调到高于最长的正常响应时间;或者让慢端点更快地响应(对大型导出使用流式传输或分页)。
text # nginx in front of a slow upstream (both default to 60s) proxy_read_timeout 300s; proxy_send_timeout 300s; # Load balancers: set the idle timeout above the longest legitimate response time - 3.
重试幂等传输
对于经由不稳定链路的下载,让 curl 重试并断点续传。这只是在排查服务端问题期间,在客户端缓解症状。
bash # For idempotent downloads: retry, and resume where the transfer stopped curl --retry 5 --retry-all-errors -C - -o big.iso https://downloads.example.com/big.iso - 4.
服务端代码:正确关闭 TLS 连接
关闭套接字之前先发送 close_notify(OpenSSL 中为 SSL_shutdown,Python 中为 unwrap(),Go 中为 tls.Conn 的 Close()),并用 Content-Length 或分块编码界定 HTTP 消息体,使正常结束不会产生歧义。
python # Server code: shut TLS down cleanly so the peer receives close_notify conn.unwrap() # Python ssl.SSLSocket: sends close_notify before closing conn.close()
相关错误
error:0A000126:SSL routines::unexpected eof while readingOpenSSL 3[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocolPython ssl module (OpenSSL)GnuTLS recv error (-110): The TLS connection was non-properly terminated.git over HTTPS with libcurl-gnutls (Debian, Ubuntu)curl: (35) TLS connect error: error:0A00010B:SSL routines::wrong version numbercurl (OpenSSL backend)