class LiteCable::Server::ClientSocket::Base

Wrapper over web socket

Attributes

active[R]
close_on_error[R]
socket[R]
version[R]

Public Class Methods

new(env, socket, version) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 13
def initialize(env, socket, version)
  log(:debug, "WebSocket version #{version}")
  @env = env
  @socket = socket
  @version = version
  @active = true

  @open_handlers = []
  @message_handlers = []
  @close_handlers = []
  @error_handlers = []

  @close_on_error = true
end

Public Instance Methods

close() click to toggle source

rubocop: enable Metrics/MethodLength

# File lib/lite_cable/server/client_socket/base.rb, line 90
def close
  return unless @active

  @close_handlers.each(&:call)
  close!

  @active = false
end
closed?() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 99
def closed?
  @socket.closed?
end
listen() click to toggle source

rubocop: disable Metrics/MethodLength

# File lib/lite_cable/server/client_socket/base.rb, line 68
def listen
  keepalive
  Thread.new do
    Thread.current.abort_on_exception = true
    begin
      @open_handlers.each(&:call)
      each_frame do |data|
        @message_handlers.each do |h|
          h.call(data)
        rescue => e # rubocop: disable Style/RescueStandardError
          log(:error, "Socket receive failed: #{e}")
          @error_handlers.each { |eh| eh.call(e, data) }
          close if close_on_error
        end
      end
    ensure
      close
    end
  end
end
onclose(&block) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 59
def onclose(&block)
  @close_handlers << block
end
onerror(&block) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 63
def onerror(&block)
  @error_handlers << block
end
onmessage(&block) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 55
def onmessage(&block)
  @message_handlers << block
end
onopen(&block) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 51
def onopen(&block)
  @open_handlers << block
end
prevent_close_on_error() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 28
def prevent_close_on_error
  @close_on_error = false
end
request() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 47
def request
  @request ||= Rack::Request.new(@env)
end
transmit(data, type: :text) click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 32
def transmit(data, type: :text)
  frame = WebSocket::Frame::Outgoing::Server.new(
    version: version,
    data: data,
    type: type
  )
  socket.write frame.to_s
rescue EOFError, Errno::ECONNRESET => e
  log(:debug, "Socket gone: #{e}")
  close
rescue IOError, Errno::EPIPE, Errno::ETIMEDOUT => e
  log(:error, "Socket send failed: #{e}")
  close
end

Private Instance Methods

close!() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 107
def close!
  if @socket.respond_to?(:closed?)
    close_socket unless @socket.closed?
  else
    close_socket
  end
end
close_socket() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 115
def close_socket
  frame = WebSocket::Frame::Outgoing::Server.new(version: version, type: :close, code: 1000)
  @socket.write(frame.to_s) if frame.supported?
  @socket.close
rescue IOError, Errno::EPIPE, Errno::ETIMEDOUT # rubocop:disable Lint/HandleExceptions
  # already closed
end
each_frame() { |data| ... } click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 137
def each_frame
  framebuffer = WebSocket::Frame::Incoming::Server.new(version: version)

  while IO.select([socket])
    data = socket.respond_to?(:recv) ? socket.recv(2000) : socket.readpartial(2000)
    break if data.empty?

    framebuffer << data
    while frame = framebuffer.next # rubocop:disable Lint/AssignmentInCondition
      case frame.type
      when :close
        return
      when :text, :binary
        yield frame.data
      end
    end
  end
rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ECONNRESET, IOError, Errno::EBADF => e
  log(:debug, "Socket frame error: #{e}")
  nil # client disconnected or timed out
end
keepalive() click to toggle source
# File lib/lite_cable/server/client_socket/base.rb, line 123
def keepalive
  thread = Thread.new do
    Thread.current.abort_on_exception = true
    loop do
      sleep 5
      transmit nil, type: :ping
    end
  end

  onclose do
    thread.kill
  end
end