ask-web-fetch 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ask/web_fetch/backends/attached_browser.rb +53 -7
- data/lib/ask/web_fetch/http.rb +13 -6
- data/lib/ask/web_fetch/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed798fddd5578a39814ea41425937d4e539827dae6c66a8e9bc1621007f5dcda
|
|
4
|
+
data.tar.gz: 698add682f0ce4f056acfee23130748bb6d359acb8004a07efc6b862dcc462da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0aa82e97aef13e08d736024d4927a96a72a554ec1cda21292d501884787ad82e756c51522a179ebfd81bdbcd5baadef6965254957613044e088e511accdfb4f1
|
|
7
|
+
data.tar.gz: e5d46eef13701008e7c4557675d372b6c4776f38f19b75be4559943b675def5d9370006cb9ef10416a7b70f6a3897f54b7c14f4afff409afd590dee0fa068ac7
|
|
@@ -50,6 +50,9 @@ module Ask
|
|
|
50
50
|
@session = @client.session(
|
|
51
51
|
@client.command('Target.attachToTarget', targetId: @target_id, flatten: true)['sessionId']
|
|
52
52
|
)
|
|
53
|
+
# Subscribed BEFORE any navigation so the idle wait sees the
|
|
54
|
+
# page's real load traffic (lazy SPAs render in waves).
|
|
55
|
+
@network = Network.new(self)
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
# Navigates and does not return until the document has actually
|
|
@@ -65,7 +68,6 @@ module Ask
|
|
|
65
68
|
|
|
66
69
|
wait_for_load
|
|
67
70
|
end
|
|
68
|
-
|
|
69
71
|
def title
|
|
70
72
|
evaluate('document.title')
|
|
71
73
|
end
|
|
@@ -78,11 +80,11 @@ module Ask
|
|
|
78
80
|
evaluate('document.documentElement.outerHTML') || ''
|
|
79
81
|
end
|
|
80
82
|
|
|
81
|
-
# Minimal network facade for the Browser backend
|
|
82
|
-
#
|
|
83
|
-
#
|
|
83
|
+
# Minimal network facade for the Browser backend: status from the
|
|
84
|
+
# Navigation Timing API, idle from CDP Network events (subscribed
|
|
85
|
+
# in the constructor, before navigation).
|
|
84
86
|
def network
|
|
85
|
-
@network
|
|
87
|
+
@network
|
|
86
88
|
end
|
|
87
89
|
|
|
88
90
|
def close
|
|
@@ -122,10 +124,22 @@ module Ask
|
|
|
122
124
|
nil
|
|
123
125
|
end
|
|
124
126
|
|
|
125
|
-
#
|
|
127
|
+
# Real network-idle detection for the attached browser, via CDP's
|
|
128
|
+
# Network domain events. The launched-browser path gets idle
|
|
129
|
+
# waiting from Ferrum's network; the attached path previously
|
|
130
|
+
# skipped it entirely (a no-op), which read lazy-loading SPAs too
|
|
131
|
+
# early — reddit's post list renders in waves, and the fetch saw
|
|
132
|
+
# only the first 2.6k of an 8k+ body. Tracks requestStarted /
|
|
133
|
+
# requestFinished events on the page session and waits until the
|
|
134
|
+
# network has been quiet for +duration+, capped at +timeout+.
|
|
126
135
|
class Network
|
|
127
136
|
def initialize(page)
|
|
128
137
|
@page = page
|
|
138
|
+
@session = page.instance_variable_get(:@session)
|
|
139
|
+
@in_flight = 0
|
|
140
|
+
@quiet_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
141
|
+
@mutex = Mutex.new
|
|
142
|
+
subscribe
|
|
129
143
|
end
|
|
130
144
|
|
|
131
145
|
def status
|
|
@@ -137,7 +151,39 @@ module Ask
|
|
|
137
151
|
0
|
|
138
152
|
end
|
|
139
153
|
|
|
140
|
-
def wait_for_idle(
|
|
154
|
+
def wait_for_idle(duration:, timeout:)
|
|
155
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
156
|
+
loop do
|
|
157
|
+
quiet = @mutex.synchronize { @in_flight.zero? }
|
|
158
|
+
if quiet && Process.clock_gettime(Process::CLOCK_MONOTONIC) - @quiet_since >= duration
|
|
159
|
+
return
|
|
160
|
+
end
|
|
161
|
+
raise Ferrum::TimeoutError, "network did not go idle within #{timeout}s" if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
|
|
162
|
+
|
|
163
|
+
sleep 0.1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
# CDP events arrive on the session's message loop; Ferrum's
|
|
170
|
+
# session dispatches them to subscribed handlers. Track the
|
|
171
|
+
# request lifecycle so "quiet" means no request has been in
|
|
172
|
+
# flight for the duration — the signal lazy SPAs settle on.
|
|
173
|
+
# Defensive: a minimal/mock session without event support
|
|
174
|
+
# (some test doubles) just skips subscription — the load-wait
|
|
175
|
+
# in go_to is still the floor.
|
|
176
|
+
def subscribe
|
|
177
|
+
return unless @session.respond_to?(:on)
|
|
178
|
+
|
|
179
|
+
@session.on('Network.requestWillBeSent') { @mutex.synchronize { @in_flight += 1 } }
|
|
180
|
+
@session.on('Network.responseReceived') { @mutex.synchronize { @quiet_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) } }
|
|
181
|
+
@session.on('Network.loadingFinished') { @mutex.synchronize { @in_flight -= 1 if @in_flight.positive? } }
|
|
182
|
+
@session.on('Network.loadingFailed') { @mutex.synchronize { @in_flight -= 1 if @in_flight.positive? } }
|
|
183
|
+
@session.command('Network.enable')
|
|
184
|
+
rescue Ferrum::Error
|
|
185
|
+
# Some attached browsers reject Network.enable (unlikely);
|
|
186
|
+
# fall back to the load-wait already done in go_to.
|
|
141
187
|
nil
|
|
142
188
|
end
|
|
143
189
|
end
|
data/lib/ask/web_fetch/http.rb
CHANGED
|
@@ -48,17 +48,24 @@ module Ask
|
|
|
48
48
|
|
|
49
49
|
# One pooled session per thread — httpx sessions are not thread-safe,
|
|
50
50
|
# and a crawl worker thread reusing its session across every page it
|
|
51
|
-
# fetches is what
|
|
52
|
-
# themselves after keep-alive timeout, so nothing
|
|
51
|
+
# fetches is what keeps timeouts and retries configured once.
|
|
52
|
+
# Sessions idle-close themselves after keep-alive timeout, so nothing
|
|
53
|
+
# to reap.
|
|
53
54
|
def self.session
|
|
54
55
|
Thread.current[SESSION_KEY] ||= build_session
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
def self.build_session
|
|
58
|
-
# :persistent
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
|
|
59
|
+
# Deliberately NOT :persistent: in httpx 1.8.1 that plugin wedges
|
|
60
|
+
# inside the selector loop when a session that already holds a
|
|
61
|
+
# pooled connection opens one to a NEW host — the operation
|
|
62
|
+
# timeout never fires and the fetch hangs forever (reproduced in
|
|
63
|
+
# plain Ruby: example.com then nytimes.com on one session).
|
|
64
|
+
# Without it httpx opens a fresh connection per host, which for a
|
|
65
|
+
# crawler hitting mostly-distinct hosts costs one TLS handshake
|
|
66
|
+
# per page and never hangs. Retries are explicit so the transient
|
|
67
|
+
# backoff that :persistent loaded internally is kept.
|
|
68
|
+
HTTPX.plugin(:retries).with(
|
|
62
69
|
timeout: {
|
|
63
70
|
connect_timeout: CONNECT_TIMEOUT,
|
|
64
71
|
read_timeout: READ_TIMEOUT,
|