websocket-driver 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,117 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Client < Hybi
5
+ def self.generate_key
6
+ Base64.encode64((1..16).map { rand(255).chr } * '').strip
7
+ end
8
+
9
+ def initialize(socket, options = {})
10
+ super
11
+
12
+ @ready_state = -1
13
+ @key = Client.generate_key
14
+ @accept = Hybi.generate_accept(@key)
15
+ end
16
+
17
+ def version
18
+ 'hybi-13'
19
+ end
20
+
21
+ def start
22
+ return false unless @ready_state == -1
23
+ @socket.write(handshake_request)
24
+ @ready_state = 0
25
+ true
26
+ end
27
+
28
+ def parse(buffer)
29
+ return super if @ready_state > 0
30
+ message = []
31
+ buffer.each_byte do |data|
32
+ case @ready_state
33
+ when 0 then
34
+ @buffer << data
35
+ validate_handshake if @buffer[-4..-1] == [0x0D, 0x0A, 0x0D, 0x0A]
36
+ when 1 then
37
+ message << data
38
+ end
39
+ end
40
+ parse(message) if @ready_state == 1
41
+ end
42
+
43
+ private
44
+
45
+ def handshake_request
46
+ uri = URI.parse(@socket.url)
47
+ host = uri.host + (uri.port ? ":#{uri.port}" : '')
48
+ path = (uri.path == '') ? '/' : uri.path
49
+ query = uri.query ? "?#{uri.query}" : ''
50
+
51
+ headers = [ "GET #{path}#{query} HTTP/1.1",
52
+ "Host: #{host}",
53
+ "Upgrade: websocket",
54
+ "Connection: Upgrade",
55
+ "Sec-WebSocket-Key: #{@key}",
56
+ "Sec-WebSocket-Version: 13"
57
+ ]
58
+
59
+ if @protocols.size > 0
60
+ headers << "Sec-WebSocket-Protocol: #{@protocols * ', '}"
61
+ end
62
+
63
+ (headers + ['', '']).join("\r\n")
64
+ end
65
+
66
+ def fail_handshake(message)
67
+ message = "Error during WebSocket handshake: #{message}"
68
+ emit(:error, ProtocolError.new(message))
69
+ @ready_state = 3
70
+ emit(:close, CloseEvent.new(ERRORS[:protocol_error], message))
71
+ end
72
+
73
+ def validate_handshake
74
+ data = Driver.encode(@buffer)
75
+ @buffer = []
76
+ response = Net::HTTPResponse.read_new(Net::BufferedIO.new(StringIO.new(data)))
77
+
78
+ unless response.code.to_i == 101
79
+ return fail_handshake("Unexpected response code: #{response.code}")
80
+ end
81
+
82
+ upgrade = response['Upgrade'] || ''
83
+ connection = response['Connection'] || ''
84
+ accept = response['Sec-WebSocket-Accept'] || ''
85
+ protocol = response['Sec-WebSocket-Protocol'] || ''
86
+
87
+ if upgrade == ''
88
+ return fail_handshake("'Upgrade' header is missing")
89
+ elsif upgrade.downcase != 'websocket'
90
+ return fail_handshake("'Upgrade' header value is not 'WebSocket'")
91
+ end
92
+
93
+ if connection == ''
94
+ return fail_handshake("'Connection' header is missing")
95
+ elsif connection.downcase != 'upgrade'
96
+ return fail_handshake("'Connection' header value is not 'Upgrade'")
97
+ end
98
+
99
+ unless accept == @accept
100
+ return fail_handshake('Sec-WebSocket-Accept mismatch')
101
+ end
102
+
103
+ unless protocol == ''
104
+ if @protocols.include?(protocol)
105
+ @protocol = protocol
106
+ else
107
+ return fail_handshake('Sec-WebSocket-Protocol mismatch')
108
+ end
109
+ end
110
+
111
+ open
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+
@@ -0,0 +1,93 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Draft75 < Driver
5
+ def initialize(socket, options = {})
6
+ super
7
+ @stage = 0
8
+ end
9
+
10
+ def version
11
+ 'hixie-75'
12
+ end
13
+
14
+ def parse(buffer)
15
+ buffer = buffer.bytes if buffer.respond_to?(:bytes)
16
+
17
+ buffer.each do |data|
18
+ case @stage
19
+ when -1 then
20
+ @body << data
21
+ send_handshake_body
22
+
23
+ when 0 then
24
+ parse_leading_byte(data)
25
+
26
+ when 1 then
27
+ value = (data & 0x7F)
28
+ @length = value + 128 * @length
29
+
30
+ if @closing and @length.zero?
31
+ @ready_state = 3
32
+ emit(:close, CloseEvent.new(nil, nil))
33
+ elsif (0x80 & data) != 0x80
34
+ if @length.zero?
35
+ @stage = 0
36
+ else
37
+ @skipped = 0
38
+ @stage = 2
39
+ end
40
+ end
41
+
42
+ when 2 then
43
+ if data == 0xFF
44
+ emit(:message, MessageEvent.new(Driver.encode(@buffer)))
45
+ @stage = 0
46
+ else
47
+ if @length
48
+ @skipped += 1
49
+ @stage = 0 if @skipped == @length
50
+ else
51
+ @buffer << data
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def frame(data, type = nil, error_type = nil)
59
+ return queue([data, type, error_type]) if @ready_state == 0
60
+ data = Driver.encode(data)
61
+ frame = ["\x00", data, "\xFF"].map(&Driver.method(:encode)) * ''
62
+ @socket.write(frame)
63
+ true
64
+ end
65
+
66
+ private
67
+
68
+ def handshake_response
69
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
70
+ upgrade << "Upgrade: WebSocket\r\n"
71
+ upgrade << "Connection: Upgrade\r\n"
72
+ upgrade << "WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
73
+ upgrade << "WebSocket-Location: #{@socket.url}\r\n"
74
+ upgrade << "\r\n"
75
+ upgrade
76
+ end
77
+
78
+ def parse_leading_byte(data)
79
+ if (0x80 & data) == 0x80
80
+ @length = 0
81
+ @stage = 1
82
+ else
83
+ @length = nil
84
+ @skipped = nil
85
+ @buffer = []
86
+ @stage = 2
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+ end
93
+
@@ -0,0 +1,97 @@
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.bytes.to_a : []
12
+ end
13
+
14
+ def version
15
+ 'hixie-76'
16
+ end
17
+
18
+ def start
19
+ return false unless super
20
+ send_handshake_body
21
+ true
22
+ end
23
+
24
+ def close(reason = nil, code = nil)
25
+ return false if @ready_state == 3
26
+ @socket.write("\xFF\x00")
27
+ @ready_state = 3
28
+ emit(:close, CloseEvent.new(nil, nil))
29
+ true
30
+ end
31
+
32
+ private
33
+
34
+ def handshake_response
35
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
36
+ upgrade << "Upgrade: WebSocket\r\n"
37
+ upgrade << "Connection: Upgrade\r\n"
38
+ upgrade << "Sec-WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
39
+ upgrade << "Sec-WebSocket-Location: #{@socket.url}\r\n"
40
+ upgrade << "\r\n"
41
+ upgrade
42
+ end
43
+
44
+ def handshake_signature
45
+ return nil unless @body.size >= BODY_SIZE
46
+
47
+ head = @body[0...BODY_SIZE].pack('C*')
48
+ head.force_encoding('ASCII-8BIT') if head.respond_to?(:force_encoding)
49
+
50
+ env = @socket.env
51
+
52
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
53
+ value1 = number_from_key(key1) / spaces_in_key(key1)
54
+
55
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
56
+ value2 = number_from_key(key2) / spaces_in_key(key2)
57
+
58
+ Digest::MD5.digest(big_endian(value1) +
59
+ big_endian(value2) +
60
+ head)
61
+ end
62
+
63
+ def send_handshake_body
64
+ return unless signature = handshake_signature
65
+ @socket.write(signature)
66
+ @stage = 0
67
+ open
68
+ parse(@body[BODY_SIZE..-1]) if @body.size > BODY_SIZE
69
+ end
70
+
71
+ def parse_leading_byte(data)
72
+ return super unless data == 0xFF
73
+ @closing = true
74
+ @length = 0
75
+ @stage = 1
76
+ end
77
+
78
+ def number_from_key(key)
79
+ key.scan(/[0-9]/).join('').to_i(10)
80
+ end
81
+
82
+ def spaces_in_key(key)
83
+ key.scan(/ /).size
84
+ end
85
+
86
+ def big_endian(number)
87
+ string = ''
88
+ [24,16,8,0].each do |offset|
89
+ string << (number >> offset & 0xFF).chr
90
+ end
91
+ string
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+
@@ -0,0 +1,43 @@
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, &listener)
10
+ @listeners[event.to_s] << listener
11
+ end
12
+
13
+ def on(event, &listener)
14
+ add_listener(event, &listener)
15
+ end
16
+
17
+ def remove_listener(event, &listener)
18
+ @listeners[event.to_s].delete(listener)
19
+ end
20
+
21
+ def remove_all_listeners(event = nil)
22
+ if event
23
+ @listeners.delete(event.to_s)
24
+ else
25
+ @listeners.clear
26
+ end
27
+ end
28
+
29
+ def emit(event, *args)
30
+ @listeners[event.to_s].each do |listener|
31
+ listener.call(*args)
32
+ end
33
+ end
34
+
35
+ def listener_count(event)
36
+ list = @listeners[event.to_s]
37
+ list && list.size
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+
@@ -0,0 +1,368 @@
1
+ module WebSocket
2
+ class Driver
3
+
4
+ class Hybi < Driver
5
+ root = File.expand_path('../hybi', __FILE__)
6
+ autoload :StreamReader, root + '/stream_reader'
7
+
8
+ def self.generate_accept(key)
9
+ Base64.encode64(Digest::SHA1.digest(key + GUID)).strip
10
+ end
11
+
12
+ GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
13
+
14
+ BYTE = 0b11111111
15
+ FIN = MASK = 0b10000000
16
+ RSV1 = 0b01000000
17
+ RSV2 = 0b00100000
18
+ RSV3 = 0b00010000
19
+ OPCODE = 0b00001111
20
+ LENGTH = 0b01111111
21
+
22
+ OPCODES = {
23
+ :continuation => 0,
24
+ :text => 1,
25
+ :binary => 2,
26
+ :close => 8,
27
+ :ping => 9,
28
+ :pong => 10
29
+ }
30
+
31
+ OPCODE_CODES = OPCODES.values
32
+ FRAGMENTED_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
33
+ OPENING_OPCODES = OPCODES.values_at(:text, :binary)
34
+
35
+ ERRORS = {
36
+ :normal_closure => 1000,
37
+ :going_away => 1001,
38
+ :protocol_error => 1002,
39
+ :unacceptable => 1003,
40
+ :encoding_error => 1007,
41
+ :policy_violation => 1008,
42
+ :too_large => 1009,
43
+ :extension_error => 1010,
44
+ :unexpected_condition => 1011
45
+ }
46
+
47
+ ERROR_CODES = ERRORS.values
48
+ MIN_RESERVED_ERROR = 3000
49
+ MAX_RESERVED_ERROR = 4999
50
+
51
+ def initialize(socket, options = {})
52
+ super
53
+ reset
54
+
55
+ @reader = StreamReader.new
56
+ @stage = 0
57
+ @masking = options[:masking]
58
+ @protocols = options[:protocols] || []
59
+ @protocols = @protocols.strip.split(/\s*,\s*/) if String === @protocols
60
+
61
+ @require_masking = options[:require_masking]
62
+ @ping_callbacks = {}
63
+ end
64
+
65
+ def version
66
+ "hybi-#{@socket.env['HTTP_SEC_WEBSOCKET_VERSION']}"
67
+ end
68
+
69
+ def parse(data)
70
+ data = data.bytes.to_a if data.respond_to?(:bytes)
71
+ @reader.put(data)
72
+ buffer = true
73
+ while buffer
74
+ case @stage
75
+ when 0 then
76
+ buffer = @reader.read(1)
77
+ parse_opcode(buffer[0]) if buffer
78
+
79
+ when 1 then
80
+ buffer = @reader.read(1)
81
+ parse_length(buffer[0]) if buffer
82
+
83
+ when 2 then
84
+ buffer = @reader.read(@length_size)
85
+ parse_extended_length(buffer) if buffer
86
+
87
+ when 3 then
88
+ buffer = @reader.read(4)
89
+ if buffer
90
+ @mask = buffer
91
+ @stage = 4
92
+ end
93
+
94
+ when 4 then
95
+ buffer = @reader.read(@length)
96
+ if buffer
97
+ @payload = buffer
98
+ emit_frame
99
+ @stage = 0
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def frame(data, type = nil, code = nil)
106
+ return queue([data, type, code]) if @ready_state == 0
107
+ return false unless @ready_state == 1
108
+
109
+ data = data.to_s unless Array === data
110
+ data = Driver.encode(data) if String === data
111
+
112
+ is_text = (String === data)
113
+ opcode = OPCODES[type || (is_text ? :text : :binary)]
114
+ buffer = data.respond_to?(:bytes) ? data.bytes.to_a : data
115
+ insert = code ? 2 : 0
116
+ length = buffer.size + insert
117
+ header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10)
118
+ offset = header + (@masking ? 4 : 0)
119
+ masked = @masking ? MASK : 0
120
+ frame = Array.new(offset)
121
+
122
+ frame[0] = FIN | opcode
123
+
124
+ if length <= 125
125
+ frame[1] = masked | length
126
+ elsif length <= 65535
127
+ frame[1] = masked | 126
128
+ frame[2] = (length >> 8) & BYTE
129
+ frame[3] = length & BYTE
130
+ else
131
+ frame[1] = masked | 127
132
+ frame[2] = (length >> 56) & BYTE
133
+ frame[3] = (length >> 48) & BYTE
134
+ frame[4] = (length >> 40) & BYTE
135
+ frame[5] = (length >> 32) & BYTE
136
+ frame[6] = (length >> 24) & BYTE
137
+ frame[7] = (length >> 16) & BYTE
138
+ frame[8] = (length >> 8) & BYTE
139
+ frame[9] = length & BYTE
140
+ end
141
+
142
+ if code
143
+ buffer = [(code >> 8) & BYTE, code & BYTE] + buffer
144
+ end
145
+
146
+ if @masking
147
+ mask = [rand(256), rand(256), rand(256), rand(256)]
148
+ frame[header...offset] = mask
149
+ buffer = Mask.mask(buffer, mask)
150
+ end
151
+
152
+ frame.concat(buffer)
153
+
154
+ @socket.write(Driver.encode(frame))
155
+ true
156
+ end
157
+
158
+ def text(message)
159
+ frame(message, :text)
160
+ end
161
+
162
+ def binary(message)
163
+ frame(message, :binary)
164
+ end
165
+
166
+ def ping(message = '', &callback)
167
+ @ping_callbacks[message] = callback if callback
168
+ frame(message, :ping)
169
+ end
170
+
171
+ def close(reason = nil, code = nil)
172
+ reason ||= ''
173
+ code ||= ERRORS[:normal_closure]
174
+
175
+ case @ready_state
176
+ when 0 then
177
+ @ready_state = 3
178
+ emit(:close, CloseEvent.new(code, reason))
179
+ true
180
+ when 1 then
181
+ frame(reason, :close, code)
182
+ @ready_state = 2
183
+ true
184
+ else
185
+ false
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ def handshake_response
192
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
193
+ return '' unless String === sec_key
194
+
195
+ accept = Hybi.generate_accept(sec_key)
196
+ protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
197
+ supported = @protocols
198
+ proto = nil
199
+
200
+ headers = [
201
+ "HTTP/1.1 101 Switching Protocols",
202
+ "Upgrade: websocket",
203
+ "Connection: Upgrade",
204
+ "Sec-WebSocket-Accept: #{accept}"
205
+ ]
206
+
207
+ if protos
208
+ protos = protos.split(/\s*,\s*/) if String === protos
209
+ proto = protos.find { |p| supported.include?(p) }
210
+ if proto
211
+ @protocol = proto
212
+ headers << "Sec-WebSocket-Protocol: #{proto}"
213
+ end
214
+ end
215
+
216
+ (headers + ['','']).join("\r\n")
217
+ end
218
+
219
+ def shutdown(code, reason)
220
+ frame(reason, :close, code)
221
+ @ready_state = 3
222
+ emit(:close, CloseEvent.new(code, reason))
223
+ end
224
+
225
+ def fail(type, message)
226
+ emit(:error, ProtocolError.new(message))
227
+ shutdown(ERRORS[type], message)
228
+ end
229
+
230
+ def parse_opcode(data)
231
+ rsvs = [RSV1, RSV2, RSV3].map { |rsv| (data & rsv) == rsv }
232
+
233
+ if rsvs.any?
234
+ return fail(:protocol_error,
235
+ "One or more reserved bits are on: reserved1 = #{rsvs[0] ? 1 : 0}" +
236
+ ", reserved2 = #{rsvs[1] ? 1 : 0 }" +
237
+ ", reserved3 = #{rsvs[2] ? 1 : 0 }")
238
+ end
239
+
240
+ @final = (data & FIN) == FIN
241
+ @opcode = (data & OPCODE)
242
+ @mask = []
243
+ @payload = []
244
+
245
+ unless OPCODES.values.include?(@opcode)
246
+ return fail(:protocol_error, "Unrecognized frame opcode: #{@opcode}")
247
+ end
248
+
249
+ unless FRAGMENTED_OPCODES.include?(@opcode) or @final
250
+ return fail(:protocol_error, "Received fragmented control frame: opcode = #{@opcode}")
251
+ end
252
+
253
+ if @mode and OPENING_OPCODES.include?(@opcode)
254
+ return fail(:protocol_error, 'Received new data frame but previous continuous frame is unfinished')
255
+ end
256
+
257
+ @stage = 1
258
+ end
259
+
260
+ def parse_length(data)
261
+ @masked = (data & MASK) == MASK
262
+ if @require_masking and not @masked
263
+ return fail(:unacceptable, 'Received unmasked frame but masking is required')
264
+ end
265
+
266
+ @length = (data & LENGTH)
267
+
268
+ if @length <= 125
269
+ @stage = @masked ? 3 : 4
270
+ else
271
+ @length_size = (@length == 126) ? 2 : 8
272
+ @stage = 2
273
+ end
274
+ end
275
+
276
+ def parse_extended_length(buffer)
277
+ @length = integer(buffer)
278
+
279
+ unless FRAGMENTED_OPCODES.include?(@opcode) or @length <= 125
280
+ return fail(:protocol_error, "Received control frame having too long payload: #{@length}")
281
+ end
282
+
283
+ @stage = @masked ? 3 : 4
284
+ end
285
+
286
+ def emit_frame
287
+ payload = @masked ? Mask.mask(@payload, @mask) : @payload
288
+
289
+ case @opcode
290
+ when OPCODES[:continuation] then
291
+ return fail(:protocol_error, 'Received unexpected continuation frame') unless @mode
292
+ @buffer.concat(payload)
293
+ if @final
294
+ message = @buffer
295
+ message = Driver.encode(message, true) if @mode == :text
296
+ reset
297
+ if message
298
+ emit(:message, MessageEvent.new(message))
299
+ else
300
+ fail(:encoding_error, 'Could not decode a text frame as UTF-8')
301
+ end
302
+ end
303
+
304
+ when OPCODES[:text] then
305
+ if @final
306
+ message = Driver.encode(payload, true)
307
+ if message
308
+ emit(:message, MessageEvent.new(message))
309
+ else
310
+ fail(:encoding_error, 'Could not decode a text frame as UTF-8')
311
+ end
312
+ else
313
+ @mode = :text
314
+ @buffer.concat(payload)
315
+ end
316
+
317
+ when OPCODES[:binary] then
318
+ if @final
319
+ emit(:message, MessageEvent.new(payload))
320
+ else
321
+ @mode = :binary
322
+ @buffer.concat(payload)
323
+ end
324
+
325
+ when OPCODES[:close] then
326
+ code = (payload.size >= 2) ? 256 * payload[0] + payload[1] : nil
327
+
328
+ unless (payload.size == 0) or
329
+ (code && code >= MIN_RESERVED_ERROR && code <= MAX_RESERVED_ERROR) or
330
+ ERROR_CODES.include?(code)
331
+ code = ERRORS[:protocol_error]
332
+ end
333
+
334
+ if payload.size > 125 or not Driver.valid_utf8?(payload[2..-1] || [])
335
+ code = ERRORS[:protocol_error]
336
+ end
337
+
338
+ reason = (payload.size > 2) ? Driver.encode(payload[2..-1], true) : ''
339
+ shutdown(code, reason || '')
340
+
341
+ when OPCODES[:ping] then
342
+ frame(payload, :pong)
343
+
344
+ when OPCODES[:pong] then
345
+ message = Driver.encode(payload, true)
346
+ callback = @ping_callbacks[message]
347
+ @ping_callbacks.delete(message)
348
+ callback.call if callback
349
+ end
350
+ end
351
+
352
+ def reset
353
+ @buffer = []
354
+ @mode = nil
355
+ end
356
+
357
+ def integer(bytes)
358
+ number = 0
359
+ bytes.each_with_index do |data, i|
360
+ number += data << (8 * (bytes.size - 1 - i))
361
+ end
362
+ number
363
+ end
364
+ end
365
+
366
+ end
367
+ end
368
+