From 070a1d5692d7eda9ea26e588c5a27b562cba8f8c Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 7 Jan 2024 12:37:44 +0300 Subject: [PATCH] fix: #216 Configure with ws/wss url --- lib/ferrum/browser.rb | 1 - lib/ferrum/browser/options.rb | 5 ++-- lib/ferrum/browser/process.rb | 48 ++++++++++++++++++++++----------- lib/ferrum/client.rb | 3 ++- lib/ferrum/client/web_socket.rb | 20 +++++++++++--- lib/ferrum/target.rb | 2 +- spec/browser_spec.rb | 21 ++++++++++++++- spec/support/global_helpers.rb | 2 +- 8 files changed, 75 insertions(+), 27 deletions(-) diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index 17fb6057..5f609a62 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -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) diff --git a/lib/ferrum/browser/options.rb b/lib/ferrum/browser/options.rb index 06a7cc17..987f9442 100644 --- a/lib/ferrum/browser/options.rb +++ b/lib/ferrum/browser/options.rb @@ -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) @@ -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 diff --git a/lib/ferrum/browser/process.rb b/lib/ferrum/browser/process.rb index 3c6e9c88..006f975a 100644 --- a/lib/ferrum/browser/process.rb +++ b/lib/ferrum/browser/process.rb @@ -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 + 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 @@ -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 @@ -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 diff --git a/lib/ferrum/client.rb b/lib/ferrum/client.rb index 80856e9a..77d94380 100644 --- a/lib/ferrum/client.rb +++ b/lib/ferrum/client.rb @@ -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) diff --git a/lib/ferrum/client/web_socket.rb b/lib/ferrum/client/web_socket.rb index 2fcb3a06..d78d5e5b 100644 --- a/lib/ferrum/client/web_socket.rb +++ b/lib/ferrum/client/web_socket.rb @@ -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 diff --git a/lib/ferrum/target.rb b/lib/ferrum/target.rb index 819a9b87..3603b323 100644 --- a/lib/ferrum/target.rb +++ b/lib/ferrum/target.rb @@ -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 diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index 508a6f5e..bc91e563 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -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 diff --git a/spec/support/global_helpers.rb b/spec/support/global_helpers.rb index 8bf9ff45..59266934 100644 --- a/spec/support/global_helpers.rb +++ b/spec/support/global_helpers.rb @@ -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