websocket-driver 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Draft75 < Driver
5
+ def initialize(socket, options = {})
6
+ super
7
+
8
+ @stage = 0
9
+ @closing = false
10
+
11
+ @headers['Upgrade'] = 'WebSocket'
12
+ @headers['Connection'] = 'Upgrade'
13
+ @headers['WebSocket-Origin'] = @socket.env['HTTP_ORIGIN']
14
+ @headers['WebSocket-Location'] = @socket.url
15
+ end
16
+
17
+ def version
18
+ 'hixie-75'
19
+ end
20
+
21
+ def close(reason = nil, code = nil)
22
+ return false if @ready_state == 3
23
+ @ready_state = 3
24
+ emit(:close, CloseEvent.new(nil, nil))
25
+ true
26
+ end
27
+
28
+ def parse(chunk)
29
+ return if @ready_state > 1
30
+
31
+ @reader.put(chunk)
32
+
33
+ @reader.each_byte do |octet|
34
+ case @stage
35
+ when -1 then
36
+ @body << octet
37
+ send_handshake_body
38
+
39
+ when 0 then
40
+ parse_leading_byte(octet)
41
+
42
+ when 1 then
43
+ @length = (octet & 0x7F) + 128 * @length
44
+
45
+ if @closing and @length.zero?
46
+ return close
47
+ elsif (octet & 0x80) != 0x80
48
+ if @length.zero?
49
+ @stage = 0
50
+ else
51
+ @skipped = 0
52
+ @stage = 2
53
+ end
54
+ end
55
+
56
+ when 2 then
57
+ if octet == 0xFF
58
+ @stage = 0
59
+ emit(:message, MessageEvent.new(Driver.encode(@buffer, UNICODE)))
60
+ else
61
+ if @length
62
+ @skipped += 1
63
+ @stage = 0 if @skipped == @length
64
+ else
65
+ @buffer << octet
66
+ return close if @buffer.size > @max_length
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def frame(buffer, type = nil, error_type = nil)
74
+ return queue([buffer, type, error_type]) if @ready_state == 0
75
+ frame = [0x00, buffer, 0xFF].pack('CA*C')
76
+ @socket.write(frame)
77
+ true
78
+ end
79
+
80
+ private
81
+
82
+ def handshake_response
83
+ start = 'HTTP/1.1 101 Web Socket Protocol Handshake'
84
+ headers = [start, @headers.to_s, '']
85
+ headers.join("\r\n")
86
+ end
87
+
88
+ def parse_leading_byte(octet)
89
+ if (octet & 0x80) == 0x80
90
+ @length = 0
91
+ @stage = 1
92
+ else
93
+ @length = nil
94
+ @skipped = nil
95
+ @buffer = []
96
+ @stage = 2
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,98 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Draft76 < Draft75
5
+ BODY_SIZE = 8
6
+
7
+ def initialize(socket, options = {})
8
+ super
9
+ input = @socket.env['rack.input']
10
+ @stage = -1
11
+ @body = (input ? input.read : String.new('')).force_encoding(BINARY)
12
+
13
+ @headers.clear
14
+ @headers['Upgrade'] = 'WebSocket'
15
+ @headers['Connection'] = 'Upgrade'
16
+ @headers['Sec-WebSocket-Origin'] = @socket.env['HTTP_ORIGIN']
17
+ @headers['Sec-WebSocket-Location'] = @socket.url
18
+ end
19
+
20
+ def version
21
+ 'hixie-76'
22
+ end
23
+
24
+ def start
25
+ return false unless super
26
+ send_handshake_body
27
+ true
28
+ end
29
+
30
+ def close(reason = nil, code = nil)
31
+ return false if @ready_state == 3
32
+ @socket.write([0xFF, 0x00].pack('C*')) if @ready_state == 1
33
+ @ready_state = 3
34
+ emit(:close, CloseEvent.new(nil, nil))
35
+ true
36
+ end
37
+
38
+ private
39
+
40
+ def handshake_response
41
+ env = @socket.env
42
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
43
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
44
+
45
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key1') unless key1
46
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key2') unless key2
47
+
48
+ number1 = number_from_key(key1)
49
+ spaces1 = spaces_in_key(key1)
50
+
51
+ number2 = number_from_key(key2)
52
+ spaces2 = spaces_in_key(key2)
53
+
54
+ if number1 % spaces1 != 0 or number2 % spaces2 != 0
55
+ raise ProtocolError.new('Client sent invalid Sec-WebSocket-Key headers')
56
+ end
57
+
58
+ @key_values = [number1 / spaces1, number2 / spaces2]
59
+
60
+ start = 'HTTP/1.1 101 WebSocket Protocol Handshake'
61
+ headers = [start, @headers.to_s, '']
62
+ headers.join("\r\n")
63
+ end
64
+
65
+ def handshake_signature
66
+ return nil unless @body.bytesize >= BODY_SIZE
67
+
68
+ head = @body[0...BODY_SIZE]
69
+ Digest::MD5.digest((@key_values + [head]).pack('N2A*'))
70
+ end
71
+
72
+ def send_handshake_body
73
+ return unless signature = handshake_signature
74
+ @socket.write(signature)
75
+ @stage = 0
76
+ open
77
+ parse(@body[BODY_SIZE..-1]) if @body.bytesize > BODY_SIZE
78
+ end
79
+
80
+ def parse_leading_byte(octet)
81
+ return super unless octet == 0xFF
82
+ @closing = true
83
+ @length = 0
84
+ @stage = 1
85
+ end
86
+
87
+ def number_from_key(key)
88
+ number = key.scan(/[0-9]/).join('')
89
+ number == '' ? Float::NAN : number.to_i(10)
90
+ end
91
+
92
+ def spaces_in_key(key)
93
+ key.scan(/ /).size
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,54 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ module EventEmitter
5
+ def initialize
6
+ @listeners = Hash.new { |h,k| h[k] = [] }
7
+ end
8
+
9
+ def add_listener(event, callable = nil, &block)
10
+ listener = callable || block
11
+ @listeners[event.to_s] << listener
12
+ listener
13
+ end
14
+
15
+ def on(event, callable = nil, &block)
16
+ if callable
17
+ add_listener(event, callable)
18
+ else
19
+ add_listener(event, &block)
20
+ end
21
+ end
22
+
23
+ def remove_listener(event, callable = nil, &block)
24
+ listener = callable || block
25
+ @listeners[event.to_s].delete(listener)
26
+ listener
27
+ end
28
+
29
+ def remove_all_listeners(event = nil)
30
+ if event
31
+ @listeners.delete(event.to_s)
32
+ else
33
+ @listeners.clear
34
+ end
35
+ end
36
+
37
+ def emit(event, *args)
38
+ @listeners[event.to_s].dup.each do |listener|
39
+ listener.call(*args)
40
+ end
41
+ end
42
+
43
+ def listener_count(event)
44
+ return 0 unless @listeners.has_key?(event.to_s)
45
+ @listeners[event.to_s].size
46
+ end
47
+
48
+ def listeners(event)
49
+ @listeners[event.to_s]
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Headers
5
+ ALLOWED_DUPLICATES = %w[set-cookie set-cookie2 warning www-authenticate]
6
+
7
+ def initialize(received = {})
8
+ @raw = received
9
+ clear
10
+
11
+ @received = {}
12
+ @raw.each { |k,v| @received[HTTP.normalize_header(k)] = v }
13
+ end
14
+
15
+ def clear
16
+ @sent = Set.new
17
+ @lines = []
18
+ end
19
+
20
+ def [](name)
21
+ @received[HTTP.normalize_header(name)]
22
+ end
23
+
24
+ def []=(name, value)
25
+ return if value.nil?
26
+ key = HTTP.normalize_header(name)
27
+ return unless @sent.add?(key) or ALLOWED_DUPLICATES.include?(key)
28
+ @lines << "#{name.strip}: #{value.to_s.strip}\r\n"
29
+ end
30
+
31
+ def inspect
32
+ @raw.inspect
33
+ end
34
+
35
+ def to_h
36
+ @raw.dup
37
+ end
38
+
39
+ def to_s
40
+ @lines.join('')
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,414 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Hybi < Driver
5
+ root = File.expand_path('../hybi', __FILE__)
6
+
7
+ autoload :Frame, root + '/frame'
8
+ autoload :Message, root + '/message'
9
+
10
+ def self.generate_accept(key)
11
+ Base64.strict_encode64(Digest::SHA1.digest(key + GUID))
12
+ end
13
+
14
+ VERSION = '13'
15
+ GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
16
+
17
+ BYTE = 0b11111111
18
+ FIN = MASK = 0b10000000
19
+ RSV1 = 0b01000000
20
+ RSV2 = 0b00100000
21
+ RSV3 = 0b00010000
22
+ OPCODE = 0b00001111
23
+ LENGTH = 0b01111111
24
+
25
+ OPCODES = {
26
+ :continuation => 0,
27
+ :text => 1,
28
+ :binary => 2,
29
+ :close => 8,
30
+ :ping => 9,
31
+ :pong => 10
32
+ }
33
+
34
+ OPCODE_CODES = OPCODES.values
35
+ MESSAGE_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
36
+ OPENING_OPCODES = OPCODES.values_at(:text, :binary)
37
+
38
+ ERRORS = {
39
+ :normal_closure => 1000,
40
+ :going_away => 1001,
41
+ :protocol_error => 1002,
42
+ :unacceptable => 1003,
43
+ :encoding_error => 1007,
44
+ :policy_violation => 1008,
45
+ :too_large => 1009,
46
+ :extension_error => 1010,
47
+ :unexpected_condition => 1011
48
+ }
49
+
50
+ ERROR_CODES = ERRORS.values
51
+ DEFAULT_ERROR_CODE = 1000
52
+ MIN_RESERVED_ERROR = 3000
53
+ MAX_RESERVED_ERROR = 4999
54
+
55
+ PACK_FORMATS = {2 => 'n', 8 => 'Q>'}
56
+
57
+ def initialize(socket, options = {})
58
+ super
59
+
60
+ @extensions = ::WebSocket::Extensions.new
61
+ @stage = 0
62
+ @masking = options[:masking]
63
+ @protocols = options[:protocols] || []
64
+ @protocols = @protocols.strip.split(/ *, */) if String === @protocols
65
+ @require_masking = options[:require_masking]
66
+ @ping_callbacks = {}
67
+
68
+ @frame = @message = nil
69
+
70
+ return unless @socket.respond_to?(:env)
71
+
72
+ if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
73
+ protos = protos.split(/ *, */) if String === protos
74
+ @protocol = protos.find { |p| @protocols.include?(p) }
75
+ else
76
+ @protocol = nil
77
+ end
78
+ end
79
+
80
+ def version
81
+ "hybi-#{VERSION}"
82
+ end
83
+
84
+ def add_extension(extension)
85
+ @extensions.add(extension)
86
+ true
87
+ end
88
+
89
+ def parse(chunk)
90
+ @reader.put(chunk)
91
+ buffer = true
92
+ while buffer
93
+ case @stage
94
+ when 0 then
95
+ buffer = @reader.read(1)
96
+ parse_opcode(buffer.getbyte(0)) if buffer
97
+
98
+ when 1 then
99
+ buffer = @reader.read(1)
100
+ parse_length(buffer.getbyte(0)) if buffer
101
+
102
+ when 2 then
103
+ buffer = @reader.read(@frame.length_bytes)
104
+ parse_extended_length(buffer) if buffer
105
+
106
+ when 3 then
107
+ buffer = @reader.read(4)
108
+ if buffer
109
+ @stage = 4
110
+ @frame.masking_key = buffer
111
+ end
112
+
113
+ when 4 then
114
+ buffer = @reader.read(@frame.length)
115
+
116
+ if buffer
117
+ @stage = 0
118
+ emit_frame(buffer)
119
+ end
120
+
121
+ else
122
+ buffer = nil
123
+ end
124
+ end
125
+ end
126
+
127
+ def binary(message)
128
+ frame(message, :binary)
129
+ end
130
+
131
+ def ping(message = '', &callback)
132
+ @ping_callbacks[message] = callback if callback
133
+ frame(message, :ping)
134
+ end
135
+
136
+ def pong(message = '')
137
+ frame(message, :pong)
138
+ end
139
+
140
+ def close(reason = nil, code = nil)
141
+ reason ||= ''
142
+ code ||= ERRORS[:normal_closure]
143
+
144
+ if @ready_state <= 0
145
+ @ready_state = 3
146
+ emit(:close, CloseEvent.new(code, reason))
147
+ true
148
+ elsif @ready_state == 1
149
+ frame(reason, :close, code)
150
+ @ready_state = 2
151
+ true
152
+ else
153
+ false
154
+ end
155
+ end
156
+
157
+ def frame(buffer, type = nil, code = nil)
158
+ return queue([buffer, type, code]) if @ready_state <= 0
159
+ return false unless @ready_state == 1
160
+
161
+ message = Message.new
162
+ frame = Frame.new
163
+ is_text = String === buffer
164
+
165
+ message.rsv1 = message.rsv2 = message.rsv3 = false
166
+ message.opcode = OPCODES[type || (is_text ? :text : :binary)]
167
+
168
+ payload = is_text ? buffer.bytes.to_a : buffer
169
+ payload = [code].pack(PACK_FORMATS[2]).bytes.to_a + payload if code
170
+ message.data = payload.pack('C*')
171
+
172
+ if MESSAGE_OPCODES.include?(message.opcode)
173
+ message = @extensions.process_outgoing_message(message)
174
+ end
175
+
176
+ frame.final = true
177
+ frame.rsv1 = message.rsv1
178
+ frame.rsv2 = message.rsv2
179
+ frame.rsv3 = message.rsv3
180
+ frame.opcode = message.opcode
181
+ frame.masked = !!@masking
182
+ frame.masking_key = SecureRandom.random_bytes(4) if frame.masked
183
+ frame.length = message.data.bytesize
184
+ frame.payload = message.data
185
+
186
+ send_frame(frame)
187
+ true
188
+
189
+ rescue ::WebSocket::Extensions::ExtensionError => error
190
+ fail(:extension_error, error.message)
191
+ end
192
+
193
+ private
194
+
195
+ def send_frame(frame)
196
+ length = frame.length
197
+ buffer = []
198
+ masked = frame.masked ? MASK : 0
199
+
200
+ buffer[0] = (frame.final ? FIN : 0) |
201
+ (frame.rsv1 ? RSV1 : 0) |
202
+ (frame.rsv2 ? RSV2 : 0) |
203
+ (frame.rsv3 ? RSV3 : 0) |
204
+ frame.opcode
205
+
206
+ if length <= 125
207
+ buffer[1] = masked | length
208
+ elsif length <= 65535
209
+ buffer[1] = masked | 126
210
+ buffer[2..3] = [length].pack(PACK_FORMATS[2]).bytes.to_a
211
+ else
212
+ buffer[1] = masked | 127
213
+ buffer[2..9] = [length].pack(PACK_FORMATS[8]).bytes.to_a
214
+ end
215
+
216
+ if frame.masked
217
+ buffer.concat(frame.masking_key.bytes.to_a)
218
+ buffer.concat(Mask.mask(frame.payload, frame.masking_key).bytes.to_a)
219
+ else
220
+ buffer.concat(frame.payload.bytes.to_a)
221
+ end
222
+
223
+ @socket.write(buffer.pack('C*'))
224
+ end
225
+
226
+ def handshake_response
227
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
228
+ version = @socket.env['HTTP_SEC_WEBSOCKET_VERSION']
229
+
230
+ unless version == VERSION
231
+ raise ProtocolError.new("Unsupported WebSocket version: #{VERSION}")
232
+ end
233
+
234
+ unless sec_key
235
+ raise ProtocolError.new('Missing handshake request header: Sec-WebSocket-Key')
236
+ end
237
+
238
+ @headers['Upgrade'] = 'websocket'
239
+ @headers['Connection'] = 'Upgrade'
240
+ @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
241
+
242
+ @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
243
+
244
+ extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
245
+ @headers['Sec-WebSocket-Extensions'] = extensions if extensions
246
+
247
+ start = 'HTTP/1.1 101 Switching Protocols'
248
+ headers = [start, @headers.to_s, '']
249
+ headers.join("\r\n")
250
+ end
251
+
252
+ def shutdown(code, reason, error = false)
253
+ @frame = @message = nil
254
+ @stage = 5
255
+ @extensions.close
256
+
257
+ frame(reason, :close, code) if @ready_state < 2
258
+ @ready_state = 3
259
+
260
+ emit(:error, ProtocolError.new(reason)) if error
261
+ emit(:close, CloseEvent.new(code, reason))
262
+ end
263
+
264
+ def fail(type, message)
265
+ return if @ready_state > 1
266
+ shutdown(ERRORS[type], message, true)
267
+ end
268
+
269
+ def parse_opcode(octet)
270
+ rsvs = [RSV1, RSV2, RSV3].map { |rsv| (octet & rsv) == rsv }
271
+
272
+ @frame = Frame.new
273
+
274
+ @frame.final = (octet & FIN) == FIN
275
+ @frame.rsv1 = rsvs[0]
276
+ @frame.rsv2 = rsvs[1]
277
+ @frame.rsv3 = rsvs[2]
278
+ @frame.opcode = (octet & OPCODE)
279
+
280
+ @stage = 1
281
+
282
+ unless @extensions.valid_frame_rsv?(@frame)
283
+ return fail(:protocol_error,
284
+ "One or more reserved bits are on: reserved1 = #{@frame.rsv1 ? 1 : 0}" +
285
+ ", reserved2 = #{@frame.rsv2 ? 1 : 0 }" +
286
+ ", reserved3 = #{@frame.rsv3 ? 1 : 0 }")
287
+ end
288
+
289
+ unless OPCODES.values.include?(@frame.opcode)
290
+ return fail(:protocol_error, "Unrecognized frame opcode: #{@frame.opcode}")
291
+ end
292
+
293
+ unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.final
294
+ return fail(:protocol_error, "Received fragmented control frame: opcode = #{@frame.opcode}")
295
+ end
296
+
297
+ if @message and OPENING_OPCODES.include?(@frame.opcode)
298
+ return fail(:protocol_error, 'Received new data frame but previous continuous frame is unfinished')
299
+ end
300
+ end
301
+
302
+ def parse_length(octet)
303
+ @frame.masked = (octet & MASK) == MASK
304
+ @frame.length = (octet & LENGTH)
305
+
306
+ if @frame.length >= 0 and @frame.length <= 125
307
+ @stage = @frame.masked ? 3 : 4
308
+ return unless check_frame_length
309
+ else
310
+ @stage = 2
311
+ @frame.length_bytes = (@frame.length == 126) ? 2 : 8
312
+ end
313
+
314
+ if @require_masking and not @frame.masked
315
+ return fail(:unacceptable, 'Received unmasked frame but masking is required')
316
+ end
317
+ end
318
+
319
+ def parse_extended_length(buffer)
320
+ @frame.length = buffer.unpack(PACK_FORMATS[buffer.bytesize]).first
321
+ @stage = @frame.masked ? 3 : 4
322
+
323
+ unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
324
+ return fail(:protocol_error, "Received control frame having too long payload: #{@frame.length}")
325
+ end
326
+
327
+ return unless check_frame_length
328
+ end
329
+
330
+ def check_frame_length
331
+ length = @message ? @message.data.bytesize : 0
332
+
333
+ if length + @frame.length > @max_length
334
+ fail(:too_large, 'WebSocket frame length too large')
335
+ false
336
+ else
337
+ true
338
+ end
339
+ end
340
+
341
+ def emit_frame(buffer)
342
+ frame = @frame
343
+ opcode = frame.opcode
344
+ payload = frame.payload = Mask.mask(buffer, @frame.masking_key)
345
+ bytesize = payload.bytesize
346
+ bytes = payload.bytes.to_a
347
+
348
+ @frame = nil
349
+
350
+ case opcode
351
+ when OPCODES[:continuation] then
352
+ return fail(:protocol_error, 'Received unexpected continuation frame') unless @message
353
+ @message << frame
354
+
355
+ when OPCODES[:text], OPCODES[:binary] then
356
+ @message = Message.new
357
+ @message << frame
358
+
359
+ when OPCODES[:close] then
360
+ code = (bytesize >= 2) ? payload.unpack(PACK_FORMATS[2]).first : nil
361
+ reason = (bytesize > 2) ? Driver.encode(bytes[2..-1] || [], UNICODE) : nil
362
+
363
+ unless (bytesize == 0) or
364
+ (code && code >= MIN_RESERVED_ERROR && code <= MAX_RESERVED_ERROR) or
365
+ ERROR_CODES.include?(code)
366
+ code = ERRORS[:protocol_error]
367
+ end
368
+
369
+ if bytesize > 125 or (bytesize > 2 and reason.nil?)
370
+ code = ERRORS[:protocol_error]
371
+ end
372
+
373
+ shutdown(code || DEFAULT_ERROR_CODE, reason || '')
374
+
375
+ when OPCODES[:ping] then
376
+ frame(payload, :pong)
377
+ emit(:ping, PingEvent.new(payload))
378
+
379
+ when OPCODES[:pong] then
380
+ message = Driver.encode(payload, UNICODE)
381
+ callback = @ping_callbacks[message]
382
+ @ping_callbacks.delete(message)
383
+ callback.call if callback
384
+ emit(:pong, PongEvent.new(payload))
385
+ end
386
+
387
+ emit_message if frame.final and MESSAGE_OPCODES.include?(opcode)
388
+ end
389
+
390
+ def emit_message
391
+ message = @extensions.process_incoming_message(@message)
392
+ @message = nil
393
+
394
+ payload = message.data
395
+
396
+ case message.opcode
397
+ when OPCODES[:text] then
398
+ payload = Driver.encode(payload, UNICODE)
399
+ when OPCODES[:binary]
400
+ payload = payload.bytes.to_a
401
+ end
402
+
403
+ if payload
404
+ emit(:message, MessageEvent.new(payload))
405
+ else
406
+ fail(:encoding_error, 'Could not decode a text frame as UTF-8')
407
+ end
408
+ rescue ::WebSocket::Extensions::ExtensionError => error
409
+ fail(:extension_error, error.message)
410
+ end
411
+ end
412
+
413
+ end
414
+ end