Skip to content

🐛 迁移测试端点从 httpbun.com 到 httpbingo.org#1617

Open
cyfung1031 wants to merge 5 commits into
scriptscat:mainfrom
cyfung1031:claude/migrate-httpbun-httpbingo
Open

🐛 迁移测试端点从 httpbun.com 到 httpbingo.org#1617
cyfung1031 wants to merge 5 commits into
scriptscat:mainfrom
cyfung1031:claude/migrate-httpbun-httpbingo

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Checklist / 检查清单

  • Fixes mentioned issues / 已修复或实现
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

背景

httpbun.com 近期频繁出现 502 错误,导致相关测试不稳定(见 #1609)。#1609 迁移到了 mockhttp.org,但 mockhttp.org 与 httpbin/httpbun 的行为差异较大(/getqueryParams 而非 args/url/post 仅接受 application/json/base64text/html 提供内容、/cookies 是无路径的 REST 资源、无 OPTIONS 路由、/drip 被 Cloudflare 缓冲导致渐进式下载测试被迫放宽断言等),需要大量断言改写和用例删除才能通过。

本 PR 改用 httpbingo.orggithub.com/mccutchen/httpbingo.org,由 go-httpbin 提供支持)。它与经典 httpbin/httpbun 的行为高度兼容,因此改动范围远小于 #1609 的 mockhttp.org 方案,绝大多数测试断言保持原样,只替换域名。

本次改动

域名替换

example/tests/*.jsexample/crontab/crontab.jse2e/gm-api.spec.tssrc/app/service/service_worker/gm_api/mv3_utils.ts 中的 httpbun.com 全部替换为 httpbingo.org(含相关注释与日志文案)。e2e/gm-api.spec.ts 覆盖的自动化用例(gm_api_sync/async_test.jswindow_message_test.js 等)全部跑在本地 mock server 上,不受这次域名切换影响;这几个文件里的改动只是把 mock server 用来替换 @connect/URL 的正则、字符串同步更新。

httpbingo.org 与 httpbun.com 的真实接口差异(3 处)

这些是本 PR 中唯一需要真正改写断言的地方,逐一用 curl / 真实浏览器核实过:

  1. /status/<code> 不再返回 JSON 体。httpbun.com 有个非标准行为:/status/200 会返回 {"code": 200};httpbingo.org 遵循标准 httpbin 语义,200 状态码返回空响应体。gm_xhr_test.js 里依赖这个行为的 "GET json [...]" 系列用例,以及 Test bug #1078/Test bug #1080 回归用例(共 11 处),改用 /get 端点校验 JSON 回显(断言从 '"code": 200' / response.code === 200 改为 '"method": "GET"' / response.method === "GET"),测试意图(JSON 序列化 + XML 解析路径)不变。

  2. cookies 接口形状不同。httpbun.com 用路径式接口 /cookies/set/{name}/{value}/cookies/delete(不带参数即可清空);httpbingo.org 用查询参数式接口 /cookies/set?name=value/cookies/delete?name(必须点名要删除的 cookie)。已按新接口调整对应用例的 URL。

  3. 查询参数、请求头回显、表单字段统一是数组。httpbingo.org(Go 实现)把这些字段回显为 url.Values 形状的数组,例如请求 /get?x=1 时响应体是 {"args": {"x": ["1"]}} 而不是 {"args": {"x": "1"}}。受影响的断言(GET basic + headers + finalUrlgm_xhr_redirect_test.js 的两个 GET basic with search params 用例、POST form-encoded 用例)已改为取数组首元素比较,而不是直接比较字符串。

其余接口(/base64 内容类型、/redirect-to 默认状态码、/ip/anyOPTIONS/drip 渐进式下载、任意 HTTP 方法访问 /headers/post 的 form/binary body 支持等)经逐一核实与 httpbun.com 时代的假设一致,测试断言未改动。

真实浏览器验证暴露的 3 个问题(均已修复)

迁移完成后,人工在 ScriptCat 1.5.0-beta 与 Tampermonkey 5.5.0 中实际跑了一遍 gm_xhr_test.js(GM_xmlhttpRequest Test Harness 1.3.0,138 项 × fetch/xhr 两种模式),发现 4 类失败;逐一排查根因后确认其中 3 类是本 PR 范围内可修的测试脚本问题,1 类(见下方"已知限制")是 ScriptCat 自身的既有产品限制,超出本次迁移范围:

  1. onprogress fires while downloading [arraybuffer](xhr 模式):负载太小,原生 XHR 几乎不触发 progress。原用例用 /drip?duration=2&delay=1&numbytes=1024(约 1KB)。在真实浏览器里直接测量确认:原生 XMLHttpRequest 要等响应累积到一定字节量才会触发 onprogress,1KB 时端到端只触发 1 次;Fetch API 的 ReadableStream reader 则对每个到达的分片都单独上报,不受此限制。httpbingo.org 的 /drip 端点本身在网络层确实是渐进式下发的(问题出在客户端 XHR 实现,不在服务端)。把两处 /drip 用例([arraybuffer][stream])的负载从 1KB 提到 2KB 后,xhr 模式反复实测稳定产生 9~23 次 progress 事件,event.loaded 首次即 ≥1000 且从未为 0——因此保留了原有 progressEvents >= 4 的强断言,lastLoaded 断言也从 fetch ? > 0 : >= 0 收紧为统一的 > 0,两种模式都不再需要放宽。

  2. Redirect handling (finalUrl changes) [manual]responseHeaders 断言假设有误redirect: "manual" 会让 ScriptCat 内部强制走 Fetch API——读 src/pkg/utils/xhr/bg_gm_xhr.ts 可确认:useFetch = isFetch || !!redirect || anonymous || isBufferStream,只要设置了 redirect(不论是不是 "manual"),无论测试脚本传入的 xhr/fetch 开关是什么,底层都会用 fetch()。因此 xhr、fetch 两种模式最终都会命中浏览器原生的 opaqueredirect 响应,其 headers 按 Fetch 规范固定为空。已在真实浏览器中对 httpbingo.org 发起全新(no-store)请求验证:redirect: "manual"res.type 恒为 "opaqueredirect"res.headers 恒为 0 项,重复 3 次结果一致——这是浏览器 Fetch 规范的固定行为,不是偶发问题,也不是 httpbingo.org 的问题。responseHeaders 断言改为只检查类型(是字符串即可),不再要求非空,两种模式一致。

  3. 上述数组回显问题 —— 见上一节第 3 点,这是导致 GET basic + headers + finalUrlgm_xhr_redirect_test.js 两个用例失败的原因,已一并列在接口差异里。

建议审查重点

  • gm_xhr_test.js/status/200/get(11 处)与 '"code": 200''"method": "GET"' 的替换是否保留了原测试意图。
  • cookies 测试的查询参数式接口调整(/cookies/set?name=value/cookies/delete?name)是否与实际请求/响应行为匹配。
  • Redirect handling [manual]responseHeaders 断言调整、/drip 负载从 1KB 提到 2KB(而不是放宽断言阈值)这两处改动方向是否合理——判断依据都是上文列出的真实浏览器实测数据,而不是猜测。

已知限制

  • redirect: "manual"responseHeaders 恒为空是浏览器 Fetch 规范对 opaqueredirect 响应的既有限制(只要设置了 redirect,ScriptCat 就会强制走 fetch),不在本 PR 范围内修复;如需要真实重定向响应头,需要 fetch_xhr.ts 改走其他机制(如 chrome.webRequest),作为独立产品问题跟进,不在本次迁移范围内。
  • example/tests/gm_download_test.js 未在真实浏览器中重新跑一遍(其 httpbingo.org 相关用例仅使用 /bytes/headers,均已通过 curl 核实且不涉及数组回显差异),风险较低。

验证

自动化:

  • pnpm exec vitest run(全量单元测试)— 3370 个用例全部通过
  • pnpm exec tsc --noEmit — 无错误
  • pnpm exec prettier --check(改动文件)— 通过
  • pnpm build && npx playwright test e2e/gm-api.spec.ts — 全部 7 个用例通过(sync/async GM API、WindowMessage、CSP、sandbox、inject、unwrap;这些用例走本地 mock server,未直接命中真实 httpbingo.org,但验证了域名替换未引入回归)

对 httpbingo.org 端点的直接核实:

  • 逐一 curl 核实 /get/post/status/{code}/redirect-to/base64/{value}/cookies/cookies/set/cookies/delete/ip/basic-auth/any/drip、任意 HTTP 方法、OPTIONSHEADDELETE 的实际响应
  • 用原始 HTTP/2 分块抓包确认 /drip 端点在网络层确实渐进式下发字节
  • 在真实浏览器中直接用原生 XMLHttpRequest/fetch/drip 端点做 progress 事件计数与 loaded 取值实测(各配置重复 5 次):numbytes=1024 时 xhr 模式仅触发 1 次 onprogressnumbytes=2048 时 xhr 模式稳定产生 9~23 次、event.loaded 恒 > 0,fetch 模式产生数百次
  • 在真实浏览器中对 httpbingo.org 发起全新(no-storefetch(url, { redirect: "manual" }) 请求验证(重复 3 次):res.type 恒为 "opaqueredirect"res.status 恒为 0res.headers 恒为 0 项

真人在真实扩展中运行:

  • 真人在 ScriptCat 1.5.0-beta 与 Tampermonkey 5.5.0 中实际运行 gm_xhr_test.js(138 项 × fetch/xhr 两种模式):首轮分别为 ✅134 ❌4(ScriptCat)与 ✅135 ❌3(Tampermonkey),4 类失败已逐项排查根因并修复(见上文"真实浏览器验证暴露的 3 个问题")
  • 受限于工具限制,未能在本次会话中对修复后的版本重新跑一遍相同的真实浏览器验证;所有改动均已对照 httpbingo.org 实际响应或真实浏览器 API 行为复核,建议合并前再人工跑一遍 gm_xhr_test.js/gm_xhr_redirect_test.js 作最终确认

cyfung1031 and others added 4 commits July 20, 2026 01:32
httpbun.com 近期频繁 502 导致测试不稳定;httpbingo.org
(https://github.com/mccutchen/httpbingo.org,由 go-httpbin 提供支持,
https://github.com/mccutchen/go-httpbin) 与 httpbin/httpbun 行为高度
兼容,已逐端点用 curl 核实(/get、/post、/status、/redirect-to、
/base64、/cookies、/ip、/basic-auth、/drip 等),因此改动远小于此前
迁移到 mockhttp.org 的方案:

- 域名替换:example/tests/*.js、example/crontab/crontab.js、
  e2e/gm-api.spec.ts、src/app/service/service_worker/gm_api/mv3_utils.ts
  中的 httpbun.com 全部替换为 httpbingo.org。
- gm_xhr_test.js 中原先依赖 httpbun 专有行为 `/status/200` 返回
  `{"code": 200}` 的 "GET json" 测试battery(含 bug scriptscat#1078/scriptscat#1080
  回归用例,共 11 处),httpbingo.org 的 /status/<code> 遵循标准 httpbin
  语义(空响应体),改用 /get 端点校验 JSON 回显(`"method": "GET"`),
  测试意图不变。
- cookies 测试改用 httpbingo.org 的查询参数式接口
  (/cookies/set?name=value、/cookies/delete?name),而非
  httpbun 的路径式 /cookies/set/{name}/{value}。
- POST form-encoded 测试适配 httpbingo.org 将表单值回显为数组
  (Go url.Values 形状)。
- 其余测试(base64 XML/parser-error 解析、/ip、/any 的 OPTIONS、/drip
  渐进式下载、/redirect-to 302 默认值、manual 模式 301 客户端回退值、
  /basic-auth、任意方法 /headers、DELETE、HEAD 等)经核实与 httpbun
  时代假设一致,仅替换域名。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
对照真人在 ScriptCat 与 Tampermonkey 中实际运行测试脚本的结果排查根因:

- "GET basic + headers + finalUrl"、gm_xhr_redirect_test.js 的 "GET basic
  with search params" 两个用例:httpbingo.org 将查询参数与请求头回显为
  数组(Go url.Values 形状,如 {"x": ["1"]}),此前断言按标量比较导致
  失败;改为按数组首元素比较,与此前 POST form-encoded 用例的同类修复
  保持一致。
- "onprogress fires while downloading [arraybuffer]"(仅 xhr 模式):在
  ScriptCat 与 Tampermonkey 中均一致复现——原生 XMLHttpRequest 对
  responseType=arraybuffer 合并 progress 事件的粒度远粗于 Fetch API 的
  ReadableStream reader(用原始抓包确认 httpbingo.org /drip 端点本身在
  网络层确实是渐进式下发的),因此仅 fetch 模式保留 >=4 事件的断言,
  xhr 模式放宽为 >=1。
- "Redirect handling (finalUrl changes) [manual]"(仅 fetch 模式):
  redirect:"manual" 在 fetch 路径下命中浏览器原生的 opaqueredirect
  响应,其 headers 按 Fetch 规范固定为空——并非 httpbingo.org 特有问题,
  也无法从响应本身修复。xhr 模式不走该路径,仍保留非空 responseHeaders
  的断言。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
上一提交为规避 xhr 模式 onprogress 事件过少的问题,把 arraybuffer 用例
的断言从 >=4 放宽为 >=1,掩盖了问题而非解决它。改为在浏览器中直接测量
真实原因:原生 XMLHttpRequest 要等响应累积到一定字节量才会触发
onprogress(实测 ~1KB 端到端仅触发 1 次),而不是像 Fetch API 的
ReadableStream reader 那样每个到达的分片都单独上报一次。将 /drip 负载
从 1KB 提高到 2KB 后,在 Chrome 中反复实测 xhr 模式稳定产生两位数的
progress 事件,因此恢复两处用例([arraybuffer]、[stream])原有的
`>=4` 强断言,不再区分 fetch/xhr。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
对照真实浏览器再次运行的结果排查:

- 两处 onprogress 用例([arraybuffer]、[stream]):既然 /drip 负载已提到
  2KB,用真实 XMLHttpRequest 反复实测 xhr 模式下 event.loaded 首次即
  ≥1000 字节、从未为 0(5 次重复实测均如此),因此把 lastLoaded 断言从
  `fetch ? > 0 : >= 0` 收紧为统一的 `> 0`,两种模式都不再需要放宽。

- "Redirect handling (finalUrl changes) [manual]":上一提交把 xhr 模式
  排除在外的假设是错的——读 src/pkg/utils/xhr/bg_gm_xhr.ts 发现
  `useFetch = isFetch || !!redirect || anonymous || isBufferStream`,
  只要设置了 redirect(包括 "manual"),无论测试脚本传入的是 xhr 还是
  fetch 开关,底层都会强制走 Fetch API。用真实浏览器对 httpbingo.org
  发起全新(no-store)请求验证:`redirect: "manual"` 下 fetch() 返回的
  Response 永远是 `type: "opaqueredirect"`、`headers` 恒为 0 项,这是
  Fetch 规范对 opaque 重定向响应的规定行为,并非 httpbingo.org 的问题,
  也不是偶发。因此 responseHeaders 的断言只检查类型(字符串),不再要求
  非空,两种模式一致。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cyfung1031

Copy link
Copy Markdown
Collaborator Author

已使用 ScriptCat 1.5.x 和 Tampermonkey 测试 example/tests/gm_xhr_test.js
均完全通过

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