ed3-precompiled_websocket-driver 0.8.0-arm64-darwin
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 +7 -0
- data/CHANGELOG.md +169 -0
- data/LICENSE.md +12 -0
- data/README.md +394 -0
- data/ext/websocket-driver/WebsocketMaskService.java +57 -0
- data/ext/websocket-driver/extconf.rb +4 -0
- data/ext/websocket-driver/websocket_mask.c +32 -0
- data/lib/3.0/websocket_mask.bundle +0 -0
- data/lib/3.1/websocket_mask.bundle +0 -0
- data/lib/3.2/websocket_mask.bundle +0 -0
- data/lib/3.3/websocket_mask.bundle +0 -0
- data/lib/3.4/websocket_mask.bundle +0 -0
- data/lib/websocket/driver/client.rb +139 -0
- data/lib/websocket/driver/draft75.rb +102 -0
- data/lib/websocket/driver/draft76.rb +99 -0
- data/lib/websocket/driver/event_emitter.rb +54 -0
- data/lib/websocket/driver/headers.rb +45 -0
- data/lib/websocket/driver/hybi/frame.rb +20 -0
- data/lib/websocket/driver/hybi/message.rb +31 -0
- data/lib/websocket/driver/hybi.rb +426 -0
- data/lib/websocket/driver/proxy.rb +66 -0
- data/lib/websocket/driver/server.rb +80 -0
- data/lib/websocket/driver/stream_reader.rb +55 -0
- data/lib/websocket/driver.rb +248 -0
- data/lib/websocket/http/headers.rb +112 -0
- data/lib/websocket/http/request.rb +45 -0
- data/lib/websocket/http/response.rb +29 -0
- data/lib/websocket/http.rb +15 -0
- data/lib/websocket/mask.rb +14 -0
- data/lib/websocket/websocket_mask.rb +9 -0
- metadata +160 -0
@@ -0,0 +1,426 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
module WebSocket
|
4
|
+
class Driver
|
5
|
+
|
6
|
+
class Hybi < Driver
|
7
|
+
root = File.expand_path('../hybi', __FILE__)
|
8
|
+
|
9
|
+
autoload :Frame, root + '/frame'
|
10
|
+
autoload :Message, root + '/message'
|
11
|
+
|
12
|
+
def self.generate_accept(key)
|
13
|
+
Base64.strict_encode64(Digest::SHA1.digest(key + GUID))
|
14
|
+
end
|
15
|
+
|
16
|
+
VERSION = '13'
|
17
|
+
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
18
|
+
|
19
|
+
BYTE = 0b11111111
|
20
|
+
FIN = MASK = 0b10000000
|
21
|
+
RSV1 = 0b01000000
|
22
|
+
RSV2 = 0b00100000
|
23
|
+
RSV3 = 0b00010000
|
24
|
+
OPCODE = 0b00001111
|
25
|
+
LENGTH = 0b01111111
|
26
|
+
|
27
|
+
OPCODES = {
|
28
|
+
:continuation => 0,
|
29
|
+
:text => 1,
|
30
|
+
:binary => 2,
|
31
|
+
:close => 8,
|
32
|
+
:ping => 9,
|
33
|
+
:pong => 10
|
34
|
+
}
|
35
|
+
|
36
|
+
OPCODE_CODES = OPCODES.values
|
37
|
+
MESSAGE_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
|
38
|
+
OPENING_OPCODES = OPCODES.values_at(:text, :binary)
|
39
|
+
|
40
|
+
ERRORS = {
|
41
|
+
:normal_closure => 1000,
|
42
|
+
:going_away => 1001,
|
43
|
+
:protocol_error => 1002,
|
44
|
+
:unacceptable => 1003,
|
45
|
+
:encoding_error => 1007,
|
46
|
+
:policy_violation => 1008,
|
47
|
+
:too_large => 1009,
|
48
|
+
:extension_error => 1010,
|
49
|
+
:unexpected_condition => 1011
|
50
|
+
}
|
51
|
+
|
52
|
+
ERROR_CODES = ERRORS.values
|
53
|
+
DEFAULT_ERROR_CODE = 1000
|
54
|
+
MIN_RESERVED_ERROR = 3000
|
55
|
+
MAX_RESERVED_ERROR = 4999
|
56
|
+
|
57
|
+
PACK_FORMATS = { 2 => 'S>', 8 => 'Q>' }
|
58
|
+
|
59
|
+
def initialize(socket, options = {})
|
60
|
+
super
|
61
|
+
|
62
|
+
@extensions = ::WebSocket::Extensions.new
|
63
|
+
@stage = 0
|
64
|
+
@masking = options[:masking]
|
65
|
+
@protocols = options[:protocols] || []
|
66
|
+
@protocols = @protocols.strip.split(/ *, */) if String === @protocols
|
67
|
+
@require_masking = options[:require_masking]
|
68
|
+
@ping_callbacks = {}
|
69
|
+
|
70
|
+
@frame = @message = nil
|
71
|
+
|
72
|
+
return unless @socket.respond_to?(:env)
|
73
|
+
|
74
|
+
if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
|
75
|
+
protos = protos.split(/ *, */) if String === protos
|
76
|
+
@protocol = protos.find { |p| @protocols.include?(p) }
|
77
|
+
else
|
78
|
+
@protocol = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def version
|
83
|
+
"hybi-#{ VERSION }"
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_extension(extension)
|
87
|
+
@extensions.add(extension)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse(chunk)
|
92
|
+
@reader.put(chunk)
|
93
|
+
buffer = true
|
94
|
+
while buffer
|
95
|
+
case @stage
|
96
|
+
when 0 then
|
97
|
+
buffer = @reader.read(1)
|
98
|
+
parse_opcode(buffer.getbyte(0)) if buffer
|
99
|
+
|
100
|
+
when 1 then
|
101
|
+
buffer = @reader.read(1)
|
102
|
+
parse_length(buffer.getbyte(0)) if buffer
|
103
|
+
|
104
|
+
when 2 then
|
105
|
+
buffer = @reader.read(@frame.length_bytes)
|
106
|
+
parse_extended_length(buffer) if buffer
|
107
|
+
|
108
|
+
when 3 then
|
109
|
+
buffer = @reader.read(4)
|
110
|
+
if buffer
|
111
|
+
@stage = 4
|
112
|
+
@frame.masking_key = buffer
|
113
|
+
end
|
114
|
+
|
115
|
+
when 4 then
|
116
|
+
buffer = @reader.read(@frame.length)
|
117
|
+
|
118
|
+
if buffer
|
119
|
+
@stage = 0
|
120
|
+
emit_frame(buffer)
|
121
|
+
end
|
122
|
+
|
123
|
+
else
|
124
|
+
buffer = nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def binary(message)
|
130
|
+
frame(message, :binary)
|
131
|
+
end
|
132
|
+
|
133
|
+
def ping(message = '', &callback)
|
134
|
+
@ping_callbacks[message] = callback if callback
|
135
|
+
frame(message, :ping)
|
136
|
+
end
|
137
|
+
|
138
|
+
def pong(message = '')
|
139
|
+
frame(message, :pong)
|
140
|
+
end
|
141
|
+
|
142
|
+
def close(reason = nil, code = nil)
|
143
|
+
reason ||= ''
|
144
|
+
code ||= ERRORS[:normal_closure]
|
145
|
+
|
146
|
+
if @ready_state <= 0
|
147
|
+
@ready_state = 3
|
148
|
+
emit(:close, CloseEvent.new(code, reason))
|
149
|
+
true
|
150
|
+
elsif @ready_state == 1
|
151
|
+
frame(reason, :close, code)
|
152
|
+
@ready_state = 2
|
153
|
+
true
|
154
|
+
else
|
155
|
+
false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def frame(buffer, type = nil, code = nil)
|
160
|
+
return queue([buffer, type, code]) if @ready_state <= 0
|
161
|
+
return false unless @ready_state == 1
|
162
|
+
|
163
|
+
message = Message.new
|
164
|
+
frame = Frame.new
|
165
|
+
|
166
|
+
is_binary = (Array === buffer or buffer.encoding == Encoding::BINARY)
|
167
|
+
payload = Driver.encode(buffer, is_binary ? nil : Encoding::UTF_8)
|
168
|
+
payload = [code, payload].pack('S>a*') if code
|
169
|
+
type ||= is_binary ? :binary : :text
|
170
|
+
|
171
|
+
message.rsv1 = message.rsv2 = message.rsv3 = false
|
172
|
+
message.opcode = OPCODES[type]
|
173
|
+
message.data = payload
|
174
|
+
|
175
|
+
if MESSAGE_OPCODES.include?(message.opcode)
|
176
|
+
message = @extensions.process_outgoing_message(message)
|
177
|
+
end
|
178
|
+
|
179
|
+
frame.final = true
|
180
|
+
frame.rsv1 = message.rsv1
|
181
|
+
frame.rsv2 = message.rsv2
|
182
|
+
frame.rsv3 = message.rsv3
|
183
|
+
frame.opcode = message.opcode
|
184
|
+
frame.masked = !!@masking
|
185
|
+
frame.masking_key = SecureRandom.random_bytes(4) if frame.masked
|
186
|
+
frame.length = message.data.bytesize
|
187
|
+
frame.payload = message.data
|
188
|
+
|
189
|
+
send_frame(frame)
|
190
|
+
true
|
191
|
+
|
192
|
+
rescue ::WebSocket::Extensions::ExtensionError => error
|
193
|
+
fail(:extension_error, error.message)
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def send_frame(frame)
|
199
|
+
length = frame.length
|
200
|
+
values = []
|
201
|
+
format = 'C2'
|
202
|
+
masked = frame.masked ? MASK : 0
|
203
|
+
|
204
|
+
values[0] = (frame.final ? FIN : 0) |
|
205
|
+
(frame.rsv1 ? RSV1 : 0) |
|
206
|
+
(frame.rsv2 ? RSV2 : 0) |
|
207
|
+
(frame.rsv3 ? RSV3 : 0) |
|
208
|
+
frame.opcode
|
209
|
+
|
210
|
+
if length <= 125
|
211
|
+
values[1] = masked | length
|
212
|
+
elsif length <= 65535
|
213
|
+
values[1] = masked | 126
|
214
|
+
values[2] = length
|
215
|
+
format << 'S>'
|
216
|
+
else
|
217
|
+
values[1] = masked | 127
|
218
|
+
values[2] = length
|
219
|
+
format << 'Q>'
|
220
|
+
end
|
221
|
+
|
222
|
+
if frame.masked
|
223
|
+
values << frame.masking_key
|
224
|
+
values << Mask.mask(frame.payload, frame.masking_key)
|
225
|
+
format << 'a4a*'
|
226
|
+
else
|
227
|
+
values << frame.payload
|
228
|
+
format << 'a*'
|
229
|
+
end
|
230
|
+
|
231
|
+
@socket.write(values.pack(format))
|
232
|
+
end
|
233
|
+
|
234
|
+
def handshake_response
|
235
|
+
sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
|
236
|
+
version = @socket.env['HTTP_SEC_WEBSOCKET_VERSION']
|
237
|
+
|
238
|
+
unless version == VERSION
|
239
|
+
raise ProtocolError.new("Unsupported WebSocket version: #{ VERSION }")
|
240
|
+
end
|
241
|
+
|
242
|
+
unless sec_key
|
243
|
+
raise ProtocolError.new('Missing handshake request header: Sec-WebSocket-Key')
|
244
|
+
end
|
245
|
+
|
246
|
+
@headers['Upgrade'] = 'websocket'
|
247
|
+
@headers['Connection'] = 'Upgrade'
|
248
|
+
@headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
|
249
|
+
|
250
|
+
@headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
|
251
|
+
|
252
|
+
extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
|
253
|
+
@headers['Sec-WebSocket-Extensions'] = extensions if extensions
|
254
|
+
|
255
|
+
start = 'HTTP/1.1 101 Switching Protocols'
|
256
|
+
headers = [start, @headers.to_s, '']
|
257
|
+
headers.join("\r\n")
|
258
|
+
end
|
259
|
+
|
260
|
+
def shutdown(code, reason, error = false)
|
261
|
+
@frame = @message = nil
|
262
|
+
@stage = 5
|
263
|
+
@extensions.close
|
264
|
+
|
265
|
+
frame(reason, :close, code) if @ready_state < 2
|
266
|
+
@ready_state = 3
|
267
|
+
|
268
|
+
emit(:error, ProtocolError.new(reason)) if error
|
269
|
+
emit(:close, CloseEvent.new(code, reason))
|
270
|
+
end
|
271
|
+
|
272
|
+
def fail(type, message)
|
273
|
+
return if @ready_state > 1
|
274
|
+
shutdown(ERRORS[type], message, true)
|
275
|
+
end
|
276
|
+
|
277
|
+
def parse_opcode(octet)
|
278
|
+
rsvs = [RSV1, RSV2, RSV3].map { |rsv| (octet & rsv) == rsv }
|
279
|
+
|
280
|
+
@frame = Frame.new
|
281
|
+
|
282
|
+
@frame.final = (octet & FIN) == FIN
|
283
|
+
@frame.rsv1 = rsvs[0]
|
284
|
+
@frame.rsv2 = rsvs[1]
|
285
|
+
@frame.rsv3 = rsvs[2]
|
286
|
+
@frame.opcode = (octet & OPCODE)
|
287
|
+
|
288
|
+
@stage = 1
|
289
|
+
|
290
|
+
unless @extensions.valid_frame_rsv?(@frame)
|
291
|
+
return fail(:protocol_error,
|
292
|
+
"One or more reserved bits are on: reserved1 = #{ @frame.rsv1 ? 1 : 0 }" +
|
293
|
+
", reserved2 = #{ @frame.rsv2 ? 1 : 0 }" +
|
294
|
+
", reserved3 = #{ @frame.rsv3 ? 1 : 0 }")
|
295
|
+
end
|
296
|
+
|
297
|
+
unless OPCODES.values.include?(@frame.opcode)
|
298
|
+
return fail(:protocol_error, "Unrecognized frame opcode: #{ @frame.opcode }")
|
299
|
+
end
|
300
|
+
|
301
|
+
unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.final
|
302
|
+
return fail(:protocol_error, "Received fragmented control frame: opcode = #{ @frame.opcode }")
|
303
|
+
end
|
304
|
+
|
305
|
+
if @message and OPENING_OPCODES.include?(@frame.opcode)
|
306
|
+
return fail(:protocol_error, 'Received new data frame but previous continuous frame is unfinished')
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def parse_length(octet)
|
311
|
+
@frame.masked = (octet & MASK) == MASK
|
312
|
+
@frame.length = (octet & LENGTH)
|
313
|
+
|
314
|
+
if @frame.length >= 0 and @frame.length <= 125
|
315
|
+
@stage = @frame.masked ? 3 : 4
|
316
|
+
return unless check_frame_length
|
317
|
+
else
|
318
|
+
@stage = 2
|
319
|
+
@frame.length_bytes = (@frame.length == 126) ? 2 : 8
|
320
|
+
end
|
321
|
+
|
322
|
+
if @require_masking and not @frame.masked
|
323
|
+
return fail(:unacceptable, 'Received unmasked frame but masking is required')
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def parse_extended_length(buffer)
|
328
|
+
@frame.length = buffer.unpack(PACK_FORMATS[buffer.bytesize]).first
|
329
|
+
@stage = @frame.masked ? 3 : 4
|
330
|
+
|
331
|
+
unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
|
332
|
+
return fail(:protocol_error, "Received control frame having too long payload: #{ @frame.length }")
|
333
|
+
end
|
334
|
+
|
335
|
+
return unless check_frame_length
|
336
|
+
end
|
337
|
+
|
338
|
+
def check_frame_length
|
339
|
+
length = @message ? @message.data.bytesize : 0
|
340
|
+
|
341
|
+
if length + @frame.length > @max_length
|
342
|
+
fail(:too_large, 'WebSocket frame length too large')
|
343
|
+
false
|
344
|
+
else
|
345
|
+
true
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def emit_frame(buffer)
|
350
|
+
frame = @frame
|
351
|
+
opcode = frame.opcode
|
352
|
+
payload = frame.payload = Mask.mask(buffer, @frame.masking_key)
|
353
|
+
bytesize = payload.bytesize
|
354
|
+
|
355
|
+
@frame = nil
|
356
|
+
|
357
|
+
case opcode
|
358
|
+
when OPCODES[:continuation] then
|
359
|
+
return fail(:protocol_error, 'Received unexpected continuation frame') unless @message
|
360
|
+
@message << frame
|
361
|
+
|
362
|
+
when OPCODES[:text], OPCODES[:binary] then
|
363
|
+
@message = Message.new
|
364
|
+
@message << frame
|
365
|
+
|
366
|
+
when OPCODES[:close] then
|
367
|
+
code, reason = payload.unpack('S>a*') if bytesize >= 2
|
368
|
+
reason = Driver.encode(reason || '', Encoding::UTF_8)
|
369
|
+
|
370
|
+
unless (bytesize == 0) or
|
371
|
+
(code && code >= MIN_RESERVED_ERROR && code <= MAX_RESERVED_ERROR) or
|
372
|
+
ERROR_CODES.include?(code)
|
373
|
+
code = ERRORS[:protocol_error]
|
374
|
+
end
|
375
|
+
|
376
|
+
if bytesize > 125 or !reason.valid_encoding?
|
377
|
+
code = ERRORS[:protocol_error]
|
378
|
+
end
|
379
|
+
|
380
|
+
shutdown(code || DEFAULT_ERROR_CODE, reason || '')
|
381
|
+
|
382
|
+
when OPCODES[:ping] then
|
383
|
+
frame(payload, :pong)
|
384
|
+
emit(:ping, PingEvent.new(payload))
|
385
|
+
|
386
|
+
when OPCODES[:pong] then
|
387
|
+
message = Driver.encode(payload, Encoding::UTF_8)
|
388
|
+
callback = @ping_callbacks[message]
|
389
|
+
@ping_callbacks.delete(message)
|
390
|
+
callback.call if callback
|
391
|
+
emit(:pong, PongEvent.new(payload))
|
392
|
+
end
|
393
|
+
|
394
|
+
emit_message if frame.final and MESSAGE_OPCODES.include?(opcode)
|
395
|
+
end
|
396
|
+
|
397
|
+
def emit_message
|
398
|
+
message = @extensions.process_incoming_message(@message)
|
399
|
+
@message = nil
|
400
|
+
|
401
|
+
payload = message.data
|
402
|
+
|
403
|
+
case message.opcode
|
404
|
+
when OPCODES[:text] then
|
405
|
+
payload = Driver.encode(payload, Encoding::UTF_8)
|
406
|
+
payload = nil unless payload.valid_encoding?
|
407
|
+
when OPCODES[:binary]
|
408
|
+
if @binary_data_format == :array
|
409
|
+
payload = payload.bytes.to_a
|
410
|
+
else
|
411
|
+
payload = Driver.encode(payload, Encoding::BINARY)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
if payload
|
416
|
+
emit(:message, MessageEvent.new(payload))
|
417
|
+
else
|
418
|
+
fail(:encoding_error, 'Could not decode a text frame as UTF-8')
|
419
|
+
end
|
420
|
+
rescue ::WebSocket::Extensions::ExtensionError => error
|
421
|
+
fail(:extension_error, error.message)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module WebSocket
|
2
|
+
class Driver
|
3
|
+
|
4
|
+
class Proxy
|
5
|
+
include EventEmitter
|
6
|
+
|
7
|
+
attr_reader :status, :headers
|
8
|
+
|
9
|
+
def initialize(client, origin, options)
|
10
|
+
super()
|
11
|
+
|
12
|
+
@client = client
|
13
|
+
@http = HTTP::Response.new
|
14
|
+
@socket = client.instance_variable_get(:@socket)
|
15
|
+
@origin = URI.parse(@socket.url)
|
16
|
+
@url = URI.parse(origin)
|
17
|
+
@options = options
|
18
|
+
@state = 0
|
19
|
+
|
20
|
+
@headers = Headers.new
|
21
|
+
@headers['Host'] = Driver.host_header(@origin)
|
22
|
+
@headers['Connection'] = 'keep-alive'
|
23
|
+
@headers['Proxy-Connection'] = 'keep-alive'
|
24
|
+
|
25
|
+
if @url.user
|
26
|
+
auth = Base64.strict_encode64([@url.user, @url.password] * ':')
|
27
|
+
@headers['Proxy-Authorization'] = 'Basic ' + auth
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_header(name, value)
|
32
|
+
return false unless @state == 0
|
33
|
+
@headers[name] = value
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
return false unless @state == 0
|
39
|
+
@state = 1
|
40
|
+
|
41
|
+
port = @origin.port || PORTS[@origin.scheme]
|
42
|
+
start = "CONNECT #{ @origin.host }:#{ port } HTTP/1.1"
|
43
|
+
headers = [start, @headers.to_s, '']
|
44
|
+
|
45
|
+
@socket.write(headers.join("\r\n"))
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(chunk)
|
50
|
+
@http.parse(chunk)
|
51
|
+
return unless @http.complete?
|
52
|
+
|
53
|
+
@status = @http.code
|
54
|
+
@headers = Headers.new(@http.headers)
|
55
|
+
|
56
|
+
if @status == 200
|
57
|
+
emit(:connect, ConnectEvent.new)
|
58
|
+
else
|
59
|
+
message = "Can't establish a connection to the server at #{ @socket.url }"
|
60
|
+
emit(:error, ProtocolError.new(message))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module WebSocket
|
2
|
+
class Driver
|
3
|
+
|
4
|
+
class Server < Driver
|
5
|
+
EVENTS = %w[open message error close ping pong]
|
6
|
+
|
7
|
+
def initialize(socket, options = {})
|
8
|
+
super
|
9
|
+
@http = HTTP::Request.new
|
10
|
+
@delegate = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def env
|
14
|
+
@http.complete? ? @http.env : nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def url
|
18
|
+
return nil unless e = env
|
19
|
+
|
20
|
+
url = "ws://#{ e['HTTP_HOST'] }"
|
21
|
+
url << e['PATH_INFO']
|
22
|
+
url << "?#{ e['QUERY_STRING'] }" unless e['QUERY_STRING'] == ''
|
23
|
+
url
|
24
|
+
end
|
25
|
+
|
26
|
+
%w[add_extension set_header start frame text binary ping close].each do |method|
|
27
|
+
define_method(method) do |*args, &block|
|
28
|
+
if @delegate
|
29
|
+
@delegate.__send__(method, *args, &block)
|
30
|
+
else
|
31
|
+
@queue << [method, args, block]
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
%w[protocol version].each do |method|
|
38
|
+
define_method(method) do
|
39
|
+
@delegate && @delegate.__send__(method)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse(chunk)
|
44
|
+
return @delegate.parse(chunk) if @delegate
|
45
|
+
|
46
|
+
@http.parse(chunk)
|
47
|
+
return fail_request('Invalid HTTP request') if @http.error?
|
48
|
+
return unless @http.complete?
|
49
|
+
|
50
|
+
@delegate = Driver.rack(self, @options)
|
51
|
+
open
|
52
|
+
|
53
|
+
EVENTS.each do |event|
|
54
|
+
@delegate.on(event) { |e| emit(event, e) }
|
55
|
+
end
|
56
|
+
|
57
|
+
emit(:connect, ConnectEvent.new)
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(buffer)
|
61
|
+
@socket.write(buffer)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def fail_request(message)
|
67
|
+
emit(:error, ProtocolError.new(message))
|
68
|
+
emit(:close, CloseEvent.new(Hybi::ERRORS[:protocol_error], message))
|
69
|
+
end
|
70
|
+
|
71
|
+
def open
|
72
|
+
@queue.each do |method, args, block|
|
73
|
+
@delegate.__send__(method, *args, &block)
|
74
|
+
end
|
75
|
+
@queue = []
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -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 = String.new('').force_encoding(Encoding::BINARY)
|
10
|
+
@offset = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def put(chunk)
|
14
|
+
return unless chunk and chunk.bytesize > 0
|
15
|
+
@buffer << chunk.force_encoding(Encoding::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 |octet|
|
34
|
+
@offset += 1
|
35
|
+
yield octet
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def prune
|
42
|
+
buffer_size = @buffer.bytesize
|
43
|
+
|
44
|
+
if @offset > buffer_size
|
45
|
+
@buffer = String.new('').force_encoding(Encoding::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
|