fix(relay): 透传客户端真实握手头(Origin/UA/Referer),消除与浏览器直连的唯一差异;镜像加 #nosw 旁路

- 上游改按 Req.headers 的 origin/user-agent/referer 透传(缺省才回退常量),避免上游按 Origin 返回与本地构建不匹配的响应(客户端握手失败自行 close=1005)

- main 加载器与 split.js 模板同步:hash 含 #nosw 时注销已注册的 Service Worker,便于排查镜像页接管问题
This commit is contained in:
TermiNexus committed 2026-10-11 19:47:19 +08:00
1 parent ad2b069659
commit 5dd5560090
3 files changed
+42 -17

No files matched your search

+10 -1
View File
@@ -511,8 +511,17 @@ WsServer.on('connection', (Client, Req) => {
let Upstream = null;
const Pending = []; // 上游未就绪时暂存竞态帧(握手帧常比上游 TLS 建连更早到达),一连上立即按原序补发
// 握手头:把客户端**真实的** Origin / User-Agent / Referer 透传给上游(与浏览器直连时一致)。
// 不能用写死的 origin:真实服务器可能按 Origin 选择构建/配置,写死会让响应与本地构建对不上,
// 表现为客户端握手失败自行断开(close code 1005)。
const UpHeaders = {
'user-agent': Req.headers['user-agent'] || UPSTREAM_HEADERS['user-agent'],
origin: Req.headers['origin'] || UPSTREAM_HEADERS.origin,
referer: Req.headers['referer'] || UPSTREAM_HEADERS.referer,
};
// 转发:客户端帧**原样直发**;上游未就绪时暂存,open 后按原序补发(不重放、不改字节)。
Upstream = new Ws(UPSTREAM, { headers: UPSTREAM_HEADERS, rejectUnauthorized: false });
Upstream = new Ws(UPSTREAM, { headers: UpHeaders, rejectUnauthorized: false });
Upstream.binaryType = 'nodebuffer';
Upstream.on('open', () => {
State.Log('上游已连接: ' + UPSTREAM + ',补发暂存 ' + Pending.length + ' 帧');
+16 -8
View File
@@ -131,15 +131,23 @@ var __webpack_modules__ = window.__modules__;
// 作用域必须是 /:首页 / 发起的 /play/... 请求才会被接管(serve.js 已放行 Service-Worker-Allowed)
// 用途:把 unpacked 下的散装小文件用单包就地命中(hornet_sw.js 内置单包缓存)
if ('serviceWorker' in navigator && /^(localhost|127\.0\.0\.1|\[::1\])$/.test(location.hostname)) {
navigator.serviceWorker
.register('/hornet_sw.js', { scope: '/', updateViaCache: 'none' })
.then(function (Reg) {
console.log('[Hornet] Service Worker 就绪(作用域 ' + Reg.scope + '),unpacked 小文件将由单包命中');
return Reg.update();
})
.catch(function (Err) {
console.warn('[Hornet] Service Worker 注册失败: ' + (Err && Err.message));
if (/#nosw/.test(location.hash)) {
// #nosw:注销已注册的 Service Worker(清除单包缓存接管),用于排查镜像页问题;生效需再刷新一次
navigator.serviceWorker.getRegistrations().then(function (Regs) {
Regs.forEach(function (Reg) { Reg.unregister(); });
console.warn('[Hornet] #nosw:已注销 Service Worker(再刷新一次生效)');
});
} else {
navigator.serviceWorker
.register('/hornet_sw.js', { scope: '/', updateViaCache: 'none' })
.then(function (Reg) {
console.log('[Hornet] Service Worker 就绪(作用域 ' + Reg.scope + '),unpacked 小文件将由单包命中');
return Reg.update();
})
.catch(function (Err) {
console.warn('[Hornet] Service Worker 注册失败: ' + (Err && Err.message));
});
}
}
return __webpack_exports__;
+16 -8
View File
@@ -365,15 +365,23 @@ var __webpack_modules__ = window.__modules__;
// 作用域必须是 /:首页 / 发起的 /play/... 请求才会被接管(serve.js 放行 Service-Worker-Allowed)
// 用途:把 unpacked 下的散装小文件用单包就地命中(hornet_sw.js 内置单包缓存)
if ('serviceWorker' in navigator && /^(localhost|127\\.0\\.0\\.1|\\[::1\\])$/.test(location.hostname)) {
navigator.serviceWorker
.register('/hornet_sw.js', { scope: '/', updateViaCache: 'none' })
.then(function (Reg) {
console.log('[Hornet] Service Worker 就绪(作用域 ' + Reg.scope + '),unpacked 小文件将由单包命中');
return Reg.update();
})
.catch(function (Err) {
console.warn('[Hornet] Service Worker 注册失败: ' + (Err && Err.message));
if (/#nosw/.test(location.hash)) {
// #nosw:注销已注册的 Service Worker(清除单包缓存接管),用于排查镜像页问题;生效需再刷新一次
navigator.serviceWorker.getRegistrations().then(function (Regs) {
Regs.forEach(function (Reg) { Reg.unregister(); });
console.warn('[Hornet] #nosw:已注销 Service Worker(再刷新一次生效)');
});
} else {
navigator.serviceWorker
.register('/hornet_sw.js', { scope: '/', updateViaCache: 'none' })
.then(function (Reg) {
console.log('[Hornet] Service Worker 就绪(作用域 ' + Reg.scope + '),unpacked 小文件将由单包命中');
return Reg.update();
})
.catch(function (Err) {
console.warn('[Hornet] Service Worker 注册失败: ' + (Err && Err.message));
});
}
}
`