class Ferrum::Network

Constants

AUTHORIZE_TYPE
CLEAR_TYPE
RESOURCE_TYPES

Attributes

traffic[R]

Public Class Methods

new(page) click to toggle source
# File lib/ferrum/network.rb, line 20
def initialize(page)
  @page = page
  @traffic = []
  @exchange = nil
end

Public Instance Methods

authorize(user:, password:, type: :server, &block) click to toggle source
# File lib/ferrum/network.rb, line 86
def authorize(user:, password:, type: :server, &block)
  unless AUTHORIZE_TYPE.include?(type)
    raise ArgumentError, ":type should be in #{AUTHORIZE_TYPE}"
  end

  if !block_given? && !@page.subscribed?("Fetch.requestPaused")
    raise ArgumentError, "Block is missing, call `authorize(...) { |r| r.continue } or subscribe to `on(:request)` events before calling it"
  end

  @authorized_ids ||= {}
  @authorized_ids[type] ||= []

  intercept

  @page.on(:request, &block)

  @page.on(:auth) do |request, index, total|
    if request.auth_challenge?(type)
      response = authorized_response(@authorized_ids[type],
                                     request.request_id,
                                     user, password)

      @authorized_ids[type] << request.request_id
      request.continue(authChallengeResponse: response)
    elsif index + 1 < total
      next # There are other callbacks that can handle this
    else
      request.abort
    end
  end
end
authorized_response(ids, request_id, username, password) click to toggle source
# File lib/ferrum/network.rb, line 187
def authorized_response(ids, request_id, username, password)
  if ids.include?(request_id)
    { response: "CancelAuth" }
  elsif username && password
    { response: "ProvideCredentials",
      username: username,
      password: password }
  else
    { response: "CancelAuth" }
  end
end
build_exchange(id) click to toggle source
# File lib/ferrum/network.rb, line 203
def build_exchange(id)
  Network::Exchange.new(@page, id).tap { |e| @traffic << e }
end
clear(type) click to toggle source
# File lib/ferrum/network.rb, line 63
def clear(type)
  unless CLEAR_TYPE.include?(type)
    raise ArgumentError, ":type should be in #{CLEAR_TYPE}"
  end

  if type == :traffic
    @traffic.clear
  else
    @page.command("Network.clearBrowserCache")
  end

  true
end
finished_connections() click to toggle source
# File lib/ferrum/network.rb, line 43
def finished_connections
  @traffic.count(&:finished?)
end
idle?(connections = 0) click to toggle source
# File lib/ferrum/network.rb, line 35
def idle?(connections = 0)
  pending_connections <= connections
end
intercept(pattern: "*", resource_type: nil) click to toggle source
# File lib/ferrum/network.rb, line 77
def intercept(pattern: "*", resource_type: nil)
  pattern = { urlPattern: pattern }
  if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)
    pattern[:resourceType] = resource_type
  end

  @page.command("Fetch.enable", handleAuthRequests: true, patterns: [pattern])
end
pending_connections() click to toggle source
# File lib/ferrum/network.rb, line 47
def pending_connections
  total_connections - finished_connections
end
request() click to toggle source
# File lib/ferrum/network.rb, line 51
def request
  @exchange&.request
end
response() click to toggle source
# File lib/ferrum/network.rb, line 55
def response
  @exchange&.response
end
select(request_id) click to toggle source
# File lib/ferrum/network.rb, line 199
def select(request_id)
  @traffic.select { |e| e.id == request_id }
end
status() click to toggle source
# File lib/ferrum/network.rb, line 59
def status
  response&.status
end
subscribe() click to toggle source
# File lib/ferrum/network.rb, line 118
def subscribe
  @page.on("Network.requestWillBeSent") do |params|
    request = Network::Request.new(params)

    # We can build exchange in two places, here on the event or when request
    # is interrupted. So we have to be careful when to create new one. We
    # create new exchange only if there's no with such id or there's but
    # it's filled with request which means this one is new but has response
    # for a redirect. So we assign response from the params to previous
    # exchange and build new exchange to assign this request to it.
    exchange = select(request.id).last
    exchange = build_exchange(request.id) unless exchange&.blank?

    # On redirects Chrome doesn't change `requestId` and there's no
    # `Network.responseReceived` event for such request. If there's already
    # exchange object with this id then we got redirected and params has
    # `redirectResponse` key which contains the response.
    if params["redirectResponse"]
      previous_exchange = select(request.id)[-2]
      response = Network::Response.new(@page, params)
      previous_exchange.response = response
    end

    exchange.request = request

    if exchange.navigation_request?(@page.main_frame.id)
      @exchange = exchange
    end
  end

  @page.on("Network.responseReceived") do |params|
    if exchange = select(params["requestId"]).last
      response = Network::Response.new(@page, params)
      exchange.response = response
    end
  end

  @page.on("Network.loadingFinished") do |params|
    exchange = select(params["requestId"]).last
    if exchange && exchange.response
      exchange.response.body_size = params["encodedDataLength"]
    end
  end

  @page.on("Network.loadingFailed") do |params|
    exchange = select(params["requestId"]).last
    exchange.error ||= Network::Error.new

    exchange.error.id = params["requestId"]
    exchange.error.type = params["type"]
    exchange.error.error_text = params["errorText"]
    exchange.error.monotonic_time = params["timestamp"]
    exchange.error.canceled = params["canceled"]
  end

  @page.on("Log.entryAdded") do |params|
    entry = params["entry"] || {}
    if entry["source"] == "network" && entry["level"] == "error"
      exchange = select(entry["networkRequestId"]).last
      exchange.error ||= Network::Error.new

      exchange.error.id = entry["networkRequestId"]
      exchange.error.url = entry["url"]
      exchange.error.description = entry["text"]
      exchange.error.timestamp = entry["timestamp"]
    end
  end
end
total_connections() click to toggle source
# File lib/ferrum/network.rb, line 39
def total_connections
  @traffic.size
end
wait_for_idle(connections: 0, duration: 0.05, timeout: @page.browser.timeout) click to toggle source
# File lib/ferrum/network.rb, line 26
def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.browser.timeout)
  start = Ferrum.monotonic_time

  until idle?(connections)
    raise TimeoutError if Ferrum.timeout?(start, timeout)
    sleep(duration)
  end
end