Class: WebsocketSequentialClient::WebSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/websocket_sequential_client/web_socket.rb

Overview

This class provides the access to a WebSocket server.

Constant Summary collapse

DEFAULT_PING_INTERVAL =
20
DEFAULT_CLOSE_CODE =
1000
DEFAULT_CLOSE_TIMEOUT =
20
RECV_SIZE =

:nodoc:

1024
WS_PORT =

:nodoc:

80

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, opt = {}) ⇒ WebSocket

Connects to a WebSocket server and returns the connected socket.

Parameters

url

URL of the server.

opt

Optional hash parameters. :ping key specifies whether to enable automatic ping. You can set true, false or { interval: 10 }. The default value is true. :headers key specifies the additional HTTP headers. You can set { headers: { "Cookie" => "name=value" } }. The default value is {} (empty hash).

Examples

# Connect to the server.
ws = WebSocketSequentialClient::WebSocket.new "ws://server-url"

# Disable automatic ping.
ws = WebSocketSequentialClient::WebSocket.new "ws://server-url", ping: false

# Set the ping interval to 10s and send additional HTTP headers.
opt = { ping: { interval: 10 }, headers: { "Cookie" => "name=value", "Header" => "Value" } }
ws = WebSocketSequentialClient::WebSocket.open "ws://server-url", opt


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/websocket_sequential_client/web_socket.rb', line 75

def initialize url, opt = {}
  opt = { ping: true, headers: {} }.merge opt

  @read_queue = ReadQueue.new
  @write_queue = WriteQueue.new

  @closed_status_mutex = Mutex.new
  @closed_status_cond_var = ConditionVariable.new
  @closed_status = nil
  @close_timeout = DEFAULT_CLOSE_TIMEOUT

  @close_code = nil
  @close_reason = nil

  @ping_th = nil

  url = URI.parse url.to_s unless url.kind_of? URI
  @url = url

  case url.scheme
  when "ws"
    @socket = TCPSocket.new(url.host, url.port || WS_PORT)
  else
    raise ArgumentError, "URL scheme must be 'ws'."
  end

  hs = handshake(opt[:headers])

  @version = hs.version

  Thread.start{ send_background }
  Thread.start{ receive_background }
  start_ping_thread opt[:ping] if opt[:ping]

rescue
  close_socket(SocketAlreadyClosed.new, SocketAlreadyClosed.new)
  raise
end

Instance Attribute Details

#close_codeObject (readonly)

Returns the value of attribute close_code.



113
114
115
# File 'lib/websocket_sequential_client/web_socket.rb', line 113

def close_code
  @close_code
end

#close_reasonObject (readonly)

Returns the value of attribute close_reason.



113
114
115
# File 'lib/websocket_sequential_client/web_socket.rb', line 113

def close_reason
  @close_reason
end

Class Method Details

.open(*args, &block) ⇒ Object

Connects to a WebSocket server and returns the connected socket.

Parameters

args

See initialize method.

block

If given, it will be called with the instance of WebSocket as the argument.

Examples

WebSocketSequentialClient::WebSocket.open "ws://server-url" do |ws|
  ws.send "message"
  puts ws.recv
end

ws = WebSocketSequentialClient::WebSocket.open "ws://server-url"
ws.send "message"
puts ws.recv
ws.close

WebSocketSequentialClient::WebSocket.open "ws://server-url", { ping: false } do |ws|
  ws.send "message"
  puts ws.recv
end


42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/websocket_sequential_client/web_socket.rb', line 42

def self.open *args, &block
  if block
    ws = self.new *args
    begin
      block.call ws
    ensure
      ws.close
    end
  else
    self.new *args
  end
end

Instance Method Details

#available?Boolean Also known as: data_available?

Returns true if a received data is in the internal buffer.

Returns:

  • (Boolean)


118
119
120
# File 'lib/websocket_sequential_client/web_socket.rb', line 118

def available?
  @read_queue.available?
end

#close(code = nil, reason = nil, opt = {}) ⇒ Object

Close the socket.

Parameters

code

Code of the close frame. The default value is 1000.

reason

The reason of the close frame. The default value is nil.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/websocket_sequential_client/web_socket.rb', line 201

def close code = nil, reason = nil, opt = {}
  code ||= DEFAULT_CLOSE_CODE
  opt = { timeout: DEFAULT_CLOSE_TIMEOUT, wait_for_response: true }.merge opt

  param = {
    code: code,
    type: :close,
    version: @version
  }
  param[:data] = reason if reason
  frame = ::WebSocket::Frame::Outgoing::Client.new(param)

  @close_timeout = opt[:timeout]
  @write_queue.process_frame frame

  wait_for_response = opt[:wait_for_response]
  if wait_for_response
    @closed_status_mutex.synchronize do
      @closed_status_cond_var.wait @closed_status_mutex until @closed_status == :closed
    end
  end
end

#recvObject Also known as: receive

Returns a received data from the server. The encoding of the returned data is “UTF-8” for text messages, “ASCII-8BIT” for binary messages.

This is a blocking method. i.e. This method is blocked until a data is received from the server.



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/websocket_sequential_client/web_socket.rb', line 129

def recv
  data = @read_queue.pop
  raise data if data.kind_of? StandardError

  case data.type
  when :text
    data.to_s.force_encoding Encoding::UTF_8
  when :binary
    data.to_s.force_encoding Encoding::BINARY
  else
    raise NotImplementedError
  end
end

#send(data, type = :guess) ⇒ Object

Sends a data to the server.

Parameters

data

String to be sent. The encoding should be “UTF-8” for text messages, “ASCII-8BIT” for binary messages.

type

Specifies :text, :binary or :guess. If :guess is specified, the type is guessed based on the encoding of data.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/websocket_sequential_client/web_socket.rb', line 151

def send data, type = :guess
  case type
  when :guess
    if data.encoding == Encoding::BINARY
      type = :binary
    elsif Encoding.compatible?(data.encoding, Encoding::UTF_8)
      type = :text
    else
      raise ArgumentError, "Invalid encoding."
    end
  when :text
    unless Encoding.compatible?(data.encoding, Encoding::UTF_8)
      raise ArgumentError, "Invalid encoding"
    end
  when :binary
    #
  else
    raise ArgumentError, "Invalid type specified"
  end

  # data.b is necessary for the current implementation of websocket gem library.
  frame = ::WebSocket::Frame::Outgoing::Client.new(data: data.b, type: type, version: @version)

  result = @write_queue.process_frame frame
  raise result if result.kind_of? StandardError
end

#send_binary(data) ⇒ Object

Sends a binary message. Same as send data, :binary.



190
191
192
# File 'lib/websocket_sequential_client/web_socket.rb', line 190

def send_binary data
  send data, :binary
end

#send_text(data) ⇒ Object

Sends a text message. Same as send data, :text.



182
183
184
# File 'lib/websocket_sequential_client/web_socket.rb', line 182

def send_text data
  send data, :text
end