_bushido-faye-websocket 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.txt +56 -0
  2. data/README.rdoc +366 -0
  3. data/examples/app.rb +50 -0
  4. data/examples/autobahn_client.rb +44 -0
  5. data/examples/client.rb +30 -0
  6. data/examples/config.ru +17 -0
  7. data/examples/haproxy.conf +21 -0
  8. data/examples/server.rb +44 -0
  9. data/examples/sse.html +39 -0
  10. data/examples/ws.html +44 -0
  11. data/ext/faye_websocket_mask/FayeWebsocketMaskService.java +61 -0
  12. data/ext/faye_websocket_mask/extconf.rb +5 -0
  13. data/ext/faye_websocket_mask/faye_websocket_mask.c +33 -0
  14. data/lib/faye/adapters/goliath.rb +47 -0
  15. data/lib/faye/adapters/rainbows.rb +32 -0
  16. data/lib/faye/adapters/rainbows_client.rb +70 -0
  17. data/lib/faye/adapters/thin.rb +62 -0
  18. data/lib/faye/eventsource.rb +124 -0
  19. data/lib/faye/websocket.rb +216 -0
  20. data/lib/faye/websocket/adapter.rb +21 -0
  21. data/lib/faye/websocket/api.rb +96 -0
  22. data/lib/faye/websocket/api/event.rb +33 -0
  23. data/lib/faye/websocket/api/event_target.rb +34 -0
  24. data/lib/faye/websocket/client.rb +84 -0
  25. data/lib/faye/websocket/draft75_parser.rb +87 -0
  26. data/lib/faye/websocket/draft76_parser.rb +84 -0
  27. data/lib/faye/websocket/hybi_parser.rb +320 -0
  28. data/lib/faye/websocket/hybi_parser/handshake.rb +78 -0
  29. data/lib/faye/websocket/hybi_parser/stream_reader.rb +29 -0
  30. data/lib/faye/websocket/utf8_match.rb +8 -0
  31. data/spec/faye/websocket/client_spec.rb +179 -0
  32. data/spec/faye/websocket/draft75_parser_examples.rb +48 -0
  33. data/spec/faye/websocket/draft75_parser_spec.rb +27 -0
  34. data/spec/faye/websocket/draft76_parser_spec.rb +34 -0
  35. data/spec/faye/websocket/hybi_parser_spec.rb +156 -0
  36. data/spec/rainbows.conf +3 -0
  37. data/spec/server.crt +15 -0
  38. data/spec/server.key +15 -0
  39. data/spec/spec_helper.rb +68 -0
  40. metadata +158 -0
@@ -0,0 +1,33 @@
1
+ module Faye::WebSocket::API
2
+ class Event
3
+
4
+ attr_reader :type, :bubbles, :cancelable
5
+ attr_accessor :target, :current_target, :event_phase, :data
6
+
7
+ CAPTURING_PHASE = 1
8
+ AT_TARGET = 2
9
+ BUBBLING_PHASE = 3
10
+
11
+ def initialize(event_type, options = {})
12
+ @type = event_type
13
+ metaclass = (class << self ; self ; end)
14
+ options.each do |key, value|
15
+ metaclass.__send__(:define_method, key) { value }
16
+ end
17
+ end
18
+
19
+ def init_event(event_type, can_bubble, cancelable)
20
+ @type = event_type
21
+ @bubbles = can_bubble
22
+ @cancelable = cancelable
23
+ end
24
+
25
+ def stop_propagation
26
+ end
27
+
28
+ def prevent_default
29
+ end
30
+
31
+ end
32
+ end
33
+
@@ -0,0 +1,34 @@
1
+ module Faye::WebSocket::API
2
+ module EventTarget
3
+
4
+ attr_accessor :onopen, :onmessage, :onerror, :onclose
5
+
6
+ def add_event_listener(event_type, listener, use_capture = false)
7
+ @listeners ||= {}
8
+ list = @listeners[event_type] ||= []
9
+ list << listener
10
+ end
11
+
12
+ def remove_event_listener(event_type, listener, use_capture = false)
13
+ return unless @listeners and @listeners[event_type]
14
+ return @listeners.delete(event_type) unless listener
15
+
16
+ @listeners[event_type].delete_if(&listener.method(:==))
17
+ end
18
+
19
+ def dispatch_event(event)
20
+ event.target = event.current_target = self
21
+ event.event_phase = Event::AT_TARGET
22
+
23
+ callback = __send__("on#{ event.type }")
24
+ callback.call(event) if callback
25
+
26
+ return unless @listeners and @listeners[event.type]
27
+ @listeners[event.type].each do |listener|
28
+ listener.call(event)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -0,0 +1,84 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Client
5
+ include API
6
+ attr_reader :protocol, :uri
7
+
8
+ def initialize(url, protocols = nil)
9
+ @parser = HybiParser.new(self, :masking => true, :protocols => protocols)
10
+ @url = url
11
+ @uri = URI.parse(url)
12
+
13
+ @protocol = ''
14
+ @ready_state = CONNECTING
15
+ @buffered_amount = 0
16
+
17
+ port = @uri.port || (@uri.scheme == 'wss' ? 443 : 80)
18
+
19
+ EventMachine.connect(@uri.host, port, Connection) do |conn|
20
+ @stream = conn
21
+ conn.parent = self
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def on_connect
28
+ @stream.start_tls if @uri.scheme == 'wss'
29
+ @handshake = @parser.create_handshake
30
+ @message = []
31
+ @stream.write(@handshake.request_data)
32
+ end
33
+
34
+ def receive_data(data)
35
+ data = WebSocket.encode(data)
36
+
37
+ case @ready_state
38
+ when CONNECTING then
39
+ @message += @handshake.parse(data)
40
+ return unless @handshake.complete?
41
+
42
+ if @handshake.valid?
43
+ @protocol = @handshake.protocol || ''
44
+ @ready_state = OPEN
45
+ event = Event.new('open')
46
+ event.init_event('open', false, false)
47
+ dispatch_event(event)
48
+
49
+ receive_data(@message)
50
+ else
51
+ @ready_state = CLOSED
52
+ event = Event.new('close', :code => 1006, :reason => '')
53
+ event.init_event('close', false, false)
54
+ dispatch_event(event)
55
+ end
56
+
57
+ when OPEN, CLOSING then
58
+ @parser.parse(data)
59
+ end
60
+ end
61
+
62
+ module Connection
63
+ attr_accessor :parent
64
+
65
+ def connection_completed
66
+ parent.__send__(:on_connect)
67
+ end
68
+
69
+ def receive_data(data)
70
+ parent.__send__(:receive_data, data)
71
+ end
72
+
73
+ def unbind
74
+ parent.close(1006, '', false)
75
+ end
76
+
77
+ def write(data)
78
+ send_data(data) rescue nil
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,87 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Draft75Parser
5
+ attr_reader :protocol
6
+
7
+ def initialize(web_socket, options = {})
8
+ @socket = web_socket
9
+ @stage = 0
10
+ end
11
+
12
+ def version
13
+ 'hixie-75'
14
+ end
15
+
16
+ def handshake_response
17
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
18
+ upgrade << "Upgrade: WebSocket\r\n"
19
+ upgrade << "Connection: Upgrade\r\n"
20
+ upgrade << "WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
21
+ upgrade << "WebSocket-Location: #{@socket.url}\r\n"
22
+ upgrade << "\r\n"
23
+ upgrade
24
+ end
25
+
26
+ def open?
27
+ true
28
+ end
29
+
30
+ def parse(buffer)
31
+ buffer.each_byte do |data|
32
+ case @stage
33
+ when 0 then
34
+ parse_leading_byte(data)
35
+
36
+ when 1 then
37
+ value = (data & 0x7F)
38
+ @length = value + 128 * @length
39
+
40
+ if @closing and @length.zero?
41
+ @socket.close(nil, nil, false)
42
+ elsif (0x80 & data) != 0x80
43
+ if @length.zero?
44
+ @socket.receive('')
45
+ @stage = 0
46
+ else
47
+ @buffer = []
48
+ @stage = 2
49
+ end
50
+ end
51
+
52
+ when 2 then
53
+ if data == 0xFF
54
+ @socket.receive(WebSocket.encode(@buffer))
55
+ @stage = 0
56
+ else
57
+ @buffer << data
58
+ if @length and @buffer.size == @length
59
+ @stage = 0
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ nil
66
+ end
67
+
68
+ def parse_leading_byte(data)
69
+ if (0x80 & data) == 0x80
70
+ @length = 0
71
+ @stage = 1
72
+ else
73
+ @length = nil
74
+ @buffer = []
75
+ @stage = 2
76
+ end
77
+ end
78
+
79
+ def frame(data, type = nil, error_type = nil)
80
+ return WebSocket.encode(data) if Array === data
81
+ ["\x00", data, "\xFF"].map(&WebSocket.method(:encode)) * ''
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+
@@ -0,0 +1,84 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class Draft76Parser < Draft75Parser
5
+ def version
6
+ 'hixie-76'
7
+ end
8
+
9
+ def handshake_response
10
+ env = @socket.env
11
+ signature = handshake_signature(env['rack.input'].read)
12
+
13
+ upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
14
+ upgrade << "Upgrade: WebSocket\r\n"
15
+ upgrade << "Connection: Upgrade\r\n"
16
+ upgrade << "Sec-WebSocket-Origin: #{env['HTTP_ORIGIN']}\r\n"
17
+ upgrade << "Sec-WebSocket-Location: #{@socket.url}\r\n"
18
+ upgrade << "\r\n"
19
+ upgrade << signature if signature
20
+ upgrade
21
+ end
22
+
23
+ def handshake_signature(head)
24
+ return nil if head.empty?
25
+ env = @socket.env
26
+
27
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
28
+ value1 = number_from_key(key1) / spaces_in_key(key1)
29
+
30
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
31
+ value2 = number_from_key(key2) / spaces_in_key(key2)
32
+
33
+ @handshake_complete = true
34
+
35
+ Digest::MD5.digest(big_endian(value1) +
36
+ big_endian(value2) +
37
+ head)
38
+ end
39
+
40
+ def open?
41
+ !!@handshake_complete
42
+ end
43
+
44
+ def parse(data)
45
+ return super if @handshake_complete
46
+ handshake_signature(data)
47
+ end
48
+
49
+ def close(code = nil, reason = nil, &callback)
50
+ return if @closed
51
+ @socket.send([0xFF, 0x00]) if @closing
52
+ @closed = true
53
+ callback.call if callback
54
+ end
55
+
56
+ private
57
+
58
+ def parse_leading_byte(data)
59
+ return super unless data == 0xFF
60
+ @closing = true
61
+ @length = 0
62
+ @stage = 1
63
+ end
64
+
65
+ def number_from_key(key)
66
+ key.scan(/[0-9]/).join('').to_i(10)
67
+ end
68
+
69
+ def spaces_in_key(key)
70
+ key.scan(/ /).size
71
+ end
72
+
73
+ def big_endian(number)
74
+ string = ''
75
+ [24,16,8,0].each do |offset|
76
+ string << (number >> offset & 0xFF).chr
77
+ end
78
+ string
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+
@@ -0,0 +1,320 @@
1
+ module Faye
2
+ class WebSocket
3
+
4
+ class HybiParser
5
+ root = File.expand_path('../hybi_parser', __FILE__)
6
+ autoload :Handshake, root + '/handshake'
7
+ autoload :StreamReader, root + '/stream_reader'
8
+
9
+ BYTE = 0b11111111
10
+ FIN = MASK = 0b10000000
11
+ RSV1 = 0b01000000
12
+ RSV2 = 0b00100000
13
+ RSV3 = 0b00010000
14
+ OPCODE = 0b00001111
15
+ LENGTH = 0b01111111
16
+
17
+ OPCODES = {
18
+ :continuation => 0,
19
+ :text => 1,
20
+ :binary => 2,
21
+ :close => 8,
22
+ :ping => 9,
23
+ :pong => 10
24
+ }
25
+
26
+ FRAGMENTED_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
27
+ OPENING_OPCODES = OPCODES.values_at(:text, :binary)
28
+
29
+ ERRORS = {
30
+ :normal_closure => 1000,
31
+ :going_away => 1001,
32
+ :protocol_error => 1002,
33
+ :unacceptable => 1003,
34
+ :encoding_error => 1007,
35
+ :policy_violation => 1008,
36
+ :too_large => 1009,
37
+ :extension_error => 1010
38
+ }
39
+
40
+ ERROR_CODES = ERRORS.values
41
+
42
+ attr_reader :protocol
43
+
44
+ def initialize(web_socket, options = {})
45
+ reset
46
+ @socket = web_socket
47
+ @reader = StreamReader.new
48
+ @stage = 0
49
+ @masking = options[:masking]
50
+ @protocols = options[:protocols]
51
+ @protocols = @protocols.split(/\s*,\s*/) if String === @protocols
52
+
53
+ @ping_callbacks = {}
54
+ end
55
+
56
+ def version
57
+ "hybi-#{@socket.env['HTTP_SEC_WEBSOCKET_VERSION']}"
58
+ end
59
+
60
+ def handshake_response
61
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
62
+ return '' unless String === sec_key
63
+
64
+ accept = Base64.encode64(Digest::SHA1.digest(sec_key + Handshake::GUID)).strip
65
+ protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
66
+ supported = @protocols
67
+ proto = nil
68
+
69
+ headers = [
70
+ "HTTP/1.1 101 Switching Protocols",
71
+ "Upgrade: websocket",
72
+ "Connection: Upgrade",
73
+ "Sec-WebSocket-Accept: #{accept}"
74
+ ]
75
+
76
+ if protos and supported
77
+ protos = protos.split(/\s*,\s*/) if String === protos
78
+ proto = protos.find { |p| supported.include?(p) }
79
+ if proto
80
+ @protocol = proto
81
+ headers << "Sec-WebSocket-Protocol: #{proto}"
82
+ end
83
+ end
84
+
85
+ (headers + ['','']).join("\r\n")
86
+ end
87
+
88
+ def create_handshake
89
+ Handshake.new(@socket.uri, @protocols)
90
+ end
91
+
92
+ def open?
93
+ true
94
+ end
95
+
96
+ def parse(data)
97
+ @reader.put(data.bytes.to_a)
98
+ buffer = true
99
+ while buffer
100
+ case @stage
101
+ when 0 then
102
+ buffer = @reader.read(1)
103
+ parse_opcode(buffer[0]) if buffer
104
+
105
+ when 1 then
106
+ buffer = @reader.read(1)
107
+ parse_length(buffer[0]) if buffer
108
+
109
+ when 2 then
110
+ buffer = @reader.read(@length_size)
111
+ parse_extended_length(buffer) if buffer
112
+
113
+ when 3 then
114
+ buffer = @reader.read(4)
115
+ if buffer
116
+ @mask = buffer
117
+ @stage = 4
118
+ end
119
+
120
+ when 4 then
121
+ buffer = @reader.read(@length)
122
+ if buffer
123
+ @payload = buffer
124
+ emit_frame
125
+ @stage = 0
126
+ end
127
+ end
128
+ end
129
+
130
+ nil
131
+ end
132
+
133
+ def frame(data, type = nil, code = nil)
134
+ return nil if @closed
135
+
136
+ is_text = (String === data)
137
+ opcode = OPCODES[type || (is_text ? :text : :binary)]
138
+ buffer = data.respond_to?(:bytes) ? data.bytes.to_a : data
139
+ insert = code ? 2 : 0
140
+ length = buffer.size + insert
141
+ header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10)
142
+ offset = header + (@masking ? 4 : 0)
143
+ masked = @masking ? MASK : 0
144
+ frame = Array.new(offset)
145
+
146
+ frame[0] = FIN | opcode
147
+
148
+ if length <= 125
149
+ frame[1] = masked | length
150
+ elsif length <= 65535
151
+ frame[1] = masked | 126
152
+ frame[2] = (length >> 8) & BYTE
153
+ frame[3] = length & BYTE
154
+ else
155
+ frame[1] = masked | 127
156
+ frame[2] = (length >> 56) & BYTE
157
+ frame[3] = (length >> 48) & BYTE
158
+ frame[4] = (length >> 40) & BYTE
159
+ frame[5] = (length >> 32) & BYTE
160
+ frame[6] = (length >> 24) & BYTE
161
+ frame[7] = (length >> 16) & BYTE
162
+ frame[8] = (length >> 8) & BYTE
163
+ frame[9] = length & BYTE
164
+ end
165
+
166
+ if code
167
+ buffer = [(code >> 8) & BYTE, code & BYTE] + buffer
168
+ end
169
+
170
+ if @masking
171
+ mask = [rand(256), rand(256), rand(256), rand(256)]
172
+ frame[header...offset] = mask
173
+ buffer = WebSocketMask.mask(buffer, mask)
174
+ end
175
+
176
+ frame.concat(buffer)
177
+
178
+ WebSocket.encode(frame)
179
+ end
180
+
181
+ def ping(message = '', &callback)
182
+ @ping_callbacks[message] = callback if callback
183
+ @socket.send(message, :ping)
184
+ end
185
+
186
+ def close(code = nil, reason = nil, &callback)
187
+ return if @closed
188
+ @closing_callback ||= callback
189
+ @socket.send(reason || '', :close, code || ERRORS[:normal_closure])
190
+ @closed = true
191
+ end
192
+
193
+ private
194
+
195
+ def parse_opcode(data)
196
+ if [RSV1, RSV2, RSV3].any? { |rsv| (data & rsv) == rsv }
197
+ return @socket.close(ERRORS[:protocol_error], nil, false)
198
+ end
199
+
200
+ @final = (data & FIN) == FIN
201
+ @opcode = (data & OPCODE)
202
+ @mask = []
203
+ @payload = []
204
+
205
+ unless OPCODES.values.include?(@opcode)
206
+ return @socket.close(ERRORS[:protocol_error], nil, false)
207
+ end
208
+
209
+ unless FRAGMENTED_OPCODES.include?(@opcode) or @final
210
+ return @socket.close(ERRORS[:protocol_error], nil, false)
211
+ end
212
+
213
+ if @mode and OPENING_OPCODES.include?(@opcode)
214
+ return @socket.close(ERRORS[:protocol_error], nil, false)
215
+ end
216
+
217
+ @stage = 1
218
+ end
219
+
220
+ def parse_length(data)
221
+ @masked = (data & MASK) == MASK
222
+ @length = (data & LENGTH)
223
+
224
+ if @length <= 125
225
+ @stage = @masked ? 3 : 4
226
+ else
227
+ @length_size = (@length == 126) ? 2 : 8
228
+ @stage = 2
229
+ end
230
+ end
231
+
232
+ def parse_extended_length(buffer)
233
+ @length = integer(buffer)
234
+ @stage = @masked ? 3 : 4
235
+ end
236
+
237
+ def emit_frame
238
+ payload = @masked ? WebSocketMask.mask(@payload, @mask) : @payload
239
+
240
+ case @opcode
241
+ when OPCODES[:continuation] then
242
+ return @socket.close(ERRORS[:protocol_error], nil, false) unless @mode
243
+ @buffer.concat(payload)
244
+ if @final
245
+ message = @buffer
246
+ message = WebSocket.encode(message, true) if @mode == :text
247
+ reset
248
+ if message
249
+ @socket.receive(message)
250
+ else
251
+ @socket.close(ERRORS[:encoding_error], nil, false)
252
+ end
253
+ end
254
+
255
+ when OPCODES[:text] then
256
+ if @final
257
+ message = WebSocket.encode(payload, true)
258
+ if message
259
+ @socket.receive(message)
260
+ else
261
+ @socket.close(ERRORS[:encoding_error], nil, false)
262
+ end
263
+ else
264
+ @mode = :text
265
+ @buffer.concat(payload)
266
+ end
267
+
268
+ when OPCODES[:binary] then
269
+ if @final
270
+ @socket.receive(payload)
271
+ else
272
+ @mode = :binary
273
+ @buffer.concat(payload)
274
+ end
275
+
276
+ when OPCODES[:close] then
277
+ code = (payload.size >= 2) ? 256 * payload[0] + payload[1] : nil
278
+
279
+ unless (payload.size == 0) or
280
+ (code && code >= 3000 && code < 5000) or
281
+ ERROR_CODES.include?(code)
282
+ code = ERRORS[:protocol_error]
283
+ end
284
+
285
+ if payload.size > 125 or not WebSocket.valid_utf8?(payload[2..-1] || [])
286
+ code = ERRORS[:protocol_error]
287
+ end
288
+
289
+ reason = (payload.size > 2) ? WebSocket.encode(payload[2..-1], true) : nil
290
+ @socket.close(code, reason, false)
291
+ @closing_callback.call if @closing_callback
292
+
293
+ when OPCODES[:ping] then
294
+ return @socket.close(ERRORS[:protocol_error], nil, false) if payload.size > 125
295
+ @socket.send(payload, :pong)
296
+
297
+ when OPCODES[:pong] then
298
+ message = WebSocket.encode(payload, true)
299
+ callback = @ping_callbacks[message]
300
+ @ping_callbacks.delete(message)
301
+ callback.call if callback
302
+ end
303
+ end
304
+
305
+ def reset
306
+ @buffer = []
307
+ @mode = nil
308
+ end
309
+
310
+ def integer(bytes)
311
+ number = 0
312
+ bytes.each_with_index do |data, i|
313
+ number += data << (8 * (bytes.size - 1 - i))
314
+ end
315
+ number
316
+ end
317
+ end
318
+
319
+ end
320
+ end