Class: Daytona::SocketIOClient
- Inherits:
-
Object
- Object
- Daytona::SocketIOClient
- Defined in:
- lib/daytona/common/socketio_client.rb
Overview
Minimal Engine.IO/Socket.IO v4 client over raw WebSocket. Supports connect with auth, heartbeat, and event reception.
Engine.IO v4 heartbeat protocol (WebSocket transport):
- Server sends PING (type 2) every pingInterval ms
- Client must respond with PONG (type 3) within pingTimeout ms
- Client monitors for missing server PINGs to detect dead connections
Constant Summary collapse
- EIO_OPEN =
Engine.IO v4 packet types
'0'- EIO_CLOSE =
'1'- EIO_PING =
'2'- EIO_PONG =
'3'- EIO_MESSAGE =
'4'- SIO_CONNECT =
Socket.IO v4 packet types (inside Engine.IO messages)
'0'- SIO_DISCONNECT =
'1'- SIO_EVENT =
'2'- SIO_CONNECT_ERROR =
'4'
Instance Attribute Summary collapse
-
#connected ⇒ Object
readonly
Returns the value of attribute connected.
Instance Method Summary collapse
-
#close ⇒ Object
Gracefully close the connection.
-
#connect ⇒ Boolean
Establish the WebSocket connection and perform Socket.IO handshake.
- #connected? ⇒ Boolean
-
#initialize(api_url:, token:, organization_id: nil, source: nil, sdk_version: nil, on_event: nil, on_disconnect: nil, connect_timeout: 5) ⇒ SocketIOClient
constructor
A new instance of SocketIOClient.
Constructor Details
#initialize(api_url:, token:, organization_id: nil, source: nil, sdk_version: nil, on_event: nil, on_disconnect: nil, connect_timeout: 5) ⇒ SocketIOClient
Returns a new instance of SocketIOClient.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/daytona/common/socketio_client.rb', line 43 def initialize(api_url:, token:, organization_id: nil, source: nil, sdk_version: nil, on_event: nil, on_disconnect: nil, connect_timeout: 5) @api_url = api_url @token = token @organization_id = organization_id @source = source @sdk_version = sdk_version @on_event = on_event @on_disconnect = on_disconnect @connect_timeout = connect_timeout @connected = false @mutex = Mutex.new @write_mutex = Mutex.new @verify_mutex = Mutex.new @tls_verified = nil @health_thread = nil @ping_interval = 25 @ping_timeout = 20 @last_server_activity = Time.now @ws = nil @close_requested = false end |
Instance Attribute Details
#connected ⇒ Object (readonly)
Returns the value of attribute connected.
35 36 37 |
# File 'lib/daytona/common/socketio_client.rb', line 35 def connected @connected end |
Instance Method Details
#close ⇒ Object
Gracefully close the connection.
155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/daytona/common/socketio_client.rb', line 155 def close @close_requested = true @health_thread&.kill @health_thread = nil send_raw(EIO_CLOSE) if @ws @ws&.close @mutex.synchronize { @connected = false } rescue StandardError # Ignore errors during close end |
#connect ⇒ Boolean
Establish the WebSocket connection and perform Socket.IO handshake.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/daytona/common/socketio_client.rb', line 69 def connect ws_url = build_ws_url connected_queue = Queue.new # Reset per-connection TLS state so a reconnect re-verifies its new socket # instead of reusing a cached result from the previous connection. @verify_mutex.synchronize { @tls_verified = nil } # Capture self because websocket-client-simple uses instance_exec for callbacks client = self # Register callbacks inside the connect block so they are installed before the # socket dials — otherwise the Engine.IO handshake can arrive before :message # is bound, stalling the connection into a polling fallback/timeout. # # verify_mode is passed explicitly: websocket-client-simple builds its own # SSLContext and defaults to no certificate verification (VERIFY_NONE). @ws = WebSocket::Client::Simple.connect(ws_url, verify_mode: OpenSSL::SSL::VERIFY_PEER) do |ws| # Capture the client before its reader thread starts so #verify_tls_peer! # can reach the SSL socket even when a frame arrives before .connect returns. @ws = ws ws.on :message do |msg| client.send(:handle_raw_message, msg.data.to_s, connected_queue) end ws.on :error do |_e| was_connected = client.instance_variable_get(:@mutex).synchronize do prev = client.instance_variable_get(:@connected) client.instance_variable_set(:@connected, false) prev end connected_queue.push(:error) unless was_connected end ws.on :close do mutex = client.instance_variable_get(:@mutex) was_connected = mutex.synchronize do prev = client.instance_variable_get(:@connected) client.instance_variable_set(:@connected, false) prev end on_disconnect = client.instance_variable_get(:@on_disconnect) close_requested = client.instance_variable_get(:@close_requested) begin on_disconnect&.call if was_connected && !close_requested rescue StandardError nil end end end result = nil begin Timeout.timeout(@connect_timeout) do ensure_tls_verified!(connected_queue) result = connected_queue.pop if result == :open send_connect_auth result = connected_queue.pop end end rescue Timeout::Error close raise "WebSocket connection timed out after #{@connect_timeout}s" end if result.is_a?(OpenSSL::SSL::SSLError) close raise result end if result != :connected close raise "WebSocket connection failed: #{result}" end @mutex.synchronize { @connected } end |
#connected? ⇒ Boolean
150 151 152 |
# File 'lib/daytona/common/socketio_client.rb', line 150 def connected? @mutex.synchronize { @connected } end |