Class: Capybara::Lightpanda::Network

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/lightpanda/network.rb

Constant Summary collapse

TRAFFIC_LIMIT =

Tracking is always on (create_page enables it), so the buffer needs the same unbounded-growth cap as Browser’s console ring buffer —one very long session would otherwise grow @traffic indefinitely.

1_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(browser) ⇒ Network

Returns a new instance of Network.



18
19
20
21
22
23
24
25
26
# File 'lib/capybara/lightpanda/network.rb', line 18

def initialize(browser)
  @browser = browser
  @traffic = []
  @traffic_mutex = Mutex.new
  @enabled = false
  @request_handler = @response_handler = nil
  @last_navigation_response = nil
  @document_request_id = nil
end

Instance Attribute Details

#browserObject (readonly)

Returns the value of attribute browser.



11
12
13
# File 'lib/capybara/lightpanda/network.rb', line 11

def browser
  @browser
end

#last_navigation_responseObject (readonly)

Status/headers of the last top-level document navigation; nil before the first navigation completes. Backs Browser#status_code / #response_headers. Captured by #subscribe’s handlers.



16
17
18
# File 'lib/capybara/lightpanda/network.rb', line 16

def last_navigation_response
  @last_navigation_response
end

Instance Method Details

#add_headers(headers) ⇒ Object



102
103
104
105
106
# File 'lib/capybara/lightpanda/network.rb', line 102

def add_headers(headers)
  enable
  @extra_headers = (@extra_headers || {}).merge(headers)
  browser.page_command("Network.setExtraHTTPHeaders", headers: @extra_headers)
end

#clearObject



71
72
73
# File 'lib/capybara/lightpanda/network.rb', line 71

def clear
  @traffic_mutex.synchronize { @traffic.clear }
end

#clear_headersObject



108
109
110
111
112
# File 'lib/capybara/lightpanda/network.rb', line 108

def clear_headers
  enable
  @extra_headers = {}
  browser.page_command("Network.setExtraHTTPHeaders", headers: {})
end

#disableObject

Caveat: disabling the domain also stops the navigation-response capture, so Browser#status_code / #response_headers freeze at their last values until the next #enable.



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/capybara/lightpanda/network.rb', line 54

def disable
  return unless @enabled

  # Tell the browser to stop emitting BEFORE unsubscribing locally:
  # otherwise an in-flight Network.responseReceived can race past the
  # already-removed handler and leave a `response: nil` entry in
  # @traffic for the matching request — which then trips
  # wait_for_idle's pending count on a future call.
  browser.command("Network.disable")
  unsubscribe
  @enabled = false
end

#enableObject

The domain toggle is connection-scoped (browser.command), while setExtraHTTPHeaders is session-scoped (browser.page_command) — see #headers=. Browser#create_page calls this, so tracking (traffic AND the navigation-response capture) is on for every page.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/capybara/lightpanda/network.rb', line 32

def enable
  return if @enabled

  # Subscribe BEFORE flipping the wire toggle (mirror image of
  # #disable's ordering): events can't be emitted while the domain is
  # off, so this order can never miss one. If the command fails
  # (Lightpanda can block commands mid-navigation), roll the handlers
  # back — orphaned duplicates would double-count every request and
  # wedge pending_connections above zero for the session.
  subscribe
  begin
    browser.command("Network.enable")
  rescue StandardError
    unsubscribe
    raise
  end
  @enabled = true
end

#extra_headersObject

Headers applied via headers= / add_headers. Backs Driver#headers.



91
# File 'lib/capybara/lightpanda/network.rb', line 91

def extra_headers = @extra_headers || {}

#headers=(headers) ⇒ Object

Setting extra headers also lazily enables the Network domain. Without this, headers were silently ignored until the caller separately ran ‘network.enable` (or `wait_for_network_idle`). Cuprite/Ferrum parity.



96
97
98
99
100
# File 'lib/capybara/lightpanda/network.rb', line 96

def headers=(headers)
  enable
  @extra_headers = headers
  browser.page_command("Network.setExtraHTTPHeaders", headers: headers)
end

#idle?(connections = 0) ⇒ Boolean

True when no more than ‘connections` requests are in-flight.

Returns:

  • (Boolean)


121
122
123
# File 'lib/capybara/lightpanda/network.rb', line 121

def idle?(connections = 0)
  pending_connections <= connections
end

#pending_connectionsObject

Count of in-flight requests (those with no response yet recorded). Cheap predicate-friendly accessor (ferrum parity).



116
117
118
# File 'lib/capybara/lightpanda/network.rb', line 116

def pending_connections
  @traffic_mutex.synchronize { @traffic.count { |t| t[:response].nil? } }
end

#resetObject

Wipe local state without sending Network.disable. Called by Browser#reset after Target.disposeBrowserContext, which destroys the subscriptions and the Network domain along with the context —leaving @enabled true would silently no-op the next #enable. Also unsubscribes locally so we don’t rely on the caller having cleared the Subscriber first.



81
82
83
84
85
86
87
88
# File 'lib/capybara/lightpanda/network.rb', line 81

def reset
  unsubscribe
  clear
  @enabled = false
  @extra_headers = nil # the fresh context never received setExtraHTTPHeaders
  @last_navigation_response = nil
  @document_request_id = nil
end

#trafficObject



67
68
69
# File 'lib/capybara/lightpanda/network.rb', line 67

def traffic
  @traffic_mutex.synchronize { @traffic.dup }
end

#wait_for_idle(timeout: 5, connections: 0) ⇒ Object



125
126
127
128
129
# File 'lib/capybara/lightpanda/network.rb', line 125

def wait_for_idle(timeout: 5, connections: 0)
  wait_for_idle!(timeout: timeout, connections: connections)
rescue TimeoutError
  false
end

#wait_for_idle!(timeout: 5, connections: 0) ⇒ Object

Raising variant of #wait_for_idle (ferrum parity). Returns true on success, raises TimeoutError on timeout so callers that treat the idle wait as a precondition don’t have to remember to check a bool.



134
135
136
137
138
139
140
141
142
# File 'lib/capybara/lightpanda/network.rb', line 134

def wait_for_idle!(timeout: 5, connections: 0) # rubocop:disable Naming/PredicateMethod
  Utils::Wait.until(
    timeout: timeout,
    interval: 0.1,
    message: "Network did not become idle within #{timeout}s " \
             "(pending=#{pending_connections}, allowed=#{connections})"
  ) { idle?(connections) }
  true
end