websocket-driver 0.5.4-java → 0.6.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c6f967537a9e90a5e34627cc7b9738443976081
4
- data.tar.gz: 7d8b0e815c6bc9fe96d63e397b0af3600df3d3ed
3
+ metadata.gz: 4c10bbc50ea2348fbb23d243ca2c26ca10b7852c
4
+ data.tar.gz: b4fae4fc45971242b4cd24d142c6abd25818fdb7
5
5
  SHA512:
6
- metadata.gz: 617369b8f735060d83e95b573dd17a7ae795675c1f3b056c20416547d884f7d17de56fcde9de9c3daea23d5db1d024dc12d269ff4fa20c2b0aa786d91f829a83
7
- data.tar.gz: 39f201f433cb3814f3833faef99af9678dc2887c87550343e317e087fc76fb258af32dc9e84dfd07499bbfcb362344b803f48f0d4fef5f6e6a2c01ca12d5617a
6
+ metadata.gz: f53a36ffb90e346faa9662e8fb270b20e3b4ac78eba3c51a1511b432c38bfcfe22ea783eac3a31f035a4d9c65d45f72bbc880ba4c0e71832edc3117614506d78
7
+ data.tar.gz: 0ad4d8184eeff96b45eb40f308059a84dbe19f12146d10f0edc7496005854e527127e8b26a24b0d526c0a0033c8449dfea91d7e8d3150f3b110dfb8227de7f2f
@@ -1,3 +1,10 @@
1
+ ### 0.6.0 / 2015-07-08
2
+
3
+ * Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
4
+ * Allow the parser to recover cleanly if event listeners raise an error
5
+ * Let the `on()` method take a lambda as a positional argument rather than a block
6
+ * Add a `pong` method for sending unsolicited pong frames
7
+
1
8
  ### 0.5.4 / 2015-03-29
2
9
 
3
10
  * Don't emit extra close frames if we receive a close frame after we already
data/README.md CHANGED
@@ -21,7 +21,7 @@ this module up to some I/O object, it will do all of this for you:
21
21
  * Deal with proxies that defer delivery of the draft-76 handshake body
22
22
  * Notify you when the socket is open and closed and when messages arrive
23
23
  * Recombine fragmented messages
24
- * Dispatch text, binary, ping and close frames
24
+ * Dispatch text, binary, ping, pong and close frames
25
25
  * Manage the socket-closing handshake process
26
26
  * Automatically reply to ping frames with a matching pong
27
27
  * Apply masking to messages sent by the client
@@ -54,6 +54,7 @@ Server-side sockets require one additional method:
54
54
  * `HTTP_CONNECTION`
55
55
  * `HTTP_HOST`
56
56
  * `HTTP_ORIGIN`
57
+ * `HTTP_SEC_WEBSOCKET_EXTENSIONS`
57
58
  * `HTTP_SEC_WEBSOCKET_KEY`
58
59
  * `HTTP_SEC_WEBSOCKET_KEY1`
59
60
  * `HTTP_SEC_WEBSOCKET_KEY2`
@@ -145,7 +146,7 @@ module Connection
145
146
  def initialize
146
147
  @driver = WebSocket::Driver.server(self)
147
148
 
148
- @driver.on(:connect) do
149
+ @driver.on :connect, -> (event) do
149
150
  if WebSocket::Driver.websocket?(@driver.env)
150
151
  @driver.start
151
152
  else
@@ -153,8 +154,8 @@ module Connection
153
154
  end
154
155
  end
155
156
 
156
- @driver.on(:message) { |e| @driver.text(e.data) }
157
- @driver.on(:close) { |e| close_connection_after_writing }
157
+ @driver.on :message, -> (e) { @driver.text(e.data) }
158
+ @driver.on :close, -> (e) { close_connection_after_writing }
158
159
  end
159
160
 
160
161
  def receive_data(data)
@@ -213,7 +214,7 @@ start sending incoming data to `driver.parse(data)` as normal, and call
213
214
  ```rb
214
215
  proxy = driver.proxy('http://username:password@proxy.example.com')
215
216
 
216
- proxy.on :connect do
217
+ proxy.on :connect, -> (event) do
217
218
  driver.start
218
219
  end
219
220
  ```
@@ -225,7 +226,7 @@ In the event that proxy connection fails, `proxy` will emit an `:error`. You can
225
226
  inspect the proxy's response via `proxy.status` and `proxy.headers`.
226
227
 
227
228
  ```rb
228
- proxy.on :error do |error|
229
+ proxy.on :error, -> (error) do
229
230
  puts error.message
230
231
  puts proxy.status
231
232
  puts proxy.headers.inspect
@@ -273,25 +274,25 @@ Note that most of these methods are commands: if they produce data that should
273
274
  be sent over the socket, they will give this to you by calling
274
275
  `socket.write(string)`.
275
276
 
276
- #### `driver.on('open') { |event| }`
277
+ #### `driver.on 'open', -> (event) { }`
277
278
 
278
- Sets the callback block to execute when the socket becomes open.
279
+ Adds a callback block to execute when the socket becomes open.
279
280
 
280
- #### `driver.on('message') { |event| }`
281
+ #### `driver.on 'message', -> (event) { }`
281
282
 
282
- Sets the callback block to execute when a message is received. `event` will have
283
- a `data` attribute containing either a string in the case of a text message or
284
- an array of integers in the case of a binary message.
283
+ Adds a callback block to execute when a message is received. `event` will have a
284
+ `data` attribute containing either a string in the case of a text message or an
285
+ array of integers in the case of a binary message.
285
286
 
286
- #### `driver.on('error') { |event| }`
287
+ #### `driver.on 'error', -> (event) { }`
287
288
 
288
- Sets the callback to execute when a protocol error occurs due to the other peer
289
+ Adds a callback to execute when a protocol error occurs due to the other peer
289
290
  sending an invalid byte sequence. `event` will have a `message` attribute
290
291
  describing the error.
291
292
 
292
- #### `driver.on('close') { |event| }`
293
+ #### `driver.on 'close', -> (event) { }`
293
294
 
294
- Sets the callback block to execute when the socket becomes closed. The `event`
295
+ Adds a callback block to execute when the socket becomes closed. The `event`
295
296
  object has `code` and `reason` attributes.
296
297
 
297
298
  #### `driver.add_extension(extension)`
@@ -339,6 +340,15 @@ when the socket receives a pong frame whose content matches `string`. Returns
339
340
  `false` if frames can no longer be sent, or if the driver does not support
340
341
  ping/pong.
341
342
 
343
+ #### `driver.pong(string = '')`
344
+
345
+ Sends a pong frame over the socket, queueing it if necessary. `string` is
346
+ optional. Returns `false` if frames can no longer be sent, or if the driver does
347
+ not support ping/pong.
348
+
349
+ You don't need to call this when a ping frame is received; pings are replied to
350
+ automatically by the driver. This method is for sending unsolicited pongs.
351
+
342
352
  #### `driver.close`
343
353
 
344
354
  Initiates the closing handshake if the socket is still open. For drivers with no
@@ -56,6 +56,7 @@ module WebSocket
56
56
  autoload :Hybi, root + '/hybi'
57
57
  autoload :Proxy, root + '/proxy'
58
58
  autoload :Server, root + '/server'
59
+ autoload :StreamReader, root + '/stream_reader'
59
60
 
60
61
  include EventEmitter
61
62
  attr_reader :protocol, :ready_state
@@ -65,6 +66,7 @@ module WebSocket
65
66
  Driver.validate_options(options, [:max_length, :masking, :require_masking, :protocols])
66
67
 
67
68
  @socket = socket
69
+ @reader = StreamReader.new
68
70
  @options = options
69
71
  @max_length = options[:max_length] || MAX_LENGTH
70
72
  @headers = Headers.new
@@ -108,6 +110,10 @@ module WebSocket
108
110
  false
109
111
  end
110
112
 
113
+ def pong(*args)
114
+ false
115
+ end
116
+
111
117
  def close(reason = nil, code = nil)
112
118
  return false unless @ready_state == 1
113
119
  @ready_state = 3
@@ -3,7 +3,7 @@ module WebSocket
3
3
 
4
4
  class Client < Hybi
5
5
  def self.generate_key
6
- Base64.encode64((1..16).map { rand(255).chr } * '').strip
6
+ Base64.strict_encode64(SecureRandom.random_bytes(16))
7
7
  end
8
8
 
9
9
  attr_reader :status, :headers
@@ -32,7 +32,7 @@ module WebSocket
32
32
  end
33
33
 
34
34
  if uri.user
35
- auth = Base64.encode64([uri.user, uri.password] * ':').gsub(/\n/, '')
35
+ auth = Base64.strict_encode64([uri.user, uri.password] * ':')
36
36
  @headers['Authorization'] = 'Basic ' + auth
37
37
  end
38
38
  end
@@ -80,8 +80,8 @@ module WebSocket
80
80
 
81
81
  def fail_handshake(message)
82
82
  message = "Error during WebSocket handshake: #{message}"
83
- emit(:error, ProtocolError.new(message))
84
83
  @ready_state = 3
84
+ emit(:error, ProtocolError.new(message))
85
85
  emit(:close, CloseEvent.new(ERRORS[:protocol_error], message))
86
86
  end
87
87
 
@@ -124,8 +124,8 @@ module WebSocket
124
124
 
125
125
  begin
126
126
  @extensions.activate(@headers['Sec-WebSocket-Extensions'])
127
- rescue ::WebSocket::Extensions::ExtensionError => e
128
- return fail_handshake(e.message)
127
+ rescue ::WebSocket::Extensions::ExtensionError => error
128
+ return fail_handshake(error.message)
129
129
  end
130
130
  end
131
131
  end
@@ -26,7 +26,9 @@ module WebSocket
26
26
  def parse(buffer)
27
27
  return if @ready_state > 1
28
28
 
29
- buffer.each_byte do |data|
29
+ @reader.put(buffer)
30
+
31
+ @reader.each_byte do |data|
30
32
  case @stage
31
33
  when -1 then
32
34
  @body << data
@@ -52,8 +54,8 @@ module WebSocket
52
54
 
53
55
  when 2 then
54
56
  if data == 0xFF
55
- emit(:message, MessageEvent.new(Driver.encode(@buffer, :utf8)))
56
57
  @stage = 0
58
+ emit(:message, MessageEvent.new(Driver.encode(@buffer, :utf8)))
57
59
  else
58
60
  if @length
59
61
  @skipped += 1
@@ -6,16 +6,20 @@ module WebSocket
6
6
  @listeners = Hash.new { |h,k| h[k] = [] }
7
7
  end
8
8
 
9
- def add_listener(event, &listener)
9
+ def add_listener(event, callable = nil, &block)
10
+ listener = callable || block
10
11
  @listeners[event.to_s] << listener
12
+ listener
11
13
  end
12
14
 
13
- def on(event, &listener)
14
- add_listener(event, &listener)
15
+ def on(event, callable = nil, &block)
16
+ add_listener(event, callable, &block)
15
17
  end
16
18
 
17
- def remove_listener(event, &listener)
19
+ def remove_listener(event, callable = nil, &block)
20
+ listener = callable || block
18
21
  @listeners[event.to_s].delete(listener)
22
+ listener
19
23
  end
20
24
 
21
25
  def remove_all_listeners(event = nil)
@@ -4,12 +4,11 @@ module WebSocket
4
4
  class Hybi < Driver
5
5
  root = File.expand_path('../hybi', __FILE__)
6
6
 
7
- autoload :Frame, root + '/frame'
8
- autoload :Message, root + '/message'
9
- autoload :StreamReader, root + '/stream_reader'
7
+ autoload :Frame, root + '/frame'
8
+ autoload :Message, root + '/message'
10
9
 
11
10
  def self.generate_accept(key)
12
- Base64.encode64(Digest::SHA1.digest(key + GUID)).strip
11
+ Base64.strict_encode64(Digest::SHA1.digest(key + GUID))
13
12
  end
14
13
 
15
14
  GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
@@ -55,7 +54,6 @@ module WebSocket
55
54
  super
56
55
 
57
56
  @extensions = ::WebSocket::Extensions.new
58
- @reader = StreamReader.new
59
57
  @stage = 0
60
58
  @masking = options[:masking]
61
59
  @protocols = options[:protocols] || []
@@ -108,15 +106,16 @@ module WebSocket
108
106
  when 3 then
109
107
  buffer = @reader.read(4)
110
108
  if buffer
111
- @frame.masking_key = buffer
112
109
  @stage = 4
110
+ @frame.masking_key = buffer
113
111
  end
114
112
 
115
113
  when 4 then
116
114
  buffer = @reader.read(@frame.length)
115
+
117
116
  if buffer
118
- emit_frame(buffer)
119
117
  @stage = 0
118
+ emit_frame(buffer)
120
119
  end
121
120
 
122
121
  else
@@ -138,6 +137,10 @@ module WebSocket
138
137
  frame(message, :ping)
139
138
  end
140
139
 
140
+ def pong(message = '')
141
+ frame(message, :pong)
142
+ end
143
+
141
144
  def close(reason = nil, code = nil)
142
145
  reason ||= ''
143
146
  code ||= ERRORS[:normal_closure]
@@ -188,6 +191,9 @@ module WebSocket
188
191
 
189
192
  send_frame(frame)
190
193
  true
194
+
195
+ rescue ::WebSocket::Extensions::ExtensionError => error
196
+ fail(:extension_error, error.message)
191
197
  end
192
198
 
193
199
  private
@@ -231,16 +237,13 @@ module WebSocket
231
237
  end
232
238
 
233
239
  @socket.write(buffer.pack('C*'))
234
-
235
- rescue ::WebSocket::Extensions::ExtensionError => e
236
- fail(:extension_error, e.message)
237
240
  end
238
241
 
239
242
  def handshake_response
240
243
  begin
241
244
  extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
242
- rescue => e
243
- fail(:protocol_error, e.message)
245
+ rescue => error
246
+ fail(:protocol_error, error.message)
244
247
  return nil
245
248
  end
246
249
 
@@ -251,19 +254,21 @@ module WebSocket
251
254
  headers.join("\r\n")
252
255
  end
253
256
 
254
- def shutdown(code, reason)
255
- frame(reason, :close, code) if @ready_state < 2
257
+ def shutdown(code, reason, error = false)
256
258
  @frame = @message = nil
257
- @ready_state = 3
258
259
  @stage = 5
259
- emit(:close, CloseEvent.new(code, reason))
260
260
  @extensions.close
261
+
262
+ frame(reason, :close, code) if @ready_state < 2
263
+ @ready_state = 3
264
+
265
+ emit(:error, ProtocolError.new(reason)) if error
266
+ emit(:close, CloseEvent.new(code, reason))
261
267
  end
262
268
 
263
269
  def fail(type, message)
264
270
  return if @ready_state > 1
265
- emit(:error, ProtocolError.new(message))
266
- shutdown(ERRORS[type], message)
271
+ shutdown(ERRORS[type], message, true)
267
272
  end
268
273
 
269
274
  def parse_opcode(data)
@@ -277,6 +282,8 @@ module WebSocket
277
282
  @frame.rsv3 = rsvs[2]
278
283
  @frame.opcode = (data & OPCODE)
279
284
 
285
+ @stage = 1
286
+
280
287
  unless @extensions.valid_frame_rsv?(@frame)
281
288
  return fail(:protocol_error,
282
289
  "One or more reserved bits are on: reserved1 = #{@frame.rsv1 ? 1 : 0}" +
@@ -295,37 +302,34 @@ module WebSocket
295
302
  if @message and OPENING_OPCODES.include?(@frame.opcode)
296
303
  return fail(:protocol_error, 'Received new data frame but previous continuous frame is unfinished')
297
304
  end
298
-
299
- @stage = 1
300
305
  end
301
306
 
302
307
  def parse_length(data)
303
308
  @frame.masked = (data & MASK) == MASK
304
- if @require_masking and not @frame.masked
305
- return fail(:unacceptable, 'Received unmasked frame but masking is required')
306
- end
307
-
308
309
  @frame.length = (data & LENGTH)
309
310
 
310
311
  if @frame.length >= 0 and @frame.length <= 125
311
- return unless check_frame_length
312
312
  @stage = @frame.masked ? 3 : 4
313
+ return unless check_frame_length
313
314
  else
314
- @frame.length_bytes = (@frame.length == 126) ? 2 : 8
315
315
  @stage = 2
316
+ @frame.length_bytes = (@frame.length == 126) ? 2 : 8
317
+ end
318
+
319
+ if @require_masking and not @frame.masked
320
+ return fail(:unacceptable, 'Received unmasked frame but masking is required')
316
321
  end
317
322
  end
318
323
 
319
324
  def parse_extended_length(buffer)
320
325
  @frame.length = integer(buffer)
326
+ @stage = @frame.masked ? 3 : 4
321
327
 
322
328
  unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
323
329
  return fail(:protocol_error, "Received control frame having too long payload: #{@frame.length}")
324
330
  end
325
331
 
326
332
  return unless check_frame_length
327
-
328
- @stage = @frame.masked ? 3 : 4
329
333
  end
330
334
 
331
335
  def check_frame_length
@@ -404,8 +408,8 @@ module WebSocket
404
408
  else
405
409
  fail(:encoding_error, 'Could not decode a text frame as UTF-8')
406
410
  end
407
- rescue ::WebSocket::Extensions::ExtensionError => e
408
- fail(:extension_error, e.message)
411
+ rescue ::WebSocket::Extensions::ExtensionError => error
412
+ fail(:extension_error, error.message)
409
413
  end
410
414
 
411
415
  def integer(buffer)
@@ -25,7 +25,7 @@ module WebSocket
25
25
  @headers['Proxy-Connection'] = 'keep-alive'
26
26
 
27
27
  if @url.user
28
- auth = Base64.encode64([@url.user, @url.password] * ':').gsub(/\n/, '')
28
+ auth = Base64.strict_encode64([@url.user, @url.password] * ':')
29
29
  @headers['Proxy-Authorization'] = 'Basic ' + auth
30
30
  end
31
31
  end
@@ -56,7 +56,7 @@ module WebSocket
56
56
  @headers = Headers.new(@http.headers)
57
57
 
58
58
  if @status == 200
59
- emit(:connect)
59
+ emit(:connect, ConnectEvent.new)
60
60
  else
61
61
  message = "Can't establish a connection to the server at #{@socket.url}"
62
62
  emit(:error, ProtocolError.new(message))
@@ -0,0 +1,55 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class StreamReader
5
+ # Try to minimise the number of reallocations done:
6
+ MINIMUM_AUTOMATIC_PRUNE_OFFSET = 128
7
+
8
+ def initialize
9
+ @buffer = Driver.encode('', :binary)
10
+ @offset = 0
11
+ end
12
+
13
+ def put(buffer)
14
+ return unless buffer and buffer.bytesize > 0
15
+ @buffer << Driver.encode(buffer, :binary)
16
+ end
17
+
18
+ # Read bytes from the data:
19
+ def read(length)
20
+ return nil if (@offset + length) > @buffer.bytesize
21
+
22
+ chunk = @buffer.byteslice(@offset, length)
23
+ @offset += chunk.bytesize
24
+
25
+ prune if @offset > MINIMUM_AUTOMATIC_PRUNE_OFFSET
26
+
27
+ return chunk
28
+ end
29
+
30
+ def each_byte
31
+ prune
32
+
33
+ @buffer.each_byte do |value|
34
+ @offset += 1
35
+ yield value
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def prune
42
+ buffer_size = @buffer.bytesize
43
+
44
+ if @offset > buffer_size
45
+ @buffer = Driver.encode('', :binary)
46
+ else
47
+ @buffer = @buffer.byteslice(@offset, buffer_size - @offset)
48
+ end
49
+
50
+ @offset = 0
51
+ end
52
+ end
53
+
54
+ end
55
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.0
5
5
  platform: java
6
6
  authors:
7
7
  - James Coglan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-29 00:00:00.000000000 Z
11
+ date: 2015-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-extensions
@@ -102,9 +102,9 @@ files:
102
102
  - lib/websocket/driver/hybi.rb
103
103
  - lib/websocket/driver/hybi/frame.rb
104
104
  - lib/websocket/driver/hybi/message.rb
105
- - lib/websocket/driver/hybi/stream_reader.rb
106
105
  - lib/websocket/driver/proxy.rb
107
106
  - lib/websocket/driver/server.rb
107
+ - lib/websocket/driver/stream_reader.rb
108
108
  - lib/websocket/driver/utf8_match.rb
109
109
  - lib/websocket/http.rb
110
110
  - lib/websocket/http/headers.rb
@@ -1,28 +0,0 @@
1
- module WebSocket
2
- class Driver
3
-
4
- class Hybi
5
- class StreamReader
6
- def initialize
7
- @buffer = Driver.encode('', :binary)
8
- end
9
-
10
- def put(string)
11
- return unless string and string.bytesize > 0
12
- @buffer << Driver.encode(string, :binary)
13
- end
14
-
15
- def read(length)
16
- buffer_size = @buffer.bytesize
17
- return nil if length > buffer_size
18
-
19
- chunk = @buffer.byteslice(0, length)
20
- @buffer = @buffer.byteslice(length, buffer_size - length)
21
-
22
- chunk
23
- end
24
- end
25
- end
26
-
27
- end
28
- end