Skip to content

Commit

Permalink
Handle pending connections losing context on frame navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
francisbeaudoin committed Oct 21, 2023
1 parent 020bd25 commit e3c86ed
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 8 deletions.
36 changes: 32 additions & 4 deletions lib/ferrum/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ def idle?(connections = 0)
end

def total_connections
@traffic.size
exchange_connections.count
end

def finished_connections
@traffic.count(&:finished?)
exchange_connections.count(&:finished?)
end

def pending_connections
Expand Down Expand Up @@ -378,7 +378,21 @@ def subscribe_request_will_be_sent

exchange.request = request

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

# When the main frame navigates Chrome doesn't send `Network.loadingFailed`
# for pending async requests. Therefore, we mark pending connections as unknown since
# they are not relevant to the current navigation.
def mark_pending_exchanges_as_unknown(navigation_exchange)
@traffic.each do |exchange|
break if exchange.id == navigation_exchange.id

exchange.unknown = true if exchange.pending?
end
end

Expand All @@ -395,7 +409,10 @@ def subscribe_response_received

def subscribe_loading_finished
@page.on("Network.loadingFinished") do |params|
response = select(params["requestId"]).last&.response
exchange = select(params["requestId"]).last
exchange.unknown = false

response = exchange&.response

if response
response.loaded = true
Expand Down Expand Up @@ -477,5 +494,16 @@ def blacklist?
def whitelist?
Array(@whitelist).any?
end

def exchange_connections
@traffic.select { |exchange| exchange_connection?(exchange) }
end

def exchange_connection?(exchange)
return false unless @page.frames.any? { |f| f.id == exchange.request&.frame_id }

return false unless exchange.request&.frame_id == @exchange.request&.frame_id
return exchange.request&.loader_id == @exchange.request&.loader_id
end
end
end
14 changes: 11 additions & 3 deletions lib/ferrum/network/exchange.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class Exchange
# @return [Error, nil]
attr_accessor :error

# Determines if the network exchange is unknown due to
# a lost of its context
#
# @return Boolean
attr_accessor :unknown

#
# Initializes the network exchange.
#
Expand All @@ -40,6 +46,7 @@ def initialize(page, id)
@page = page
@intercepted_request = nil
@request = @response = @error = nil
@unknown = false
end

#
Expand Down Expand Up @@ -74,12 +81,12 @@ def blocked?

#
# Determines if the request was blocked, a response was returned, or if an
# error occurred.
# error occurred or the exchange is unknown and cannot be inferred.
#
# @return [Boolean]
#
def finished?
blocked? || response&.loaded? || !error.nil?
blocked? || response&.loaded? || !error.nil? || unknown
end

#
Expand Down Expand Up @@ -147,7 +154,8 @@ def inspect
"@intercepted_request=#{@intercepted_request.inspect} " \
"@request=#{@request.inspect} " \
"@response=#{@response.inspect} " \
"@error=#{@error.inspect}>"
"@error=#{@error.inspect}>" \
"@unknown=#{@unknown.inspect}>"
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions lib/ferrum/network/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ def frame_id
@params["frameId"]
end

#
# The loader ID of the request.
#
# @return [String]
#
def loader_id
@params["loaderId"]
end

#
# The request timestamp.
#
Expand Down
6 changes: 5 additions & 1 deletion lib/ferrum/page/frames.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,16 @@ def subscribe_execution_context_destroyed
execution_id = params["executionContextId"]
frame = frame_by(execution_id: execution_id)
frame&.execution_id = nil
frame&.state = :stopped_loading
end
end

def subscribe_execution_contexts_cleared
on("Runtime.executionContextsCleared") do
@frames.each_value { |f| f.execution_id = nil }
@frames.each_value do |f|
f.execution_id = nil
f.state = :stopped_loading
end
end
end

Expand Down

0 comments on commit e3c86ed

Please sign in to comment.