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)


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

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)


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

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)


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

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)


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

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
96
97
# 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
          unless(die || connection.closed?)
            IO.select([connection, control_r])
            retry
          end
        rescue EOFError
          connection.close
        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)


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

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