Class: Bogo::Websocket::Client

Inherits:
Object
  • Object
show all
Includes:
Lazy, Memoization
Defined in:
lib/bogo-websocket/websocket.rb

Overview

Simple websocket client

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ self

Create a new websocket client



45
46
47
48
49
50
51
52
53
# File 'lib/bogo-websocket/websocket.rb', line 45

def initialize(args={})
  load_data(args)
  @control_r, @control_w = IO.pipe
  @die = false
  setup_connection
  perform_handshake
  @lock = Mutex.new
  @container = start!
end

Instance Attribute Details

#clientWebSocket::Frame::Incoming::Client (readonly)

Returns:

  • (WebSocket::Frame::Incoming::Client)


32
33
34
# File 'lib/bogo-websocket/websocket.rb', line 32

def client
  @client
end

#connectionTCPSocket, OpenSSL::SSL::SSLSocket (readonly)

Returns:

  • (TCPSocket, OpenSSL::SSL::SSLSocket)


28
29
30
# File 'lib/bogo-websocket/websocket.rb', line 28

def connection
  @connection
end

#containerThread (readonly)

Returns:

  • (Thread)


30
31
32
# File 'lib/bogo-websocket/websocket.rb', line 30

def container
  @container
end

#control_rIO (readonly)

Returns:

  • (IO)


38
39
40
# File 'lib/bogo-websocket/websocket.rb', line 38

def control_r
  @control_r
end

#control_wIO (readonly)

Returns:

  • (IO)


40
41
42
# File 'lib/bogo-websocket/websocket.rb', line 40

def control_w
  @control_w
end

#dieTrueClass, FalseClass (readonly)

Returns:

  • (TrueClass, FalseClass)


36
37
38
# File 'lib/bogo-websocket/websocket.rb', line 36

def die
  @die
end

#handshakeWebSocket::Handshake::Client (readonly)

Returns:

  • (WebSocket::Handshake::Client)


34
35
36
# File 'lib/bogo-websocket/websocket.rb', line 34

def handshake
  @handshake
end

Instance Method Details

#build_ssl_contextOpenSSL::SSL::SSLContext

Returns:

  • (OpenSSL::SSL::SSLContext)


167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/bogo-websocket/websocket.rb', line 167

def build_ssl_context
  if(ssl_context)
    ssl_context
  else
    ctx = OpenSSL::SSL::SSLContext.new
    if(ssl_key || ssl_certificate)
      ctx.cert = OpenSSL::X509::Certificate.new(File.read(ssl_certificate))
      ctx.key = OpenSSL::PKey::RSA.new(File.read(ssl_key))
    end
    ctx
  end
end

#closeObject

Close the connection



63
64
65
66
67
68
# File 'lib/bogo-websocket/websocket.rb', line 63

def close
  connection.close
  @die = true
  control_w.write 'closed'
  container.join
end

#handle_message(message) ⇒ Object

Handle an incoming message

Parameters:

  • message (WebSocket::Frame::Incoming::Client)


100
101
102
103
104
105
106
107
108
109
110
# File 'lib/bogo-websocket/websocket.rb', line 100

def handle_message(message)
  case message.type
  when :binary, :text
    on_message.call(message.data)
  when :ping
    transmit(message.data, :pong)
  when :close
    connection.close
    on_disconnect.call
  end
end

#perform_handshakeTrueClass

Setup the handshake and perform handshake with remote connection

Returns:

  • (TrueClass)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/bogo-websocket/websocket.rb', line 130

def perform_handshake
  port = destination.port || destination.scheme == 'wss' ? 443 : 80
  @handshake = WebSocket::Handshake::Client.new(
    :host => destination.host,
    :port => port,
    :secure => destination.scheme == 'wss',
    :path => path,
    :query => URI.encode_www_form(params),
    :headers => headers
  )
  connection.write handshake.to_s
  reply = ''
  until(handshake.finished?)
    reply << connection.read(1)
    if(reply.index(/\r\n\r\n/m))
      handshake << reply
      reply = ''
    end
  end
  unless(handshake.valid?)
    raise ArgumentError.new 'Invalid handshake. Failed to connect!'
  end
  @client = WebSocket::Frame::Incoming::Client.new(:version => handshake.version)
  true
end

#setup_connectionTCPSocket, OpenSSL::SSL::SSLSocket

Returns:

  • (TCPSocket, OpenSSL::SSL::SSLSocket)


157
158
159
160
161
162
163
164
# File 'lib/bogo-websocket/websocket.rb', line 157

def setup_connection
  socket = TCPSocket.new(destination.host, destination.port)
  if(destination.scheme == 'wss')
    socket = OpenSSL::SSL::SSLSocket.new(socket, build_ssl_context)
    socket.connect
  end
  @connection = socket
end

#start!Object

Start the reader



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/bogo-websocket/websocket.rb', line 71

def start!
  unless(@container)
    @container = Thread.new do
      until(die || connection.closed?)
        begin
          unless(die || connection.closed?)
            client << connection.read_nonblock(2046)
            if(message = client.next)
              handle_message(message)
            end
          end
        rescue IO::WaitReadable, EOFError
          unless(die || connection.closed?)
            IO.select([connection, control_r])
            retry
          end
        rescue => error
          on_error.call(error)
          raise
        end
      end
      @connection = nil
    end
  end
end

#transmit(data, type) ⇒ Object

Send data to socket

Parameters:

  • data (String)
  • type (Symbol)


116
117
118
119
120
121
122
123
124
125
# File 'lib/bogo-websocket/websocket.rb', line 116

def transmit(data, type)
  message = WebSocket::Frame::Outgoing::Client.new(
    :version => handshake.version,
    :data => data,
    :type => type
  )
  result = connection.write message.to_s
  connection.flush
  result
end

#write(line) ⇒ Object

Write to socket

Parameters:

  • line (String)


58
59
60
# File 'lib/bogo-websocket/websocket.rb', line 58

def write(line)
  transmit(line, :text)
end