[rb] fix silent hang on oversized WebSocket frames#17655
Conversation
websocket-ruby drops frames larger than WebSocket.max_frame_size (20MB default) and keeps returning nil, so the listener spun forever and the process appeared to hang (e.g. large CDP data: URL payloads under request interception). Raise the limit to 100MB for our connections (lazily, and only when the user has not already set a larger value, so non-DevTools users and custom configs are unaffected), and surface an undecodable frame as a logged error that stops the listener instead of hanging silently. Fixes SeleniumHQ#17264
Review Summary by QodoFix silent hang on oversized WebSocket frames
WalkthroughsDescription• Raise WebSocket frame size limit to 100MB for DevTools connections • Surface dropped frames as logged errors instead of silent hangs • Add frame error detection to stop listener gracefully • Include unit tests for frame size and error handling Diagramflowchart LR
A["WebSocket Frame Received"] --> B["Check Frame Size"]
B --> C{"Exceeds Limit?"}
C -->|No| D["Process Frame"]
C -->|Yes| E["frame_dropped? Detects Error"]
E --> F["Log Error Message"]
F --> G["Stop Listener Gracefully"]
D --> H["Continue Processing"]
File Changes1. rb/lib/selenium/webdriver/common/websocket_connection.rb
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
19 rules 1.
|
|
Code review by qodo was updated up to the latest commit 510ee4e |
|
Code review by qodo was updated up to the latest commit c85c861 |
|
Code review by qodo was updated up to the latest commit f23041f |
|
Two complementary fixes: apply_frame_size_limit — raises WebSocket.max_frame_size to 100 MB inside WebSocketConnection#initialize, not at load time, and only when the current limit is lower. Non-devtools users or anyone who already configured a larger value are unaffected. websocket-ruby exposes max_frame_size as a global-only setting; there is no per-instance option. frame_dropped? — checks incoming_frame.error? (per-instance flag, not should_raise) after each read. When set, logs an actionable error and closes the connection cleanly instead of leaving a dead listener spinning on a stale buffer. should_raise was left untouched — using incoming_frame.error? is the per-instance equivalent and avoids affecting unrelated connections. Tested the fix locally. The two-part approach is correct: apply_frame_size_limit in initialize prevents the hang for the common case (DevTools heap snapshots, large screenshots, etc.) without affecting users who already set a higher limit Unit tests cover both paths: the limit bump (raises when lower, leaves alone when higher) and the frame_dropped? shutdown. Builds cleanly on #17286. |
💥 What does this PR do?
The Ruby devtools/bidi websocket listener can hang when it gets a frame bigger than WebSocket.max_frame_size (websocket-ruby defaults to 20MB).
Websocket-ruby raises TooLong internally but rescues it unless you set should_raise, so incoming_frame.next just returns nil, the oversized bytes stay buffered, and the loop spins forever at ~100% cpu.
builds on #17286, thanks @Chandan25sharma. left the track_cancelled part out, that's separate.
🔧 Implementation Notes
the bump happens in WebSocketConnection#initialize, not at load time, and only when the current limit is lower, so non-devtools users or anyone who already set a bigger value aren't affected. websocket-ruby only
exposes max_frame_size globally so there's no per-instance option. didn't touch should_raise, detection uses the per-instance incoming_frame.error? instead.
🤖 AI assistance
🔄 Types of changes