Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
摘要
将 WAF 挑战恢复从「绑定 UI Host 的一次性尝试」重构为「由 Client 持有、可跨 UI 生命周期存活的长期会话」。Host 挂载/卸载/前后台切换不再杀死恢复流程,而是销毁当前浏览器尝试、暂停挑战预算,等 Host 回来后再开新尝试继续。同时引入 Host 注册身份与 generation 机制,彻底隔离旧实例的迟到事件。
背景与动机
旧实现的
WafChallengeCoordinator只有两个全局布尔量(hostMounted/foreground),且一旦失去前台立即failActive(FOREGROUND_REQUIRED),导致:onDispose事件可覆盖新实例状态(cookie 竞态的根源之一);核心改动
1. Host 注册与身份隔离(
WafChallengeCoordinator.kt)WafHostRegistration不透明令牌:每次 UI 挂载registerHost()领取一个;setHostAvailability(registration, mounted, isForeground)/unregisterHost(registration)按身份操作,带generation计数器;2. 两层模型:
ActiveFlight(会话)+ActiveAttempt(尝试)CompletableDeferred结果(并发请求共享)、当前 attempt、runner 协程。存活到出结果或 client 关闭;3. 单消费者事件循环
CookieSubmitted/AttemptFailed/HostChanged统一走Channel,由 flight runner 的select循环消费;attempt id 永不重用 + generation 只增不减,陈旧事件天然失配被丢弃。4. 预算与超时
hostWaitTimeoutMillis(默认 30s,校验 100ms–10min):每一段连续 Host 中断有独立上限;verifier用剩余预算单独约束;验证阶段与 UI 无关。5. 关闭语义
close()幂等(@Volatile closed),任意阶段以CANCELLED完成 flight 并取消 runner;关闭后resolve()立即返回CANCELLED不挂起。6. 其他文件
WafRecoveryModels.kthostWaitTimeoutMillis;WafHostState新增WaitingForHost,Verifying携带hostYamiboWafChallengeHost.ktverifying.host === registration;对外签名不变WafChallengeCoordinatorTest.ktREADME.mdCHANGELOG.md/build.gradle.kts新流程
flowchart TD A[检测到 WAF 挑战<br/>resolve] --> B{enabled?} B -- 否 --> C[FOREGROUND_REQUIRED] B -- 是 --> D{closed?} D -- 是 --> E[CANCELLED] D -- 否 --> F{锁内二次检查 closed<br/>已有活跃 Flight?} F -- 是 --> J[加入现有 Flight<br/>await 同一 result] F -- 否 --> G[创建 Flight<br/>WaitingForHost<br/>启动 runner + drainPendingEvents] J --> RES[WafResolution] G --> W1[awaitUsableHost<br/>等待可用 Host] W1 -- 等到 Host --> V[创建 Attempt<br/>绑定 host 令牌 + generation<br/>Verifying] W1 -- 30s 到期无 Host --> C V --> L[awaitAttemptOutcome<br/>select 事件流<br/>截止 = 剩余挑战预算] L -- HostLost --> P[销毁 Attempt<br/>暂停预算<br/>WaitingForHost] P --> W1 L -- CookieSubmitted --> N[checkingCookie<br/>销毁 WebView<br/>写入 NOX] N --> O[剩余预算内 verifier] O -- 通过 --> OK[Verified] O -- 失败/超时 --> F2[VERIFICATION_FAILED / TIMED_OUT<br/>清除 NOX] L -- AttemptFailed --> F3[对应 disposition] L -- 预算耗尽 --> T2[TIMED_OUT] L -- close 取消 --> E2[CANCELLED] OK --> RES F2 --> RES F3 --> RES T2 --> RES E2 --> RES场景行为对照
FOREGROUND_REQUIREDFOREGROUND_REQUIRED,回来需刷新close()CANCELLED影响与兼容性
YamiboWafChallengeHost对外签名不变,调用方零改动;hostWaitTimeoutMillis或enabled = false;WafRecoveryConfig为公开 data class,新增构造参数:源码兼容、二进制需重新编译;测试覆盖
新增/改写测试验证:无 Host 先等后成功、Host 等待超时、前后台切换保 flight + 预算暂停(400ms 预算 vs 500ms 中断)、陈旧 detach 不能复活旧 Host、陈旧 attempt 回调被忽略、快速前后台切换开新 attempt、飞行中中断超时、verifier 取消归为失败、关闭取消等待中 flight、关闭后 resolve 立即返回。
已知风险(可跟进项)
delay时序断言,慢速 CI 上有偶发抖动可能;