websocket-driver 0.5.4-java → 0.6.0-java

Sign up to get free protection for your applications and to get access to all the features.
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