face-faye 0.8.9

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.
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