html2rss 0.24.0 → 0.26.0
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/README.md +76 -2
- data/html2rss.gemspec +4 -1
- data/lib/html2rss/auto_source/cleanup.rb +62 -36
- data/lib/html2rss/auto_source/scraper/html.rb +68 -145
- data/lib/html2rss/auto_source/scraper/json_state/article_normalizer.rb +116 -0
- data/lib/html2rss/auto_source/scraper/json_state/candidate_detector.rb +63 -0
- data/lib/html2rss/auto_source/scraper/json_state/document_scanner.rb +179 -0
- data/lib/html2rss/auto_source/scraper/json_state/value_finder.rb +55 -0
- data/lib/html2rss/auto_source/scraper/json_state.rb +31 -401
- data/lib/html2rss/auto_source/scraper/schema/item_list.rb +2 -14
- data/lib/html2rss/auto_source/scraper/semantic_html/entry_deduplicator.rb +35 -30
- data/lib/html2rss/auto_source/scraper/semantic_html.rb +40 -120
- data/lib/html2rss/auto_source/scraper/sitemap/parser.rb +161 -0
- data/lib/html2rss/auto_source/scraper/sitemap.rb +10 -7
- data/lib/html2rss/auto_source/scraper/wordpress_api.rb +6 -2
- data/lib/html2rss/auto_source/scraper/xhr_articles.rb +69 -0
- data/lib/html2rss/auto_source/scraper.rb +109 -53
- data/lib/html2rss/auto_source/segment.rb +29 -0
- data/lib/html2rss/auto_source/segmenter/cluster.rb +163 -0
- data/lib/html2rss/auto_source/segmenter/list.rb +103 -0
- data/lib/html2rss/auto_source/segmenter/primary_link.rb +80 -0
- data/lib/html2rss/auto_source/segmenter/semantic.rb +104 -0
- data/lib/html2rss/auto_source/segmenter.rb +95 -0
- data/lib/html2rss/auto_source.rb +79 -21
- data/lib/html2rss/capture.rb +315 -0
- data/lib/html2rss/cli.rb +72 -18
- data/lib/html2rss/config/auto_source_contract.rb +5 -1
- data/lib/html2rss/config/request_controls.rb +33 -0
- data/lib/html2rss/config/schema.rb +67 -26
- data/lib/html2rss/config/selectors_validator.rb +60 -21
- data/lib/html2rss/config/validator.rb +33 -35
- data/lib/html2rss/config.rb +6 -2
- data/lib/html2rss/error.rb +15 -3
- data/lib/html2rss/feed_builder/rss.rb +17 -5
- data/lib/html2rss/feed_pipeline/auto_fallback.rb +16 -6
- data/lib/html2rss/feed_pipeline/runtime_policy.rb +3 -43
- data/lib/html2rss/feed_pipeline.rb +11 -0
- data/lib/html2rss/feed_result.rb +1 -1
- data/lib/html2rss/hash_util.rb +17 -0
- data/lib/html2rss/html/article_extractor/category_extractor.rb +10 -36
- data/lib/html2rss/html/article_extractor/date_extractor.rb +2 -7
- data/lib/html2rss/html/article_extractor/enclosure_extractor.rb +5 -50
- data/lib/html2rss/html/article_extractor/image_extractor.rb +5 -16
- data/lib/html2rss/html/article_rules/category.rb +55 -0
- data/lib/html2rss/html/article_rules/date.rb +25 -0
- data/lib/html2rss/html/article_rules/enclosure.rb +72 -0
- data/lib/html2rss/html/article_rules/image.rb +109 -0
- data/lib/html2rss/html/article_rules.rb +10 -0
- data/lib/html2rss/html/rendering/audio_renderer.rb +2 -12
- data/lib/html2rss/html/rendering/escaped_attributes.rb +27 -0
- data/lib/html2rss/html/rendering/image_renderer.rb +2 -12
- data/lib/html2rss/html/rendering/pdf_renderer.rb +2 -8
- data/lib/html2rss/html/rendering/video_renderer.rb +2 -12
- data/lib/html2rss/html/sst_article_extractor.rb +308 -0
- data/lib/html2rss/link_destination/destination_facts.rb +40 -0
- data/lib/html2rss/link_destination/noise_policy.rb +68 -0
- data/lib/html2rss/link_destination/path_classifier.rb +208 -0
- data/lib/html2rss/link_destination/text_classifier.rb +64 -0
- data/lib/html2rss/link_destination.rb +8 -0
- data/lib/html2rss/mcp/server.rb +577 -0
- data/lib/html2rss/mcp.rb +21 -0
- data/lib/html2rss/request_service/blocked_surface.rb +24 -1
- data/lib/html2rss/request_service/botasaurus_contract.rb +89 -9
- data/lib/html2rss/request_service/botasaurus_strategy.rb +4 -2
- data/lib/html2rss/request_service/budget.rb +7 -35
- data/lib/html2rss/request_service/context.rb +0 -6
- data/lib/html2rss/request_service/faraday_strategy.rb +87 -3
- data/lib/html2rss/request_service/network_guard.rb +5 -3
- data/lib/html2rss/request_service/policy.rb +1 -1
- data/lib/html2rss/request_service/response.rb +15 -1
- data/lib/html2rss/request_service/strategy.rb +1 -2
- data/lib/html2rss/request_service.rb +4 -9
- data/lib/html2rss/scoring/cluster_scorer.rb +87 -0
- data/lib/html2rss/scoring/container_assessor.rb +77 -0
- data/lib/html2rss/scoring/engine.rb +145 -0
- data/lib/html2rss/scoring/link_resolver.rb +84 -0
- data/lib/html2rss/scoring/observation.rb +53 -0
- data/lib/html2rss/scoring/ranked_segment.rb +33 -0
- data/lib/html2rss/scoring/score.rb +11 -0
- data/lib/html2rss/scoring.rb +8 -0
- data/lib/html2rss/selectors/extractors/attribute.rb +15 -0
- data/lib/html2rss/selectors/extractors/href.rb +12 -0
- data/lib/html2rss/selectors/extractors/html.rb +12 -0
- data/lib/html2rss/selectors/extractors/static.rb +14 -0
- data/lib/html2rss/selectors/extractors/text.rb +11 -0
- data/lib/html2rss/selectors/post_processors/gsub.rb +18 -0
- data/lib/html2rss/selectors/post_processors/html_to_markdown.rb +11 -0
- data/lib/html2rss/selectors/post_processors/markdown_to_html.rb +12 -0
- data/lib/html2rss/selectors/post_processors/parse_time.rb +11 -0
- data/lib/html2rss/selectors/post_processors/parse_uri.rb +11 -0
- data/lib/html2rss/selectors/post_processors/sanitize_html.rb +23 -1
- data/lib/html2rss/selectors/post_processors/substring.rb +21 -0
- data/lib/html2rss/selectors/post_processors/template.rb +20 -0
- data/lib/html2rss/selectors/schema_doc.rb +99 -0
- data/lib/html2rss/selectors.rb +0 -20
- data/lib/html2rss/sst/attrs.rb +91 -0
- data/lib/html2rss/sst/document.rb +23 -0
- data/lib/html2rss/sst/index.rb +112 -0
- data/lib/html2rss/sst/node.rb +147 -0
- data/lib/html2rss/sst/normalizer.rb +171 -0
- data/lib/html2rss/sst/tags.rb +26 -0
- data/lib/html2rss/sst/text.rb +81 -0
- data/lib/html2rss/sst.rb +8 -0
- data/lib/html2rss/url.rb +11 -7
- data/lib/html2rss/version.rb +1 -1
- data/lib/html2rss.rb +57 -26
- data/schema/html2rss-config.schema.json +428 -85
- metadata +93 -27
- data/lib/html2rss/auto_source/discovery/dom_clustering/group_scorer.rb +0 -80
- data/lib/html2rss/auto_source/discovery/dom_clustering/overlap_resolver.rb +0 -86
- data/lib/html2rss/auto_source/discovery/dom_clustering.rb +0 -119
- data/lib/html2rss/auto_source/discovery/list_candidates.rb +0 -94
- data/lib/html2rss/auto_source/discovery/semantic_anchor_candidates.rb +0 -219
- data/lib/html2rss/auto_source/discovery/semantic_containers.rb +0 -71
- data/lib/html2rss/auto_source/discovery/sitemap.rb +0 -159
- data/lib/html2rss/auto_source/discovery.rb +0 -14
- data/lib/html2rss/auto_source/link_heuristics/anchor_signals.rb +0 -28
- data/lib/html2rss/auto_source/link_heuristics/container_assessor.rb +0 -106
- data/lib/html2rss/auto_source/link_heuristics/container_signals.rb +0 -80
- data/lib/html2rss/auto_source/link_heuristics/destination_facts.rb +0 -42
- data/lib/html2rss/auto_source/link_heuristics/href_extractor.rb +0 -38
- data/lib/html2rss/auto_source/link_heuristics/path_classifier.rb +0 -221
- data/lib/html2rss/auto_source/link_heuristics/text_classifier.rb +0 -66
- data/lib/html2rss/auto_source/link_heuristics.rb +0 -139
- data/lib/html2rss/request_service/browserless_strategy.rb +0 -132
- data/lib/html2rss/request_service/puppet_commander/navigation_guards.rb +0 -148
- data/lib/html2rss/request_service/puppet_commander/preload_runner.rb +0 -86
- data/lib/html2rss/request_service/puppet_commander.rb +0 -95
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Html2rss
|
|
4
|
-
class RequestService
|
|
5
|
-
class PuppetCommander
|
|
6
|
-
##
|
|
7
|
-
# Owns Puppeteer request interception, deferred navigation errors, main-frame
|
|
8
|
-
# filtering, and redirect-chain validation via {Policy}.
|
|
9
|
-
#
|
|
10
|
-
# Does not re-own Policy rules — only calls +validate_*!+ on +ctx.policy+.
|
|
11
|
-
class NavigationGuards
|
|
12
|
-
class << self
|
|
13
|
-
##
|
|
14
|
-
# Resolves the final response URL for Response building and IP validation.
|
|
15
|
-
#
|
|
16
|
-
# @param navigation_response [Puppeteer::HTTPResponse, nil]
|
|
17
|
-
# @param fallback_url [String, Html2rss::Url]
|
|
18
|
-
# @return [Html2rss::Url]
|
|
19
|
-
def response_url(navigation_response, fallback_url)
|
|
20
|
-
raw_url = navigation_response&.url || fallback_url.to_s
|
|
21
|
-
Html2rss::Url.from_absolute(raw_url)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
##
|
|
26
|
-
# @param ctx [Context] request context providing policy and origin
|
|
27
|
-
# @param skip_request_resources [Set<String>] resource types to abort
|
|
28
|
-
def initialize(ctx:, skip_request_resources:)
|
|
29
|
-
@ctx = ctx
|
|
30
|
-
@skip_request_resources = skip_request_resources
|
|
31
|
-
@navigation_error = nil
|
|
32
|
-
@latest_navigation_response = nil
|
|
33
|
-
@main_frame = nil
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
##
|
|
37
|
-
# Captures the main frame and wires request/response interceptors.
|
|
38
|
-
#
|
|
39
|
-
# @param page [Puppeteer::Page] browser page
|
|
40
|
-
# @return [void]
|
|
41
|
-
def install!(page)
|
|
42
|
-
@main_frame = page.main_frame if page.respond_to?(:main_frame)
|
|
43
|
-
page.request_interception = true
|
|
44
|
-
page.on('request') { |request| handle_request(request) }
|
|
45
|
-
page.on('response') { |response| handle_response(response) }
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
##
|
|
49
|
-
# Clears deferred error and latest navigation response before goto.
|
|
50
|
-
#
|
|
51
|
-
# @return [void]
|
|
52
|
-
def begin_navigation!
|
|
53
|
-
@navigation_error = nil
|
|
54
|
-
@latest_navigation_response = nil
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
##
|
|
58
|
-
# Re-raises a deferred navigation error when one was captured.
|
|
59
|
-
#
|
|
60
|
-
# @return [void]
|
|
61
|
-
# @raise [Html2rss::Error] when a navigation request or response validation failed
|
|
62
|
-
def raise_deferred_error!
|
|
63
|
-
raise @navigation_error if @navigation_error
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
##
|
|
67
|
-
# @return [Puppeteer::HTTPResponse, nil] latest main-frame navigation response
|
|
68
|
-
attr_reader :latest_navigation_response
|
|
69
|
-
|
|
70
|
-
##
|
|
71
|
-
# Validates the remote IP of a navigation response via Policy.
|
|
72
|
-
#
|
|
73
|
-
# @param navigation_response [Puppeteer::HTTPResponse, nil]
|
|
74
|
-
# @return [void]
|
|
75
|
-
def validate_final!(navigation_response)
|
|
76
|
-
final_url = self.class.response_url(navigation_response, ctx.url)
|
|
77
|
-
ctx.policy.validate_remote_ip!(ip: remote_ip(navigation_response), url: final_url)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
private
|
|
81
|
-
|
|
82
|
-
attr_reader :ctx, :skip_request_resources, :main_frame
|
|
83
|
-
|
|
84
|
-
def handle_request(request)
|
|
85
|
-
validate_request!(request)
|
|
86
|
-
|
|
87
|
-
skip_request_resources.member?(request.resource_type) ? request.abort : request.continue
|
|
88
|
-
rescue Html2rss::Error => error
|
|
89
|
-
store_navigation_error(error, navigation_request: request.navigation_request?)
|
|
90
|
-
request.abort
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def handle_response(response)
|
|
94
|
-
@latest_navigation_response = response if main_frame_navigation_response?(response)
|
|
95
|
-
validate_final!(response)
|
|
96
|
-
rescue Html2rss::Error => error
|
|
97
|
-
store_navigation_error(error, navigation_request: response.request.navigation_request?)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def validate_request!(request)
|
|
101
|
-
validate_navigation_redirect_chain!(request)
|
|
102
|
-
validate_navigation_target!(request)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def main_frame_navigation_response?(response)
|
|
106
|
-
request = response.request
|
|
107
|
-
return false unless request.navigation_request?
|
|
108
|
-
return true unless request.respond_to?(:frame)
|
|
109
|
-
|
|
110
|
-
frame = request.frame
|
|
111
|
-
return true if frame.nil?
|
|
112
|
-
return frame == main_frame unless main_frame.nil?
|
|
113
|
-
return true unless frame.respond_to?(:parent_frame)
|
|
114
|
-
|
|
115
|
-
frame.parent_frame.nil?
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def remote_ip(navigation_response)
|
|
119
|
-
navigation_response.remote_address&.ip
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def request_chain(request)
|
|
123
|
-
(request.redirect_chain + [request]).map { |entry| request_url(entry) }
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def request_url(request)
|
|
127
|
-
Html2rss::Url.from_absolute(request.url)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def validate_navigation_redirect_chain!(request)
|
|
131
|
-
request_chain(request).each_cons(2) do |from_url, to_url|
|
|
132
|
-
ctx.policy.validate_redirect!(from_url:, to_url:, origin_url: ctx.origin_url, relation: ctx.relation)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def validate_navigation_target!(request)
|
|
137
|
-
ctx.policy.validate_request!(url: request_url(request), origin_url: ctx.origin_url, relation: ctx.relation)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def store_navigation_error(error, navigation_request:)
|
|
141
|
-
return unless navigation_request
|
|
142
|
-
|
|
143
|
-
@navigation_error = error if @navigation_error.nil?
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Html2rss
|
|
4
|
-
class RequestService
|
|
5
|
-
class PuppetCommander
|
|
6
|
-
##
|
|
7
|
-
# Runs Browserless preload choreography: wait, click, and scroll against
|
|
8
|
-
# the interaction budget on {Context#budget}.
|
|
9
|
-
class PreloadRunner
|
|
10
|
-
##
|
|
11
|
-
# @param ctx [Context] request context providing preload config and budget
|
|
12
|
-
def initialize(ctx:)
|
|
13
|
-
@ctx = ctx
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
##
|
|
17
|
-
# Executes configured preload actions on the page, if any.
|
|
18
|
-
#
|
|
19
|
-
# @param page [Puppeteer::Page] browser page after initial navigation
|
|
20
|
-
# @return [void]
|
|
21
|
-
def call(page)
|
|
22
|
-
preload_config = ctx.browserless_preload
|
|
23
|
-
return unless preload_config
|
|
24
|
-
|
|
25
|
-
wait_after(page, preload_config[:wait_after_ms])
|
|
26
|
-
click_selectors(page, preload_config[:click_selectors]) if preload_config[:click_selectors]
|
|
27
|
-
scroll_down(page, preload_config[:scroll_down]) if preload_config[:scroll_down]
|
|
28
|
-
wait_after(page, preload_config[:wait_after_ms])
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
attr_reader :ctx
|
|
34
|
-
|
|
35
|
-
def wait_after(page, timeout_ms)
|
|
36
|
-
return unless timeout_ms
|
|
37
|
-
|
|
38
|
-
ctx.budget.consume_interaction!
|
|
39
|
-
page.wait_for_timeout(timeout_ms)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def click_selectors(page, selectors)
|
|
43
|
-
selectors.each { |selector_config| click_selector(page, selector_config) }
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def scroll_down(page, config)
|
|
47
|
-
iterations = config.fetch(:iterations, 1)
|
|
48
|
-
wait_after_ms = config[:wait_after_ms]
|
|
49
|
-
previous_height = nil
|
|
50
|
-
|
|
51
|
-
iterations.times do
|
|
52
|
-
updated_height = perform_scroll_iteration(page, wait_after_ms, previous_height)
|
|
53
|
-
break unless updated_height
|
|
54
|
-
|
|
55
|
-
previous_height = updated_height
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def click_selector(page, config)
|
|
60
|
-
selector = config.fetch(:selector)
|
|
61
|
-
max_clicks = config.fetch(:max_clicks, 1)
|
|
62
|
-
wait_after_ms = config[:wait_after_ms]
|
|
63
|
-
|
|
64
|
-
max_clicks.times do
|
|
65
|
-
break unless (element = page.query_selector(selector))
|
|
66
|
-
|
|
67
|
-
ctx.budget.consume_interaction!
|
|
68
|
-
element.click
|
|
69
|
-
wait_after(page, wait_after_ms)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def perform_scroll_iteration(page, wait_after_ms, previous_height)
|
|
74
|
-
ctx.budget.consume_interaction!
|
|
75
|
-
page.evaluate('() => window.scrollTo(0, document.body.scrollHeight)')
|
|
76
|
-
wait_after(page, wait_after_ms)
|
|
77
|
-
|
|
78
|
-
current_height = page.evaluate('() => document.body.scrollHeight')
|
|
79
|
-
return if previous_height && current_height <= previous_height
|
|
80
|
-
|
|
81
|
-
current_height
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Html2rss
|
|
4
|
-
class RequestService
|
|
5
|
-
##
|
|
6
|
-
# Commands the Puppeteer Browser to the website and builds the Response.
|
|
7
|
-
#
|
|
8
|
-
# Public interface is {#call}; page setup, navigation, and body collection
|
|
9
|
-
# are private orchestration steps.
|
|
10
|
-
class PuppetCommander
|
|
11
|
-
# Request header names that must not be forwarded to the browser session.
|
|
12
|
-
BROWSER_UNSAFE_HEADERS = %w[
|
|
13
|
-
host connection content-length transfer-encoding
|
|
14
|
-
sec-fetch-dest sec-fetch-mode sec-fetch-site sec-fetch-user
|
|
15
|
-
upgrade-insecure-requests
|
|
16
|
-
].to_set.freeze
|
|
17
|
-
|
|
18
|
-
# @param ctx [Context]
|
|
19
|
-
# @param browser [Puppeteer::Browser]
|
|
20
|
-
# @param skip_request_resources [Set<String>] the resource types not to request
|
|
21
|
-
# @param referer [String] the referer to use for the request
|
|
22
|
-
def initialize(ctx,
|
|
23
|
-
browser,
|
|
24
|
-
skip_request_resources: %w[stylesheet image media font].to_set,
|
|
25
|
-
referer: [ctx.url.scheme, ctx.url.host].join('://'))
|
|
26
|
-
@ctx = ctx
|
|
27
|
-
@browser = browser
|
|
28
|
-
@referer = referer
|
|
29
|
-
@navigation_guards = NavigationGuards.new(ctx:, skip_request_resources:)
|
|
30
|
-
@preload_runner = PreloadRunner.new(ctx:)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
##
|
|
34
|
-
# Visits the request URL and normalizes the page into a response object.
|
|
35
|
-
#
|
|
36
|
-
# @return [Response] rendered page response
|
|
37
|
-
def call
|
|
38
|
-
page = new_page
|
|
39
|
-
navigation_response = navigate_to_destination(page, ctx.url)
|
|
40
|
-
preload_runner.call(page)
|
|
41
|
-
navigation_guards.raise_deferred_error!
|
|
42
|
-
final_navigation_response = navigation_guards.latest_navigation_response || navigation_response
|
|
43
|
-
navigation_guards.validate_final!(final_navigation_response)
|
|
44
|
-
build_response(page, final_navigation_response)
|
|
45
|
-
ensure
|
|
46
|
-
page&.close
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
attr_reader :ctx, :browser, :referer, :navigation_guards, :preload_runner
|
|
52
|
-
|
|
53
|
-
def new_page
|
|
54
|
-
page = browser.new_page
|
|
55
|
-
configure_page(page)
|
|
56
|
-
navigation_guards.install!(page)
|
|
57
|
-
page
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def configure_page(page)
|
|
61
|
-
page.extra_http_headers = browser_headers
|
|
62
|
-
page.default_navigation_timeout = navigation_timeout_ms
|
|
63
|
-
page.default_timeout = navigation_timeout_ms
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def navigate_to_destination(page, url)
|
|
67
|
-
navigation_guards.begin_navigation!
|
|
68
|
-
page.goto(url, wait_until: 'networkidle0', referer:, timeout: navigation_timeout_ms).tap do
|
|
69
|
-
navigation_guards.raise_deferred_error!
|
|
70
|
-
end
|
|
71
|
-
rescue StandardError
|
|
72
|
-
navigation_guards.raise_deferred_error!
|
|
73
|
-
|
|
74
|
-
raise
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def navigation_timeout_ms
|
|
78
|
-
ctx.budget.effective_timeout_ms(fallback: ctx.policy.total_timeout_seconds)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def browser_headers
|
|
82
|
-
ctx.headers.reject { |key, _| BROWSER_UNSAFE_HEADERS.include?(key.to_s.downcase) }
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def build_response(page, navigation_response)
|
|
86
|
-
Response.new(
|
|
87
|
-
body: page.content,
|
|
88
|
-
headers: navigation_response&.headers || {},
|
|
89
|
-
url: NavigationGuards.response_url(navigation_response, ctx.url),
|
|
90
|
-
status: navigation_response&.status
|
|
91
|
-
)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|