class Vox::Gateway::WebSocket

Websocket that handles data interchange for {Vox::Gateway::Client}.

Constants

LOGGER

The logger used for output.

ZLIB_SUFFIX

Zlib boundary used for separating messages split into multiple frames.

Attributes

driver[R]
thread[R]
url[R]

Public Class Methods

new(url, port: nil, compression: true) click to toggle source
# File lib/vox/gateway/websocket.rb, line 24
def initialize(url, port: nil, compression: true)
  @url = url
  @uri = URI.parse(url)
  @port = port || @uri.scheme == 'wss' ? 443 : 80
  @inflate = Zlib::Inflate.new if compression
end

Public Instance Methods

close(reason = nil, code = 1000) click to toggle source

Close the websocket connection. @param reason [String] The reason for closing the websocket. @param code [Integer] The code to close the websocket with. @return [true, false] Whether the websocket closed successfully.

# File lib/vox/gateway/websocket.rb, line 99
def close(reason = nil, code = 1000)
  @driver.close(reason, code)
end
connect() click to toggle source

Connect to the websocket server. @return [Thread] The thread handling the read loop.

# File lib/vox/gateway/websocket.rb, line 44
def connect
  # Flush the zlib buffer
  @inflate&.reset

  # Create a socket connection to the URL
  @socket = create_socket

  # Initialize the websocket driver
  setup_driver

  # Read until our websocket closes.
  @thread = Thread.new do
    read_loop
  end
end
read() click to toggle source

@!visibility private @return [nil]

# File lib/vox/gateway/websocket.rb, line 91
def read
  @driver.parse(@socket.readpartial(4096))
end
send(message) click to toggle source

Send a `text` message. @param message [String] The `text` message to write to the websocket. @return [true, false] Whether the message was sent successfully.

# File lib/vox/gateway/websocket.rb, line 63
def send(message)
  LOGGER.debug { "[OUT] #{message} " }
  @driver.text(message)
end
send_binary(data) click to toggle source

Send a `binary` frame. @param data [String] The binary data to write to the websocket. @return [true, false] Whether the data was send successfully.

# File lib/vox/gateway/websocket.rb, line 79
def send_binary(data)
  @driver.binary(data)
end
send_json(hash) click to toggle source

Serialize a hash to send as a `text` message. @param hash [Hash] The hash to serialize and send as a `text` message. @return [true, false] Whether the message was sent successfully.

# File lib/vox/gateway/websocket.rb, line 71
def send_json(hash)
  data = MultiJson.dump(hash)
  send(data)
end
write(data) click to toggle source

@!visibility private @param data [String] The data to write to the socket.

# File lib/vox/gateway/websocket.rb, line 85
def write(data)
  @socket.write(data)
end

Private Instance Methods

create_socket() click to toggle source

Create a socket, create an SSL socket instead for wss. @return [TCPSocket, SSLSocket]

# File lib/vox/gateway/websocket.rb, line 124
def create_socket
  if @uri.scheme == 'wss'
    create_ssl_socket.tap(&:connect)
  else
    TCPSocket.new(@uri.host, @port)
  end
end
create_ssl_socket() click to toggle source

Create an SSL socket for WSS.

# File lib/vox/gateway/websocket.rb, line 133
def create_ssl_socket
  ctx = OpenSSL::SSL::SSLContext.new
  ctx.set_params ssl_version: :TLSv1_2

  socket = TCPSocket.new(@uri.host, @port)
  OpenSSL::SSL::SSLSocket.new(socket, ctx)
end
on_close(event) click to toggle source

Handle close events.

# File lib/vox/gateway/websocket.rb, line 171
def on_close(event)
  LOGGER.debug { "WebSocket is closing (#{event.code}) #{event.reason}" }
  emit(:close, { code: event.code, reason: event.reason })
end
on_message(event) click to toggle source

Handle parsed message events.

# File lib/vox/gateway/websocket.rb, line 155
def on_message(event)
  data = if @inflate
           packed = event.data.pack('c*')
           @inflate << packed
           return unless packed.end_with?(ZLIB_SUFFIX)

           @inflate.inflate('')
         else
           event.data
         end

  LOGGER.debug { "[IN] #{data[0].ord == 131 ? data.inspect : data}" }
  emit(:message, data)
end
on_open(_event) click to toggle source

Handle open events.

# File lib/vox/gateway/websocket.rb, line 149
def on_open(_event)
  LOGGER.debug { 'Connection open' }
  emit(:open)
end
read_loop() click to toggle source

Read from the socket until the websocket driver reports as closed.

# File lib/vox/gateway/websocket.rb, line 107
def read_loop
  read until @driver.state == :closed
rescue SystemCallError => e
  LOGGER.error { "(#{e.class.name.split('::').last}) #{e.message}" }
rescue EOFError => e
  LOGGER.error { 'EOF in websocket loop' }
end
register_handlers() click to toggle source

Register the base handlers.

# File lib/vox/gateway/websocket.rb, line 142
def register_handlers
  @driver.on(:open, &method(:on_open))
  @driver.on(:message, &method(:on_message))
  @driver.on(:close, &method(:on_close))
end
setup_driver() click to toggle source
# File lib/vox/gateway/websocket.rb, line 115
def setup_driver
  @driver = ::WebSocket::Driver.client(self)
  register_handlers
  @driver.start
end