Skip to content

fix(download): HTTP 4xx/5xx 立即失败并保留原文(不再误报「中途断开」) - #98

Merged
wangzhizhou merged 1 commit into
mainfrom
fix/download-http-status-fail-fast
Oct 21, 2026
Merged

wangzhizhou merged 1 commit into
mainfrom
fix/download-http-status-fail-fast

Conversation

@wangzhizhou

Copy link
Copy Markdown
Contributor

fix(download): HTTP 4xx/5xx 立即失败并保留原文,不再误报「中途断开」

根因:HttpClient.stream_range 的续传重试把 requests.HTTPError(状态码错误)
和「流式中途断连」混在一起,任何 4xx/5xx 都会被当成抖动重试满 RESUME_ATTEMPTS
次,最后统一报「下载中途多次断开」。

实测后果(Adoptium 没有 Java 25 的 windows/aarch64 构建,URL 直接 404):

  • 一个必然失败的状态码换来 4 次无谓请求;
  • 用户与日志看到的都是「网络不好」,真实原因被掩盖;
  • 夜间验收连红 7 天:harness 靠 requests 原文
    404 Client Error: Not Found for url: https://api.adoptium.net/v3/binary/latest
    把「上游缺该平台产物」判成 UP(gap)(警告),却被这句「中途断开」挡成了 FAIL。

改动:

  • infra/http.py:新增 HttpStatusError(RuntimeError)(带 status/url)。
    stream_range 遇到 requests.HTTPError 立即抛出、消息保留 requests 原文;
    唯一例外是续传时服务器回 416 且本地已取全 → 按成功返回(那正是"下完了")。
    放弃续传时的消息补上最后一次的底层错误,别再只有一句看不到原因的文案。
  • services/java.py:404 时补一句可执行提示(Temurin 未发布该 OS/arch 的该大版本,
    可自装 Java 或换平台),原文留在末尾供 harness 判定。
  • scripts/acceptance.py:log_contains 改成两侧都去掉全部空白再比 —— rich 软换行
    会把 URL 切开,旧实现只把空白压成一个空格,断点落在标记内部时仍然匹配不到。

顺带修本地测试的静默自杀(与本修复无关,但挡住了验证):

  • 新增根 conftest.py,autouse 把 ORZMC_RUNTIME 钉成 python。未钉时
    resolve_runtime() 会读本机状态目录的 install.conf:装过 Swift 通道的机器上
    main() 会转发给 Swift 真身,POSIX 下是 os.execv —— pytest 进程被整个替换,
    表现为退出码 1、无 traceback、无用例汇总(本机 test_usage_error_exit_code_is_one
    起整批"消失")。CI 一直钉着这个变量,现在本地一致。

测试:4xx 只请求一次、状态码/URL/原文都在;416 已取全按成功;
平台缺口消息 → harness 的 GAP_MARKS 在任意断行下都能认出(新增用例,
把「实现消息」「harness 标记」「软换行」绑在一起守)。ruff / mypy /
pytest(485 passed, 3 skipped)/ 父仓库 scripts/accept/cli.py(25 passed)全绿。

AGENTS.md 同步记录这两条(4xx 不重试 + 测试钉住 runtime)。

根因:`HttpClient.stream_range` 的续传重试把 `requests.HTTPError`(状态码错误)
和「流式中途断连」混在一起,任何 4xx/5xx 都会被当成抖动重试满 `RESUME_ATTEMPTS`
次,最后统一报「下载中途多次断开」。

实测后果(Adoptium 没有 Java 25 的 windows/aarch64 构建,URL 直接 404):
- 一个必然失败的状态码换来 4 次无谓请求;
- 用户与日志看到的都是「网络不好」,真实原因被掩盖;
- **夜间验收连红 7 天**:harness 靠 requests 原文
  `404 Client Error: Not Found for url: https://api.adoptium.net/v3/binary/latest`
  把「上游缺该平台产物」判成 `UP(gap)`(警告),却被这句「中途断开」挡成了 FAIL。

改动:
- `infra/http.py`:新增 `HttpStatusError(RuntimeError)`(带 `status`/`url`)。
  `stream_range` 遇到 `requests.HTTPError` 立即抛出、消息保留 requests 原文;
  唯一例外是续传时服务器回 **416 且本地已取全** → 按成功返回(那正是"下完了")。
  放弃续传时的消息补上最后一次的底层错误,别再只有一句看不到原因的文案。
- `services/java.py`:404 时补一句可执行提示(Temurin 未发布该 OS/arch 的该大版本,
  可自装 Java 或换平台),原文留在末尾供 harness 判定。
- `scripts/acceptance.py`:`log_contains` 改成两侧都去掉**全部**空白再比 —— rich 软换行
  会把 URL 切开,旧实现只把空白压成一个空格,断点落在标记内部时仍然匹配不到。

顺带修本地测试的静默自杀(与本修复无关,但挡住了验证):
- 新增根 `conftest.py`,autouse 把 `ORZMC_RUNTIME` 钉成 `python`。未钉时
  `resolve_runtime()` 会读本机状态目录的 `install.conf`:装过 Swift 通道的机器上
  `main()` 会转发给 Swift 真身,POSIX 下是 `os.execv` —— pytest 进程被整个替换,
  表现为退出码 1、无 traceback、无用例汇总(本机 `test_usage_error_exit_code_is_one`
  起整批"消失")。CI 一直钉着这个变量,现在本地一致。

测试:4xx 只请求一次、状态码/URL/原文都在;416 已取全按成功;
平台缺口消息 → harness 的 `GAP_MARKS` 在任意断行下都能认出(新增用例,
把「实现消息」「harness 标记」「软换行」绑在一起守)。`ruff` / `mypy` /
`pytest`(485 passed, 3 skipped)/ 父仓库 `scripts/accept/cli.py`(25 passed)全绿。

AGENTS.md 同步记录这两条(4xx 不重试 + 测试钉住 runtime)。
@wangzhizhou
wangzhizhou merged commit c78eff6 into main Oct 21, 2026
11 checks passed
@wangzhizhou
wangzhizhou deleted the fix/download-http-status-fail-fast branch October 21, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant