Skip to content

fix: 修复网页终端被浏览器 BFCache/后台冻结掐断后误报 1006 断连 - #340

Open
ZYWNB666 wants to merge 1 commit into
jumpserver:v5.0from
ZYWNB666:fix/luna-terminal-bfcache-freeze
Open

ZYWNB666 wants to merge 1 commit into
jumpserver:v5.0from
ZYWNB666:fix/luna-terminal-bfcache-freeze

Conversation

@ZYWNB666

Copy link
Copy Markdown

问题现象

用户在浏览器使用网页终端(Luna)时,以下两个场景回来都会看到弹窗:

连接异常中断(关闭码 1006):未收到 Koko 结束通知,请检查网络、代理或 Koko 服务

而 koko 服务端全程无任何异常,弹窗把锅甩给了网络和 Koko,实际是浏览器行为:

  1. Back-Forward Cache:Chrome/Edge 允许持有 WebSocket 的页面在同标签页导航离开时冻结进 BFCache 而非销毁,并主动关闭页面上的全部 WebSocket(服务端只收到一次干净的 1001 going away)。用户后退返回时页面从 BFCache 原样恢复,终端 WS 早已死亡,Luna 只能按异常关闭报 1006。
  2. 后台标签页自动冻结:Chromium 在内存/能耗压力下冻结后台标签页,JS 全部暂停,Luna 的应用层 keepalive 停止,连接随后被中间层(NLB/反代空闲超时)掐断,用户切回标签页时同样看到 1006。

修复内容

在 web 镜像组装层加两层防护:

  1. luna-bfcache.conf + includes/common.conf:用 map $http_accept $luna_cache_control 仅对文档导航响应(Accept 含 text/html)返回 Cache-Control: no-store,使 Luna 页面不再具备进入 BFCache 的资格;带内容哈希的 JS/CSS 等子资源 Accept 不含 text/html,浏览器缓存行为完全不变(add_header 对空值不发送头)。
  2. utils/luna-terminal-session-guard.js(由 install-luna-terminal-guard.sh 在构建期注入 Luna 各 HTML 入口,<head> 内同步加载于应用启动之前):包装原生 WebSocket 构造器,仅为 /koko/ws/terminal 连接在 Web Locks 上持有一把 shared 锁(连接 CONNECTING 阶段即申请、close 即释放,多终端/多标签共享模式互不排队)。Chromium 的后台冻结策略将持有 Web Lock 的页面列为不可冻结,后台标签页因此持续保活,不再被中间层空闲超时回收。

脚本特性:文件名带内容哈希(升级不读旧缓存);不改协议、不碰终端数据、不读取凭据;不支持 Web Locks 的浏览器自动退化为原行为并输出 console 警告;暴露 window.__jmsTerminalSessionGuard(supported/activeSockets/heldLocks/failures)便于线上验证。

验证

已在基于 v5.0.0-ce 镜像体系的生产环境部署验证:

  1. 终端页同标签页导航离开/后退往返,不再复现 1006(服务端不再出现页面冻结排空产生的会话关闭);
  2. 终端标签页后台放置 10 分钟以上返回,会话与进程保持原样,无弹窗、无重登(heldLocks > 0);
  3. 打开多个终端关闭其一,其余连接的锁不受影响;全部关闭后 activeSockets:0、heldLocks:0;
  4. Accept: text/html 的文档请求响应头为 no-store,JS/CSS 子资源响应头不变;
  5. utils/ 附带锁生命周期的 node 单元测试(7 用例通过);nginx 配置通过 nginx -t 校验。

兼容性

  • 仅影响 /luna/ 的 HTML 文档响应(新增一个响应头)与 Luna HTML 入口(注入一个静态 JS);
  • 不影响 lina(/ui/)与其它静态资源的缓存;
  • 旧浏览器(无 Web Locks)保持原有行为。

参考

…d freezing

Two browser behaviors kill open terminal websockets while the server side
stays perfectly healthy, and Luna then reports a misleading
'abnormal closure (1006): no Koko end notice' dialog when the user returns:

1. Back-Forward Cache: Chrome/Edge may freeze a page holding websockets on
   same-tab navigation and drain all of its websockets (the server only sees
   a clean 1001 going away). Restoring the page from BFCache resurrects a
   page whose terminal socket is already dead.

2. Background tab freezing: Chromium freezes background tabs under memory /
   energy pressure, which stops Luna's application-level keepalive pings;
   intermediate idle timeouts (NLB, reverse proxies) then cut the
   connection.

Fix both at the web image assembly layer:

- Serve Cache-Control: no-store for Luna *document navigations* only, by
  mapping on $http_accept, so such pages are no longer eligible for BFCache.
  Hashed static assets keep their immutable browser cache.

- Install a tiny guard script into Luna's HTML entries (before the app
  boots) that holds a shared Web Lock per open /koko/ws/terminal websocket.
  Chromium's background freezing policy exempts pages holding Web Locks, so
  a terminal tab parked in the background keeps pinging instead of being
  frozen and cut by idle timeouts.

The guard is plain synchronous JS: it wraps the WebSocket constructor, does
not touch protocols, terminal data or credentials, and degrades to the
original behavior (with a console warning) where Web Locks are unavailable.
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