websocket-driver 0.6.0 → 0.6.1
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +4 -4
- data/lib/websocket/driver/client.rb +6 -6
- data/lib/websocket/driver/draft75.rb +19 -20
- data/lib/websocket/driver/draft76.rb +6 -16
- data/lib/websocket/driver/event_emitter.rb +5 -1
- data/lib/websocket/driver/headers.rb +1 -1
- data/lib/websocket/driver/hybi.rb +21 -37
- data/lib/websocket/driver/proxy.rb +3 -3
- data/lib/websocket/driver/server.rb +5 -5
- data/lib/websocket/driver/stream_reader.rb +5 -5
- data/lib/websocket/http/headers.rb +4 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a069093c87a31c96d30a911bf3855fec2efc384
|
4
|
+
data.tar.gz: ea4afb6bcd928bdd6075e68da402875d9470b2f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01272dedc949458909b2b6dc3acab09a12812ac5de1b0e1d44796d8406678de2445cfe11cdc47e52d447022c0ea5f86acae8aa592ed5c2575073f2c065411e1a
|
7
|
+
data.tar.gz: 9bd6c3e39ede403a35a3388b1593a8866e3b3dc851e15f86f1799f9a1abe8887fd4888733c35573faf13dd603487110928fbd91fd0f2de61aac8c43a5dbf8131
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
### 0.6.1 / 2015-07-13
|
2
|
+
|
3
|
+
* Fix how events are stored in `EventEmitter` to fix a backward-compatibility
|
4
|
+
violation introduced in the last release
|
5
|
+
* Use the `Array#pack` and `String#unpack` methods for reading/writing numbers
|
6
|
+
to buffers rather than including duplicate logic for this
|
7
|
+
|
1
8
|
### 0.6.0 / 2015-07-08
|
2
9
|
|
3
10
|
* Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
|
data/README.md
CHANGED
@@ -274,23 +274,23 @@ Note that most of these methods are commands: if they produce data that should
|
|
274
274
|
be sent over the socket, they will give this to you by calling
|
275
275
|
`socket.write(string)`.
|
276
276
|
|
277
|
-
#### `driver.on
|
277
|
+
#### `driver.on :open, -> (event) { }`
|
278
278
|
|
279
279
|
Adds a callback block to execute when the socket becomes open.
|
280
280
|
|
281
|
-
#### `driver.on
|
281
|
+
#### `driver.on :message, -> (event) { }`
|
282
282
|
|
283
283
|
Adds a callback block to execute when a message is received. `event` will have a
|
284
284
|
`data` attribute containing either a string in the case of a text message or an
|
285
285
|
array of integers in the case of a binary message.
|
286
286
|
|
287
|
-
#### `driver.on
|
287
|
+
#### `driver.on :error, -> (event) { }`
|
288
288
|
|
289
289
|
Adds a callback to execute when a protocol error occurs due to the other peer
|
290
290
|
sending an invalid byte sequence. `event` will have a `message` attribute
|
291
291
|
describing the error.
|
292
292
|
|
293
|
-
#### `driver.on
|
293
|
+
#### `driver.on :close, -> (event) { }`
|
294
294
|
|
295
295
|
Adds a callback block to execute when the socket becomes closed. The `event`
|
296
296
|
object has `code` and `reason` attributes.
|
@@ -21,10 +21,10 @@ module WebSocket
|
|
21
21
|
path = (uri.path == '') ? '/' : uri.path
|
22
22
|
@pathname = path + (uri.query ? '?' + uri.query : '')
|
23
23
|
|
24
|
-
@headers['Host']
|
25
|
-
@headers['Upgrade']
|
26
|
-
@headers['Connection']
|
27
|
-
@headers['Sec-WebSocket-Key']
|
24
|
+
@headers['Host'] = host
|
25
|
+
@headers['Upgrade'] = 'websocket'
|
26
|
+
@headers['Connection'] = 'Upgrade'
|
27
|
+
@headers['Sec-WebSocket-Key'] = @key
|
28
28
|
@headers['Sec-WebSocket-Version'] = '13'
|
29
29
|
|
30
30
|
if @protocols.size > 0
|
@@ -52,11 +52,11 @@ module WebSocket
|
|
52
52
|
true
|
53
53
|
end
|
54
54
|
|
55
|
-
def parse(
|
55
|
+
def parse(chunk)
|
56
56
|
return if @ready_state == 3
|
57
57
|
return super if @ready_state > 0
|
58
58
|
|
59
|
-
@http.parse(
|
59
|
+
@http.parse(chunk)
|
60
60
|
return fail_handshake('Invalid HTTP response') if @http.error?
|
61
61
|
return unless @http.complete?
|
62
62
|
|
@@ -6,9 +6,9 @@ module WebSocket
|
|
6
6
|
super
|
7
7
|
@stage = 0
|
8
8
|
|
9
|
-
@headers['Upgrade']
|
10
|
-
@headers['Connection']
|
11
|
-
@headers['WebSocket-Origin']
|
9
|
+
@headers['Upgrade'] = 'WebSocket'
|
10
|
+
@headers['Connection'] = 'Upgrade'
|
11
|
+
@headers['WebSocket-Origin'] = @socket.env['HTTP_ORIGIN']
|
12
12
|
@headers['WebSocket-Location'] = @socket.url
|
13
13
|
end
|
14
14
|
|
@@ -23,37 +23,36 @@ module WebSocket
|
|
23
23
|
true
|
24
24
|
end
|
25
25
|
|
26
|
-
def parse(
|
26
|
+
def parse(chunk)
|
27
27
|
return if @ready_state > 1
|
28
28
|
|
29
|
-
@reader.put(
|
29
|
+
@reader.put(chunk)
|
30
30
|
|
31
|
-
@reader.each_byte do |
|
31
|
+
@reader.each_byte do |octet|
|
32
32
|
case @stage
|
33
33
|
when -1 then
|
34
|
-
@body <<
|
34
|
+
@body << octet
|
35
35
|
send_handshake_body
|
36
36
|
|
37
37
|
when 0 then
|
38
|
-
parse_leading_byte(
|
38
|
+
parse_leading_byte(octet)
|
39
39
|
|
40
40
|
when 1 then
|
41
|
-
|
42
|
-
@length = value + 128 * @length
|
41
|
+
@length = (octet & 0x7F) + 128 * @length
|
43
42
|
|
44
43
|
if @closing and @length.zero?
|
45
44
|
return close
|
46
|
-
elsif (
|
45
|
+
elsif (octet & 0x80) != 0x80
|
47
46
|
if @length.zero?
|
48
47
|
@stage = 0
|
49
48
|
else
|
50
49
|
@skipped = 0
|
51
|
-
@stage
|
50
|
+
@stage = 2
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
55
54
|
when 2 then
|
56
|
-
if
|
55
|
+
if octet == 0xFF
|
57
56
|
@stage = 0
|
58
57
|
emit(:message, MessageEvent.new(Driver.encode(@buffer, :utf8)))
|
59
58
|
else
|
@@ -61,7 +60,7 @@ module WebSocket
|
|
61
60
|
@skipped += 1
|
62
61
|
@stage = 0 if @skipped == @length
|
63
62
|
else
|
64
|
-
@buffer <<
|
63
|
+
@buffer << octet
|
65
64
|
return close if @buffer.size > @max_length
|
66
65
|
end
|
67
66
|
end
|
@@ -69,10 +68,10 @@ module WebSocket
|
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
72
|
-
def frame(
|
73
|
-
return queue([
|
74
|
-
frame = [
|
75
|
-
@socket.write(
|
71
|
+
def frame(buffer, type = nil, error_type = nil)
|
72
|
+
return queue([buffer, type, error_type]) if @ready_state == 0
|
73
|
+
frame = [0x00, buffer, 0xFF].pack('CA*C')
|
74
|
+
@socket.write(frame)
|
76
75
|
true
|
77
76
|
end
|
78
77
|
|
@@ -84,8 +83,8 @@ module WebSocket
|
|
84
83
|
headers.join("\r\n")
|
85
84
|
end
|
86
85
|
|
87
|
-
def parse_leading_byte(
|
88
|
-
if (
|
86
|
+
def parse_leading_byte(octet)
|
87
|
+
if (octet & 0x80) == 0x80
|
89
88
|
@length = 0
|
90
89
|
@stage = 1
|
91
90
|
else
|
@@ -29,7 +29,7 @@ module WebSocket
|
|
29
29
|
|
30
30
|
def close(reason = nil, code = nil)
|
31
31
|
return false if @ready_state == 3
|
32
|
-
@socket.write(
|
32
|
+
@socket.write([0xFF, 0x00].pack('C*'))
|
33
33
|
@ready_state = 3
|
34
34
|
emit(:close, CloseEvent.new(nil, nil))
|
35
35
|
true
|
@@ -53,9 +53,7 @@ module WebSocket
|
|
53
53
|
key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
|
54
54
|
value2 = number_from_key(key2) / spaces_in_key(key2)
|
55
55
|
|
56
|
-
Digest::MD5.digest(
|
57
|
-
big_endian(value2) +
|
58
|
-
head)
|
56
|
+
Digest::MD5.digest([value1, value2, head].pack('N2A*'))
|
59
57
|
end
|
60
58
|
|
61
59
|
def send_handshake_body
|
@@ -66,11 +64,11 @@ module WebSocket
|
|
66
64
|
parse(@body[BODY_SIZE..-1]) if @body.bytesize > BODY_SIZE
|
67
65
|
end
|
68
66
|
|
69
|
-
def parse_leading_byte(
|
70
|
-
return super unless
|
67
|
+
def parse_leading_byte(octet)
|
68
|
+
return super unless octet == 0xFF
|
71
69
|
@closing = true
|
72
|
-
@length
|
73
|
-
@stage
|
70
|
+
@length = 0
|
71
|
+
@stage = 1
|
74
72
|
end
|
75
73
|
|
76
74
|
def number_from_key(key)
|
@@ -80,14 +78,6 @@ module WebSocket
|
|
80
78
|
def spaces_in_key(key)
|
81
79
|
key.scan(/ /).size
|
82
80
|
end
|
83
|
-
|
84
|
-
def big_endian(number)
|
85
|
-
string = Driver.encode('', :binary)
|
86
|
-
[24, 16, 8, 0].each do |offset|
|
87
|
-
string << (number >> offset & 0xFF).chr
|
88
|
-
end
|
89
|
-
string
|
90
|
-
end
|
91
81
|
end
|
92
82
|
|
93
83
|
end
|
@@ -13,7 +13,11 @@ module WebSocket
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def on(event, callable = nil, &block)
|
16
|
-
|
16
|
+
if callable
|
17
|
+
add_listener(event, callable)
|
18
|
+
else
|
19
|
+
add_listener(event, &block)
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
23
|
def remove_listener(event, callable = nil, &block)
|
@@ -50,6 +50,8 @@ module WebSocket
|
|
50
50
|
MIN_RESERVED_ERROR = 3000
|
51
51
|
MAX_RESERVED_ERROR = 4999
|
52
52
|
|
53
|
+
PACK_FORMATS = {2 => 'n', 8 => 'Q>'}
|
54
|
+
|
53
55
|
def initialize(socket, options = {})
|
54
56
|
super
|
55
57
|
|
@@ -66,8 +68,8 @@ module WebSocket
|
|
66
68
|
sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
|
67
69
|
protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
|
68
70
|
|
69
|
-
@headers['Upgrade']
|
70
|
-
@headers['Connection']
|
71
|
+
@headers['Upgrade'] = 'websocket'
|
72
|
+
@headers['Connection'] = 'Upgrade'
|
71
73
|
@headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
|
72
74
|
|
73
75
|
if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
|
@@ -86,8 +88,8 @@ module WebSocket
|
|
86
88
|
true
|
87
89
|
end
|
88
90
|
|
89
|
-
def parse(
|
90
|
-
@reader.put(
|
91
|
+
def parse(chunk)
|
92
|
+
@reader.put(chunk)
|
91
93
|
buffer = true
|
92
94
|
while buffer
|
93
95
|
case @stage
|
@@ -158,21 +160,19 @@ module WebSocket
|
|
158
160
|
end
|
159
161
|
end
|
160
162
|
|
161
|
-
def frame(
|
162
|
-
return queue([
|
163
|
+
def frame(buffer, type = nil, code = nil)
|
164
|
+
return queue([buffer, type, code]) if @ready_state <= 0
|
163
165
|
return false unless @ready_state == 1
|
164
166
|
|
165
167
|
message = Message.new
|
166
168
|
frame = Frame.new
|
167
|
-
is_text = String ===
|
169
|
+
is_text = String === buffer
|
168
170
|
|
169
171
|
message.rsv1 = message.rsv2 = message.rsv3 = false
|
170
172
|
message.opcode = OPCODES[type || (is_text ? :text : :binary)]
|
171
173
|
|
172
|
-
payload = is_text ?
|
173
|
-
if code
|
174
|
-
payload = [(code >> 8) & BYTE, code & BYTE, *payload]
|
175
|
-
end
|
174
|
+
payload = is_text ? buffer.bytes.to_a : buffer
|
175
|
+
payload = [code].pack(PACK_FORMATS[2]).bytes.to_a + payload if code
|
176
176
|
message.data = payload.pack('C*')
|
177
177
|
|
178
178
|
if MESSAGE_OPCODES.include?(message.opcode)
|
@@ -215,18 +215,10 @@ module WebSocket
|
|
215
215
|
buffer[1] = masked | length
|
216
216
|
elsif length <= 65535
|
217
217
|
buffer[1] = masked | 126
|
218
|
-
buffer[2] = (
|
219
|
-
buffer[3] = length & BYTE
|
218
|
+
buffer[2..3] = [length].pack(PACK_FORMATS[2]).bytes.to_a
|
220
219
|
else
|
221
220
|
buffer[1] = masked | 127
|
222
|
-
buffer[2] = (
|
223
|
-
buffer[3] = (length >> 48) & BYTE
|
224
|
-
buffer[4] = (length >> 40) & BYTE
|
225
|
-
buffer[5] = (length >> 32) & BYTE
|
226
|
-
buffer[6] = (length >> 24) & BYTE
|
227
|
-
buffer[7] = (length >> 16) & BYTE
|
228
|
-
buffer[8] = (length >> 8) & BYTE
|
229
|
-
buffer[9] = length & BYTE
|
221
|
+
buffer[2..9] = [length].pack(PACK_FORMATS[8]).bytes.to_a
|
230
222
|
end
|
231
223
|
|
232
224
|
if frame.masked
|
@@ -271,16 +263,16 @@ module WebSocket
|
|
271
263
|
shutdown(ERRORS[type], message, true)
|
272
264
|
end
|
273
265
|
|
274
|
-
def parse_opcode(
|
275
|
-
rsvs = [RSV1, RSV2, RSV3].map { |rsv| (
|
266
|
+
def parse_opcode(octet)
|
267
|
+
rsvs = [RSV1, RSV2, RSV3].map { |rsv| (octet & rsv) == rsv }
|
276
268
|
|
277
269
|
@frame = Frame.new
|
278
270
|
|
279
|
-
@frame.final = (
|
271
|
+
@frame.final = (octet & FIN) == FIN
|
280
272
|
@frame.rsv1 = rsvs[0]
|
281
273
|
@frame.rsv2 = rsvs[1]
|
282
274
|
@frame.rsv3 = rsvs[2]
|
283
|
-
@frame.opcode = (
|
275
|
+
@frame.opcode = (octet & OPCODE)
|
284
276
|
|
285
277
|
@stage = 1
|
286
278
|
|
@@ -304,9 +296,9 @@ module WebSocket
|
|
304
296
|
end
|
305
297
|
end
|
306
298
|
|
307
|
-
def parse_length(
|
308
|
-
@frame.masked = (
|
309
|
-
@frame.length = (
|
299
|
+
def parse_length(octet)
|
300
|
+
@frame.masked = (octet & MASK) == MASK
|
301
|
+
@frame.length = (octet & LENGTH)
|
310
302
|
|
311
303
|
if @frame.length >= 0 and @frame.length <= 125
|
312
304
|
@stage = @frame.masked ? 3 : 4
|
@@ -322,7 +314,7 @@ module WebSocket
|
|
322
314
|
end
|
323
315
|
|
324
316
|
def parse_extended_length(buffer)
|
325
|
-
@frame.length =
|
317
|
+
@frame.length = buffer.unpack(PACK_FORMATS[buffer.bytesize]).first
|
326
318
|
@stage = @frame.masked ? 3 : 4
|
327
319
|
|
328
320
|
unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
|
@@ -411,14 +403,6 @@ module WebSocket
|
|
411
403
|
rescue ::WebSocket::Extensions::ExtensionError => error
|
412
404
|
fail(:extension_error, error.message)
|
413
405
|
end
|
414
|
-
|
415
|
-
def integer(buffer)
|
416
|
-
number = 0
|
417
|
-
buffer.each_byte.with_index do |data, i|
|
418
|
-
number += data << (8 * (buffer.bytesize - 1 - i))
|
419
|
-
end
|
420
|
-
number
|
421
|
-
end
|
422
406
|
end
|
423
407
|
|
424
408
|
end
|
@@ -48,11 +48,11 @@ module WebSocket
|
|
48
48
|
true
|
49
49
|
end
|
50
50
|
|
51
|
-
def parse(
|
52
|
-
@http.parse(
|
51
|
+
def parse(chunk)
|
52
|
+
@http.parse(chunk)
|
53
53
|
return unless @http.complete?
|
54
54
|
|
55
|
-
@status
|
55
|
+
@status = @http.code
|
56
56
|
@headers = Headers.new(@http.headers)
|
57
57
|
|
58
58
|
if @status == 200
|
@@ -39,10 +39,10 @@ module WebSocket
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def parse(
|
43
|
-
return @delegate.parse(
|
42
|
+
def parse(chunk)
|
43
|
+
return @delegate.parse(chunk) if @delegate
|
44
44
|
|
45
|
-
@http.parse(
|
45
|
+
@http.parse(chunk)
|
46
46
|
return fail_request('Invalid HTTP request') if @http.error?
|
47
47
|
return unless @http.complete?
|
48
48
|
|
@@ -56,8 +56,8 @@ module WebSocket
|
|
56
56
|
emit(:connect, ConnectEvent.new)
|
57
57
|
end
|
58
58
|
|
59
|
-
def write(
|
60
|
-
@socket.write(Driver.encode(
|
59
|
+
def write(buffer)
|
60
|
+
@socket.write(Driver.encode(buffer, :binary))
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
@@ -10,9 +10,9 @@ module WebSocket
|
|
10
10
|
@offset = 0
|
11
11
|
end
|
12
12
|
|
13
|
-
def put(
|
14
|
-
return unless
|
15
|
-
@buffer << Driver.encode(
|
13
|
+
def put(chunk)
|
14
|
+
return unless chunk and chunk.bytesize > 0
|
15
|
+
@buffer << Driver.encode(chunk, :binary)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Read bytes from the data:
|
@@ -30,9 +30,9 @@ module WebSocket
|
|
30
30
|
def each_byte
|
31
31
|
prune
|
32
32
|
|
33
|
-
@buffer.each_byte do |
|
33
|
+
@buffer.each_byte do |octet|
|
34
34
|
@offset += 1
|
35
|
-
yield
|
35
|
+
yield octet
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -51,9 +51,9 @@ module WebSocket
|
|
51
51
|
@stage == -1
|
52
52
|
end
|
53
53
|
|
54
|
-
def parse(
|
55
|
-
|
56
|
-
if
|
54
|
+
def parse(chunk)
|
55
|
+
chunk.each_byte do |octet|
|
56
|
+
if octet == LF and @stage < 2
|
57
57
|
@buffer.pop if @buffer.last == CR
|
58
58
|
if @buffer.empty?
|
59
59
|
complete if @stage == 1
|
@@ -71,7 +71,7 @@ module WebSocket
|
|
71
71
|
end
|
72
72
|
@buffer = []
|
73
73
|
else
|
74
|
-
@buffer <<
|
74
|
+
@buffer << octet if @stage >= 0
|
75
75
|
error if @stage < 2 and @buffer.size > MAX_LINE_LENGTH
|
76
76
|
end
|
77
77
|
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.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Coglan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-extensions
|