bidi2pdf 0.1.3 → 0.1.4
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/.rubocop.yml +19 -1
- data/CHANGELOG.md +27 -3
- data/docker/Dockerfile.chromedriver +5 -0
- data/docker/entrypoint.sh +11 -1
- data/lib/bidi2pdf/bidi/add_headers_interceptor.rb +18 -21
- data/lib/bidi2pdf/bidi/auth_interceptor.rb +31 -38
- data/lib/bidi2pdf/bidi/browser_tab.rb +32 -52
- data/lib/bidi2pdf/bidi/client.rb +24 -52
- data/lib/bidi2pdf/bidi/command_manager.rb +50 -28
- data/lib/bidi2pdf/bidi/commands/add_intercept.rb +41 -0
- data/lib/bidi2pdf/bidi/commands/base.rb +73 -0
- data/lib/bidi2pdf/bidi/commands/browser_close.rb +15 -0
- data/lib/bidi2pdf/bidi/commands/browser_create_user_context.rb +15 -0
- data/lib/bidi2pdf/bidi/commands/browsing_context_close.rb +25 -0
- data/lib/bidi2pdf/bidi/commands/browsing_context_navigate.rb +31 -0
- data/lib/bidi2pdf/bidi/commands/browsing_context_print.rb +28 -0
- data/lib/bidi2pdf/bidi/commands/cancel_auth.rb +26 -0
- data/lib/bidi2pdf/bidi/commands/create_tab.rb +11 -0
- data/lib/bidi2pdf/bidi/commands/create_window.rb +32 -0
- data/lib/bidi2pdf/bidi/commands/get_user_contexts.rb +15 -0
- data/lib/bidi2pdf/bidi/commands/network_continue.rb +29 -0
- data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +116 -0
- data/lib/bidi2pdf/bidi/commands/provide_credentials.rb +33 -0
- data/lib/bidi2pdf/bidi/commands/script_evaluate.rb +33 -0
- data/lib/bidi2pdf/bidi/commands/session_end.rb +15 -0
- data/lib/bidi2pdf/bidi/commands/session_status.rb +15 -0
- data/lib/bidi2pdf/bidi/commands/session_subscribe.rb +25 -0
- data/lib/bidi2pdf/bidi/commands/set_tab_cookie.rb +63 -0
- data/lib/bidi2pdf/bidi/commands/set_usercontext_cookie.rb +67 -0
- data/lib/bidi2pdf/bidi/commands.rb +27 -0
- data/lib/bidi2pdf/bidi/connection_manager.rb +16 -13
- data/lib/bidi2pdf/bidi/event_manager.rb +2 -0
- data/lib/bidi2pdf/bidi/interceptor.rb +75 -0
- data/lib/bidi2pdf/bidi/network_events.rb +0 -1
- data/lib/bidi2pdf/bidi/session.rb +139 -65
- data/lib/bidi2pdf/bidi/user_context.rb +25 -31
- data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +2 -0
- data/lib/bidi2pdf/chromedriver_manager.rb +2 -1
- data/lib/bidi2pdf/cli.rb +1 -3
- data/lib/bidi2pdf/launcher.rb +3 -1
- data/lib/bidi2pdf/version.rb +1 -1
- data/lib/bidi2pdf.rb +12 -0
- data/sig/bidi2pdf/bidi/command_manager.rbs +41 -0
- data/sig/bidi2pdf/bidi/commands/add_intercept.rbs +21 -0
- data/sig/bidi2pdf/bidi/commands/base.rbs +27 -0
- data/sig/bidi2pdf/bidi/commands/browser_close.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/browser_create_user_context.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/browsing_context_close.rbs +11 -0
- data/sig/bidi2pdf/bidi/commands/browsing_context_navigate.rbs +15 -0
- data/sig/bidi2pdf/bidi/commands/browsing_context_print.rbs +14 -0
- data/sig/bidi2pdf/bidi/commands/cancel_auth.rbs +11 -0
- data/sig/bidi2pdf/bidi/commands/create_tab.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/create_window.rbs +19 -0
- data/sig/bidi2pdf/bidi/commands/get_user_contexts.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/network_continue.rbs +19 -0
- data/sig/bidi2pdf/bidi/commands/print_parameters_validator.rbs +53 -0
- data/sig/bidi2pdf/bidi/commands/provide_credentials.rbs +15 -0
- data/sig/bidi2pdf/bidi/commands/script_evaluate.rbs +17 -0
- data/sig/bidi2pdf/bidi/commands/session_end.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/session_status.rbs +9 -0
- data/sig/bidi2pdf/bidi/commands/session_subscribe.rbs +15 -0
- data/sig/bidi2pdf/bidi/commands/set_tab_cookie.rbs +31 -0
- data/sig/bidi2pdf/bidi/commands/set_usercontext_cookie.rbs +27 -0
- data/sig/bidi2pdf/bidi/commands.rbs +6 -0
- data/sig/bidi2pdf/bidi/connection_manager.rbs +17 -0
- data/sig/bidi2pdf/bidi/interceptor.rbs +31 -0
- data/tasks/coverage.rake +16 -0
- metadata +65 -11
- data/lib/bidi2pdf/bidi/print_parameters_validator.rb +0 -114
- data/sig/bidi2pdf/bidi/print_parameters_validator.rbs +0 -44
- data/sig/bidi2pdf/chrome/chromedriver_downloader.rbs +0 -11
- data/sig/bidi2pdf/chrome/downloader_helper.rbs +0 -9
- data/sig/bidi2pdf/chrome/finder.rbs +0 -27
- data/sig/bidi2pdf/chrome/platform.rbs +0 -13
- data/sig/bidi2pdf/chrome/version_resolver.rbs +0 -19
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bidi2pdf
|
4
|
+
module Bidi
|
5
|
+
module Commands
|
6
|
+
class SetUsercontextCookie < SetTabCookie
|
7
|
+
include Base
|
8
|
+
|
9
|
+
attr_reader :user_context_id, :source_origin
|
10
|
+
|
11
|
+
def initialize(name:,
|
12
|
+
value:,
|
13
|
+
domain:,
|
14
|
+
user_context_id:,
|
15
|
+
source_origin:,
|
16
|
+
path: "/",
|
17
|
+
secure: true,
|
18
|
+
http_only: false,
|
19
|
+
same_site: "strict",
|
20
|
+
ttl: 30)
|
21
|
+
super(name: name, value: value,
|
22
|
+
domain: domain,
|
23
|
+
path: path,
|
24
|
+
secure: secure,
|
25
|
+
http_only: http_only,
|
26
|
+
same_site: same_site,
|
27
|
+
ttl: ttl,
|
28
|
+
browsing_context_id: nil)
|
29
|
+
|
30
|
+
@user_context_id = user_context_id
|
31
|
+
@source_origin = source_origin
|
32
|
+
end
|
33
|
+
|
34
|
+
def expiry
|
35
|
+
Time.now.to_i + ttl
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_name
|
39
|
+
"storage.setCookie"
|
40
|
+
end
|
41
|
+
|
42
|
+
def params
|
43
|
+
{
|
44
|
+
cookie: {
|
45
|
+
name: name,
|
46
|
+
value: {
|
47
|
+
type: "string",
|
48
|
+
value: value
|
49
|
+
},
|
50
|
+
domain: domain,
|
51
|
+
path: path,
|
52
|
+
secure: secure,
|
53
|
+
httpOnly: http_only,
|
54
|
+
sameSite: same_site,
|
55
|
+
expiry: expiry
|
56
|
+
},
|
57
|
+
partition: {
|
58
|
+
type: "storageKey",
|
59
|
+
userContext: user_context_id,
|
60
|
+
sourceOrigin: source_origin
|
61
|
+
}
|
62
|
+
}.compact
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bidi2pdf
|
4
|
+
module Bidi
|
5
|
+
module Commands
|
6
|
+
require_relative "commands/base"
|
7
|
+
require_relative "commands/create_window"
|
8
|
+
require_relative "commands/create_tab"
|
9
|
+
require_relative "commands/add_intercept"
|
10
|
+
require_relative "commands/set_tab_cookie"
|
11
|
+
require_relative "commands/set_usercontext_cookie"
|
12
|
+
require_relative "commands/session_status"
|
13
|
+
require_relative "commands/get_user_contexts"
|
14
|
+
require_relative "commands/script_evaluate"
|
15
|
+
require_relative "commands/browser_create_user_context"
|
16
|
+
require_relative "commands/browser_close"
|
17
|
+
require_relative "commands/browsing_context_close"
|
18
|
+
require_relative "commands/browsing_context_navigate"
|
19
|
+
require_relative "commands/browsing_context_print"
|
20
|
+
require_relative "commands/session_subscribe"
|
21
|
+
require_relative "commands/session_end"
|
22
|
+
require_relative "commands/cancel_auth"
|
23
|
+
require_relative "commands/network_continue"
|
24
|
+
require_relative "commands/provide_credentials"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -6,28 +6,31 @@ module Bidi2pdf
|
|
6
6
|
def initialize(logger:)
|
7
7
|
@logger = logger
|
8
8
|
@connected = false
|
9
|
-
@
|
10
|
-
@cv = ConditionVariable.new
|
9
|
+
@connection_queue = Thread::Queue.new
|
11
10
|
end
|
12
11
|
|
13
12
|
def mark_connected
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
return if @connected
|
14
|
+
|
15
|
+
@connected = true
|
16
|
+
@logger.debug "WebSocket connection is open"
|
17
|
+
@connection_queue.push(true)
|
18
18
|
end
|
19
19
|
|
20
20
|
def wait_until_open(timeout:)
|
21
|
-
@
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
return true if @connected
|
22
|
+
|
23
|
+
@logger.debug "Waiting for WebSocket connection to open"
|
24
|
+
|
25
|
+
begin
|
26
|
+
Timeout.timeout(timeout) do
|
27
|
+
@connection_queue.pop
|
25
28
|
end
|
29
|
+
rescue Timeout::Error
|
30
|
+
raise Bidi2pdf::WebsocketError, "WebSocket connection did not open in time #{timeout} sec."
|
26
31
|
end
|
27
32
|
|
28
|
-
|
29
|
-
|
30
|
-
@logger.debug "WebSocket connection is open"
|
33
|
+
true
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bidi2pdf
|
4
|
+
module Bidi
|
5
|
+
module Interceptor
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def phases = raise(NotImplementedError, "Interceptors must implement phases")
|
12
|
+
|
13
|
+
def events = raise(NotImplementedError, "Interceptors must implement events")
|
14
|
+
end
|
15
|
+
|
16
|
+
def url_patterns = raise(NotImplementedError, "Interceptors must implement url_patterns")
|
17
|
+
|
18
|
+
def context = raise(NotImplementedError, "Interceptors must implement context")
|
19
|
+
|
20
|
+
def process_interception(_event_response, _navigation_id, _network_id, _url) = raise(NotImplementedError, "Interceptors must implement process_interception")
|
21
|
+
|
22
|
+
def register_with_client(client:)
|
23
|
+
@client = client
|
24
|
+
|
25
|
+
cmd = Bidi2pdf::Bidi::Commands::AddIntercept.new context: context, phases: self.class.phases, url_patterns: url_patterns
|
26
|
+
|
27
|
+
client.send_cmd_and_wait(cmd) do |response|
|
28
|
+
@interceptor_id = response["result"]["intercept"]
|
29
|
+
|
30
|
+
Bidi2pdf.logger.debug "Interceptor added: #{@interceptor_id}"
|
31
|
+
|
32
|
+
client.on_event(*self.class.events, &method(:handle_event))
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# rubocop: disable Metrics/AbcSize
|
39
|
+
def handle_event(response)
|
40
|
+
event_response = response["params"]
|
41
|
+
|
42
|
+
return unless event_response["intercepts"]&.include?(interceptor_id) && event_response["isBlocked"]
|
43
|
+
|
44
|
+
navigation_id = event_response["navigation"]
|
45
|
+
network_id = event_response["request"]["request"]
|
46
|
+
url = event_response["request"]["url"]
|
47
|
+
|
48
|
+
# Log the interception
|
49
|
+
Bidi2pdf.logger.debug "Interceptor #{interceptor_id} handling event: #{navigation_id}/#{network_id}/#{url}"
|
50
|
+
|
51
|
+
process_interception(event_response, navigation_id, network_id, url)
|
52
|
+
rescue StandardError => e
|
53
|
+
Bidi2pdf.logger.error "Error handling event: #{e.message}"
|
54
|
+
Bidi2pdf.logger.error e.backtrace.join("\n")
|
55
|
+
raise e
|
56
|
+
end
|
57
|
+
|
58
|
+
# rubocop: enable Metrics/AbcSize
|
59
|
+
|
60
|
+
def interceptor_id
|
61
|
+
@interceptor_id
|
62
|
+
end
|
63
|
+
|
64
|
+
def client
|
65
|
+
@client
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_phases!
|
69
|
+
valid_phases = [Phases::BEFORE_REQUEST, Phases::RESPONSE_STARTED, Phases::AUTH_REQUIRED]
|
70
|
+
|
71
|
+
raise ArgumentError, "Unsupported phase(s): #{self.class.phases}" unless self.class.phases.all? { |phase| valid_phases.include?(phase) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -68,7 +68,6 @@ module Bidi2pdf
|
|
68
68
|
end
|
69
69
|
|
70
70
|
if Time.now - start_time > timeout
|
71
|
-
# rubocop:disable Layout/LineLength
|
72
71
|
Bidi2pdf.logger.warn "⏰ Timeout while waiting for network events to complete. Still in progress: #{in_progress.map(&:id)}"
|
73
72
|
# rubocop:enable Layout/LineLength
|
74
73
|
break
|
@@ -10,134 +10,208 @@ require_relative "user_context"
|
|
10
10
|
module Bidi2pdf
|
11
11
|
module Bidi
|
12
12
|
class Session
|
13
|
-
SUBSCRIBE_EVENTS = %w[
|
14
|
-
|
15
|
-
script
|
16
|
-
].freeze
|
13
|
+
SUBSCRIBE_EVENTS = %w[log script].freeze
|
14
|
+
DEFAULT_CHROME_ARGS = %w[--disable-gpu --disable-popup-blocking --disable-hang-monitor].freeze
|
17
15
|
|
18
|
-
attr_reader :session_uri, :started
|
16
|
+
attr_reader :session_uri, :started, :chrome_args
|
19
17
|
|
20
|
-
def initialize(session_url:, headless: true)
|
18
|
+
def initialize(session_url:, headless: true, chrome_args: DEFAULT_CHROME_ARGS)
|
21
19
|
@session_uri = URI(session_url)
|
22
20
|
@headless = headless
|
23
|
-
@client = nil
|
24
|
-
@browser = nil
|
25
|
-
@websocket_url = nil
|
26
21
|
@started = false
|
22
|
+
@chrome_args = chrome_args.dup
|
27
23
|
end
|
28
24
|
|
29
25
|
def start
|
26
|
+
return if started?
|
27
|
+
|
30
28
|
@started = true
|
31
29
|
client
|
30
|
+
rescue StandardError => e
|
31
|
+
@started = false
|
32
|
+
raise e
|
32
33
|
end
|
33
34
|
|
34
35
|
def client
|
35
|
-
@client ||=
|
36
|
-
end
|
37
|
-
|
38
|
-
def close
|
39
|
-
client&.send_cmd_and_wait("session.end", {}) do |response|
|
40
|
-
Bidi2pdf.logger.debug "Session ended: #{response}"
|
41
|
-
@client&.close
|
42
|
-
@client = nil
|
43
|
-
@websocket_url = nil
|
44
|
-
@browser = nil
|
45
|
-
end
|
36
|
+
@client ||= started? ? create_client : nil
|
46
37
|
end
|
47
38
|
|
48
39
|
def browser
|
49
40
|
@browser ||= create_browser
|
50
41
|
end
|
51
42
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
43
|
+
def close
|
44
|
+
return unless started?
|
45
|
+
|
46
|
+
2.times do |attempt|
|
47
|
+
client&.send_cmd_and_wait(Bidi2pdf::Bidi::Commands::SessionEnd.new, timeout: 1) do |response|
|
48
|
+
Bidi2pdf.logger.info "Session ended: #{response}"
|
49
|
+
|
50
|
+
cleanup
|
51
|
+
end
|
52
|
+
break
|
53
|
+
rescue CmdTimeoutError
|
54
|
+
Bidi2pdf.logger.error "Session end command timed out. Retrying... (#{attempt + 1})"
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
def user_contexts
|
59
|
+
send_cmd(Bidi2pdf::Bidi::Commands::GetUserContexts.new) { |resp| Bidi2pdf.logger.debug "User contexts: #{resp}" }
|
60
|
+
end
|
61
|
+
|
58
62
|
def status
|
59
|
-
|
60
|
-
|
61
|
-
|
63
|
+
send_cmd(Bidi2pdf::Bidi::Commands::SessionStatus.new) { |resp| Bidi2pdf.logger.info "Session status: #{resp.inspect}" }
|
64
|
+
end
|
65
|
+
|
66
|
+
def started?
|
67
|
+
@started
|
68
|
+
end
|
69
|
+
|
70
|
+
def websocket_url
|
71
|
+
return @websocket_url if @websocket_url
|
72
|
+
|
73
|
+
@websocket_url = if %w[ws wss].include?(session_uri.scheme)
|
74
|
+
session_uri.to_s
|
75
|
+
else
|
76
|
+
create_new_session
|
77
|
+
end
|
62
78
|
end
|
63
79
|
|
64
80
|
private
|
65
81
|
|
82
|
+
def send_cmd(command, &block)
|
83
|
+
client&.send_cmd_and_wait(command, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
# rubocop: disable Metrics/AbcSize
|
66
87
|
def create_browser
|
67
88
|
start
|
68
|
-
|
69
|
-
|
70
|
-
@client.wait_until_open
|
89
|
+
client.start
|
90
|
+
client.wait_until_open
|
71
91
|
|
72
92
|
Bidi2pdf.logger.info "Subscribing to events"
|
73
93
|
|
74
|
-
|
75
|
-
|
94
|
+
Bidi::Client.new(websocket_url).tap do |event_client|
|
95
|
+
event_client.start
|
96
|
+
event_client.wait_until_open
|
76
97
|
|
77
|
-
|
78
|
-
|
98
|
+
event_client.on_event(*SUBSCRIBE_EVENTS) do |data|
|
99
|
+
Bidi2pdf.logger.debug "Received event: #{data["method"]}, params: #{data["params"]}"
|
100
|
+
end
|
79
101
|
end
|
80
102
|
|
81
|
-
Bidi::Browser.new(
|
103
|
+
Bidi::Browser.new(client)
|
82
104
|
end
|
83
105
|
|
106
|
+
# rubocop: enable Metrics/AbcSize
|
107
|
+
|
84
108
|
def create_client
|
85
109
|
Bidi::Client.new(websocket_url).tap(&:start)
|
86
110
|
end
|
87
111
|
|
88
|
-
|
89
|
-
|
90
|
-
|
112
|
+
def create_new_session
|
113
|
+
session_data = exec_api_call(session_request)
|
114
|
+
Bidi2pdf.logger.debug "Session data: #{session_data}"
|
91
115
|
|
92
|
-
|
93
|
-
|
94
|
-
return @websocket_url
|
95
|
-
end
|
116
|
+
value = session_data["value"]
|
117
|
+
handle_error(value) if value.nil? || value["error"]
|
96
118
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
119
|
+
session_id = value["sessionId"]
|
120
|
+
ws_url = value["capabilities"]["webSocketUrl"]
|
121
|
+
|
122
|
+
Bidi2pdf.logger.info "Created session with ID: #{session_id}"
|
123
|
+
Bidi2pdf.logger.info "WebSocket URL: #{ws_url}"
|
124
|
+
ws_url
|
125
|
+
end
|
102
126
|
|
103
|
-
|
127
|
+
def session_request
|
128
|
+
session_chrome_args = chrome_args.dup
|
129
|
+
session_chrome_args << "--headless" if @headless
|
104
130
|
|
105
|
-
|
131
|
+
{
|
106
132
|
"capabilities" => {
|
107
133
|
"alwaysMatch" => {
|
108
134
|
"browserName" => "chrome",
|
109
|
-
"goog:chromeOptions" => {
|
110
|
-
"args" => args
|
111
|
-
},
|
135
|
+
"goog:chromeOptions" => { "args" => session_chrome_args },
|
112
136
|
"goog:prerenderingDisabled" => true,
|
113
|
-
"unhandledPromptBehavior" => {
|
114
|
-
default: "ignore"
|
115
|
-
},
|
137
|
+
"unhandledPromptBehavior" => { default: "ignore" },
|
116
138
|
"acceptInsecureCerts" => true,
|
117
139
|
"webSocketUrl" => true
|
118
140
|
}
|
119
141
|
}
|
120
142
|
}
|
143
|
+
end
|
121
144
|
|
122
|
-
|
145
|
+
def exec_api_call(payload)
|
146
|
+
response = Net::HTTP.post(session_uri, payload.to_json, "Content-Type" => "application/json")
|
147
|
+
body = response.body
|
148
|
+
code = response.code.to_i
|
123
149
|
|
124
|
-
|
125
|
-
|
150
|
+
if code != 200
|
151
|
+
log_api_error("Failed to create session", code, body)
|
152
|
+
return build_error("Session creation failed", "Response code: #{code}")
|
153
|
+
end
|
126
154
|
|
127
|
-
|
155
|
+
JSON.parse(body)
|
156
|
+
rescue StandardError => e
|
157
|
+
error_type = error_category(e)
|
158
|
+
build_error(error_type, "#{error_description(error_type)} #{e.message}", e.backtrace)
|
159
|
+
end
|
128
160
|
|
129
|
-
|
161
|
+
def log_api_error(message, code, body)
|
162
|
+
Bidi2pdf.logger.error "#{message}. Response code: #{code}"
|
163
|
+
Bidi2pdf.logger.error "Response body: #{body}"
|
164
|
+
end
|
130
165
|
|
131
|
-
|
132
|
-
|
166
|
+
def error_category(exception)
|
167
|
+
case exception
|
168
|
+
when Errno::ECONNREFUSED then "Connection refused"
|
169
|
+
when JSON::ParserError then "Invalid JSON response"
|
170
|
+
else "Unknown error"
|
171
|
+
end
|
172
|
+
end
|
133
173
|
|
134
|
-
|
135
|
-
|
174
|
+
def error_description(type)
|
175
|
+
{
|
176
|
+
"Connection refused" => "Could not connect to the session URL:",
|
177
|
+
"Invalid JSON response" => "Could not parse the response:",
|
178
|
+
"Unknown error" => "An unknown error occurred:"
|
179
|
+
}[type]
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_error(error, message, backtrace = nil)
|
183
|
+
{
|
184
|
+
"value" => {
|
185
|
+
"error" => error,
|
186
|
+
"message" => message,
|
187
|
+
"stacktrace" => backtrace&.join("\n")
|
188
|
+
}.compact
|
189
|
+
}
|
190
|
+
end
|
136
191
|
|
137
|
-
|
192
|
+
def handle_error(value)
|
193
|
+
error = value["error"]
|
194
|
+
return unless error
|
195
|
+
|
196
|
+
msg = value["message"]
|
197
|
+
trace = value["stacktrace"]
|
198
|
+
|
199
|
+
Bidi2pdf.logger.error "Error: #{error} message: #{msg}"
|
200
|
+
Bidi2pdf.logger.error "Stacktrace: #{trace}" if trace
|
201
|
+
|
202
|
+
if msg =~ /probably user data directory is already in use/
|
203
|
+
Bidi2pdf.logger.info "Container detected with headless-only support, ensure xvfb is started" unless @headless
|
204
|
+
Bidi2pdf.logger.info "Check chromedriver permissions and --user-data-dir"
|
205
|
+
end
|
206
|
+
|
207
|
+
raise SessionNotStartedError,
|
208
|
+
"Session not started. Check logs for more details. Error: #{error} message: #{msg}"
|
138
209
|
end
|
139
210
|
|
140
|
-
|
211
|
+
def cleanup
|
212
|
+
@client&.close
|
213
|
+
@client = @websocket_url = @browser = nil
|
214
|
+
end
|
141
215
|
end
|
142
216
|
end
|
143
217
|
end
|
@@ -14,14 +14,16 @@ module Bidi2pdf
|
|
14
14
|
|
15
15
|
def context_id
|
16
16
|
@context_id ||= begin
|
17
|
-
|
18
|
-
|
19
|
-
end
|
17
|
+
res = client.send_cmd_and_wait(Bidi2pdf::Bidi::Commands::BrowserCreateUserContext.new) do |response|
|
18
|
+
raise "Error creating user context: #{response.inspect}" if response["error"]
|
20
19
|
|
21
|
-
|
20
|
+
response["result"]["userContext"]
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
Bidi2pdf.logger.debug "User context created: #{res.inspect}"
|
24
|
+
|
25
|
+
res
|
26
|
+
end
|
25
27
|
end
|
26
28
|
|
27
29
|
def set_cookie(
|
@@ -35,36 +37,28 @@ module Bidi2pdf
|
|
35
37
|
same_site: "strict",
|
36
38
|
ttl: 30
|
37
39
|
)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
},
|
53
|
-
partition: {
|
54
|
-
type: "storageKey",
|
55
|
-
userContext: context_id,
|
56
|
-
sourceOrigin: source_origin
|
57
|
-
}
|
58
|
-
}) do |response|
|
40
|
+
cmd = Bidi2pdf::Bidi::Commands::SetUsercontextCookie.new(
|
41
|
+
user_context_id: context_id,
|
42
|
+
source_origin: source_origin,
|
43
|
+
name: name,
|
44
|
+
value: value,
|
45
|
+
domain: domain,
|
46
|
+
path: path,
|
47
|
+
secure: secure,
|
48
|
+
http_only: http_only,
|
49
|
+
same_site: same_site,
|
50
|
+
ttl: ttl
|
51
|
+
)
|
52
|
+
|
53
|
+
client.send_cmd_and_wait(cmd) do |response|
|
59
54
|
Bidi2pdf.logger.debug "Cookie set: #{response.inspect}"
|
60
55
|
end
|
61
56
|
end
|
62
57
|
|
63
58
|
def create_browser_window
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
}) do |response|
|
59
|
+
cmd = Bidi2pdf::Bidi::Commands::CreateWindow.new(user_context_id: context_id)
|
60
|
+
|
61
|
+
client.send_cmd_and_wait(cmd) do |response|
|
68
62
|
browsing_context_id = response["result"]["context"]
|
69
63
|
|
70
64
|
BrowserTab.new(client, browsing_context_id, context_id)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "chromedriver/binary"
|
4
|
+
require "securerandom"
|
4
5
|
|
5
6
|
module Bidi2pdf
|
6
7
|
class ChromedriverManager
|
@@ -140,7 +141,7 @@ module Bidi2pdf
|
|
140
141
|
|
141
142
|
def build_cmd
|
142
143
|
bin = Chromedriver::Binary::ChromedriverDownloader.driver_path
|
143
|
-
user_data_dir = File.join(Dir.tmpdir, "bidi2pdf", "user_data")
|
144
|
+
user_data_dir = File.join(Dir.tmpdir, "bidi2pdf", "user_data", SecureRandom.hex(8))
|
144
145
|
|
145
146
|
cmd = [bin]
|
146
147
|
cmd << "--port=#{@port}" unless @port.zero?
|
data/lib/bidi2pdf/cli.rb
CHANGED
@@ -122,7 +122,7 @@ module Bidi2pdf
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def validate_print_options(opts)
|
125
|
-
Bidi2pdf::Bidi::PrintParametersValidator.validate!(opts)
|
125
|
+
Bidi2pdf::Bidi::Commands::PrintParametersValidator.validate!(opts)
|
126
126
|
rescue ArgumentError => e
|
127
127
|
raise Thor::Error, "Invalid print option: #{e.message}"
|
128
128
|
end
|
@@ -177,7 +177,6 @@ module Bidi2pdf
|
|
177
177
|
end
|
178
178
|
|
179
179
|
def launcher
|
180
|
-
# rubocop:disable Layout/BeginEndAlignment
|
181
180
|
@launcher ||= begin
|
182
181
|
username, password = parse_auth(merged_options[:auth]) if merged_options[:auth]
|
183
182
|
|
@@ -195,7 +194,6 @@ module Bidi2pdf
|
|
195
194
|
print_options: print_options
|
196
195
|
)
|
197
196
|
end
|
198
|
-
# rubocop:enable Layout/BeginEndAlignment
|
199
197
|
end
|
200
198
|
|
201
199
|
# rubocop:enable Metrics/AbcSize
|
data/lib/bidi2pdf/launcher.rb
CHANGED
@@ -21,6 +21,7 @@ module Bidi2pdf
|
|
21
21
|
@wait_network_idle = wait_network_idle
|
22
22
|
@print_options = print_options || {}
|
23
23
|
@remote_browser_url = remote_browser_url
|
24
|
+
@custom_session = nil
|
24
25
|
end
|
25
26
|
|
26
27
|
# rubocop:enable Metrics/ParameterLists
|
@@ -42,13 +43,14 @@ module Bidi2pdf
|
|
42
43
|
|
43
44
|
def stop
|
44
45
|
@manager&.stop
|
46
|
+
@custom_session&.close
|
45
47
|
end
|
46
48
|
|
47
49
|
private
|
48
50
|
|
49
51
|
def session
|
50
52
|
if @remote_browser_url
|
51
|
-
Bidi::Session.new(session_url: @remote_browser_url, headless: @headless)
|
53
|
+
@custom_session = Bidi::Session.new(session_url: @remote_browser_url, headless: @headless)
|
52
54
|
else
|
53
55
|
@manager = ChromedriverManager.new(port: @port, headless: @headless)
|
54
56
|
@manager.start
|
data/lib/bidi2pdf/version.rb
CHANGED