Class: Selenium::WebDriver::DevTools::NetworkInterceptor Private
Relationships & Source Files | |
Inherits: | Object |
Defined in: | rb/lib/selenium/webdriver/devtools/network_interceptor.rb |
Overview
Wraps the network request/response interception, providing thread-safety guarantees and handling special cases such as browser canceling requests midst interception.
You should not be using this class directly, use Driver#intercept
instead.
Constant Summary
-
CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE =
CDP fails to get body on certain responses (301) and raises: “Can only get response body on requests captured after headers received.”
'-32000'
-
INVALID_INTERCEPTION_ID_ERROR_CODE =
CDP fails to operate with intercepted requests. Typical reason is browser cancelling intercepted requests/responses.
'-32602'
Class Method Summary
- .new(devtools) ⇒ NetworkInterceptor constructor Internal use only
Instance Attribute Summary
Instance Method Summary
- #intercept(&block) Internal use only
- #cancelled?(network_id) ⇒ Boolean private Internal use only
-
#cancelled_requests
private
Internal use only
Ensure usage of cancelled_requests is thread-safe via synchronization!
- #continue_request(id) (also: #continue_response) private Internal use only
-
#continue_response(id)
private
Alias for #continue_request.
- #fetch_response_body(id) private Internal use only
- #intercept_request(id, params, &block) private Internal use only
- #intercept_response(id, params) {|mutable| ... } private Internal use only
- #mutate_request(request) private Internal use only
- #mutate_response(response) private Internal use only
-
#pending_response_requests
private
Internal use only
We should be thread-safe to use the hash without synchronization because its keys are interception job identifiers and they should be unique within a devtools session.
- #request_paused(data, &block) private Internal use only
-
#response?(params) ⇒ Boolean
private
Internal use only
The presence of any of these fields indicate we’re at the response stage.
- #track_cancelled_request(data) private Internal use only
- #with_cancellable_request(network_id) private Internal use only
Constructor Details
.new(devtools) ⇒ NetworkInterceptor
Instance Attribute Details
#devtools (rw, private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 57
attr_accessor :devtools, :lock
#lock (rw, private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 57
attr_accessor :devtools, :lock
Instance Method Details
#cancelled?(network_id) ⇒ Boolean
(private)
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 167
def cancelled?(network_id) lock.synchronize { !!cancelled_requests.delete(network_id) } end
#cancelled_requests (private)
Ensure usage of cancelled_requests is thread-safe via synchronization!
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 67
def cancelled_requests @cancelled_requests ||= [] end
#continue_request(id) (private) Also known as: #continue_response
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 127
def continue_request(id) devtools.fetch.continue_request(request_id: id) end
#continue_response(id) (private)
Alias for #continue_request.
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 130
alias continue_response continue_request
#fetch_response_body(id) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 155
def fetch_response_body(id) devtools.fetch.get_response_body(request_id: id).dig('result', 'body') rescue Error::WebDriverError => e raise unless e. .start_with?(CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE) end
#intercept(&block)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 46
def intercept(&block) devtools.network.on(:loading_failed) { |params| track_cancelled_request(params) } devtools.fetch.on(:request_paused) { |params| request_paused(params, &block) } devtools.network.set_cache_disabled(cache_disabled: true) devtools.network.enable devtools.fetch.enable(patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}]) end
#intercept_request(id, params, &block) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 97
def intercept_request(id, params, &block) original = DevTools::Request.from(id, params) mutable = DevTools::Request.from(id, params) block.call(mutable) do |&continue| pending_response_requests[id] = continue if original == mutable continue_request(original.id) else mutate_request(mutable) end end end
#intercept_response(id, params) {|mutable| ... } (private)
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 112
def intercept_response(id, params) return continue_response(id) unless block_given? body = fetch_response_body(id) original = DevTools::Response.from(id, body, params) mutable = DevTools::Response.from(id, body, params) yield mutable if original == mutable continue_response(id) else mutate_response(mutable) end end
#mutate_request(request) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 132
def mutate_request(request) devtools.fetch.continue_request( request_id: request.id, url: request.url, method: request.method, post_data: (Base64.strict_encode64(request.post_data) if request.post_data), headers: request.headers.map do |k, v| {name: k, value: v} end ) end
#mutate_response(response) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 144
def mutate_response(response) devtools.fetch.fulfill_request( request_id: response.id, body: (Base64.strict_encode64(response.body) if response.body), response_code: response.code, response_headers: response.headers.map do |k, v| {name: k, value: v} end ) end
#pending_response_requests (private)
We should be thread-safe to use the hash without synchronization because its keys are interception job identifiers and they should be unique within a devtools session.
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 62
def pending_response_requests @pending_response_requests ||= {} end
#request_paused(data, &block) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 77
def request_paused(data, &block) id = data['requestId'] network_id = data['networkId'] with_cancellable_request(network_id) do if response?(data) block = pending_response_requests.delete(id) intercept_response(id, data, &block) else intercept_request(id, data, &block) end end end
#response?(params) ⇒ Boolean
(private)
The presence of any of these fields indicate we’re at the response stage.
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 93
def response?(params) params.key?('responseStatusCode') || params.key?('responseErrorReason') end
#track_cancelled_request(data) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 71
def track_cancelled_request(data) return unless data['canceled'] lock.synchronize { cancelled_requests << data['requestId'] } end
#with_cancellable_request(network_id) (private)
[ GitHub ]# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 161
def with_cancellable_request(network_id) yield rescue Error::WebDriverError => e raise if e. .start_with?(INVALID_INTERCEPTION_ID_ERROR_CODE) && !cancelled?(network_id) end