Class: OmniAI::Google::Live::Connection
- Inherits:
-
Object
- Object
- OmniAI::Google::Live::Connection
- Defined in:
- lib/omniai/google/live/connection.rb
Overview
A WebSocket client over TCP or TLS, driven by websocket-driver with one reader thread per connection.
Callbacks run on the reader thread. Writes may come from any thread: they are serialized by a mutex and bounded
by write_timeout, so a stalled peer can't block the caller indefinitely. An idle connection is pinged, so a
silently dropped path is noticed. on_close is called exactly once, whether the peer closes, the socket hits
EOF or errors, a write or pong times out, or a callback raises.
Defined Under Namespace
Classes: ConnectTimeout, PongTimeout, WriteTimeout
Constant Summary collapse
- CONNECT_TIMEOUT =
seconds, TCP and TLS together
10- CLOSE_TIMEOUT =
seconds
2- PING_INTERVAL =
seconds without a frame before pinging
15- PONG_TIMEOUT =
seconds
10- READ_CHUNK =
bytes
16_384
Instance Attribute Summary collapse
- #url ⇒ String readonly
Instance Method Summary collapse
-
#close ⇒ Object
Sends a close frame and waits briefly for the peer's reply before dropping the socket.
- #connect ⇒ Object
-
#initialize(url:, headers:, write_timeout:, on_open:, on_message:, on_close:) ⇒ Connection
constructor
A new instance of Connection.
- #open? ⇒ Boolean
- #send_text(payload) ⇒ Object
-
#write(data) ⇒ Object
Called by websocket-driver (under the mutex) for every outgoing frame.
Constructor Details
#initialize(url:, headers:, write_timeout:, on_open:, on_message:, on_close:) ⇒ Connection
Returns a new instance of Connection.
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/omniai/google/live/connection.rb', line 35 def initialize(url:, headers:, write_timeout:, on_open:, on_message:, on_close:) @url = url @headers = headers @write_timeout = write_timeout @on_open = on_open @on_message = @on_close = on_close @mutex = Mutex.new @pending = [] @finished = false end |
Instance Attribute Details
#url ⇒ String (readonly)
27 28 29 |
# File 'lib/omniai/google/live/connection.rb', line 27 def url @url end |
Instance Method Details
#close ⇒ Object
Sends a close frame and waits briefly for the peer's reply before dropping the socket. Called from a callback (the reader thread itself), it only sends the frame: the reader finishes when the reply arrives.
82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/omniai/google/live/connection.rb', line 82 def close return if @finished || @reader.nil? begin @mutex.synchronize { @driver.close } rescue WriteTimeout, IOError, SystemCallError nil end return if Thread.current == @reader abort! unless @reader.join(CLOSE_TIMEOUT) @reader.join(CLOSE_TIMEOUT) end |
#connect ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/omniai/google/live/connection.rb', line 48 def connect @io = open_socket @wake_reader, @wake_writer = IO.pipe # for abort!: on Linux, closing a socket doesn't interrupt a select on it @driver = WebSocket::Driver.client(self, binary_data_format: :string) @headers.each { |name, value| @driver.set_header(name, value) } @driver.on(:open) { @pending << [:open] } @driver.on(:message) { |event| @pending << [:message, event.data] } @driver.on(:close) { |event| @pending << [:close, event.code, event.reason] } @mutex.synchronize { @driver.start } @reader = Thread.new { read_loop } @reader.name = "omniai-google-live" @reader.report_on_exception = false rescue StandardError [@io, @wake_reader, @wake_writer].compact.each { |io| close_quietly(io) } unless @reader raise end |
#open? ⇒ Boolean
66 67 68 |
# File 'lib/omniai/google/live/connection.rb', line 66 def open? !@finished && !@aborted end |
#send_text(payload) ⇒ Object
73 74 75 76 77 78 |
# File 'lib/omniai/google/live/connection.rb', line 73 def send_text(payload) @mutex.synchronize { @driver.text(payload) } rescue WriteTimeout => e abort!(e) raise end |
#write(data) ⇒ Object
Called by websocket-driver (under the mutex) for every outgoing frame.
99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/omniai/google/live/connection.rb', line 99 def write(data) deadline = monotonic + @write_timeout data = data.b until data.empty? result = @io.write_nonblock(data, exception: false) case result when :wait_writable then wait!(nil, [@io.to_io], deadline) { write_timeout } when :wait_readable then wait!([@io.to_io], nil, deadline) { write_timeout } else data = data.byteslice(result..) end end end |