faye 0.7.2 → 0.8.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 might be problematic. Click here for more details.

Files changed (54) hide show
  1. data/History.txt +15 -3
  2. data/README.rdoc +2 -6
  3. data/lib/faye-browser-min.js +1 -1
  4. data/lib/faye.rb +25 -36
  5. data/lib/faye/adapters/rack_adapter.rb +43 -22
  6. data/lib/faye/engines/connection.rb +7 -10
  7. data/lib/faye/engines/memory.rb +28 -28
  8. data/lib/faye/engines/proxy.rb +109 -0
  9. data/lib/faye/mixins/logging.rb +1 -8
  10. data/lib/faye/mixins/timeouts.rb +1 -1
  11. data/lib/faye/protocol/channel.rb +3 -3
  12. data/lib/faye/protocol/client.rb +50 -45
  13. data/lib/faye/protocol/extensible.rb +11 -18
  14. data/lib/faye/protocol/server.rb +53 -38
  15. data/lib/faye/transport/http.rb +49 -27
  16. data/lib/faye/transport/transport.rb +3 -1
  17. data/lib/faye/transport/web_socket.rb +6 -10
  18. data/lib/faye/util/namespace.rb +2 -2
  19. data/spec/browser.html +3 -1
  20. data/spec/encoding_helper.rb +7 -0
  21. data/spec/javascript/client_spec.js +0 -5
  22. data/spec/javascript/engine/memory_spec.js +7 -0
  23. data/spec/javascript/engine_spec.js +22 -57
  24. data/spec/javascript/server/handshake_spec.js +10 -6
  25. data/spec/javascript/server/integration_spec.js +11 -10
  26. data/spec/javascript/server/publish_spec.js +85 -0
  27. data/spec/javascript/server_spec.js +5 -51
  28. data/spec/node.js +6 -6
  29. data/spec/ruby/client_spec.rb +1 -1
  30. data/spec/ruby/engine/memory_spec.rb +7 -0
  31. data/spec/ruby/{engine_spec.rb → engine_examples.rb} +28 -34
  32. data/spec/ruby/rack_adapter_spec.rb +1 -1
  33. data/spec/ruby/server/handshake_spec.rb +10 -6
  34. data/spec/ruby/server/publish_spec.rb +81 -0
  35. data/spec/ruby/server_spec.rb +6 -44
  36. data/spec/spec_helper.rb +5 -18
  37. data/spec/testswarm +1 -5
  38. metadata +105 -180
  39. data/lib/faye/engines/base.rb +0 -66
  40. data/lib/faye/engines/redis.rb +0 -225
  41. data/lib/faye/thin_extensions.rb +0 -75
  42. data/lib/faye/util/web_socket.rb +0 -89
  43. data/lib/faye/util/web_socket/api.rb +0 -103
  44. data/lib/faye/util/web_socket/client.rb +0 -82
  45. data/lib/faye/util/web_socket/draft75_parser.rb +0 -53
  46. data/lib/faye/util/web_socket/draft76_parser.rb +0 -53
  47. data/lib/faye/util/web_socket/protocol8_parser.rb +0 -324
  48. data/spec/javascript/web_socket/client_spec.js +0 -121
  49. data/spec/javascript/web_socket/draft75parser_spec.js +0 -39
  50. data/spec/javascript/web_socket/protocol8parser_spec.js +0 -153
  51. data/spec/redis.conf +0 -42
  52. data/spec/ruby/web_socket/client_spec.rb +0 -126
  53. data/spec/ruby/web_socket/draft75_parser_spec.rb +0 -41
  54. data/spec/ruby/web_socket/protocol8_parser_spec.rb +0 -145
@@ -0,0 +1,109 @@
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 = 128
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 { EM.run } unless EM.reactor_running?
15
+ Thread.pass until EM.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
+ debug 'Created new engine: ?', @options
49
+ end
50
+
51
+ def connect(client_id, options = {}, &callback)
52
+ debug 'Accepting connection from ?', client_id
53
+ @engine.ping(client_id)
54
+ conn = connection(client_id, true)
55
+ conn.connect(options, &callback)
56
+ @engine.empty_queue(client_id)
57
+ end
58
+
59
+ def has_connection?(client_id)
60
+ @connections.has_key?(client_id)
61
+ end
62
+
63
+ def connection(client_id, create)
64
+ conn = @connections[client_id]
65
+ return conn if conn or not create
66
+ @connections[client_id] = Connection.new(self, client_id)
67
+ end
68
+
69
+ def close_connection(client_id)
70
+ debug 'Closing connection for ?', client_id
71
+ @connections.delete(client_id)
72
+ end
73
+
74
+ def open_socket(client_id, socket)
75
+ conn = connection(client_id, true)
76
+ conn.socket = socket
77
+ end
78
+
79
+ def deliver(client_id, messages)
80
+ return if !messages || messages.empty?
81
+ conn = connection(client_id, false)
82
+ return false unless conn
83
+ messages.each(&conn.method(:deliver))
84
+ true
85
+ end
86
+
87
+ def generate_id
88
+ Engine.random
89
+ end
90
+
91
+ def flush(client_id)
92
+ debug 'Flushing connection for ?', client_id
93
+ conn = connection(client_id, false)
94
+ conn.flush!(true) if conn
95
+ end
96
+
97
+ def disconnect
98
+ @engine.disconnect if @engine.respond_to?(:disconnect)
99
+ end
100
+
101
+ def publish(message)
102
+ channels = Channel.expand(message['channel'])
103
+ @engine.publish(message, channels)
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+
@@ -1,11 +1,4 @@
1
1
  module Faye
2
-
3
- class << self
4
- attr_accessor :logger
5
- end
6
-
7
- self.logger = method(:puts)
8
-
9
2
  module Logging
10
3
 
11
4
  DEFAULT_LOG_LEVEL = :error
@@ -42,7 +35,7 @@ module Faye
42
35
  timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
43
36
  banner = " [#{ level.to_s.upcase }] [#{ self.class.name }] "
44
37
 
45
- puts(timestamp + banner + message)
38
+ Faye.logger.call(timestamp + banner + message)
46
39
  end
47
40
 
48
41
  LOG_LEVELS.each do |level, value|
@@ -1,7 +1,7 @@
1
1
  module Faye
2
2
  module Timeouts
3
3
  def add_timeout(name, delay, &block)
4
- Faye.ensure_reactor_running!
4
+ Engine.ensure_reactor_running!
5
5
  @timeouts ||= {}
6
6
  return if @timeouts.has_key?(name)
7
7
  @timeouts[name] = EventMachine.add_timer(delay) do
@@ -22,8 +22,8 @@ module Faye
22
22
  UNSUBSCRIBE = '/meta/unsubscribe'
23
23
  DISCONNECT = '/meta/disconnect'
24
24
 
25
- META = 'meta'
26
- SERVICE = 'service'
25
+ META = :meta
26
+ SERVICE = :service
27
27
 
28
28
  class << self
29
29
  def expand(name)
@@ -50,7 +50,7 @@ module Faye
50
50
 
51
51
  def parse(name)
52
52
  return nil unless valid?(name)
53
- name.split('/')[1..-1]
53
+ name.split('/')[1..-1].map { |s| s.to_sym }
54
54
  end
55
55
 
56
56
  def unparse(segments)
@@ -16,16 +16,19 @@ module Faye
16
16
  NONE = 'none'
17
17
 
18
18
  CONNECTION_TIMEOUT = 60.0
19
+ DEFAULT_RETRY = 5.0
19
20
 
20
- attr_accessor :cookies
21
- attr_reader :endpoint, :client_id
21
+ attr_reader :endpoint, :client_id, :retry
22
22
 
23
23
  def initialize(endpoint = nil, options = {})
24
24
  info('New client created for ?', endpoint)
25
25
 
26
26
  @endpoint = endpoint || RackAdapter::DEFAULT_ENDPOINT
27
+ @cookies = CookieJar::Jar.new
28
+ @headers = {}
27
29
  @options = options
28
30
  @disabled = []
31
+ @retry = @options[:retry] || DEFAULT_RETRY
29
32
 
30
33
  select_transport(MANDATORY_CONNECTION_TYPES)
31
34
 
@@ -46,6 +49,10 @@ module Faye
46
49
  @disabled << feature
47
50
  end
48
51
 
52
+ def set_header(name, value)
53
+ @headers[name.to_s] = value.to_s
54
+ end
55
+
49
56
  def state
50
57
  case @state
51
58
  when UNCONNECTED then :UNCONNECTED
@@ -181,40 +188,41 @@ module Faye
181
188
  # * ext
182
189
  # * id
183
190
  # * timestamp
184
- def subscribe(channels, force = false, &block)
185
- if Array === channels
186
- return channels.each do |channel|
191
+ def subscribe(channel, force = false, &block)
192
+ if Array === channel
193
+ return channel.each do |channel|
187
194
  subscribe(channel, force, &block)
188
195
  end
189
196
  end
190
197
 
191
- subscription = Subscription.new(self, channels, block)
198
+ subscription = Subscription.new(self, channel, block)
199
+ has_subscribe = @channels.has_subscription?(channel)
192
200
 
193
- if not force and @channels.has_subscription?(channels)
194
- @channels.subscribe([channels], block)
195
- subscription.set_deferred_status(:succeeded)
196
- return subscription
201
+ unless force
202
+ @channels.subscribe([channel], block)
203
+ if has_subscribe
204
+ subscription.set_deferred_status(:succeeded)
205
+ return subscription
206
+ end
197
207
  end
198
208
 
199
209
  connect {
200
- info('Client ? attempting to subscribe to ?', @client_id, channels)
210
+ info('Client ? attempting to subscribe to ?', @client_id, channel)
201
211
 
202
212
  send({
203
213
  'channel' => Channel::SUBSCRIBE,
204
214
  'clientId' => @client_id,
205
- 'subscription' => channels
215
+ 'subscription' => channel
206
216
 
207
217
  }) do |response|
208
- if response['successful']
209
-
210
- channels = [response['subscription']].flatten
211
- info('Subscription acknowledged for ? to ?', @client_id, channels)
212
- @channels.subscribe(channels, block)
213
-
214
- subscription.set_deferred_status(:succeeded)
215
- else
218
+ unless response['successful']
216
219
  subscription.set_deferred_status(:failed, Error.parse(response['error']))
220
+ next @channels.unsubscribe(channel, block)
217
221
  end
222
+
223
+ channels = [response['subscription']].flatten
224
+ info('Subscription acknowledged for ? to ?', @client_id, channels)
225
+ subscription.set_deferred_status(:succeeded)
218
226
  end
219
227
  }
220
228
  subscription
@@ -230,30 +238,29 @@ module Faye
230
238
  # * ext
231
239
  # * id
232
240
  # * timestamp
233
- def unsubscribe(channels, &block)
234
- if Array === channels
235
- return channels.each do |channel|
241
+ def unsubscribe(channel, &block)
242
+ if Array === channel
243
+ return channel.each do |channel|
236
244
  unsubscribe(channel, &block)
237
245
  end
238
246
  end
239
247
 
240
- dead = @channels.unsubscribe(channels, block)
248
+ dead = @channels.unsubscribe(channel, block)
241
249
  return unless dead
242
250
 
243
251
  connect {
244
- info('Client ? attempting to unsubscribe from ?', @client_id, channels)
252
+ info('Client ? attempting to unsubscribe from ?', @client_id, channel)
245
253
 
246
254
  send({
247
255
  'channel' => Channel::UNSUBSCRIBE,
248
256
  'clientId' => @client_id,
249
- 'subscription' => channels
257
+ 'subscription' => channel
250
258
 
251
259
  }) do |response|
252
- if response['successful']
260
+ next unless response['successful']
253
261
 
254
- channels = [response['subscription']].flatten
255
- info('Unsubscription acknowledged for ? from ?', @client_id, channels)
256
- end
262
+ channels = [response['subscription']].flatten
263
+ info('Unsubscription acknowledged for ? from ?', @client_id, channels)
257
264
  end
258
265
  }
259
266
  end
@@ -265,10 +272,6 @@ module Faye
265
272
  # * id * error
266
273
  # * ext * ext
267
274
  def publish(channel, data)
268
- unless Grammar::CHANNEL_NAME =~ channel
269
- raise "Cannot publish: '#{channel}' is not a valid channel name"
270
- end
271
-
272
275
  publication = Publication.new
273
276
  connect {
274
277
  info('Client ? queueing published message to ?: ?', @client_id, channel, data)
@@ -290,17 +293,17 @@ module Faye
290
293
 
291
294
  def receive_message(message)
292
295
  pipe_through_extensions(:incoming, message) do |message|
293
- if message
294
- handle_advice(message['advice']) if message['advice']
295
-
296
- callback = @response_callbacks[message['id']]
297
- if callback
298
- @response_callbacks.delete(message['id'])
299
- callback.call(message)
300
- end
301
-
302
- deliver_message(message)
296
+ next unless message
297
+
298
+ handle_advice(message['advice']) if message['advice']
299
+
300
+ callback = @response_callbacks[message['id']]
301
+ if callback
302
+ @response_callbacks.delete(message['id'])
303
+ callback.call(message)
303
304
  end
305
+
306
+ deliver_message(message)
304
307
  end
305
308
  end
306
309
 
@@ -309,6 +312,8 @@ module Faye
309
312
  def select_transport(transport_types)
310
313
  Transport.get(self, transport_types) do |transport|
311
314
  @transport = transport
315
+ @transport.cookies = @cookies
316
+ @transport.headers = @headers
312
317
 
313
318
  transport.bind :down do
314
319
  if @transport_up.nil? or @transport_up
@@ -352,7 +357,7 @@ module Faye
352
357
  end
353
358
 
354
359
  def deliver_message(message)
355
- return unless message['channel'] and message['data']
360
+ return unless message.has_key?('channel') and message.has_key?('data')
356
361
  info('Client ? calling listeners for ? with ?', @client_id, message['channel'], message['data'])
357
362
  @channels.distribute_message(message)
358
363
  end
@@ -11,12 +11,9 @@ module Faye
11
11
  def remove_extension(extension)
12
12
  return unless @extensions
13
13
  @extensions.delete_if do |ext|
14
- if ext == extension
15
- extension.removed(self) if extension.respond_to?(:removed)
16
- true
17
- else
18
- false
19
- end
14
+ next false unless ext == extension
15
+ extension.removed(self) if extension.respond_to?(:removed)
16
+ true
20
17
  end
21
18
  end
22
19
 
@@ -27,19 +24,15 @@ module Faye
27
24
  extensions = @extensions.dup
28
25
 
29
26
  pipe = lambda do |message|
30
- if !message
31
- callback.call(message)
27
+ next callback.call(message) unless message
28
+
29
+ extension = extensions.shift
30
+ next callback.call(message) unless extension
31
+
32
+ if extension.respond_to?(stage)
33
+ extension.__send__(stage, message, pipe)
32
34
  else
33
- extension = extensions.shift
34
- if (!extension)
35
- callback.call(message)
36
- else
37
- if extension.respond_to?(stage)
38
- extension.__send__(stage, message, pipe)
39
- else
40
- pipe.call(message)
41
- end
42
- end
35
+ pipe.call(message)
43
36
  end
44
37
  end
45
38
  pipe.call(message)
@@ -3,8 +3,6 @@ module Faye
3
3
 
4
4
  include Logging
5
5
  include Extensible
6
-
7
- META_METHODS = %w[handshake connect disconnect subscribe unsubscribe]
8
6
 
9
7
  attr_reader :engine
10
8
 
@@ -18,11 +16,18 @@ module Faye
18
16
  end
19
17
 
20
18
  def flush_connection(messages)
21
- [messages].flatten.each do |message|
22
- client_id = message["clientId"]
23
- info 'Flushing connection for ?', client_id
24
- @engine.flush(client_id) if client_id
25
- end
19
+ client_id = [messages].flatten.first['clientId']
20
+ return unless client_id
21
+ info 'Flushing connection for ?', client_id
22
+ @engine.flush(client_id) if client_id
23
+ end
24
+
25
+ def open_socket(client_id, socket)
26
+ @engine.open_socket(client_id, socket)
27
+ end
28
+
29
+ def close_socket(client_id)
30
+ @engine.flush(client_id)
26
31
  end
27
32
 
28
33
  def process(messages, local = false, &callback)
@@ -63,11 +68,12 @@ module Faye
63
68
 
64
69
  def make_response(message)
65
70
  response = {}
66
- %w[id clientId channel error].each do |field|
67
- if message[field]
68
- response[field] = message[field]
69
- end
70
- end
71
+
72
+ response['id'] = message['id'] if message['id']
73
+ response['clientId'] = message['clientId'] if message['clientId']
74
+ response['channel'] = message['channel'] if message['channel']
75
+ response['error'] = message['error'] if message['error']
76
+
71
77
  response['successful'] = !response['error']
72
78
  response
73
79
  end
@@ -77,47 +83,51 @@ module Faye
77
83
  info 'Handling message: ? (local: ?)', message, local
78
84
 
79
85
  channel_name = message['channel']
86
+ error = message['error']
80
87
 
81
88
  return handle_meta(message, local, &callback) if Channel.meta?(channel_name)
82
89
 
83
- @engine.publish(message) unless message['error'] or Grammar::CHANNEL_NAME !~ channel_name
84
-
85
- if message['clientId']
86
- response = make_response(message)
87
- response['successful'] = !response['error']
88
- callback.call([response])
89
- else
90
- callback.call([])
90
+ if Grammar::CHANNEL_NAME !~ channel_name
91
+ error = Faye::Error.channel_invalid(channel_name)
91
92
  end
93
+
94
+ @engine.publish(message) unless error
95
+
96
+ response = make_response(message)
97
+ response['error'] = error if error
98
+ response['successful'] = !response['error']
99
+ callback.call([response])
92
100
  end
93
101
 
94
102
  def handle_meta(message, local, &callback)
95
- method = Channel.parse(message['channel'])[1]
103
+ method = Channel.parse(message['channel'])[1]
104
+ client_id = message['clientId']
96
105
 
97
- unless META_METHODS.include?(method)
98
- response = make_response(message)
99
- response['error'] = Faye::Error.channel_forbidden(message['channel'])
100
- response['successful'] = false
101
- return callback.call([response])
102
- end
103
-
104
106
  __send__(method, message, local) do |responses|
105
107
  responses = [responses].flatten
106
- responses.each(&method(:advize))
108
+ responses.each { |r| advize(r, message['connectionType']) }
107
109
  callback.call(responses)
108
110
  end
109
111
  end
110
112
 
111
- def advize(response)
113
+ def advize(response, connection_type)
112
114
  return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
113
115
 
116
+ if connection_type == 'eventsource'
117
+ interval = (@engine.timeout * 1000).floor
118
+ timeout = 0
119
+ else
120
+ interval = (@engine.interval * 1000).floor
121
+ timeout = (@engine.timeout * 1000).floor
122
+ end
123
+
114
124
  advice = response['advice'] ||= {}
115
125
  if response['error']
116
126
  advice['reconnect'] ||= 'handshake'
117
127
  else
118
128
  advice['reconnect'] ||= 'retry'
119
- advice['interval'] ||= (@engine.interval * 1000).floor
120
- advice['timeout'] ||= (@engine.timeout * 1000).floor
129
+ advice['interval'] ||= interval
130
+ advice['timeout'] ||= timeout
121
131
  end
122
132
  end
123
133
 
@@ -173,13 +183,18 @@ module Faye
173
183
 
174
184
  response['successful'] = response['error'].nil?
175
185
 
176
- if response['successful']
177
- @engine.connect(response['clientId'], message['advice']) do |events|
178
- callback.call([response] + events)
179
- end
180
- else
186
+ if !response['successful']
181
187
  response.delete('clientId')
182
- callback.call(response)
188
+ next callback.call(response)
189
+ end
190
+
191
+ if message['connectionType'] == 'eventsource'
192
+ message['advice'] ||= {}
193
+ message['advice']['timeout'] = 0
194
+ end
195
+
196
+ @engine.connect(response['clientId'], message['advice']) do |events|
197
+ callback.call([response] + events)
183
198
  end
184
199
  end
185
200
  end