websocket-driver-kontena 0.6.5

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