Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: #216 Configure with ws/wss url #435

Merged
merged 1 commit into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/ferrum/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ def start

begin
@process.start
@options.ws_url = @process.ws_url&.merge(path: "/")
@options.default_user_agent = @process.default_user_agent

@client = Client.new(@process.ws_url, options)
Expand Down
5 changes: 3 additions & 2 deletions lib/ferrum/browser/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ class Options

attr_reader :window_size, :logger, :ws_max_receive_size,
:js_errors, :base_url, :slowmo, :pending_connection_errors,
:url, :env, :process_timeout, :browser_name, :browser_path,
:url, :ws_url, :env, :process_timeout, :browser_name, :browser_path,
:save_path, :proxy, :port, :host, :headless, :browser_options,
:ignore_default_browser_options, :xvfb, :flatten
attr_accessor :timeout, :ws_url, :default_user_agent
attr_accessor :timeout, :default_user_agent

def initialize(options = nil)
@options = Hash(options&.dup)
Expand Down Expand Up @@ -44,6 +44,7 @@ def initialize(options = nil)
@logger = parse_logger(@options[:logger])
@base_url = parse_base_url(@options[:base_url]) if @options[:base_url]
@url = @options[:url].to_s if @options[:url]
@ws_url = @options[:ws_url].to_s if @options[:ws_url]

@options = @options.merge(window_size: @window_size).freeze
@browser_options = @options.fetch(:browser_options, {}).freeze
Expand Down
48 changes: 32 additions & 16 deletions lib/ferrum/browser/process.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ def self.directory_remover(path)
def initialize(options)
@pid = @xvfb = @user_data_dir = nil

if options.ws_url
response = parse_json_version(options.ws_url)
self.ws_url = response&.[]("webSocketDebuggerUrl") || options.ws_url
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@route in this case, the final result will still be whichever value headless server returns.

Imagine a case with docker-compose and Headless Chrome (2.0) available over "chrome" hostname, with ws_url then being ws://chrome:4000 (with then "chrome" resolving to the IP of the chrome container and not to localhost. In this scenario, even thought options.ws_url is ws://chrome:4000, Chrome will return the following:

=> {"Browser"=>"HeadlessChrome/121.0.6167.57",
 "Protocol-Version"=>"1.3",
 "User-Agent"=>"Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/121.0.6167.57 Safari/537.36",
 "V8-Version"=>"12.1.285.20",
 "WebKit-Version"=>"537.36 (@add6d6ffbc3a1c7e78cc15e6ba2dcb15208bedd5)",
 "webSocketDebuggerUrl"=>"ws://0.0.0.0:4000",
 "Debugger-Version"=>"add6d6ffbc3a1c7e78cc15e6ba2dcb15208bedd5"}

and Ferrum will assign ws_url to ws://0.0.0.0:4000. This value is then used for connections from the container running Ferrum (for example, Rails container with Cuprite) -> those connections won't work, as there is no Chrome running on 0.0.0.0:4000, it's running on chrome:4000.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore what I wrote, I just noticed it's already fixed here a981af2, but it's not yet released.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea release is going to happen in 1-2 weeks I think.

return
end

if options.url
url = URI.join(options.url, "/json/version")
response = JSON.parse(::Net::HTTP.get(url))
self.ws_url = response["webSocketDebuggerUrl"]
parse_browser_versions
response = parse_json_version(options.url)
self.ws_url = response&.[]("webSocketDebuggerUrl")
return
end

Expand Down Expand Up @@ -100,7 +104,7 @@ def start
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))

parse_ws_url(read_io, @process_timeout)
parse_browser_versions
parse_json_version(ws_url)
ensure
close_io(read_io, write_io)
end
Expand Down Expand Up @@ -174,25 +178,37 @@ def ws_url=(url)
@port = @ws_url.port
end

def parse_browser_versions
return unless ws_url.is_a?(Addressable::URI)
def close_io(*ios)
ios.each do |io|
io.close unless io.closed?
rescue IOError
raise unless RUBY_ENGINE == "jruby"
end
end

def parse_json_version(url)
url = URI.join(url, "/json/version")

if %w[wss ws].include?(url.scheme)
url.scheme = case url.scheme
when "ws"
"http"
when "wss"
"https"
end
end

version_url = URI.parse(ws_url.merge(scheme: "http", path: "/json/version"))
response = JSON.parse(::Net::HTTP.get(version_url))
response = JSON.parse(::Net::HTTP.get(URI(url.to_s)))

@v8_version = response["V8-Version"]
@browser_version = response["Browser"]
@webkit_version = response["WebKit-Version"]
@default_user_agent = response["User-Agent"]
@protocol_version = response["Protocol-Version"]
end

def close_io(*ios)
ios.each do |io|
io.close unless io.closed?
rescue IOError
raise unless RUBY_ENGINE == "jruby"
end
response
rescue StandardError
# nop
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/ferrum/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ class Client
extend Forwardable
delegate %i[timeout timeout=] => :options

attr_reader :options, :subscriber
attr_reader :ws_url, :options, :subscriber

def initialize(ws_url, options)
@command_id = 0
@ws_url = ws_url
@options = options
@pendings = Concurrent::Hash.new
@ws = WebSocket.new(ws_url, options.ws_max_receive_size, options.logger)
Expand Down
20 changes: 16 additions & 4 deletions lib/ferrum/client/web_socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,27 @@ module Ferrum
class Client
class WebSocket
WEBSOCKET_BUG_SLEEP = 0.05
DEFAULT_PORTS = { "ws" => 80, "wss" => 443 }.freeze
SKIP_LOGGING_SCREENSHOTS = !ENV["FERRUM_LOGGING_SCREENSHOTS"]

attr_reader :url, :messages

def initialize(url, max_receive_size, logger)
@url = url
@logger = logger
uri = URI.parse(@url)
@sock = TCPSocket.new(uri.host, uri.port)
@url = url
@logger = logger
uri = URI.parse(@url)
port = uri.port || DEFAULT_PORTS[uri.scheme]

if port == 443
tcp = TCPSocket.new(uri.host, port)
ssl_context = OpenSSL::SSL::SSLContext.new
@sock = OpenSSL::SSL::SSLSocket.new(tcp, ssl_context)
@sock.sync_close = true
@sock.connect
else
@sock = TCPSocket.new(uri.host, port)
end

max_receive_size ||= ::WebSocket::Driver::MAX_LENGTH
@driver = ::WebSocket::Driver.client(self, max_length: max_receive_size)
@messages = Queue.new
Expand Down
2 changes: 1 addition & 1 deletion lib/ferrum/target.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def build_client
end

def ws_url
options.ws_url.merge(path: "/devtools/page/#{id}").to_s
@browser_client.ws_url.merge(path: "/devtools/page/#{id}")
end
end
end
21 changes: 20 additions & 1 deletion spec/browser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,29 @@
end

it "supports :url argument" do
with_external_browser do |url|
with_external_browser do |url, process|
browser = Ferrum::Browser.new(url: url)
browser.go_to(base_url)
expect(browser.body).to include("Hello world!")
expect(process.v8_version).not_to be_nil
expect(process.browser_version).not_to be_nil
expect(process.webkit_version).not_to be_nil
expect(process.default_user_agent).not_to be_nil
expect(process.protocol_version).not_to be_nil
ensure
browser&.quit
end
end

it "supports :ws_url argument" do
with_external_browser do |url, process|
uri = Addressable::URI.parse(url)
browser = Ferrum::Browser.new(ws_url: "ws://#{uri.host}:#{uri.port}")
expect(process.v8_version).not_to be_nil
expect(process.browser_version).not_to be_nil
expect(process.webkit_version).not_to be_nil
expect(process.default_user_agent).not_to be_nil
expect(process.protocol_version).not_to be_nil
ensure
browser&.quit
end
Expand Down
2 changes: 1 addition & 1 deletion spec/support/global_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def with_external_browser(host: "127.0.0.1", port: 32_001)

begin
process.start
yield "http://#{host}:#{port}"
yield "http://#{host}:#{port}", process
ensure
process.stop
end
Expand Down