class WsClient::Client

Constants

FRAME_SIZE
MS_2

Attributes

connect_options[R]
handshake[R]
message_queue[R]
url[R]

Public Class Methods

new() click to toggle source
# File lib/ws_client.rb, line 22
def initialize
  @message_queue = ::Queue.new
end

Public Instance Methods

close() click to toggle source
# File lib/ws_client.rb, line 84
def close
  return if @closed

  write_data nil, :type => :close rescue nil
ensure
  @closed = true
  @socket.close if @socket
  @tcp_socket.close if @tcp_socket
  @socket = nil
  @tcp_socket = nil
end
closed?() click to toggle source
# File lib/ws_client.rb, line 96
def closed?
  !open?
end
connect(url = nil, options={}) click to toggle source
# File lib/ws_client.rb, line 26
def connect(url = nil, options={})
  return if open?

  @connect_options = options
  @connect_url = @url = url || @connect_url || @url

  raise "No URL to connect to" if url.nil?

  uri = URI.parse url
  @socket = TCPSocket.new(uri.host, uri.port || (uri.scheme == 'wss' ? 443 : 80))
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

  if ['https', 'wss'].include?(uri.scheme)
    ssl_context = options[:ssl_context] || begin
    ctx = OpenSSL::SSL::SSLContext.new
    ctx.ssl_version = options[:ssl_version] || 'SSLv23'
    ctx.verify_mode = options[:verify_mode] || OpenSSL::SSL::VERIFY_NONE #use VERIFY_PEER for verification
    cert_store = OpenSSL::X509::Store.new
    cert_store.set_default_paths
    ctx.cert_store = cert_store
    ctx
    end

    # Keep a handle to the TCPSocket, because the SSLSocket will not clean it up for us.
    @tcp_socket = @socket
    @socket = ::OpenSSL::SSL::SSLSocket.new(@tcp_socket, ssl_context)
    @socket.connect
  end

  @closed = false

  handshake
rescue OpenSSL::SSL::SSLError, EOFError
  # Re-use the socket cleanup logic if we have a connect failure.
  close
  raise
end
open?() click to toggle source
# File lib/ws_client.rb, line 100
def open?
  @handshake && @handshake.finished? && !@closed
end
reconnect() click to toggle source
# File lib/ws_client.rb, line 64
def reconnect
  connect(nil)
end
send_data(data, timeout = MS_2, opt = { :type => :text })
Alias for: send_data_and_wait
send_data_and_wait(data, timeout = MS_2, opt = { :type => :text }) click to toggle source

Returns all responses that have been accepted

# File lib/ws_client.rb, line 69
def send_data_and_wait(data, timeout = MS_2, opt = { :type => :text })
  response_data = []
  write_data(data, opt)
  pull_next_message_off_of_socket(@socket, timeout)

  if message_queue.length > 0
    message_queue.length.times do
      response_data << message_queue.pop
    end
  end

  response_data
end
Also aliased as: send_data

Private Instance Methods

pull_next_message_off_of_socket(socket, timeout = 10, last_frame = nil) click to toggle source
# File lib/ws_client.rb, line 133
def pull_next_message_off_of_socket(socket, timeout = 10, last_frame = nil)
  read_sockets, _, _ = IO.select([socket], nil, nil, timeout)

  if read_sockets && read_sockets[0]
    frame = last_frame || ::WebSocket::Frame::Incoming::Client.new

    begin
      frame << socket.read_nonblock(FRAME_SIZE)

      if socket.respond_to?(:pending)
        frame << socket.read(socket.pending) while socket.pending > 0
      end

      if msg = frame.next
        case msg.type
        when :ping
          send_data_and_wait(msg.data, 10, :type => :pong)
        else
          message_queue << msg
        end

        pull_next_message_off_of_socket(socket, MS_2) # 2ms penalty for new frames
      else
        pull_next_message_off_of_socket(socket, timeout, frame)
      end
    rescue IO::WaitReadable
      IO.select([socket])
      retry
    rescue IO::WaitWritable
      IO.select(nil, [socket])
      retry
    rescue => e
      close
      raise
    end
  end
end
write_data(data, opt) click to toggle source
# File lib/ws_client.rb, line 171
def write_data(data, opt)
  frame = ::WebSocket::Frame::Outgoing::Client.new(:data => data, :type => opt[:type], :version => @handshake.version)
  frame_str = frame.to_s

  loop do
    break if frame_str.empty? || @closed

    begin
      num_bytes_written = @socket.write_nonblock(frame_str)
      frame_str = frame_str[num_bytes_written..-1]
    rescue IO::WaitReadable
      IO.select([@socket]) # OpenSSL needs to read internally
      retry
    rescue IO::WaitWritable, Errno::EINTR
      IO.select(nil, [@socket])
      retry
    rescue => e
      close
      raise
    end
  end
end