faye-websocket 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of faye-websocket might be problematic. Click here for more details.

@@ -0,0 +1,110 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ CONNECTING = 0
5
+ OPEN = 1
6
+ CLOSING = 2
7
+ CLOSED = 3
8
+
9
+ module API
10
+ attr_reader :url, :ready_state, :buffered_amount
11
+ attr_accessor :onopen, :onmessage, :onerror, :onclose
12
+
13
+ def receive(data)
14
+ return false unless ready_state == OPEN
15
+ event = Event.new('message')
16
+ event.init_event('message', false, false)
17
+ event.data = data
18
+ dispatch_event(event)
19
+ end
20
+
21
+ def send(data, type = nil, error_type = nil)
22
+ return false if ready_state == CLOSED
23
+ data = WebSocket.encode(data) if String === data
24
+ frame = @parser.frame(data, type, error_type)
25
+ @stream.write(frame) if frame
26
+ end
27
+
28
+ def close(code = nil, reason = nil, ack = true)
29
+ return if [CLOSING, CLOSED].include?(ready_state)
30
+
31
+ @ready_state = CLOSING
32
+
33
+ close = lambda do
34
+ @ready_state = CLOSED
35
+ @stream.close_connection_after_writing
36
+ event = Event.new('close', :code => code || 1000, :reason => reason || '')
37
+ event.init_event('close', false, false)
38
+ dispatch_event(event)
39
+ end
40
+
41
+ if ack
42
+ if @parser.respond_to?(:close)
43
+ @parser.close(code, reason, &close)
44
+ else
45
+ close.call
46
+ end
47
+ else
48
+ @parser.close(code, reason) if @parser.respond_to?(:close)
49
+ close.call
50
+ end
51
+ end
52
+
53
+ def add_event_listener(type, listener, use_capture)
54
+ @listeners ||= {}
55
+ list = @listeners[event_type] ||= []
56
+ list << listener
57
+ end
58
+
59
+ def remove_event_listener(type, listener, use_capture)
60
+ return unless @listeners and @listeners[event_type]
61
+ return @listeners.delete(event_type) unless listener
62
+
63
+ @listeners[event_type].delete_if(&listener.method(:==))
64
+ end
65
+
66
+ def dispatch_event(event)
67
+ event.target = event.current_target = self
68
+ event.event_phase = Event::AT_TARGET
69
+
70
+ callback = __send__("on#{ event.type }")
71
+ callback.call(event) if callback
72
+
73
+ return unless @listeners and @listeners[event_type]
74
+ @listeners[event_type].each do |listener|
75
+ listener.call(*args)
76
+ end
77
+ end
78
+ end
79
+
80
+ class Event
81
+ attr_reader :type, :bubbles, :cancelable
82
+ attr_accessor :target, :current_target, :event_phase, :data
83
+
84
+ CAPTURING_PHASE = 1
85
+ AT_TARGET = 2
86
+ BUBBLING_PHASE = 3
87
+
88
+ def initialize(event_type, options = {})
89
+ @type = event_type
90
+ metaclass = (class << self ; self ; end)
91
+ options.each do |key, value|
92
+ metaclass.__send__(:define_method, key) { value }
93
+ end
94
+ end
95
+
96
+ def init_event(event_type, can_bubble, cancelable)
97
+ @type = event_type
98
+ @bubbles = can_bubble
99
+ @cancelable = cancelable
100
+ end
101
+
102
+ def stop_propagation
103
+ end
104
+
105
+ def prevent_default
106
+ end
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Client
5
+ include API
6
+ attr_reader :uri
7
+
8
+ def initialize(url)
9
+ @parser = Protocol8Parser.new(self, :masking => true)
10
+ @url = url
11
+ @uri = URI.parse(url)
12
+
13
+ @ready_state = CONNECTING
14
+ @buffered_amount = 0
15
+
16
+ port = @uri.port || (@uri.scheme == 'wss' ? 443 : 80)
17
+
18
+ EventMachine.connect(@uri.host, port, Connection) do |conn|
19
+ @stream = conn
20
+ conn.parent = self
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def on_connect
27
+ @stream.start_tls if @uri.scheme == 'wss'
28
+ @handshake = @parser.create_handshake
29
+ @message = []
30
+ @stream.write(@handshake.request_data)
31
+ end
32
+
33
+ def receive_data(data)
34
+ data = WebSocket.encode(data)
35
+
36
+ case @ready_state
37
+ when CONNECTING then
38
+ @message += @handshake.parse(data)
39
+ return unless @handshake.complete?
40
+
41
+ if @handshake.valid?
42
+ @ready_state = OPEN
43
+ event = Event.new('open')
44
+ event.init_event('open', false, false)
45
+ dispatch_event(event)
46
+
47
+ receive_data(@message)
48
+ else
49
+ @ready_state = CLOSED
50
+ event = Event.new('error')
51
+ event.init_event('error', false, false)
52
+ dispatch_event(event)
53
+ end
54
+
55
+ when OPEN, CLOSING then
56
+ @parser.parse(data)
57
+ end
58
+ end
59
+
60
+ module Connection
61
+ attr_accessor :parent
62
+
63
+ def connection_completed
64
+ parent.__send__(:on_connect)
65
+ end
66
+
67
+ def receive_data(data)
68
+ parent.__send__(:receive_data, data)
69
+ end
70
+
71
+ def unbind
72
+ parent.close(1006, '', false)
73
+ end
74
+
75
+ def write(data)
76
+ send_data(data)
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,53 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Draft75Parser
5
+ def initialize(web_socket)
6
+ @socket = web_socket
7
+ @buffer = []
8
+ @buffering = false
9
+ end
10
+
11
+ def version
12
+ 'draft-75'
13
+ end
14
+
15
+ def handshake_response
16
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
17
+ upgrade << "Upgrade: WebSocket\r\n"
18
+ upgrade << "Connection: Upgrade\r\n"
19
+ upgrade << "WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
20
+ upgrade << "WebSocket-Location: #{@socket.url}\r\n"
21
+ upgrade << "\r\n"
22
+ upgrade
23
+ end
24
+
25
+ def parse(data)
26
+ data.each_byte(&method(:handle_byte))
27
+ end
28
+
29
+ def frame(data, type = nil, error_type = nil)
30
+ ["\x00", data, "\xFF"].map(&WebSocket.method(:encode)) * ''
31
+ end
32
+
33
+ private
34
+
35
+ def handle_byte(data)
36
+ case data
37
+ when 0x00 then
38
+ @buffering = true
39
+
40
+ when 0xFF then
41
+ @socket.receive(WebSocket.encode(@buffer))
42
+ @buffer = []
43
+ @buffering = false
44
+
45
+ else
46
+ @buffer.push(data) if @buffering
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+
@@ -0,0 +1,53 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Draft76Parser < Draft75Parser
5
+ def version
6
+ 'draft-76'
7
+ end
8
+
9
+ def handshake_response
10
+ env = @socket.env
11
+
12
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
13
+ value1 = number_from_key(key1) / spaces_in_key(key1)
14
+
15
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
16
+ value2 = number_from_key(key2) / spaces_in_key(key2)
17
+
18
+ hash = Digest::MD5.digest(big_endian(value1) +
19
+ big_endian(value2) +
20
+ env['rack.input'].read)
21
+
22
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
23
+ upgrade << "Upgrade: WebSocket\r\n"
24
+ upgrade << "Connection: Upgrade\r\n"
25
+ upgrade << "Sec-WebSocket-Origin: #{env['HTTP_ORIGIN']}\r\n"
26
+ upgrade << "Sec-WebSocket-Location: #{@socket.url}\r\n"
27
+ upgrade << "\r\n"
28
+ upgrade << hash
29
+ upgrade
30
+ end
31
+
32
+ private
33
+
34
+ def number_from_key(key)
35
+ key.scan(/[0-9]/).join('').to_i(10)
36
+ end
37
+
38
+ def spaces_in_key(key)
39
+ key.scan(/ /).size
40
+ end
41
+
42
+ def big_endian(number)
43
+ string = ''
44
+ [24,16,8,0].each do |offset|
45
+ string << (number >> offset & 0xFF).chr
46
+ end
47
+ string
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+
@@ -0,0 +1,324 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Protocol8Parser
5
+ GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
6
+
7
+ FIN = MASK = 0b10000000
8
+ RSV1 = 0b01000000
9
+ RSV2 = 0b00100000
10
+ RSV3 = 0b00010000
11
+ OPCODE = 0b00001111
12
+ LENGTH = 0b01111111
13
+
14
+ OPCODES = {
15
+ :continuation => 0,
16
+ :text => 1,
17
+ :binary => 2,
18
+ :close => 8,
19
+ :ping => 9,
20
+ :pong => 10
21
+ }
22
+
23
+ FRAGMENTED_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
24
+ OPENING_OPCODES = OPCODES.values_at(:text, :binary)
25
+
26
+ ERRORS = {
27
+ :normal_closure => 1000,
28
+ :going_away => 1001,
29
+ :protocol_error => 1002,
30
+ :unacceptable => 1003,
31
+ :encoding_error => 1007,
32
+ :policy_violation => 1008,
33
+ :too_large => 1009,
34
+ :extension_error => 1010
35
+ }
36
+
37
+ ERROR_CODES = ERRORS.values
38
+
39
+ class Handshake
40
+ def initialize(uri)
41
+ @uri = uri
42
+ @key = Base64.encode64((1..16).map { rand(255).chr } * '').strip
43
+ @accept = Base64.encode64(Digest::SHA1.digest(@key + GUID)).strip
44
+ @buffer = []
45
+ end
46
+
47
+ def request_data
48
+ hostname = @uri.host + (@uri.port ? ":#{@uri.port}" : '')
49
+
50
+ handshake = "GET #{@uri.path}#{@uri.query ? '?' : ''}#{@uri.query} HTTP/1.1\r\n"
51
+ handshake << "Host: #{hostname}\r\n"
52
+ handshake << "Upgrade: websocket\r\n"
53
+ handshake << "Connection: Upgrade\r\n"
54
+ handshake << "Sec-WebSocket-Key: #{@key}\r\n"
55
+ handshake << "Sec-WebSocket-Version: 8\r\n"
56
+ handshake << "\r\n"
57
+
58
+ handshake
59
+ end
60
+
61
+ def parse(data)
62
+ message = []
63
+ complete = false
64
+ data.each_byte do |byte|
65
+ if complete
66
+ message << byte
67
+ else
68
+ @buffer << byte
69
+ complete ||= complete?
70
+ end
71
+ end
72
+ message
73
+ end
74
+
75
+ def complete?
76
+ @buffer[-4..-1] == [0x0D, 0x0A, 0x0D, 0x0A]
77
+ end
78
+
79
+ def valid?
80
+ data = WebSocket.encode(@buffer)
81
+ response = Net::HTTPResponse.read_new(Net::BufferedIO.new(StringIO.new(data)))
82
+ return false unless response.code.to_i == 101
83
+
84
+ upgrade, connection = response['Upgrade'], response['Connection']
85
+
86
+ upgrade and upgrade =~ /^websocket$/i and
87
+ connection and connection.split(/\s*,\s*/).include?('Upgrade') and
88
+ response['Sec-WebSocket-Accept'] == @accept
89
+ end
90
+ end
91
+
92
+ def initialize(web_socket, options = {})
93
+ reset
94
+ @socket = web_socket
95
+ @stage = 0
96
+ @masking = options[:masking]
97
+ end
98
+
99
+ def version
100
+ "protocol-#{@socket.env['HTTP_SEC_WEBSOCKET_VERSION']}"
101
+ end
102
+
103
+ def handshake_response
104
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
105
+ return '' unless String === sec_key
106
+
107
+ accept = Base64.encode64(Digest::SHA1.digest(sec_key + GUID)).strip
108
+
109
+ upgrade = "HTTP/1.1 101 Switching Protocols\r\n"
110
+ upgrade << "Upgrade: websocket\r\n"
111
+ upgrade << "Connection: Upgrade\r\n"
112
+ upgrade << "Sec-WebSocket-Accept: #{accept}\r\n"
113
+ upgrade << "\r\n"
114
+ upgrade
115
+ end
116
+
117
+ def create_handshake
118
+ Handshake.new(@socket.uri)
119
+ end
120
+
121
+ def parse(data)
122
+ data.each_byte do |byte|
123
+ case @stage
124
+ when 0 then parse_opcode(byte)
125
+ when 1 then parse_length(byte)
126
+ when 2 then parse_extended_length(byte)
127
+ when 3 then parse_mask(byte)
128
+ when 4 then parse_payload(byte)
129
+ end
130
+ emit_frame if @stage == 4 and @length == 0
131
+ end
132
+ end
133
+
134
+ def frame(data, type = nil, code = nil)
135
+ return nil if @closed
136
+
137
+ type ||= (String === data ? :text : :binary)
138
+ data = data.bytes.to_a if data.respond_to?(:bytes)
139
+
140
+ if code
141
+ data = [code].pack('n').bytes.to_a + data
142
+ end
143
+
144
+ frame = (FIN | OPCODES[type]).chr
145
+ length = data.size
146
+ masked = @masking ? MASK : 0
147
+
148
+ case length
149
+ when 0..125 then
150
+ frame << (masked | length).chr
151
+ when 126..65535 then
152
+ frame << (masked | 126).chr
153
+ frame << [length].pack('n')
154
+ else
155
+ frame << (masked | 127).chr
156
+ frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
157
+ end
158
+
159
+ if @masking
160
+ mask = (1..4).map { rand 256 }
161
+ data.each_with_index do |byte, i|
162
+ data[i] = byte ^ mask[i % 4]
163
+ end
164
+ frame << mask.pack('C*')
165
+ end
166
+
167
+ WebSocket.encode(frame) + WebSocket.encode(data)
168
+ end
169
+
170
+ def close(code = nil, reason = nil, &callback)
171
+ return if @closed
172
+ @closing_callback ||= callback
173
+ @socket.send(reason || '', :close, code || ERRORS[:normal_closure])
174
+ @closed = true
175
+ end
176
+
177
+ private
178
+
179
+ def parse_opcode(data)
180
+ if [RSV1, RSV2, RSV3].any? { |rsv| (data & rsv) == rsv }
181
+ return @socket.close(ERRORS[:protocol_error], nil, false)
182
+ end
183
+
184
+ @final = (data & FIN) == FIN
185
+ @opcode = (data & OPCODE)
186
+ @mask = []
187
+ @payload = []
188
+
189
+ unless OPCODES.values.include?(@opcode)
190
+ return @socket.close(ERRORS[:protocol_error], nil, false)
191
+ end
192
+
193
+ unless FRAGMENTED_OPCODES.include?(@opcode) or @final
194
+ return @socket.close(ERRORS[:protocol_error], nil, false)
195
+ end
196
+
197
+ if @mode and OPENING_OPCODES.include?(@opcode)
198
+ return @socket.close(ERRORS[:protocol_error], nil, false)
199
+ end
200
+
201
+ @stage = 1
202
+ end
203
+
204
+ def parse_length(data)
205
+ @masked = (data & MASK) == MASK
206
+ @length = (data & LENGTH)
207
+
208
+ if @length <= 125
209
+ @stage = @masked ? 3 : 4
210
+ else
211
+ @length_buffer = []
212
+ @length_size = (@length == 126) ? 2 : 8
213
+ @stage = 2
214
+ end
215
+ end
216
+
217
+ def parse_extended_length(data)
218
+ @length_buffer << data
219
+ return unless @length_buffer.size == @length_size
220
+ @length = integer(@length_buffer)
221
+ @stage = @masked ? 3 : 4
222
+ end
223
+
224
+ def parse_mask(data)
225
+ @mask << data
226
+ return if @mask.size < 4
227
+ @stage = 4
228
+ end
229
+
230
+ def parse_payload(data)
231
+ @payload << data
232
+ return if @payload.size < @length
233
+ emit_frame
234
+ end
235
+
236
+ def emit_frame
237
+ payload = unmask(@payload, @mask)
238
+
239
+ case @opcode
240
+ when OPCODES[:continuation] then
241
+ return @socket.close(ERRORS[:protocol_error], nil, false) unless @mode
242
+ @buffer += payload
243
+ if @final
244
+ message = @buffer
245
+ message = WebSocket.encode(message, true) if @mode == :text
246
+ reset
247
+ if message
248
+ @socket.receive(message)
249
+ else
250
+ @socket.close(ERRORS[:encoding_error], nil, false)
251
+ end
252
+ end
253
+
254
+ when OPCODES[:text] then
255
+ if @final
256
+ message = WebSocket.encode(payload, true)
257
+ if message
258
+ @socket.receive(message)
259
+ else
260
+ @socket.close(ERRORS[:encoding_error], nil, false)
261
+ end
262
+ else
263
+ @mode = :text
264
+ @buffer += payload
265
+ end
266
+
267
+ when OPCODES[:binary] then
268
+ if @final
269
+ @socket.receive(payload)
270
+ else
271
+ @mode = :binary
272
+ @buffer += payload
273
+ end
274
+
275
+ when OPCODES[:close] then
276
+ code = (payload.size >= 2) ? 256 * payload[0] + payload[1] : nil
277
+
278
+ unless (payload.size == 0) or
279
+ (code && code >= 3000 && code < 5000) or
280
+ ERROR_CODES.include?(code)
281
+ code = ERRORS[:protocol_error]
282
+ end
283
+
284
+ if payload.size > 125 or not WebSocket.valid_utf8?(payload[2..-1] || [])
285
+ code = ERRORS[:protocol_error]
286
+ end
287
+
288
+ reason = (payload.size > 2) ? WebSocket.encode(payload[2..-1], true) : nil
289
+ @socket.close(code, reason, false)
290
+ @closing_callback.call if @closing_callback
291
+
292
+ when OPCODES[:ping] then
293
+ return @socket.close(ERRORS[:protocol_error], nil, false) if payload.size > 125
294
+ @socket.send(payload, :pong)
295
+ end
296
+ @stage = 0
297
+ end
298
+
299
+ def reset
300
+ @buffer = []
301
+ @mode = nil
302
+ end
303
+
304
+ def integer(bytes)
305
+ number = 0
306
+ bytes.each_with_index do |data, i|
307
+ number += data << (8 * (bytes.size - 1 - i))
308
+ end
309
+ number
310
+ end
311
+
312
+ def unmask(payload, mask)
313
+ unmasked = []
314
+ payload.each_with_index do |byte, i|
315
+ byte = byte ^ mask[i % 4] if mask.size > 0
316
+ unmasked << byte
317
+ end
318
+ unmasked
319
+ end
320
+ end
321
+
322
+ end
323
+ end
324
+