faye-websocket 0.1.0

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.

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
+