From 0e70fa35fe1621336fcf3a803e6f999ad9f5e623 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Tue, 13 Oct 2026 15:14:53 -0500 Subject: [PATCH] Imported WebSocket client and supporting cert paths helper method --- lib/common.rb | 9 +++++ lib/websocket_client.rb | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 lib/common.rb create mode 100644 lib/websocket_client.rb diff --git a/lib/common.rb b/lib/common.rb new file mode 100644 index 0000000..0b2a35a --- /dev/null +++ b/lib/common.rb @@ -0,0 +1,9 @@ +module W3DHubLauncher + def self.ca_bundle_path + [ + "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu + "/etc/pki/tls/certs/ca-bundle.crt", # RHEL/Fedora/CentOS + "/etc/ssl/ca-bundle.pem" # Some other distros + ].find { |path| File.exist?(path) } + end +end diff --git a/lib/websocket_client.rb b/lib/websocket_client.rb new file mode 100644 index 0000000..f12b746 --- /dev/null +++ b/lib/websocket_client.rb @@ -0,0 +1,76 @@ +module W3DHubLauncher + class WebSocketClient + def initialize + @errored = nil + @connection = nil + + @events = { + open: nil, + message: nil, + close: nil, + error: nil + } + end + + def connect(endpoint, headers: nil, &block) + yield(self) + + Sync do |task| + ssl_context = W3DHubLauncher.ca_bundle_path ? OpenSSL::SSL::SSLContext.new : nil + # ssl_context&.alpn_protocols = Async::HTTP::Protocol::HTTP11.names + ssl_context&.set_params( + ca_file: W3DHubLauncher.ca_bundle_path, + verify_mode: OpenSSL::SSL::VERIFY_PEER + ) + + # endpoint = Async::HTTP::Endpoint.parse(endpoint, alpn_protocols: Async::HTTP::Protocol::HTTP11.names, ssl_context: ssl_context) + endpoint = Async::HTTP::Endpoint.parse(endpoint, ssl_context: ssl_context) + + Async::WebSocket::Client.connect(endpoint, headers: headers) do |connection| + @connection = connection + + @events[:open]&.call + + while message = connection.read + @events[:message].call(message) + end + # FIXME: Don't rescue for all ta errors? + rescue => error + @errored = true + @events[:error]&.call(error) + ensure + @events[:close]&.call unless @errored + @connection = nil + @errored = false + end + end + + self + end + + def on(event, &block) + raise "Event must be a symbol" unless event.is_a?(Symbol) + raise "Unknown event: #{event.inspect}" unless @events.keys.include?(event) + raise "No block given for #{event.inspect}" unless block_given? + + @events[event] = block + end + + def send(data, type: :text) + @connection&.write(data) + @connection&.flush + end + + def close + @connection&.close + end + + def open? + !closed? + end + + def closed? + @connection&.closed? + end + end +end