Skip to content

fix(proxy): 对 HTML 导航请求带 accept-encoding: identity,让 polyfill 注入真正落地(#53 / #96 的修复此前从未生效) - #151

Closed
CHYMZXS wants to merge 1 commit into
shaobeichen:mainfrom
CHYMZXS:fix/html-injection-identity
Closed

CHYMZXS wants to merge 1 commit into
shaobeichen:mainfrom
CHYMZXS:fix/html-injection-identity

Conversation

@CHYMZXS

@CHYMZXS CHYMZXS commented Oct 2, 2026

Copy link
Copy Markdown

问题

lib/proxy.mjs 的 HTML 注入分支带 !isCompressed(...) 条件,而浏览器导航必然携带 Accept-Encoding,dsh web 于是返回 gzip 压缩文档 → 注入永不命中。

结果是 data-dsh-pocket-polyfill(crypto.randomUUID / AbortSignal.any)与 data-dsh-pocket-transport-shim 对所有真实浏览器都是死代码:

缺 AbortSignal.any 的内核(Android WebView / 厂商浏览器 < Chrome 116,例如 Hermit 套壳)在连接层直接抛 TypeError,/api/remote.mux 建链后立即断开,页面永久停在「重新连接中」(connection.connecting)。

修复

对 HTML 导航请求要求 identity(lib/proxy.mjs,+1 行有效代码):

const headers = loopbackAuthority({ ...req.headers }, upstream);
// HTML 导航请求向上游要**未压缩**文档(issue #150)
if (injectHtml && isHtmlRequest(req)) headers['accept-encoding'] = 'identity';

安全性:上游若忽略 identity,仍走原有 isCompressed 分支原样透传压缩流,无损坏风险;JS/CSS 等子资源的压缩透传不受影响(只对 HTML 导航生效,已加回归用例覆盖)。

验证

本机实测(真实 dsh web + 代理)

Accept-Encoding 响应 content-encoding 是否注入 polyfill
(无) (none) ✅ true
gzip, deflate, br gzip ❌ false(修复前)
identity (none) ✅ true

上游真实 dsh web 尊重 identity(39778 字节明文 HTML),修复路径成立。

真实 Chromium 复现

/api/remote.mux 是 DSH 实时通道,三组对照:

场景 WebSocket 结果 页面表现
A) 正常 Chromium(原生有 AbortSignal.any) 101,无关闭 稳定 ✅
B) 加载前删除 AbortSignal.any(模拟 WebView < 116) 101 后立即关闭 TypeError: AbortSignal.any is not a function → [connection] connection lost, retry #1…#5,一直「重新连接中」 ❌
C) 删除后注入现有 polyfill(= 本 PR 效果) 101,无关闭 稳定,重连告警 0 次 ✅

测试

node --test test/proxy.test.js          → 38/38 通过
node --test test/proxy.test.js test/polyfill.test.js \
             test/transport-shim.test.js test/smoke.test.js  → 55/55 通过

新增 2 例:

  • HTML 导航请求向上游要 identity,注入对真实浏览器生效——假上游尊重 Accept-Encoding(像 dsh web 一样),断言上游收到 identity、响应无 content-encoding、且 polyfill / AbortSignal.any / transport shim 均已注入
  • 上游忽略 identity 时,压缩流仍原样透传(不损坏)——覆盖兜底路径

既有用例「压缩 HTML(gzip)不注入 polyfill」语义未变(仍绿),仅补充注释说明它现在覆盖的是「上游忽略 identity」的兜底路径。

全量测试说明

npm test 有 3 例失败,均为 Windows 本地环境问题,与本改动无关——已用 git stash 回到干净 HEAD 复现同样失败:

  • download.test.js:104(超时)
  • tunnel-args.test.js:38、:56(假 cloudflared 二进制无 .exe 后缀 → ENOENT)

CI(Linux)不受影响。

与 PR #130 的关系

#130 独立发现了同一问题(原文「polyfill 和 transport shim 对真实浏览器全是死代码」),但该 PR 还捆绑了 TRUSTED_TRANSPORT_SHIM、「远程设置」开关等需要权衡的功能,至今 open 未合并。

本 PR 只取其中最小、无争议的一项(identity,即 #130 的第 1 项),把注入先救活,不引入任何新开关或信任语义变化。若维护者倾向一次性合并 #130 的完整方案,可直接以 #130 为准、关闭本 PR。

Closes #150

HTML 注入分支带 !isCompressed(...) 条件,而浏览器导航必然携带
Accept-Encoding,dsh web 于是返回 gzip 压缩文档 → 注入永不命中。
data-dsh-pocket-polyfill(crypto.randomUUID / AbortSignal.any)与
data-dsh-pocket-transport-shim 对所有真实浏览器都是死代码,
issue #53 / #96 的修复因此从未生效。

缺 AbortSignal.any 的内核(Android WebView / 厂商浏览器 < Chrome 116)
在连接层直接抛 TypeError,/api/remote.mux 建链后立即断开,页面永久停在
「重新连接中」(connection.connecting)。

修复:HTML 导航请求向上游要 identity,注入即可落地。上游若忽略
identity,isCompressed 分支照旧原样透传压缩流,无损坏风险;
JS/CSS 等子资源的压缩透传不受影响。

测试:
- 新增「HTML 导航要 identity 并完成注入」(假上游尊重 Accept-Encoding)
- 新增「上游忽略 identity 时压缩流仍原样透传」
- 既有「压缩 HTML 不注入」用例改为明确覆盖上述兜底路径

Closes #150
@CHYMZXS CHYMZXS closed this by deleting the head repository Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant