Skip to content

Latest commit

 

History

History
171 lines (130 loc) · 13.5 KB

exec-plan-0.3.5.md

File metadata and controls

171 lines (130 loc) · 13.5 KB

执行计划 —— 0.3.5:额外信任的 CA,TLS 握手有时限,Windows CI 恢复

输入 PR #24 feat/extra-ca-certs @ bd76c9e · PR #25 fix/tls-handshake-timeout @ cfdab98(作者都是 yspbwx2010,maintainerCanModify: true)
基线 origin/master @ 2cc7310(0.3.4)
落地 两个 PR 各自 squash 合入;对 #25 的修正直接追加在 #25 上(已推送 97a0657)
目标版本 0.3.5(D1–D4 按建议采纳)
跨仓库 mcpplibs/tinyhttps → gitcode mcpp-res/tinyhttps(CN 镜像)→ mcpplibs/mcpp-index
文档日期 2026-10-10

标注约定:✅ 已核验源码 · 🔁 已在本地复现或跑通 · ❓ 推断,未实测


0. TL;DR

  • 两个 PR 的方向都对,设计也符合本项目的约定:只新增、尾部默认参数、新字段放最后、错误写进 statusText、状态只放在传输链最底层的 Socket(沿用 0.3.4 的 I1)、测试不联网(hermetic)。和业界通行做法也一致(§2)。
  • #24 可以直接合,不需要追加 commit。剩下几处小问题不影响合入(§3.3)。
  • #25 有一个回归,已修复并推到 PR 上 🔁:handshake 的等待改成了 poll,信号会让它以 EINTR 返回,即使 handler 装的是 SA_RESTART 也一样。结果是装了信号 handler 的进程(profiler、定时器)握手会被误报成 “TLS handshake timed out”。修复前新加的用例在 100 ms 就失败了(时限是 1000 ms)。
  • Windows CI 红是 runner 镜像的变化,和两个 PR 都无关 ✅:win22/20261004.326 带的是 Git for Windows 2.56,原来硬编码的 mingw64/etc/ssl/certs/ca-bundle.crt 不存在了。修复已随 #25 一起推送:向 git 查询 bundle 的路径,查不到再在安装目录里搜索。
  • 合入顺序是 #25 → #24:#25 带着 CI 修复,先合入后 master 才能变绿;#24 再同步 master,同时解决 CHANGELOG 的冲突。

待你决定

# 决策 建议
D1 版本号用 0.3.5 还是 0.4.0 0.3.5。#25 严格说改变了行为(握手超过 connectTimeoutMs 会失败),不符合 0.3.4 “补丁版本只新增”的约定。但它修的是一个永久卡死的问题;而且 0.4.0 不会被 ^0.3.x 解析到,最需要这个修复的正是已经在用 ^0.3.x 的项目。在 CHANGELOG 里用 “Behaviour change” 单列说明
D2 connectTimeoutMs 继续按阶段各自计时,还是改成整个建连过程共用一个 deadline 0.3.5 保持按阶段计时(和现有的代理应答等待一致,PR 已在文档里写明)。共用 deadline 和 curl 的语义一样,更符合直觉,留到 0.4 统一做(§4)
D3 #24 是否同时提供内存中的 PEM(如 extraCaPem) 这次不做,作为后续单独的一项。路径方式已经覆盖代理、企业 CA 这些主要场景
D4 Socket::set_deadline / deadline_hit / wait_before_recv 都是 public(因为整个类是 export 的) 接受。set_stop / stop_possible 已经是这样,跨 partition 用 friend 在各家编译器上的风险更大;注释已写明它们是给 TLS 层用的

1. CI 失败分析(run 37911364113 / job 113757995241)

项 内容
现象 run (x86_64-windows-musl, above openkal, on Windows) 运行 4 秒后失败:cp: cannot stat '/c/Program Files/Git/mingw64/etc/ssl/certs/ca-bundle.crt' ✅
根因 runner 镜像从 win22/20260927.320 换成了 win22/20261004.326,Git for Windows 也从 2.55 升到 2.56 ✅。2.56 把环境从 MINGW64 换成了 UCRT64,bundle 现在在 /c/Program Files/Git/ucrt64/etc/ssl/certs/ca-bundle.crt(新 CI 日志里打印出来的路径)✅
影响 所有分支的这个 job 都会失败,在 smoke 程序运行之前就挂了。和 #24、#25 的代码都无关
修复 已推送到 #25 的 bf99dbd:先用 git config --system http.sslcainfo 查路径(经 cygpath 转换),查不到就在 /c/Program Files/Git 下搜索 */ssl/certs/ca-bundle.crt,还找不到就报 ::error:: 并给出明确原因。同时打印 git --version 和最终用到的路径,方便下次镜像再变时定位
验证 #25 的 CI run 37968516327:10 个 job 全绿,这个 job 用 git config 查到了 ucrt64 下的路径;smoke 输出 cancellation: ok (stopped at 200 ms, returned after 261 ms) ✅
顺带发现 这个 job 里 framing parsers: ok 和 cancellation: ok 之间有约 39 s 的空档,10-05 master 的运行里也有,所以不是这两个 PR 引入的。空档出现在计时开始之前,原因没有查 ❓,列入 §4

2. 设计评审

2.1 是否符合本项目的约定

约定(来源) #24 #25
只新增:尾部默认参数、新字段放最后(0.3.4 §0) ✅ extraCaFile 放在最后;proxy_tunnel 加了尾部默认参数 ✅ connect_over 加了尾部默认参数;CHANGELOG 点名说明了成员函数指针的不兼容
状态只放最底层 Socket,TlsSocket 只往下转发(0.3.4 I1) 不涉及 ✅ set_deadline / deadline_hit 都经 lower_ 往下转发,https 代理隧道的情况有测试覆盖
状态只在调用期间存在(0.3.4 I2) 不涉及 ✅ 握手结束就清掉 deadline,有测试覆盖
不配置时行为不变(0.3.4 I3) ✅ 字段为空时一行代码都不走 ⚠️ 默认的 10 s 会生效,这是有意的行为变化(见 D1)
失败原因写进 statusText,能直接看懂 ✅ 错误信息里带文件路径 ✅ 显示 TLS handshake timed out;代理那一跳还会带上代理地址
测试不联网,能在 Windows 上跑 ✅ 已加进 Windows 的 hermetic 列表 ⚠️ 原 PR 没有加,现已补上(bf99dbd)
不引入新的环境变量或全局状态 ✅ 只增加一个字段 ✅ 不新增配置项

2.2 语义和业界做法对照

#24 extraCaFile:在默认 store 之外再追加信任的 CA

实现 语义
Node NODE_EXTRA_CA_CERTS 追加,和 #24 完全一样,名字也对应
reqwest add_root_certificate 追加到内置根证书
curl --cacert / CURLOPT_CAINFO、Python requests verify=path、Go tls.Config.RootCAs 替换(本库对应的是 SSL_CERT_FILE)

结论:库里原本只有“替换”的方式(SSL_CERT_FILE),#24 补上了“追加”,这是业界常见的另一半。文件不可用时直接失败、不跳过,这个判断对:跳过的话,错误最后会变成一个不提文件的证书校验失败。

#25 握手时限并入 connectTimeoutMs

实现 握手的时限
curl CURLOPT_CONNECTTIMEOUT 包含 TLS 握手,并且整个建连阶段共用一个时限
Go http.Transport.TLSHandshakeTimeout 单独一项,DefaultTransport 默认 10 s
#25 包含握手,但每个阶段各自计时

结论:并入 connectTimeoutMs(不新增配置项)和 curl 一致,默认 10 s 也和 Go 的默认值相同。差别在于各阶段各自计时,最坏情况下总时间是 N × timeout。PR 和 README 都已写明(见 D2)。

2.3 API 好不好用

点 评价
cfg.extraCaFile = "/etc/corp/root-ca.pem"; 一行就能用 好。代理那一跳自动生效,不用再配置一次
只接受文件路径 证书打包进程序、或者从配置中心拿到的场景要先落盘再用(D3)
只能指定一个文件 和 Node 一样,可以把多个证书拼成一个文件,够用
verifySsl=false 时文件读不到也会失败 有意设计(所有机器上报同样的错),README 里再补一句说明会更好
握手超时不需要任何改动就生效 好,解决的是默认配置下的卡死问题
connect_over(..., handshakeTimeoutMs = -1) 和 connect(..., timeoutMs, ...) 的默认值不对称 为了兼容只能这样:connect_over 原本就不带超时

3. 问题清单与处理

3.1 #25(已推送:cfdab98..97a0657)

# 级别 问题 处理
H1 高 EINTR 被当成 deadline 到期。wait_before_recv 只要等待失败就设置 deadline_hit_,不看时钟。没有 token 时只调用一次 poll,信号一来就返回。master 上走的是阻塞的 recv,在 SA_RESTART 下会自动重启,所以这是回归 🔁 3013b98:提前返回就按剩余时间继续等,只有时钟确实到了才算 deadline 到期。新用例 ASignalDuringTheHandshakeIsNotATimeout:时限 1000 ms,每 100 ms 用 pthread_kill 给握手线程发一次 SIGUSR1。修复前 100 ms 就失败,修复后通过 🔁
M1 中 test_handshake_timeout 没进 Windows 的 hermetic 列表,WSAPoll 路径没人测 bf99dbd
M2 中 Windows openkal job 的 bundle 路径(§1) bf99dbd
L1 低 CHANGELOG 没提导出的 TlsSocket::connect 现在也会限制握手时间 97a0657
L2 低 移动构造和移动赋值复制了 deadline_,但没复制 deadline_hit_ 3013b98
— 已知限制 握手期间的 send 是阻塞的;readTimeoutMs 管不到阻塞的写(README 里的说法和实际不符) 不在本版本处理,单列一项(§4)

本地结果(Linux, LLVM 22)🔁:test_handshake_timeout 9/9、test_cancel 24、test_proxy 43、test_pool 27、test_tls_verify 6、test_framing 17,全部通过。

3.2 #24:可以直接合入,不追加 commit

本地结果 🔁:test_extra_ca 11/11,其余测试不受影响。#24 和 #25 合在一起后,全部测试也都通过(test_extra_ca + test_handshake_timeout 一起跑过)。

3.3 #24 的小问题(合入后视情况再处理,不阻塞本版本)

  • 在 Linux 上,路径是目录时 ifstream 也能打开,错误信息变成 “cannot parse”,不是 “cannot read”。加一个 is_regular_file 判断就能修。
  • 部分证书解析失败(ret > 0)不会报错,和默认 bundle 的处理一致,README 可以补一句。
  • 每建一个新连接都要重新读、重新解析一次文件。默认 bundle 也是这样,暂时不缓存。

4. 0.3.5 之后(不在本版本范围内)

项 说明
写操作不阻塞 bio_send / write_all 在阻塞 socket 上永远拿不到 WANT_WRITE,所以 readTimeoutMs 管不到阻塞的写,握手期间的写也不受 deadline 约束。这是 0.3.4 D4 留下的问题
整个建连共用一个 deadline(D2) DNS、TCP(每个地址)、代理应答、两次握手共用一个 connectTimeoutMs。行为有变化,适合 0.4
extraCaPem(D3) 直接从内存加载 PEM
openkal Windows smoke 的 39 s 空档 见 §1,master 上已有,原因待查
poll_fd 统一处理 EINTR TCP connect 和代理应答的等待也有同样的问题,H1 只修了握手。可以在 poll_fd 一处统一修

5. 任务依赖图

T0 计划(本文)← 你在这里审
 │
 ├─ T1 #25 追加 commit(已推送 97a0657)→ CI 全绿 ✅(run 37968516327)
 │
 ├─ T2 更新 #25 的 PR 描述:Not covered 里关于 EINTR 的说法改成“已修复”;补充 CI 的变化
 │
 ├─ T3 squash 合入 #25(依赖 T1、T2)
 │
 ├─ T4 #24 同步 master:合并两边 CHANGELOG 的 “Unreleased” 段 → CI 全绿 → squash 合入
 │
 ├─ T5 发版 commit:CHANGELOG 的 Unreleased → 0.3.5;mcpp.toml version;README 安装行(mcpp add / 依赖示例)
 │
 └─ T6 发布(依赖全部)
       tag 0.3.5 + GitHub release
       → gitcode mcpp-res/tinyhttps 上传 0.3.5 tarball(核对和 GitHub archive 的 sha256 一致)
       → mcpp-index PR:pkgs/t/tinyhttps.lua 加 0.3.5(GLOBAL / CN,三个平台),
         tests/examples/tinyhttps 升到 0.3.5,加一条 `extraCaFile` 的 API 检查
       → 用已发布的 index 实测:新建项目写 "^0.3.1" 能解析到 0.3.5,GLOBAL 和 CN 两个源都验证

关键路径:T1(CI)→ T3 → T4 → T5 → T6。

6. 验收

  • #24、#25 以及两者合并后,hermetic 测试在本地全部通过 🔁
  • H1 能复现,修复后用例通过 🔁;已推送到 #25
  • #25 CI 全绿(run 37968516327,10/10),包括 Windows openkal 和 Windows hermetic(test_handshake_timeout)
  • #25 squash 合入(9d4315e);#24 同步 master(合并时 CHANGELOG 和 Windows 测试列表都有冲突)后 CI 10/10 全绿,squash 合入(a0f7749)
  • 版本号 0.3.5(D1);发版 commit 6153cdd 的 master CI 10/10 全绿;tag 0.3.5,GitHub release
  • gitcode mcpp-res/tinyhttps 0.3.5 的 tarball 和 GitHub archive 的 sha256 一致(6f75f9e6…58a3)
  • mcpp-index 合入(mcpplibs/mcpp-index#517,bf7e87c):PR 的全部检查通过,包括 openkal measure 和 linux/macos/windows workspace;合入后 Publish Index Artifact、validate、deploy-site 也都通过
  • 真实验证:用已发布的 index 新建项目,依赖写 "^0.3.1",在 GLOBAL 和 CN 两个源下(都清了缓存)都解析到 0.3.5。真实 HTTPS 请求返回 200;握手不回应的本地对端在 connectTimeoutMs=500 下约 520 ms 报 TLS handshake timed out;extraCaFile 文件不存在时,报错里带路径;指向系统 bundle 时请求正常返回 200
  • 注:mcpp 在一个源失败时会自动换另一个源,所以“切到某个源跑通”证明不了用的是哪个源。两个 URL 内容一致,是直接下载后比对 sha256 得出的