123456789_123456789_123456789_123456789_123456789_

Class: Selenium::WebDriver::DevTools::NetworkInterceptor Private

Do not use. This class is for internal use only.
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

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(devtools) ⇒ NetworkInterceptor

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/devtools/network_interceptor.rb', line 41

def initialize(devtools)
  @devtools = devtools
  @lock = Mutex.new
end

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)

[ GitHub ]

  
# 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!

[ GitHub ]

  
# 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.

[ GitHub ]

  
# 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.message.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)

Yields:

  • (mutable)
[ GitHub ]

  
# 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.

[ GitHub ]

  
# 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.

[ GitHub ]

  
# 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.message.start_with?(INVALID_INTERCEPTION_ID_ERROR_CODE) && !cancelled?(network_id)
end