Class: Waithook::WebsocketClient

Inherits:
Object
  • Object
show all
Defined in:
lib/waithook/websocket_client.rb

Defined Under Namespace

Classes: Waiter

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ WebsocketClient

Returns a new instance of WebsocketClient.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/waithook/websocket_client.rb', line 23

def initialize(options = {})
  # required: :host, :path

  @host = options[:host]
  @port = options[:port] || 80
  @path = options[:path]

  @use_ssl = options.has_key?(:ssl) ? options[:ssl] : @port == 443
  @options = options

  @waiters = []
  @connect_waiters = []
  @handshake_received = false
  @messages = Queue.new
  @is_open = false

  @output = options[:output] || $stdout

  if options[:logger] === false
    @output = StringIO.new
  end

  if options[:logger] && options[:logger] != true
    @logger = options[:logger]
  else
    @logger = LoggerWithTrace.new(@output).setup(
      progname: self.class.name,
      level: options[:logger_level] || :info
    )
  end
end

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



10
11
12
# File 'lib/waithook/websocket_client.rb', line 10

def logger
  @logger
end

Instance Method Details

#_handshake_recieved!Object



145
146
147
148
149
150
# File 'lib/waithook/websocket_client.rb', line 145

def _handshake_recieved!
  @handshake_received = true
  while waiter = @connect_waiters.shift
    waiter.notify(true)
  end
end

#_notify_waiters(type, payload) ⇒ Object



152
153
154
155
156
# File 'lib/waithook/websocket_client.rb', line 152

def _notify_waiters(type, payload)
  while waiter = @waiters.shift
    waiter.notify([type, payload])
  end
end

#_process_frame(message) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/waithook/websocket_client.rb', line 165

def _process_frame(message)
  logger.trace "Received :#{message.type} #{message.data ? "DATA: #{message.data}" : "(no data)"}"

  if message.type == :ping
    send_pong!
  end
  if message.type == :text
    @messages.push([message.type, message.data])
    _notify_waiters(message.type, message.data)
  end
end

#_send_frame(type, payload = nil) ⇒ Object



158
159
160
161
162
163
# File 'lib/waithook/websocket_client.rb', line 158

def _send_frame(type, payload = nil)
  wait_handshake!
  frame = WebSocket::Frame::Outgoing::Client.new(version: @handshake.version, data: payload, type: type)
  logger.trace "Sending :#{frame.type} #{payload ? "DATA: #{frame.data}" : "(no data)"}"
  @socket.write(frame.to_s)
end

#_start_parser!Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/waithook/websocket_client.rb', line 89

def _start_parser!
  @reader, @writter = IO.pipe
  @processing_thread = Thread.new do
    Thread.current.abort_on_exception = true
    begin
      logger.debug "Start reading in thread"
      handshake_response = _wait_handshake_response
      @handshake << handshake_response
      logger.trace "Handshake received:\n #{handshake_response}"

      @frame_parser = WebSocket::Frame::Incoming::Client.new
      _handshake_recieved!
      _wait_frames!
    rescue Object => error
      logger.error "#{error.class}: #{error.message}\n#{error.backtrace.join("\n")}"
      raise error
    end
  end
end

#_wait_frames!Object



177
178
179
180
181
182
183
184
# File 'lib/waithook/websocket_client.rb', line 177

def _wait_frames!
  while char = @socket.getc
    @frame_parser << char
    while message = @frame_parser.next
      _process_frame(message)
    end
  end
end

#_wait_handshake_responseObject



186
187
188
189
190
191
192
193
194
195
196
# File 'lib/waithook/websocket_client.rb', line 186

def _wait_handshake_response
  logger.debug "Waiting handshake response"
  data = []
  while line = @socket.gets
    data << line
    if line == "\r\n"
      break
    end
  end
  data.join("")
end

#close!(options = {send_close: true}) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/waithook/websocket_client.rb', line 198

def close!(options = {send_close: true})
  unless @is_open
    logger.info "Already closed"
    return false
  end

  logger.info "Disconnecting from #{@host} #{@port}"
  @processing_thread.kill
  _send_frame(:close) if options[:send_close]
  @socket.close
  @is_open = false

  return true
end

#connect!Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/waithook/websocket_client.rb', line 55

def connect!
  logger.info "Connecting to #{@host} #{@port}"

  tcp_socket = TCPSocket.open(@host, @port)

  if @use_ssl
    require 'openssl'
    ctx = OpenSSL::SSL::SSLContext.new
    ctx.ssl_version = @options[:ssl_version] if @options[:ssl_version]
    ctx.verify_mode = @options[:verify_mode] if @options[:verify_mode]
    cert_store = OpenSSL::X509::Store.new
    cert_store.set_default_paths
    ctx.cert_store = cert_store
    @socket = ::OpenSSL::SSL::SSLSocket.new(tcp_socket, ctx)
    @socket.connect
  else
    @socket = tcp_socket
  end

  @is_open = true
  @handshake = WebSocket::Handshake::Client.new(url: "ws://#{@host}/#{@path}")

  logger.trace "Sending handshake:\n#{@handshake}"

  @socket.print(@handshake)
  _start_parser!

  self
end

#connected?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/waithook/websocket_client.rb', line 85

def connected?
  !!@is_open
end

#send_message!(payload) ⇒ Object



117
118
119
# File 'lib/waithook/websocket_client.rb', line 117

def send_message!(payload)
  _send_frame(:text, payload)
end

#send_ping!Object



109
110
111
# File 'lib/waithook/websocket_client.rb', line 109

def send_ping!
  _send_frame(:ping)
end

#send_pong!Object



113
114
115
# File 'lib/waithook/websocket_client.rb', line 113

def send_pong!
  _send_frame(:pong)
end

#wait_connectedObject



138
139
140
141
142
143
# File 'lib/waithook/websocket_client.rb', line 138

def wait_connected
  return true if @handshake_received
  waiter = Waiter.new
  @connect_waiters << waiter
  waiter.wait
end

#wait_handshake!Object



121
122
123
124
125
126
# File 'lib/waithook/websocket_client.rb', line 121

def wait_handshake!
  while !@handshake_received
    sleep 0.001
  end
  self
end

#wait_messageObject



134
135
136
# File 'lib/waithook/websocket_client.rb', line 134

def wait_message
  @messages.pop
end

#wait_new_messageObject



128
129
130
131
132
# File 'lib/waithook/websocket_client.rb', line 128

def wait_new_message
  waiter = Waiter.new
  @waiters << waiter
  waiter.wait
end