From a76beb75bd36fb10e6de38a49c0da5441edd5f35 Mon Sep 17 00:00:00 2001 From: Chandan sharma Date: Wed, 1 Apr 2026 16:39:39 +0300 Subject: [PATCH] [rb] fix silent hang on large WebSocket frame - Sets `WebSocket.should_raise = true` to prevent `websocket-ruby` from returning `nil` frames on failure, which previously caused infinite read loops and silent hangs. - Increases the default `WebSocket.max_frame_size` to 100MB (from 20MB) to handle large CDP payloads such as Data URLs and screenshots. - Updates `WebSocketConnection#send_cmd` to fail immediately if the listener thread dies, avoiding a 30-second timeout wait when the connection is already broken. - Changes WebSocket error logging from `debug` to `warn` to make failures visible to users by default. - Adds `track_cancelled` option to `Driver#intercept` (default: `true`), enabling users to opt-out of the `Network` domain to reduce bandwidth from chatty events. --- .../driver_extensions/has_network_interception.rb | 4 ++-- .../webdriver/common/websocket_connection.rb | 14 ++++++++++++-- .../webdriver/devtools/network_interceptor.rb | 6 +++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb b/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb index e93ae0b3e6e76..0dbaf663dabed 100644 --- a/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +++ b/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb @@ -59,7 +59,7 @@ module HasNetworkInterception # @yieldparam [Proc] continue block which proceeds with the request and optionally yields response # - def intercept(&block) + def intercept(**options, &block) if browser == :firefox WebDriver.logger.deprecate( 'Driver#intercept on Firefox', @@ -68,7 +68,7 @@ def intercept(&block) ) end @interceptor ||= DevTools::NetworkInterceptor.new(devtools) - @interceptor.intercept(&block) + @interceptor.intercept(**options, &block) end end # HasNetworkInterception end # DriverExtensions diff --git a/rb/lib/selenium/webdriver/common/websocket_connection.rb b/rb/lib/selenium/webdriver/common/websocket_connection.rb index 0774a46918009..67231aa746b47 100644 --- a/rb/lib/selenium/webdriver/common/websocket_connection.rb +++ b/rb/lib/selenium/webdriver/common/websocket_connection.rb @@ -19,6 +19,9 @@ require 'websocket' +WebSocket.should_raise = true +WebSocket.max_frame_size = 100 * 1024 * 1024 + module Selenium module WebDriver class WebSocketConnection @@ -109,7 +112,10 @@ def send_cmd(**payload) raise e, "WebSocket is closed (#{e.class}: #{e.message})" end - wait.until { @messages_mtx.synchronize { messages.delete(id) } } + wait.until do + @messages_mtx.synchronize { messages.delete(id) } || + (raise Error::WebDriverError, "WebSocket listener thread is dead" unless @socket_thread&.alive?) + end end private @@ -144,7 +150,11 @@ def attach_socket_listener end end rescue *CONNECTION_ERRORS, WebSocket::Error => e - WebDriver.logger.debug "WebSocket listener closed: #{e.class}: #{e.message}", id: :ws + if e.is_a?(WebSocket::Error) + WebDriver.logger.warn "WebSocket listener closed due to error: #{e.class}: #{e.message}", id: :ws + else + WebDriver.logger.debug "WebSocket listener closed: #{e.class}: #{e.message}", id: :ws + end end end diff --git a/rb/lib/selenium/webdriver/devtools/network_interceptor.rb b/rb/lib/selenium/webdriver/devtools/network_interceptor.rb index 95e3236a63fae..9aeed5920215e 100644 --- a/rb/lib/selenium/webdriver/devtools/network_interceptor.rb +++ b/rb/lib/selenium/webdriver/devtools/network_interceptor.rb @@ -43,12 +43,12 @@ def initialize(devtools) @lock = Mutex.new end - def intercept(&block) - devtools.network.on(:loading_failed) { |params| track_cancelled_request(params) } + def intercept(track_cancelled: true, &block) + devtools.network.on(:loading_failed) { |params| track_cancelled_request(params) } if track_cancelled devtools.fetch.on(:request_paused) { |params| request_paused(params, &block) } devtools.network.set_cache_disabled(cache_disabled: true) - devtools.network.enable + devtools.network.enable if track_cancelled devtools.fetch.enable(patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}]) end