Skip to content

fix: chunk oversized extension WebSocket responses - #19

Merged
xhwSkhizein merged 2 commits into
mainfrom
fix/extension-response-chunking
Jul 24, 2026
Merged

fix: chunk oversized extension WebSocket responses#19
xhwSkhizein merged 2 commits into
mainfrom
fix/extension-response-chunking

Conversation

@xhwSkhizein

@xhwSkhizein xhwSkhizein commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Frame oversized extension response payloads as response-chunk messages so large page HTML (for example WeChat articles) no longer trips the default 1 MiB WebSocket limit and disconnects mid-read.
  • Reassemble chunked responses in the daemon extension session, and fall back to Playwright on read when an older extension still disconnects with message too big / Extension disconnected.
  • Bump the Browser CLI Bridge extension to 0.1.1 and document the failure mode in AGENTS.md.

Test plan

  • node --test browser-cli-extension/tests/response_framing.test.js
  • uv run pytest tests/unit/test_extension_transport.py tests/unit/test_daemon_browser_service.py
  • scripts/lint.sh
  • scripts/guard.sh
  • Reload the unpacked extension from browser-cli-extension/, run browser-cli reload, then browser-cli read https://mp.weixin.qq.com/s/TSxWeY7yCZ3HiV_zNXon-g

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Large browser-extension responses are now split into smaller messages and reliably reassembled.
    • Improved handling of incomplete or invalid response chunks.
    • Page reads now fall back to Playwright when the extension disconnects or exceeds message-size limits.
  • Documentation

    • Added troubleshooting guidance for oversized extension responses.
  • Chores

    • Updated the browser extension version to 0.1.1.

Large HTML captures were sent as a single response frame and tripped the
default 1 MiB WebSocket limit, disconnecting the extension mid-read.
Frame big responses as response-chunk messages and fall back to Playwright
when an older extension still disconnects during read.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@xhwSkhizein, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39bc12c7-2980-4ac4-ad38-a44770172b61

📥 Commits

Reviewing files that changed from the base of the PR and between f43a81b and 6d4b41b.

📒 Files selected for processing (5)
  • src/browser_cli/extension/__init__.py
  • src/browser_cli/extension/protocol.py
  • src/browser_cli/extension/session.py
  • tests/unit/test_daemon_browser_service.py
  • tests/unit/test_extension_transport.py
📝 Walkthrough

Walkthrough

The extension now chunks oversized WebSocket responses, reassembles them in ExtensionSession, clears incomplete buffers, and expands read_page fallback detection for extension disconnect and message-size errors. Tests cover framing, reassembly, incomplete chunks, and Playwright fallback.

Changes

Extension response reliability

Layer / File(s) Summary
Response framing and transmission
browser-cli-extension/src/protocol.js, browser-cli-extension/src/background/*, browser-cli-extension/manifest.json, browser-cli-extension/tests/response_framing.test.js, src/browser_cli/extension/protocol.py, src/browser_cli/extension/__init__.py
Defines the 256 KiB response chunk size, sends oversized responses as ordered response-chunk frames, updates the extension version, and tests framing behavior and socket validation.
Response chunk reassembly
src/browser_cli/extension/session.py, tests/unit/test_extension_transport.py
Buffers chunks by request ID, reassembles complete JSON responses, rejects incomplete data, and clears buffers during success or session failure.
Read fallback and troubleshooting
src/browser_cli/daemon/browser_service.py, tests/unit/test_daemon_browser_service.py, AGENTS.md
Recognizes additional extension read failures for Playwright fallback and documents oversized-response troubleshooting. במ

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Extension
  participant WebSocket
  participant ExtensionHub
  participant ExtensionSession
  participant BrowserService
  participant Playwright
  Extension->>WebSocket: Send response or response-chunk frames
  WebSocket->>ExtensionHub: Deliver response-chunk messages
  ExtensionHub->>ExtensionSession: append_response_chunk(payload)
  ExtensionSession-->>BrowserService: Resolve reassembled response
  BrowserService->>Playwright: Retry read on extension failure
  Playwright-->>BrowserService: Return HTML
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes summary and test plan, but omits most required template sections like Related issue, Type of Change, Checklist, and Architectural Boundaries. Add the missing template sections, especially Related issue, Type of Change, Checklist, Architectural Boundaries, and Screenshots/Output if applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the core change: chunking oversized extension WebSocket responses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/extension-response-chunking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/browser_cli/extension/session.py (1)

129-131: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Clear partial chunk buffers when a request finishes or times out.

A stream that never delivers its final chunk leaves _response_buffers[request.id] retained for the session lifetime after the 30-second request timeout. Remove it in this finally block.

Proposed fix
         finally:
             self._pending.pop(request.id, None)
             self._artifact_events.pop(request.id, None)
+            self._response_buffers.pop(request.id, None)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/browser_cli/extension/session.py` around lines 129 - 131, Update the
finally block in the request-handling method to also remove request.id from
_response_buffers, alongside _pending and _artifact_events. Ensure cleanup
occurs for both completed and timed-out requests without affecting other
response buffers.
🧹 Nitpick comments (1)
tests/unit/test_daemon_browser_service.py (1)

642-668: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover all newly added fallback patterns.

This test exercises only Extension disconnected.; regressions in message too big or exceeds limit of would go unnoticed. Parameterize the case over all three new messages and assert the selected message is propagated in driver_fallback["error"].

Based on the added fallback patterns in this PR.

Suggested parameterization
+@pytest.mark.parametrize(
+    "extension_error",
+    ("Extension disconnected.", "message too big", "exceeds limit of 1048576"),
+)
 def test_read_page_falls_back_to_playwright_when_extension_disconnects_mid_read(
     _patched_browser_service: _FakeExtensionHub,
+    extension_error: str,
 ) -> None:
...
-            raise RuntimeError("Extension disconnected.")
+            raise RuntimeError(extension_error)
...
-        assert "Extension disconnected" in payload["driver_fallback"]["error"]
+        assert extension_error in payload["driver_fallback"]["error"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_daemon_browser_service.py` around lines 642 - 668,
Parameterize
test_read_page_falls_back_to_playwright_when_extension_disconnects_mid_read over
the three supported extension failure messages: “Extension disconnected.”,
“message too big”, and “exceeds limit of”. Raise the selected message from
_extension_capture_html and assert that the same message is propagated in
driver_fallback["error"], while preserving the existing fallback and
active-driver assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/browser_cli/extension/session.py`:
- Around line 169-186: Validate that the reassembled response’s response.id
matches the chunk envelope request_id before calling resolve_response(response).
On mismatch, remove the buffered request and set its future to
OperationFailedError with error_code "EXTENSION_RESPONSE_CHUNK_INVALID",
preserving the existing invalid-response handling and preventing resolution of
the wrong request.
- Around line 68-79: The chunk handler around int(...) must reject malformed,
negative, and excessively large indexes before buffering so conversion errors
cannot terminate the session. Define and apply a bounded non-negative index
validation before updating the chunk collection, then update
ExtensionResponseChunks.assemble to verify continuity using min/max and len
without constructing range(index + 1). Add regressions covering malformed input
and an oversized index.

---

Outside diff comments:
In `@src/browser_cli/extension/session.py`:
- Around line 129-131: Update the finally block in the request-handling method
to also remove request.id from _response_buffers, alongside _pending and
_artifact_events. Ensure cleanup occurs for both completed and timed-out
requests without affecting other response buffers.

---

Nitpick comments:
In `@tests/unit/test_daemon_browser_service.py`:
- Around line 642-668: Parameterize
test_read_page_falls_back_to_playwright_when_extension_disconnects_mid_read over
the three supported extension failure messages: “Extension disconnected.”,
“message too big”, and “exceeds limit of”. Raise the selected message from
_extension_capture_html and assert that the same message is propagated in
driver_fallback["error"], while preserving the existing fallback and
active-driver assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8eb8cfae-6fb5-4344-8458-6dc99fee34b5

📥 Commits

Reviewing files that changed from the base of the PR and between 2b14082 and f43a81b.

📒 Files selected for processing (12)
  • AGENTS.md
  • browser-cli-extension/manifest.json
  • browser-cli-extension/src/background.js
  • browser-cli-extension/src/background/response_framing.js
  • browser-cli-extension/src/protocol.js
  • browser-cli-extension/tests/response_framing.test.js
  • src/browser_cli/daemon/browser_service.py
  • src/browser_cli/extension/__init__.py
  • src/browser_cli/extension/protocol.py
  • src/browser_cli/extension/session.py
  • tests/unit/test_daemon_browser_service.py
  • tests/unit/test_extension_transport.py

Comment thread src/browser_cli/extension/session.py
Comment thread src/browser_cli/extension/session.py
Validate chunk indexes and reassembled response ids before resolving
pending requests, clean response buffers on request completion, and
cover the new failure paths in transport and read-fallback tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@xhwSkhizein
xhwSkhizein merged commit 18aa4e1 into main Jul 24, 2026
8 checks passed
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