Class: Obxcura::Client
- Inherits:
-
Object
- Object
- Obxcura::Client
- Defined in:
- lib/obxcura/client.rb
Overview
The CDP transport: a single WebSocket to the browser endpoint.
Built directly on websocket-driver, because the higher-level
websocket-client-simple reads the socket one byte at a time (@socket.getc)
— a ~1MB CDP frame there takes seconds and can wedge the read loop. Here a
single reader thread pumps raw bytes through the driver (readpartial +
driver.parse), which parses frames efficiently.
This object is the driver's I/O adapter: websocket-driver calls #url for the handshake and #write to put bytes on the wire.
Everything on the wire is multiplexed over this one socket. Command replies
come back keyed by the id we assigned (globally unique across the
connection), so we resolve those regardless of session. Events instead carry
a sessionId, so we fan them out to whichever Page subscribed for that
session. Browser-level events (no sessionId) go to the :browser slot.
Constant Summary collapse
- DEFAULT_TIMEOUT =
Returns default seconds to wait for a command reply.
30- MAX_MESSAGE_SIZE =
Obscura frames stay well under this; it's just a guard against a runaway allocation — a sane cap on the receive size.
64 * 1024 * 1024
- READ_CHUNK =
Bytes pulled per read syscall. Small is fine — the driver reassembles frames across reads; this only bounds how much we buffer at once.
512
Instance Attribute Summary collapse
-
#url ⇒ String
readonly
The WebSocket URL of the browser endpoint.
Instance Method Summary collapse
-
#close ⇒ void
Close the driver, socket and reader thread.
-
#closing? ⇒ Boolean
Whether the connection is being (or has been) torn down.
-
#command(method, params = {}, session_id: nil, timeout: @timeout) ⇒ Hash
Send a CDP command and block until its reply arrives.
-
#initialize(url, timeout: DEFAULT_TIMEOUT) ⇒ Client
constructor
Open the WebSocket to the browser and block until the handshake completes.
-
#subscribe(session_id) {|method, params| ... } ⇒ Proc
Register a handler for events on a given session.
-
#unsubscribe(session_id) ⇒ Proc?
Remove the handler previously registered for a session.
-
#write(data) ⇒ void
I/O adapter for websocket-driver: puts outgoing bytes on the wire.
Constructor Details
#initialize(url, timeout: DEFAULT_TIMEOUT) ⇒ Client
Open the WebSocket to the browser and block until the handshake completes.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/obxcura/client.rb', line 48 def initialize(url, timeout: DEFAULT_TIMEOUT) @url = url @timeout = timeout @command_id = 0 @mutex = Mutex.new @write_mutex = Mutex.new @pending = {} @subscribers = {} @open = Queue.new connect end |
Instance Attribute Details
#url ⇒ String (readonly)
Returns the WebSocket URL of the browser endpoint.
41 42 43 |
# File 'lib/obxcura/client.rb', line 41 def url @url end |
Instance Method Details
#close ⇒ void
This method returns an undefined value.
Close the driver, socket and reader thread. Safe to call more than once.
109 110 111 112 113 114 115 116 |
# File 'lib/obxcura/client.rb', line 109 def close @closing = true @driver&.close @socket&.close @reader&.kill rescue IOError, SystemCallError nil end |
#closing? ⇒ Boolean
Returns whether the connection is being (or has been) torn down.
119 120 121 |
# File 'lib/obxcura/client.rb', line 119 def closing? @closing == true end |
#command(method, params = {}, session_id: nil, timeout: @timeout) ⇒ Hash
Send a CDP command and block until its reply arrives.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/obxcura/client.rb', line 70 def command(method, params = {}, session_id: nil, timeout: @timeout) id = next_id queue = Queue.new @mutex.synchronize { @pending[id] = queue } frame = { id: id, method: method, params: params } frame[:sessionId] = session_id if session_id @driver.text(JSON.generate(frame)) = Timeout.timeout(timeout, TimeoutError, "#{method} timed out after #{timeout}s") { queue.pop } raise ProtocolError, ["error"]["message"] if ["error"] ["result"] ensure @mutex.synchronize { @pending.delete(id) } end |
#subscribe(session_id) {|method, params| ... } ⇒ Proc
Register a handler for events on a given session.
94 95 96 |
# File 'lib/obxcura/client.rb', line 94 def subscribe(session_id, &block) @subscribers[session_id] = block end |
#unsubscribe(session_id) ⇒ Proc?
Remove the handler previously registered for a session.
102 103 104 |
# File 'lib/obxcura/client.rb', line 102 def unsubscribe(session_id) @subscribers.delete(session_id) end |
#write(data) ⇒ void
This method returns an undefined value.
I/O adapter for websocket-driver: puts outgoing bytes on the wire. Writes are serialized so command threads and the driver's own control frames (ping/pong/close) never interleave and corrupt the stream.
129 130 131 132 133 |
# File 'lib/obxcura/client.rb', line 129 def write(data) @write_mutex.synchronize { @socket.write(data) } rescue IOError, SystemCallError => e warn "[Obxcura] websocket write error: #{e}" unless closing? end |