html2rss 0.27.1 → 0.27.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 898e609f0c0ae81285356d4c1793d89c8bd76cd3615f51aa28ed5bc352d1bce6
4
- data.tar.gz: e384fccef00524f53c733959298f8f363a4400b8142fcb7b5484e06423e478ca
3
+ metadata.gz: '09b4a391aa87101ab6612c07c0c434b9ebc6198fc3bed8c14c2f8b90bd61bc6a'
4
+ data.tar.gz: d7fac305311719a6b131be4e89fe78919aef1b45c763a22c471b24583b41d7fa
5
5
  SHA512:
6
- metadata.gz: 70e479939781e18fa0206659fade0bc12f1aa272932e2e479ff07c5ea46c55e825c2d428bc5b51a62095a01bcc638615e346f895b179062b8f90347227e572c4
7
- data.tar.gz: ada830ba830f0f0637122fb4f8d0f0803f292fed2afa1024cdaf29eee46c89c638a7d958459380e9b3d1009a9f1ed46571d553e1fe164d58be13156cff74e6b7
6
+ metadata.gz: d5fb2cdfc2b95e64de5ac42ffbe1479c15fe63b01f4b922d721d3099ffdd418c9293a1672217838612d5cf4f7bbc2e153cf83ffbcc85e8f30bf8a1a17bb20a79
7
+ data.tar.gz: a9f51aa30dfdf8e3e222ed1a49216e857cd056f473408273ad3781e862d743f657a67473fe691423e1fb1252fbe2935ee6eb1cef009d3b2662d03b66f7bdb90c
@@ -7,7 +7,7 @@ module Html2rss
7
7
  ##
8
8
  # Maps html2rss request/response handling to botasaurus-scrape-api OpenAPI 2.0
9
9
  # +ScrapeRequest+ / +ScrapeSuccess+ / +ScrapeError+ (sibling +openapi.yaml+).
10
- class BotasaurusContract # rubocop:disable Metrics/ClassLength -- Success/Error envelopes stay with the owner
10
+ class BotasaurusContract
11
11
  # Closed set from OpenAPI ExecutionMode.
12
12
  EXECUTION_MODES = %w[auto request browser].freeze
13
13
  # Closed set from OpenAPI NavigationMode.
@@ -25,12 +25,19 @@ module Html2rss
25
25
  # OpenAPI WindowSize required keys (positive integers).
26
26
  WINDOW_SIZE_PROPERTIES = %i[width height].freeze
27
27
 
28
- # Published wait clamp floor (`[1, 20]` in OpenAPI description).
28
+ # Faraday POST /scrape cap mirroring botasaurus-scrape-api total scrape wall (`SCRAPE_TIMEOUT_SECONDS`).
29
+ SCRAPE_TIMEOUT_SECONDS = Integer(ENV.fetch('BOTASAURUS_SCRAPE_TIMEOUT_SECONDS', 45))
30
+ # Post-boot navigate/wait budget mirrored from scrape-api (`SCRAPE_WORK_TIMEOUT_SECONDS`).
31
+ SCRAPE_WORK_TIMEOUT_SECONDS = Integer(ENV.fetch('BOTASAURUS_SCRAPE_WORK_TIMEOUT_SECONDS', 30))
32
+ # Seconds added to the scrape total for client transport overhead.
33
+ TRANSPORT_BUFFER_SECONDS = 2
34
+
35
+ # Published wait clamp floor (`[1, SCRAPE_WORK_TIMEOUT_SECONDS]` in OpenAPI description).
29
36
  MIN_WAIT_TIMEOUT_SECONDS = 1
30
37
  # Published wait clamp ceiling; YAML values above this are rejected.
31
- MAX_WAIT_TIMEOUT_SECONDS = 20
38
+ MAX_WAIT_TIMEOUT_SECONDS = SCRAPE_WORK_TIMEOUT_SECONDS
32
39
  # OpenAPI wait_timeout_seconds default (omitted from the client payload).
33
- DEFAULT_WAIT_TIMEOUT_SECONDS = 15
40
+ DEFAULT_WAIT_TIMEOUT_SECONDS = [15, SCRAPE_WORK_TIMEOUT_SECONDS].min
34
41
 
35
42
  # OpenAPI max_retries maximum (default 2 is applied upstream when omitted).
36
43
  MAX_RETRIES = 3
@@ -40,12 +47,6 @@ module Html2rss
40
47
  # Allowlisted ChallengeSignal keys nested under diagnostics.challenge.
41
48
  CHALLENGE_KEYS = %w[blocked detected marker].freeze
42
49
 
43
- # Remaining seconds at or below which Botasaurus retries are disabled.
44
- TIGHT_BUDGET_SECONDS = 12
45
-
46
- # Seconds reserved from remaining budget before setting wait_timeout_seconds.
47
- BUDGET_WAIT_RESERVE_SECONDS = 2
48
-
49
50
  ##
50
51
  # Parsed OpenAPI ScrapeSuccess envelope (HTTP 200).
51
52
  class Success
@@ -246,7 +247,6 @@ module Html2rss
246
247
  # @param url [Html2rss::Url] canonical URL to scrape
247
248
  # @param headers [Hash] request headers from context
248
249
  # @param options [Hash] validated request.botasaurus options
249
- # @param remaining_timeout_seconds [Numeric, nil] shared request budget remainder for clamps
250
250
  # @option options [String] :execution_mode
251
251
  # @option options [String] :navigation_mode
252
252
  # @option options [Integer] :max_retries
@@ -264,19 +264,19 @@ module Html2rss
264
264
  # @option options [String] :lang
265
265
  # @option options [Hash] :cookies
266
266
  # @option options [Hash] :headers
267
- def initialize(url:, headers: {}, options: {}, remaining_timeout_seconds: nil)
267
+ def initialize(url:, headers: {}, options: {})
268
268
  @url = url
269
269
  @headers = headers
270
270
  @options = options
271
- @remaining_timeout_seconds = remaining_timeout_seconds
272
271
  end
273
272
 
274
- # @return [Hash] payload for POST /scrape (explicit options plus budget clamp-downs)
273
+ # @return [Hash] payload for POST /scrape (explicit options plus wait cap)
275
274
  def request_payload
276
275
  payload = { url: url.to_s }.merge(filtered_options)
277
276
  forwarded_headers = merged_headers
278
277
  payload[:headers] = forwarded_headers if forwarded_headers&.any?
279
- clamp_for_budget(payload)
278
+ cap_wait_timeout!(payload)
279
+ payload
280
280
  end
281
281
 
282
282
  # @param transport_response [Faraday::Response] upstream HTTP response
@@ -291,7 +291,7 @@ module Html2rss
291
291
 
292
292
  private
293
293
 
294
- attr_reader :url, :headers, :options, :remaining_timeout_seconds
294
+ attr_reader :url, :headers, :options
295
295
 
296
296
  def json_object(body)
297
297
  payload = JSON.parse(body)
@@ -315,36 +315,6 @@ module Html2rss
315
315
  end
316
316
  end
317
317
 
318
- def clamp_for_budget(payload)
319
- remaining = remaining_timeout_seconds
320
- clamped = payload.dup
321
- unless remaining.nil?
322
- apply_tight_retries!(clamped, remaining)
323
- apply_budget_wait!(clamped, remaining)
324
- end
325
- cap_wait_timeout!(clamped)
326
- clamped
327
- end
328
-
329
- def apply_tight_retries!(payload, remaining)
330
- payload[:max_retries] = 0 if remaining <= TIGHT_BUDGET_SECONDS
331
- end
332
-
333
- def apply_budget_wait!(payload, remaining)
334
- budget_wait = budget_wait_seconds(remaining)
335
- configured = payload[:wait_timeout_seconds]
336
- if configured
337
- payload[:wait_timeout_seconds] = [configured, budget_wait].min
338
- elsif budget_wait < DEFAULT_WAIT_TIMEOUT_SECONDS
339
- payload[:wait_timeout_seconds] = budget_wait
340
- end
341
- end
342
-
343
- def budget_wait_seconds(remaining)
344
- reserved = [MIN_WAIT_TIMEOUT_SECONDS, (remaining - BUDGET_WAIT_RESERVE_SECONDS).floor].max
345
- [reserved, MAX_WAIT_TIMEOUT_SECONDS].min
346
- end
347
-
348
318
  def cap_wait_timeout!(payload)
349
319
  wait = payload[:wait_timeout_seconds]
350
320
  return unless wait
@@ -2,12 +2,20 @@
2
2
 
3
3
  require 'faraday'
4
4
  require 'json'
5
+ require 'securerandom'
5
6
 
6
7
  module Html2rss
7
8
  class RequestService
8
9
  ##
9
10
  # Strategy to delegate fetching to a Botasaurus scrape API.
10
11
  class BotasaurusStrategy < Strategy
12
+ # Content-Type negotiation for the scrape API transport hop.
13
+ TRANSPORT_ACCEPT = 'application/json'
14
+ # Disable compressed bodies so Faraday returns raw JSON without implicit decoding surprises.
15
+ TRANSPORT_ENCODING = 'identity'
16
+ # Correlates each POST /scrape with botasaurus-scrape-api request logs.
17
+ REQUEST_ID_HEADER = 'X-Request-Id'
18
+
11
19
  private
12
20
 
13
21
  def fetch
@@ -18,7 +26,9 @@ module Html2rss
18
26
  end
19
27
 
20
28
  def post_scrape_request
21
- transport_response = client.post('/scrape', JSON.generate(contract.request_payload), content_type_header)
29
+ request_id = SecureRandom.uuid
30
+ Log.debug("#{self.class}: POST /scrape #{REQUEST_ID_HEADER}=#{request_id}")
31
+ transport_response = client.post('/scrape', JSON.generate(contract.request_payload), post_headers(request_id))
22
32
  contract.parse_response(transport_response)
23
33
  end
24
34
 
@@ -64,13 +74,28 @@ module Html2rss
64
74
  @contract ||= BotasaurusContract.new(
65
75
  url: ctx.url,
66
76
  headers: ctx.headers,
67
- options: ctx.request.fetch(:botasaurus, {}),
68
- remaining_timeout_seconds: attempt_timeout_seconds
77
+ options: ctx.request.fetch(:botasaurus, {})
69
78
  )
70
79
  end
71
80
 
72
81
  def client
73
- @client ||= Faraday.new(url: scraper_base_url.to_s, request: request_options)
82
+ # No :gzip middleware on this client — compression is for remote target fetches only.
83
+ @client ||= Faraday.new(url: scraper_base_url.to_s, headers: client_headers, request: request_options)
84
+ end
85
+
86
+ def client_headers
87
+ {
88
+ 'User-Agent' => Config::RequestHeaders::DEFAULT_USER_AGENT,
89
+ 'Accept' => TRANSPORT_ACCEPT,
90
+ 'Accept-Encoding' => TRANSPORT_ENCODING
91
+ }
92
+ end
93
+
94
+ def post_headers(request_id)
95
+ {
96
+ 'Content-Type' => 'application/json',
97
+ REQUEST_ID_HEADER => request_id
98
+ }
74
99
  end
75
100
 
76
101
  def request_options
@@ -78,13 +103,10 @@ module Html2rss
78
103
  end
79
104
 
80
105
  def attempt_timeout_seconds
81
- @attempt_timeout_seconds ||= ctx.budget.effective_timeout_seconds(
82
- fallback: ctx.policy.total_timeout_seconds
83
- )
84
- end
85
-
86
- def content_type_header
87
- { 'Content-Type' => 'application/json' }
106
+ @attempt_timeout_seconds ||= begin
107
+ budget = ctx.budget.effective_timeout_seconds(fallback: ctx.policy.total_timeout_seconds)
108
+ [budget, BotasaurusContract::SCRAPE_TIMEOUT_SECONDS + BotasaurusContract::TRANSPORT_BUFFER_SECONDS].min
109
+ end
88
110
  end
89
111
 
90
112
  def scraper_base_url
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Html2rss
4
4
  # Current application version.
5
- VERSION = '0.27.1'
5
+ VERSION = '0.27.2'
6
6
  public_constant :VERSION
7
7
  end
@@ -698,7 +698,7 @@
698
698
  "type": "null"
699
699
  },
700
700
  "minimum": 1,
701
- "maximum": 20
701
+ "maximum": 30
702
702
  },
703
703
  "scroll": {
704
704
  "type": "boolean",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html2rss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.1
4
+ version: 0.27.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gil Desmarais
@@ -486,7 +486,7 @@ licenses:
486
486
  - MIT
487
487
  metadata:
488
488
  allowed_push_host: https://rubygems.org
489
- changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.27.1
489
+ changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.27.2
490
490
  rubygems_mfa_required: 'true'
491
491
  rdoc_options: []
492
492
  require_paths: