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,5 @@
1
+ module Faye
2
+ class Publication
3
+ include EventMachine::Deferrable
4
+ end
5
+ end
@@ -0,0 +1,293 @@
1
+ module Faye
2
+ class Server
3
+
4
+ autoload :Socket, File.join(ROOT, 'faye', 'protocol', 'socket')
5
+
6
+ include Logging
7
+ include Extensible
8
+
9
+ META_METHODS = %w[handshake connect disconnect subscribe unsubscribe]
10
+
11
+ attr_reader :engine
12
+
13
+ def initialize(options = {})
14
+ @options = options || {}
15
+ engine_opts = @options[:engine] || {}
16
+ engine_opts[:timeout] = @options[:timeout]
17
+ @engine = Faye::Engine.get(engine_opts)
18
+
19
+ info 'Created new server: ?', @options
20
+ end
21
+
22
+ def flush_connection(messages)
23
+ client_id = Faye.client_id_from_messages(messages)
24
+ info 'Flushing connection for ?', client_id
25
+ @engine.flush(client_id) if client_id
26
+ end
27
+
28
+ def open_socket(client_id, socket)
29
+ @engine.open_socket(client_id, Socket.new(self, socket))
30
+ end
31
+
32
+ def close_socket(client_id)
33
+ @engine.flush(client_id)
34
+ end
35
+
36
+ def process(messages, local = false, &callback)
37
+ messages = [messages].flatten
38
+ info 'Processing messages: ? (local: ?)', messages, local
39
+
40
+ return callback.call([]) if messages.size == 0
41
+ processed, responses = 0, []
42
+
43
+ gather_replies = lambda do |replies|
44
+ responses.concat(replies)
45
+ processed += 1
46
+ responses.compact!
47
+ info 'Returning replies: ?', responses
48
+ callback.call(responses) if processed == messages.size
49
+ end
50
+
51
+ handle_reply = lambda do |replies|
52
+ extended, expected = 0, replies.size
53
+ gather_replies.call(replies) if expected == 0
54
+
55
+ replies.each_with_index do |reply, i|
56
+ debug 'Processing reply: ?', reply
57
+ pipe_through_extensions(:outgoing, reply) do |message|
58
+ replies[i] = message
59
+ extended += 1
60
+ gather_replies.call(replies) if extended == expected
61
+ end
62
+ end
63
+ end
64
+
65
+ messages.each do |message|
66
+ pipe_through_extensions(:incoming, message) do |piped_message|
67
+ handle(piped_message, local, &handle_reply)
68
+ end
69
+ end
70
+ end
71
+
72
+ def make_response(message)
73
+ response = {}
74
+
75
+ response['id'] = message['id'] if message['id']
76
+ response['clientId'] = message['clientId'] if message['clientId']
77
+ response['channel'] = message['channel'] if message['channel']
78
+ response['error'] = message['error'] if message['error']
79
+
80
+ response['successful'] = !response['error']
81
+ response
82
+ end
83
+
84
+ def handle(message, local = false, &callback)
85
+ return callback.call([]) if !message
86
+ info 'Handling message: ? (local: ?)', message, local
87
+
88
+ channel_name = message['channel']
89
+ error = message['error']
90
+
91
+ return handle_meta(message, local, &callback) if Channel.meta?(channel_name)
92
+
93
+ if Grammar::CHANNEL_NAME !~ channel_name
94
+ error = Faye::Error.channel_invalid(channel_name)
95
+ end
96
+
97
+ @engine.publish(message) unless error
98
+
99
+ response = make_response(message)
100
+ response['error'] = error if error
101
+ response['successful'] = !response['error']
102
+ callback.call([response])
103
+ end
104
+
105
+ def handle_meta(message, local, &callback)
106
+ method = Channel.parse(message['channel'])[1]
107
+ client_id = message['clientId']
108
+
109
+ unless META_METHODS.include?(method)
110
+ response = make_response(message)
111
+ response['error'] = Faye::Error.channel_forbidden(message['channel'])
112
+ response['successful'] = false
113
+ return callback.call([response])
114
+ end
115
+
116
+ __send__(method, message, local) do |responses|
117
+ responses = [responses].flatten
118
+ responses.each { |r| advize(r, message['connectionType']) }
119
+ callback.call(responses)
120
+ end
121
+ end
122
+
123
+ def advize(response, connection_type)
124
+ return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
125
+
126
+ if connection_type == 'eventsource'
127
+ interval = (@engine.timeout * 1000).floor
128
+ timeout = 0
129
+ else
130
+ interval = (@engine.interval * 1000).floor
131
+ timeout = (@engine.timeout * 1000).floor
132
+ end
133
+
134
+ advice = response['advice'] ||= {}
135
+ if response['error']
136
+ advice['reconnect'] ||= 'handshake'
137
+ else
138
+ advice['reconnect'] ||= 'retry'
139
+ advice['interval'] ||= interval
140
+ advice['timeout'] ||= timeout
141
+ end
142
+ end
143
+
144
+ # MUST contain * version
145
+ # * supportedConnectionTypes
146
+ # MAY contain * minimumVersion
147
+ # * ext
148
+ # * id
149
+ def handshake(message, local = false, &callback)
150
+ response = make_response(message)
151
+ response['version'] = BAYEUX_VERSION
152
+
153
+ response['error'] = Error.parameter_missing('version') if message['version'].nil?
154
+
155
+ client_conns = message['supportedConnectionTypes']
156
+
157
+ response['supportedConnectionTypes'] = CONNECTION_TYPES
158
+
159
+ if client_conns
160
+ common_conns = client_conns.select { |c| CONNECTION_TYPES.include?(c) }
161
+ response['error'] = Error.conntype_mismatch(*client_conns) if common_conns.empty?
162
+ else
163
+ response['error'] = Error.parameter_missing('supportedConnectionTypes')
164
+ end
165
+
166
+ response['successful'] = response['error'].nil?
167
+ return callback.call(response) unless response['successful']
168
+
169
+ @engine.create_client do |client_id|
170
+ response['clientId'] = client_id
171
+ callback.call(response)
172
+ end
173
+ end
174
+
175
+ # MUST contain * clientId
176
+ # * connectionType
177
+ # MAY contain * ext
178
+ # * id
179
+ def connect(message, local = false, &callback)
180
+ response = make_response(message)
181
+ client_id = message['clientId']
182
+ connection_type = message['connectionType']
183
+
184
+ @engine.client_exists(client_id) do |exists|
185
+ response['error'] = Error.client_unknown(client_id) unless exists
186
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
187
+
188
+ unless CONNECTION_TYPES.include?(connection_type)
189
+ response['error'] = Error.conntype_mismatch(connection_type)
190
+ end
191
+
192
+ response['error'] = Error.parameter_missing('connectionType') if connection_type.nil?
193
+
194
+ response['successful'] = response['error'].nil?
195
+
196
+ if !response['successful']
197
+ response.delete('clientId')
198
+ next callback.call(response)
199
+ end
200
+
201
+ if message['connectionType'] == 'eventsource'
202
+ message['advice'] ||= {}
203
+ message['advice']['timeout'] = 0
204
+ end
205
+
206
+ @engine.connect(response['clientId'], message['advice']) do |events|
207
+ callback.call([response] + events)
208
+ end
209
+ end
210
+ end
211
+
212
+ # MUST contain * clientId
213
+ # MAY contain * ext
214
+ # * id
215
+ def disconnect(message, local = false, &callback)
216
+ response = make_response(message)
217
+ client_id = message['clientId']
218
+
219
+ @engine.client_exists(client_id) do |exists|
220
+ response['error'] = Error.client_unknown(client_id) unless exists
221
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
222
+
223
+ response['successful'] = response['error'].nil?
224
+ response.delete('clientId') unless response['successful']
225
+
226
+ @engine.destroy_client(client_id) if response['successful']
227
+ callback.call(response)
228
+ end
229
+ end
230
+
231
+ # MUST contain * clientId
232
+ # * subscription
233
+ # MAY contain * ext
234
+ # * id
235
+ def subscribe(message, local = false, &callback)
236
+ response = make_response(message)
237
+ client_id = message['clientId']
238
+ subscription = [message['subscription']].flatten
239
+
240
+ @engine.client_exists(client_id) do |exists|
241
+ response['error'] = Error.client_unknown(client_id) unless exists
242
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
243
+ response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
244
+
245
+ response['subscription'] = message['subscription'] || []
246
+
247
+ subscription.each do |channel|
248
+ next if response['error']
249
+ response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
250
+ response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
251
+
252
+ next if response['error']
253
+ @engine.subscribe(client_id, channel)
254
+ end
255
+
256
+ response['successful'] = response['error'].nil?
257
+ callback.call(response)
258
+ end
259
+ end
260
+
261
+ # MUST contain * clientId
262
+ # * subscription
263
+ # MAY contain * ext
264
+ # * id
265
+ def unsubscribe(message, local = false, &callback)
266
+ response = make_response(message)
267
+ client_id = message['clientId']
268
+ subscription = [message['subscription']].flatten
269
+
270
+ @engine.client_exists(client_id) do |exists|
271
+ response['error'] = Error.client_unknown(client_id) unless exists
272
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
273
+ response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
274
+
275
+ response['subscription'] = message['subscription'] || []
276
+
277
+ subscription.each do |channel|
278
+ next if response['error']
279
+ response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
280
+ response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
281
+
282
+ next if response['error']
283
+ @engine.unsubscribe(client_id, channel)
284
+ end
285
+
286
+ response['successful'] = response['error'].nil?
287
+ callback.call(response)
288
+ end
289
+ end
290
+
291
+ end
292
+ end
293
+
@@ -0,0 +1,23 @@
1
+ module Faye
2
+ class Server
3
+
4
+ class Socket
5
+ def initialize(server, socket)
6
+ @server = server
7
+ @socket = socket
8
+ end
9
+
10
+ def send(message)
11
+ @server.pipe_through_extensions(:outgoing, message) do |piped_message|
12
+ @socket.send(Faye.to_json([piped_message]))
13
+ end
14
+ end
15
+
16
+ def close
17
+ @socket.close
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,24 @@
1
+ module Faye
2
+ class Subscription
3
+ include EventMachine::Deferrable
4
+
5
+ def initialize(client, channels, callback)
6
+ @client = client
7
+ @channels = channels
8
+ @callback = callback
9
+ @cancelled = false
10
+ end
11
+
12
+ def cancel
13
+ return if @cancelled
14
+ @client.unsubscribe(@channels, &@callback)
15
+ @cancelled = true
16
+ end
17
+
18
+ def unsubscribe
19
+ cancel
20
+ end
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,76 @@
1
+ module Faye
2
+
3
+ class Transport::Http < Transport
4
+ def self.usable?(client, endpoint, &callback)
5
+ callback.call(endpoint.is_a?(String))
6
+ end
7
+
8
+ def request(message, timeout)
9
+ retry_block = retry_block(message, timeout)
10
+
11
+ content = Faye.to_json(message)
12
+ cookies = @cookies.get_cookies(@endpoint)
13
+ params = build_params(URI.parse(@endpoint), content, cookies)
14
+ request = create_request(params)
15
+
16
+ request.callback do
17
+ handle_response(request.response, retry_block)
18
+ store_cookies([*request.response_header['SET_COOKIE']].compact)
19
+ end
20
+ request.errback do
21
+ retry_block.call
22
+ trigger(:down)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def build_params(uri, content, cookies)
29
+ {
30
+ :head => {
31
+ 'Content-Length' => content.bytesize,
32
+ 'Content-Type' => 'application/json',
33
+ 'Cookie' => cookies * '; ',
34
+ 'Host' => uri.host
35
+ }.merge(@headers),
36
+
37
+ :body => content,
38
+ :timeout => -1 # for em-http-request < 1.0
39
+ }
40
+ end
41
+
42
+ def create_request(params)
43
+ version = EventMachine::HttpRequest::VERSION.split('.')[0].to_i
44
+ client = if version >= 1
45
+ options = { # for em-http-request >= 1.0
46
+ :inactivity_timeout => 0 # connection inactivity (post-setup) timeout (0 = disable timeout)
47
+ }
48
+ EventMachine::HttpRequest.new(@endpoint, options)
49
+ else
50
+ EventMachine::HttpRequest.new(@endpoint)
51
+ end
52
+
53
+ client.post(params)
54
+ end
55
+
56
+ def handle_response(response, retry_block)
57
+ message = MultiJson.load(response) rescue nil
58
+ if message
59
+ receive(message)
60
+ trigger(:up)
61
+ else
62
+ retry_block.call
63
+ trigger(:down)
64
+ end
65
+ end
66
+
67
+ def store_cookies(cookies)
68
+ cookies.each do |cookie|
69
+ @cookies.set_cookie(@endpoint, cookie)
70
+ end
71
+ end
72
+ end
73
+
74
+ Transport.register 'long-polling', Transport::Http
75
+
76
+ end
@@ -0,0 +1,22 @@
1
+ module Faye
2
+
3
+ class Transport::Local < Transport
4
+ def self.usable?(client, endpoint, &callback)
5
+ callback.call(endpoint.is_a?(Server))
6
+ end
7
+
8
+ def batching?
9
+ false
10
+ end
11
+
12
+ def request(message, timeout)
13
+ message = Faye.copy_object(message)
14
+ @endpoint.process(message, true) do |responses|
15
+ receive(Faye.copy_object(responses))
16
+ end
17
+ end
18
+ end
19
+
20
+ Transport.register 'in-process', Transport::Local
21
+
22
+ end