Consolidate the browser task runtime around the callback path, add safer artifact opening for Zhihu exports, and cover the new service/browser flows with focused tests and supporting docs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.9 KiB
WS Browser Welcome Frame Compatibility Design
Background
Manual smoke verification after the ws-native browser backend auth replacement showed that real sgBrowser sends a banner text frame immediately after the websocket connection is established:
Welcome! You are client #1
The current ws-native path treats the first received text frame as a protocol status frame. In src/browser/ws_backend.rs, WsBrowserBackend::invoke(...) reads one text frame and immediately parses it as an integer status code. That works for the existing deterministic tests, but it fails against the real browser because the first frame is a human-readable welcome banner rather than 0 or another numeric status.
This means the auth replacement is working — the old invalid hmac seed: session key must not be empty error no longer appears — but real smoke still fails on protocol parsing.
Goal
Make the ws service path tolerate exactly one initial welcome/banner text frame from the real browser websocket, without weakening the general ws protocol semantics.
Non-goals
This change must not:
- Relax parsing of arbitrary non-protocol text frames
- Change
WsBrowserBackendinto a browser-specific parser for banners - Affect the legacy pipe path
- Add retry loops or broader reconnection logic
- Change callback handling semantics
Chosen approach
Handle the welcome banner only in ServiceBrowserWsClient.
Why this layer
ServiceBrowserWsClient is already the real-browser adapter used only by the ws service path in src/service/server.rs. The welcome frame is a quirk of the real browser endpoint rather than a property of the shared ws protocol abstraction. Keeping the compatibility behavior in the service-side client preserves the stricter semantics of WsBrowserBackend for all other callers and test doubles.
Behavioral rules
- Only the first received text frame after establishing a browser websocket connection may be treated as a welcome/banner candidate.
- If that first text frame matches the real banner shape (currently observed as
Welcome! You are client #1), the client discards it and continues waiting for the actual protocol frame. - The welcome skip is one-time only per connection, not per request. Because
ServiceBrowserWsClientholds a persistent socket, this state must survive multipleinvoke(...)calls on the same underlying websocket. - After the welcome skip:
- status frames must still be numeric strings
- callback frames must still match the existing JSON-array callback protocol
- any other malformed frame remains a protocol error
- Timeout, close/reset, and connect-failure semantics remain unchanged.
Matching strategy
Use a narrow string check in ServiceBrowserWsClient for a welcome/banner frame:
- starts with
Welcome! You are client #
This is intentionally strict. We are adapting one known real-browser behavior, not introducing a generic “ignore garbage text” mode.
Tests
New red tests
Add focused unit tests under src/service/server.rs tests:
-
Positive case:
- fake websocket server sends:
Welcome! You are client #10
- then
WsBrowserBackend.invoke(Action::Navigate, ...)succeeds
- fake websocket server sends:
-
Negative case:
- fake websocket server sends a different first text frame that does not match the known welcome prefix
- assert the call still fails as a protocol error rather than silently skipping the frame
The positive test must fail before the implementation change and pass after it. The negative test guards the non-goal that we are not introducing a generic “ignore arbitrary text” mode.
Regression coverage
Re-run:
cargo test service::server::tests -- --nocapturecargo test --test browser_ws_backend_test -- --nocapturecargo test --test service_task_flow_test -- --nocapture
If those pass, re-run the earlier mixed ws+pipe sweep to confirm no unexpected regression escaped the targeted checks.
Risks and controls
Risk: swallowing a legitimate protocol error
Control:
- only allow the one-time skip on the first received text frame
- only skip frames matching the known welcome prefix
Risk: broadening behavior beyond service ws path
Control:
- keep the change entirely inside
ServiceBrowserWsClient - do not modify
WsBrowserBackendparsing rules
Acceptance criteria
The fix is complete only if all of the following are true:
- The positive welcome-banner test fails before the change and passes after it.
- The negative malformed-first-frame test proves that non-matching first text frames still fail as protocol errors.
- Real ws service smoke no longer fails with
invalid browser status frame: Welcome! You are client #1when using the configured real sgBrowser endpoint. - Existing ws backend tests remain green.
- Existing service task-flow regression remains green.
- Pipe behavior remains unchanged, verified by the mixed ws+pipe regression suite.