face-faye 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/History.txt +304 -0
  2. data/README.rdoc +83 -0
  3. data/lib/faye-browser-min.js +2 -0
  4. data/lib/faye-browser-min.js.map +8 -0
  5. data/lib/faye-browser.js +2194 -0
  6. data/lib/faye.rb +122 -0
  7. data/lib/faye/adapters/rack_adapter.rb +216 -0
  8. data/lib/faye/adapters/static_server.rb +56 -0
  9. data/lib/faye/engines/connection.rb +60 -0
  10. data/lib/faye/engines/memory.rb +112 -0
  11. data/lib/faye/engines/proxy.rb +121 -0
  12. data/lib/faye/error.rb +49 -0
  13. data/lib/faye/mixins/logging.rb +47 -0
  14. data/lib/faye/mixins/publisher.rb +30 -0
  15. data/lib/faye/mixins/timeouts.rb +22 -0
  16. data/lib/faye/protocol/channel.rb +124 -0
  17. data/lib/faye/protocol/client.rb +376 -0
  18. data/lib/faye/protocol/extensible.rb +43 -0
  19. data/lib/faye/protocol/grammar.rb +58 -0
  20. data/lib/faye/protocol/publication.rb +5 -0
  21. data/lib/faye/protocol/server.rb +293 -0
  22. data/lib/faye/protocol/socket.rb +23 -0
  23. data/lib/faye/protocol/subscription.rb +24 -0
  24. data/lib/faye/transport/http.rb +76 -0
  25. data/lib/faye/transport/local.rb +22 -0
  26. data/lib/faye/transport/transport.rb +116 -0
  27. data/lib/faye/transport/web_socket.rb +92 -0
  28. data/lib/faye/util/namespace.rb +20 -0
  29. data/spec/browser.html +45 -0
  30. data/spec/encoding_helper.rb +7 -0
  31. data/spec/install.sh +78 -0
  32. data/spec/javascript/channel_spec.js +15 -0
  33. data/spec/javascript/client_spec.js +729 -0
  34. data/spec/javascript/engine/memory_spec.js +7 -0
  35. data/spec/javascript/engine_spec.js +417 -0
  36. data/spec/javascript/faye_spec.js +34 -0
  37. data/spec/javascript/grammar_spec.js +66 -0
  38. data/spec/javascript/node_adapter_spec.js +307 -0
  39. data/spec/javascript/publisher_spec.js +27 -0
  40. data/spec/javascript/server/connect_spec.js +168 -0
  41. data/spec/javascript/server/disconnect_spec.js +121 -0
  42. data/spec/javascript/server/extensions_spec.js +60 -0
  43. data/spec/javascript/server/handshake_spec.js +145 -0
  44. data/spec/javascript/server/integration_spec.js +131 -0
  45. data/spec/javascript/server/publish_spec.js +85 -0
  46. data/spec/javascript/server/subscribe_spec.js +247 -0
  47. data/spec/javascript/server/unsubscribe_spec.js +245 -0
  48. data/spec/javascript/server_spec.js +121 -0
  49. data/spec/javascript/transport_spec.js +135 -0
  50. data/spec/node.js +55 -0
  51. data/spec/phantom.js +17 -0
  52. data/spec/ruby/channel_spec.rb +17 -0
  53. data/spec/ruby/client_spec.rb +741 -0
  54. data/spec/ruby/engine/memory_spec.rb +7 -0
  55. data/spec/ruby/engine_examples.rb +427 -0
  56. data/spec/ruby/faye_spec.rb +30 -0
  57. data/spec/ruby/grammar_spec.rb +68 -0
  58. data/spec/ruby/publisher_spec.rb +27 -0
  59. data/spec/ruby/rack_adapter_spec.rb +236 -0
  60. data/spec/ruby/server/connect_spec.rb +170 -0
  61. data/spec/ruby/server/disconnect_spec.rb +120 -0
  62. data/spec/ruby/server/extensions_spec.rb +68 -0
  63. data/spec/ruby/server/handshake_spec.rb +143 -0
  64. data/spec/ruby/server/integration_spec.rb +133 -0
  65. data/spec/ruby/server/publish_spec.rb +81 -0
  66. data/spec/ruby/server/subscribe_spec.rb +247 -0
  67. data/spec/ruby/server/unsubscribe_spec.rb +247 -0
  68. data/spec/ruby/server_spec.rb +121 -0
  69. data/spec/ruby/transport_spec.rb +136 -0
  70. data/spec/spec_helper.rb +11 -0
  71. data/spec/testswarm +42 -0
  72. data/spec/thin_proxy.rb +37 -0
  73. metadata +441 -0
@@ -0,0 +1,112 @@
1
+ module Faye
2
+ module Engine
3
+
4
+ class Memory
5
+ include Timeouts
6
+
7
+ def self.create(server, options)
8
+ new(server, options)
9
+ end
10
+
11
+ def initialize(server, options)
12
+ @server = server
13
+ @options = options
14
+ @namespace = Namespace.new
15
+ @clients = {}
16
+ @channels = {}
17
+ @messages = {}
18
+ end
19
+
20
+ def create_client(&callback)
21
+ client_id = @namespace.generate
22
+ @server.debug 'Created new client ?', client_id
23
+ ping(client_id)
24
+ @server.trigger(:handshake, client_id)
25
+ callback.call(client_id)
26
+ end
27
+
28
+ def destroy_client(client_id, &callback)
29
+ return unless @namespace.exists?(client_id)
30
+
31
+ if @clients.has_key?(client_id)
32
+ @clients[client_id].each { |channel| unsubscribe(client_id, channel) }
33
+ end
34
+
35
+ remove_timeout(client_id)
36
+ @namespace.release(client_id)
37
+ @messages.delete(client_id)
38
+ @server.debug 'Destroyed client ?', client_id
39
+ @server.trigger(:disconnect, client_id)
40
+ callback.call if callback
41
+ end
42
+
43
+ def client_exists(client_id, &callback)
44
+ callback.call(@namespace.exists?(client_id))
45
+ end
46
+
47
+ def ping(client_id)
48
+ timeout = @server.timeout
49
+ return unless Numeric === timeout
50
+ @server.debug 'Ping ?, ?', client_id, timeout
51
+ remove_timeout(client_id)
52
+ add_timeout(client_id, 2 * timeout) { destroy_client(client_id) }
53
+ end
54
+
55
+ def subscribe(client_id, channel, &callback)
56
+ @clients[client_id] ||= Set.new
57
+ should_trigger = @clients[client_id].add?(channel)
58
+
59
+ @channels[channel] ||= Set.new
60
+ @channels[channel].add(client_id)
61
+
62
+ @server.debug 'Subscribed client ? to channel ?', client_id, channel
63
+ @server.trigger(:subscribe, client_id, channel) if should_trigger
64
+ callback.call(true) if callback
65
+ end
66
+
67
+ def unsubscribe(client_id, channel, &callback)
68
+ if @clients.has_key?(client_id)
69
+ should_trigger = @clients[client_id].delete?(channel)
70
+ @clients.delete(client_id) if @clients[client_id].empty?
71
+ end
72
+
73
+ if @channels.has_key?(channel)
74
+ @channels[channel].delete(client_id)
75
+ @channels.delete(channel) if @channels[channel].empty?
76
+ end
77
+
78
+ @server.debug 'Unsubscribed client ? from channel ?', client_id, channel
79
+ @server.trigger(:unsubscribe, client_id, channel) if should_trigger
80
+ callback.call(true) if callback
81
+ end
82
+
83
+ def publish(message, channels)
84
+ @server.debug 'Publishing message ?', message
85
+
86
+ clients = Set.new
87
+
88
+ channels.each do |channel|
89
+ next unless subs = @channels[channel]
90
+ subs.each(&clients.method(:add))
91
+ end
92
+
93
+ clients.each do |client_id|
94
+ @server.debug 'Queueing for client ?: ?', client_id, message
95
+ @messages[client_id] ||= []
96
+ @messages[client_id] << Faye.copy_object(message)
97
+ empty_queue(client_id)
98
+ end
99
+
100
+ @server.trigger(:publish, message['clientId'], message['channel'], message['data'])
101
+ end
102
+
103
+ def empty_queue(client_id)
104
+ return unless @server.has_connection?(client_id)
105
+ @server.deliver(client_id, @messages[client_id])
106
+ @messages.delete(client_id)
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+
@@ -0,0 +1,121 @@
1
+ module Faye
2
+ module Engine
3
+
4
+ METHODS = %w[create_client client_exists destroy_client ping subscribe unsubscribe]
5
+ MAX_DELAY = 0.0
6
+ INTERVAL = 0.0
7
+ TIMEOUT = 60.0
8
+ ID_LENGTH = 160
9
+
10
+ autoload :Connection, File.expand_path('../connection', __FILE__)
11
+ autoload :Memory, File.expand_path('../memory', __FILE__)
12
+
13
+ def self.ensure_reactor_running!
14
+ Thread.new { EventMachine.run } unless EventMachine.reactor_running?
15
+ Thread.pass until EventMachine.reactor_running?
16
+ end
17
+
18
+ def self.get(options)
19
+ Proxy.new(options)
20
+ end
21
+
22
+ def self.random(bitlength = ID_LENGTH)
23
+ limit = 2 ** bitlength - 1
24
+ max_size = limit.to_s(36).size
25
+ string = rand(limit).to_s(36)
26
+ string = '0' + string while string.size < max_size
27
+ string
28
+ end
29
+
30
+ class Proxy
31
+ include Publisher
32
+ include Logging
33
+
34
+ attr_reader :interval, :timeout
35
+
36
+ extend Forwardable
37
+ def_delegators :@engine, *METHODS
38
+
39
+ def initialize(options)
40
+ @options = options
41
+ @connections = {}
42
+ @interval = @options[:interval] || INTERVAL
43
+ @timeout = @options[:timeout] || TIMEOUT
44
+
45
+ engine_class = @options[:type] || Memory
46
+ @engine = engine_class.create(self, @options)
47
+
48
+ bind :disconnect do |client_id|
49
+ EventMachine.next_tick { close_connection(client_id) }
50
+ end
51
+
52
+ debug 'Created new engine: ?', @options
53
+ end
54
+
55
+ def connect(client_id, options = {}, &callback)
56
+ debug 'Accepting connection from ?', client_id
57
+ @engine.ping(client_id)
58
+ conn = connection(client_id, true)
59
+ conn.connect(options, &callback)
60
+ @engine.empty_queue(client_id)
61
+ end
62
+
63
+ def has_connection?(client_id)
64
+ @connections.has_key?(client_id)
65
+ end
66
+
67
+ def connection(client_id, create)
68
+ conn = @connections[client_id]
69
+ return conn if conn or not create
70
+ @connections[client_id] = Connection.new(self, client_id)
71
+ trigger('connection:open', client_id)
72
+ @connections[client_id]
73
+ end
74
+
75
+ def close_connection(client_id)
76
+ debug 'Closing connection for ?', client_id
77
+ conn = @connections[client_id]
78
+ return unless conn
79
+ conn.socket.close if conn.socket
80
+ trigger('connection:close', client_id)
81
+ @connections.delete(client_id)
82
+ end
83
+
84
+ def open_socket(client_id, socket)
85
+ return unless client_id
86
+ conn = connection(client_id, true)
87
+ conn.socket = socket
88
+ end
89
+
90
+ def deliver(client_id, messages)
91
+ return if !messages || messages.empty?
92
+ conn = connection(client_id, false)
93
+ return false unless conn
94
+ messages.each(&conn.method(:deliver))
95
+ true
96
+ end
97
+
98
+ def generate_id
99
+ Engine.random
100
+ end
101
+
102
+ def flush(client_id)
103
+ return unless client_id
104
+ debug 'Flushing connection for ?', client_id
105
+ conn = connection(client_id, false)
106
+ conn.flush!(true) if conn
107
+ end
108
+
109
+ def disconnect
110
+ @engine.disconnect if @engine.respond_to?(:disconnect)
111
+ end
112
+
113
+ def publish(message)
114
+ channels = Channel.expand(message['channel'])
115
+ @engine.publish(message, channels)
116
+ end
117
+ end
118
+
119
+ end
120
+ end
121
+
@@ -0,0 +1,49 @@
1
+ module Faye
2
+ class Error
3
+
4
+ def self.method_missing(type, *args)
5
+ code = const_get(type.to_s.upcase)
6
+ new(code[0], args, code[1]).to_s
7
+ end
8
+
9
+ def self.parse(message)
10
+ message ||= ''
11
+ return new(nil, [], message) unless Grammar::ERROR =~ message
12
+
13
+ parts = message.split(':')
14
+ code = parts[0].to_i
15
+ params = parts[1].split(',')
16
+ message = parts[2]
17
+
18
+ new(code, params, message)
19
+ end
20
+
21
+ attr_reader :code, :params, :message
22
+
23
+ def initialize(code, params, message)
24
+ @code = code
25
+ @params = params
26
+ @message = message
27
+ end
28
+
29
+ def to_s
30
+ "#{ @code }:#{ @params * ',' }:#{ @message }"
31
+ end
32
+
33
+ # http://code.google.com/p/cometd/wiki/BayeuxCodes
34
+ VERSION_MISMATCH = [300, 'Version mismatch']
35
+ CONNTYPE_MISMATCH = [301, 'Connection types not supported']
36
+ EXT_MISMATCH = [302, 'Extension mismatch']
37
+ BAD_REQUEST = [400, 'Bad request']
38
+ CLIENT_UNKNOWN = [401, 'Unknown client']
39
+ PARAMETER_MISSING = [402, 'Missing required parameter']
40
+ CHANNEL_FORBIDDEN = [403, 'Forbidden channel']
41
+ CHANNEL_UNKNOWN = [404, 'Unknown channel']
42
+ CHANNEL_INVALID = [405, 'Invalid channel']
43
+ EXT_UNKNOWN = [406, 'Unknown extension']
44
+ PUBLISH_FAILED = [407, 'Failed to publish']
45
+ SERVER_ERROR = [500, 'Internal server error']
46
+
47
+ end
48
+ end
49
+
@@ -0,0 +1,47 @@
1
+ module Faye
2
+ module Logging
3
+
4
+ DEFAULT_LOG_LEVEL = :error
5
+
6
+ LOG_LEVELS = {
7
+ :error => 3,
8
+ :warn => 2,
9
+ :info => 1,
10
+ :debug => 0
11
+ }
12
+
13
+ class << self
14
+ attr_writer :log_level
15
+
16
+ def log_level
17
+ @log_level || DEFAULT_LOG_LEVEL
18
+ end
19
+ end
20
+
21
+ attr_writer :log_level
22
+
23
+ def log_level
24
+ @log_level || Logging.log_level
25
+ end
26
+
27
+ def log(message_args, level)
28
+ return unless Faye.logger
29
+ return if LOG_LEVELS[log_level] > LOG_LEVELS[level]
30
+
31
+ message = message_args.shift.gsub(/\?/) do
32
+ Faye.to_json(message_args.shift)
33
+ end
34
+
35
+ timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
36
+ banner = " [#{ level.to_s.upcase }] [#{ self.class.name }] "
37
+
38
+ Faye.logger.call(timestamp + banner + message)
39
+ end
40
+
41
+ LOG_LEVELS.each do |level, value|
42
+ define_method(level) { |*args| log(args, level) }
43
+ end
44
+
45
+ end
46
+ end
47
+
@@ -0,0 +1,30 @@
1
+ module Faye
2
+ module Publisher
3
+
4
+ def count_listeners(event_type)
5
+ return 0 unless @subscribers and @subscribers[event_type]
6
+ @subscribers[event_type].size
7
+ end
8
+
9
+ def bind(event_type, &listener)
10
+ @subscribers ||= {}
11
+ list = @subscribers[event_type] ||= []
12
+ list << listener
13
+ end
14
+
15
+ def unbind(event_type, &listener)
16
+ return unless @subscribers and @subscribers[event_type]
17
+ return @subscribers.delete(event_type) unless listener
18
+
19
+ @subscribers[event_type].delete_if(&listener.method(:==))
20
+ end
21
+
22
+ def trigger(event_type, *args)
23
+ return unless @subscribers and @subscribers[event_type]
24
+ listeners = @subscribers[event_type].dup
25
+ listeners.each { |listener| listener.call(*args) }
26
+ end
27
+
28
+ end
29
+ end
30
+
@@ -0,0 +1,22 @@
1
+ module Faye
2
+ module Timeouts
3
+ def add_timeout(name, delay, &block)
4
+ Engine.ensure_reactor_running!
5
+ @timeouts ||= {}
6
+ return if @timeouts.has_key?(name)
7
+ @timeouts[name] = EventMachine.add_timer(delay) do
8
+ @timeouts.delete(name)
9
+ block.call
10
+ end
11
+ end
12
+
13
+ def remove_timeout(name)
14
+ @timeouts ||= {}
15
+ timeout = @timeouts[name]
16
+ return if timeout.nil?
17
+ EventMachine.cancel_timer(timeout)
18
+ @timeouts.delete(name)
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,124 @@
1
+ module Faye
2
+ class Channel
3
+
4
+ include Publisher
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def <<(message)
12
+ trigger(:message, message)
13
+ end
14
+
15
+ def unused?
16
+ count_listeners(:message).zero?
17
+ end
18
+
19
+ HANDSHAKE = '/meta/handshake'
20
+ CONNECT = '/meta/connect'
21
+ SUBSCRIBE = '/meta/subscribe'
22
+ UNSUBSCRIBE = '/meta/unsubscribe'
23
+ DISCONNECT = '/meta/disconnect'
24
+
25
+ META = 'meta'
26
+ SERVICE = 'service'
27
+
28
+ class << self
29
+ def expand(name)
30
+ segments = parse(name)
31
+ channels = ['/**', name]
32
+
33
+ copy = segments.dup
34
+ copy[copy.size - 1] = '*'
35
+ channels << unparse(copy)
36
+
37
+ 1.upto(segments.size - 1) do |i|
38
+ copy = segments[0...i]
39
+ copy << '**'
40
+ channels << unparse(copy)
41
+ end
42
+
43
+ channels
44
+ end
45
+
46
+ def valid?(name)
47
+ Grammar::CHANNEL_NAME =~ name or
48
+ Grammar::CHANNEL_PATTERN =~ name
49
+ end
50
+
51
+ def parse(name)
52
+ return nil unless valid?(name)
53
+ name.split('/')[1..-1]
54
+ end
55
+
56
+ def unparse(segments)
57
+ '/' + segments.join('/')
58
+ end
59
+
60
+ def meta?(name)
61
+ segments = parse(name)
62
+ segments ? (segments.first == META) : nil
63
+ end
64
+
65
+ def service?(name)
66
+ segments = parse(name)
67
+ segments ? (segments.first == SERVICE) : nil
68
+ end
69
+
70
+ def subscribable?(name)
71
+ return nil unless valid?(name)
72
+ not meta?(name) and not service?(name)
73
+ end
74
+ end
75
+
76
+ class Set
77
+ def initialize(parent = nil, value = nil)
78
+ @channels = {}
79
+ end
80
+
81
+ def keys
82
+ @channels.keys
83
+ end
84
+
85
+ def remove(name)
86
+ @channels.delete(name)
87
+ end
88
+
89
+ def has_subscription?(name)
90
+ @channels.has_key?(name)
91
+ end
92
+
93
+ def subscribe(names, callback)
94
+ return unless callback
95
+ names.each do |name|
96
+ channel = @channels[name] ||= Channel.new(name)
97
+ channel.bind(:message, &callback)
98
+ end
99
+ end
100
+
101
+ def unsubscribe(name, callback)
102
+ channel = @channels[name]
103
+ return false unless channel
104
+ channel.unbind(:message, &callback)
105
+ if channel.unused?
106
+ remove(name)
107
+ true
108
+ else
109
+ false
110
+ end
111
+ end
112
+
113
+ def distribute_message(message)
114
+ channels = Channel.expand(message['channel'])
115
+ channels.each do |name|
116
+ channel = @channels[name]
117
+ channel.trigger(:message, message['data']) if channel
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end
124
+