faye 0.5.5 → 0.6.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.
- data/History.txt +14 -0
- data/README.rdoc +98 -0
- data/Rakefile +17 -15
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +14 -5
- data/lib/faye/adapters/rack_adapter.rb +12 -5
- data/lib/faye/engines/base.rb +62 -0
- data/lib/faye/engines/connection.rb +63 -0
- data/lib/faye/engines/memory.rb +89 -0
- data/lib/faye/engines/redis.rb +141 -0
- data/lib/faye/error.rb +16 -4
- data/lib/faye/mixins/publisher.rb +6 -0
- data/lib/faye/protocol/channel.rb +34 -86
- data/lib/faye/protocol/client.rb +36 -52
- data/lib/faye/protocol/extensible.rb +3 -0
- data/lib/faye/protocol/server.rb +119 -169
- data/lib/faye/transport/http.rb +45 -0
- data/lib/faye/transport/local.rb +15 -0
- data/lib/faye/{network → transport}/transport.rb +36 -49
- data/spec/browser.html +35 -0
- data/spec/install.sh +48 -0
- data/spec/javascript/channel_spec.js +15 -0
- data/spec/javascript/client_spec.js +610 -0
- data/spec/javascript/engine_spec.js +319 -0
- data/spec/javascript/faye_spec.js +15 -0
- data/spec/javascript/grammar_spec.js +66 -0
- data/spec/javascript/node_adapter_spec.js +276 -0
- data/spec/javascript/server/connect_spec.js +168 -0
- data/spec/javascript/server/disconnect_spec.js +121 -0
- data/spec/javascript/server/extensions_spec.js +60 -0
- data/spec/javascript/server/handshake_spec.js +153 -0
- data/spec/javascript/server/subscribe_spec.js +245 -0
- data/spec/javascript/server/unsubscribe_spec.js +245 -0
- data/spec/javascript/server_spec.js +146 -0
- data/spec/javascript/transport_spec.js +130 -0
- data/spec/node.js +34 -0
- data/spec/ruby/channel_spec.rb +17 -0
- data/spec/ruby/client_spec.rb +615 -0
- data/spec/ruby/engine_spec.rb +312 -0
- data/spec/ruby/faye_spec.rb +14 -0
- data/spec/ruby/grammar_spec.rb +68 -0
- data/spec/ruby/rack_adapter_spec.rb +209 -0
- data/spec/ruby/server/connect_spec.rb +170 -0
- data/spec/ruby/server/disconnect_spec.rb +120 -0
- data/spec/ruby/server/extensions_spec.rb +69 -0
- data/spec/ruby/server/handshake_spec.rb +151 -0
- data/spec/ruby/server/subscribe_spec.rb +247 -0
- data/spec/ruby/server/unsubscribe_spec.rb +247 -0
- data/spec/ruby/server_spec.rb +138 -0
- data/spec/ruby/transport_spec.rb +128 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/testswarm.pl +200 -0
- data/spec/thin_proxy.rb +36 -0
- metadata +119 -84
- data/Manifest.txt +0 -27
- data/README.txt +0 -98
- data/lib/faye/protocol/connection.rb +0 -111
- data/test/scenario.rb +0 -172
- data/test/test_channel.rb +0 -54
- data/test/test_clients.rb +0 -381
- data/test/test_grammar.rb +0 -86
- data/test/test_server.rb +0 -488
@@ -1,5 +1,6 @@
|
|
1
1
|
module Faye
|
2
2
|
module Extensible
|
3
|
+
include Logging
|
3
4
|
|
4
5
|
def add_extension(extension)
|
5
6
|
@extensions ||= []
|
@@ -20,6 +21,8 @@ module Faye
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def pipe_through_extensions(stage, message, &callback)
|
24
|
+
debug 'Passing through ? extensions: ?', stage, message
|
25
|
+
|
23
26
|
return callback.call(message) unless @extensions
|
24
27
|
extensions = @extensions.dup
|
25
28
|
|
data/lib/faye/protocol/server.rb
CHANGED
@@ -5,30 +5,35 @@ module Faye
|
|
5
5
|
include Extensible
|
6
6
|
|
7
7
|
def initialize(options = {})
|
8
|
-
|
9
|
-
@options
|
10
|
-
|
11
|
-
@
|
12
|
-
|
8
|
+
@options = options || {}
|
9
|
+
engine_opts = @options[:engine] || {}
|
10
|
+
engine_opts[:timeout] = @options[:timeout]
|
11
|
+
@engine = Faye::Engine.get(engine_opts)
|
12
|
+
|
13
|
+
info 'Created new server: ?', @options
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
|
16
|
+
def flush_connection(messages)
|
17
|
+
[messages].flatten.each do |message|
|
18
|
+
client_id = message["clientId"]
|
19
|
+
info 'Flushing connection for ?', client_id
|
20
|
+
@engine.flush(client_id) if client_id
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
|
-
def process(messages,
|
20
|
-
socket = local_or_remote.is_a?(WebSocket) ? local_or_remote : nil
|
21
|
-
local = (local_or_remote == true)
|
22
|
-
|
23
|
-
debug('Processing messages from ? client', local ? 'LOCAL' : 'REMOTE')
|
24
|
-
|
24
|
+
def process(messages, local = false, &callback)
|
25
25
|
messages = [messages].flatten
|
26
|
+
info 'Processing messages: ? (local: ?)', messages, local
|
27
|
+
|
28
|
+
return callback.call([]) if messages.size == 0
|
26
29
|
processed, responses = 0, []
|
27
30
|
|
28
31
|
gather_replies = lambda do |replies|
|
29
32
|
responses.concat(replies)
|
30
33
|
processed += 1
|
31
|
-
|
34
|
+
responses.compact!
|
35
|
+
info 'Returning replies: ?', responses
|
36
|
+
callback.call(responses) if processed == messages.size
|
32
37
|
end
|
33
38
|
|
34
39
|
handle_reply = lambda do |replies|
|
@@ -36,6 +41,7 @@ module Faye
|
|
36
41
|
gather_replies.call(replies) if expected == 0
|
37
42
|
|
38
43
|
replies.each_with_index do |reply, i|
|
44
|
+
debug 'Processing reply: ?', reply
|
39
45
|
pipe_through_extensions(:outgoing, reply) do |message|
|
40
46
|
replies[i] = message
|
41
47
|
extended += 1
|
@@ -46,33 +52,11 @@ module Faye
|
|
46
52
|
|
47
53
|
messages.each do |message|
|
48
54
|
pipe_through_extensions(:incoming, message) do |piped_message|
|
49
|
-
handle(piped_message,
|
55
|
+
handle(piped_message, local, &handle_reply)
|
50
56
|
end
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
54
|
-
def flush_connection(messages)
|
55
|
-
[messages].flatten.each do |message|
|
56
|
-
connection = @connections[message['clientId']]
|
57
|
-
connection.flush! if connection
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def connection(id)
|
64
|
-
return @connections[id] if @connections.has_key?(id)
|
65
|
-
connection = Connection.new(id, @options)
|
66
|
-
connection.add_subscriber(:stale_connection, method(:destroy_connection))
|
67
|
-
@connections[id] = connection
|
68
|
-
end
|
69
|
-
|
70
|
-
def destroy_connection(connection)
|
71
|
-
connection.disconnect!
|
72
|
-
connection.remove_subscriber(:stale_connection, method(:destroy_connection))
|
73
|
-
@connections.delete(connection.id)
|
74
|
-
end
|
75
|
-
|
76
60
|
def make_response(message)
|
77
61
|
response = {}
|
78
62
|
%w[id clientId channel error].each do |field|
|
@@ -84,22 +68,15 @@ module Faye
|
|
84
68
|
response
|
85
69
|
end
|
86
70
|
|
87
|
-
def
|
88
|
-
return if message['error']
|
89
|
-
@channels.glob(message['channel']).each do |channel|
|
90
|
-
channel << message
|
91
|
-
info('Publishing message ? from client ? to ?', message['data'], message['clientId'], channel.name)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def handle(message, socket = nil, local = false, &callback)
|
71
|
+
def handle(message, local = false, &callback)
|
96
72
|
return callback.call([]) if !message
|
73
|
+
info 'Handling message: ? (local: ?)', message, local
|
97
74
|
|
98
|
-
distribute_message(message)
|
99
75
|
channel_name = message['channel']
|
76
|
+
@engine.publish(message) unless message['error'] or Grammar::CHANNEL_NAME !~ channel_name
|
100
77
|
|
101
78
|
if Channel.meta?(channel_name)
|
102
|
-
handle_meta(message,
|
79
|
+
handle_meta(message, local, &callback)
|
103
80
|
elsif message['clientId'].nil?
|
104
81
|
callback.call([])
|
105
82
|
else
|
@@ -109,46 +86,24 @@ module Faye
|
|
109
86
|
end
|
110
87
|
end
|
111
88
|
|
112
|
-
def handle_meta(message,
|
113
|
-
|
89
|
+
def handle_meta(message, local, &callback)
|
90
|
+
method = Channel.parse(message['channel'])[1]
|
114
91
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
callback.call([response])
|
122
|
-
end
|
123
|
-
|
124
|
-
def accept_connection(options, response, socket, &callback)
|
125
|
-
info('Accepting connection from ?', response['clientId'])
|
126
|
-
|
127
|
-
connection = connection(response['clientId'])
|
128
|
-
|
129
|
-
# Disabled because CometD doesn't like messages not being
|
130
|
-
# delivered as part of a /meta/* response
|
131
|
-
# if socket
|
132
|
-
# return connection.socket = socket
|
133
|
-
# end
|
134
|
-
|
135
|
-
connection.connect(options) do |events|
|
136
|
-
info('Sending event messages to ?', response['clientId'])
|
137
|
-
debug('Events for ?: ?', response['clientId'], events)
|
138
|
-
callback.call([response] + events)
|
92
|
+
__send__(method, message, local) do |responses|
|
93
|
+
responses = [responses].flatten
|
94
|
+
responses.each(&method(:advize))
|
95
|
+
callback.call(responses)
|
139
96
|
end
|
140
97
|
end
|
141
98
|
|
142
99
|
def advize(response)
|
143
|
-
connection = @connections[response['clientId']]
|
144
|
-
|
145
100
|
advice = response['advice'] ||= {}
|
146
|
-
if
|
147
|
-
advice['reconnect'] ||= 'retry'
|
148
|
-
advice['interval'] ||= (connection.interval * 1000).floor
|
149
|
-
advice['timeout'] ||= (connection.timeout * 1000).floor
|
150
|
-
else
|
101
|
+
if response['error']
|
151
102
|
advice['reconnect'] ||= 'handshake'
|
103
|
+
else
|
104
|
+
advice['reconnect'] ||= 'retry'
|
105
|
+
advice['interval'] ||= (@engine.interval * 1000).floor
|
106
|
+
advice['timeout'] ||= (@engine.timeout * 1000).floor
|
152
107
|
end
|
153
108
|
end
|
154
109
|
|
@@ -157,7 +112,7 @@ module Faye
|
|
157
112
|
# MAY contain * minimumVersion
|
158
113
|
# * ext
|
159
114
|
# * id
|
160
|
-
def handshake(message, local = false)
|
115
|
+
def handshake(message, local = false, &callback)
|
161
116
|
response = make_response(message)
|
162
117
|
response['version'] = BAYEUX_VERSION
|
163
118
|
|
@@ -177,128 +132,123 @@ module Faye
|
|
177
132
|
end
|
178
133
|
|
179
134
|
response['successful'] = response['error'].nil?
|
180
|
-
return response unless response['successful']
|
135
|
+
return callback.call(response) unless response['successful']
|
181
136
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
137
|
+
@engine.create_client do |client_id|
|
138
|
+
response['clientId'] = client_id
|
139
|
+
callback.call(response)
|
140
|
+
end
|
186
141
|
end
|
187
142
|
|
188
143
|
# MUST contain * clientId
|
189
144
|
# * connectionType
|
190
145
|
# MAY contain * ext
|
191
146
|
# * id
|
192
|
-
def connect(message, local = false)
|
193
|
-
response
|
194
|
-
|
195
|
-
client_id = message['clientId']
|
196
|
-
connection = client_id ? @connections[client_id] : nil
|
147
|
+
def connect(message, local = false, &callback)
|
148
|
+
response = make_response(message)
|
149
|
+
client_id = message['clientId']
|
197
150
|
connection_type = message['connectionType']
|
198
151
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
152
|
+
@engine.client_exists(client_id) do |exists|
|
153
|
+
response['error'] = Error.client_unknown(client_id) unless exists
|
154
|
+
response['error'] = Error.parameter_missing('clientId') if client_id.nil?
|
155
|
+
|
156
|
+
unless CONNECTION_TYPES.include?(connection_type)
|
157
|
+
response['error'] = Error.conntype_mismatch(connection_type)
|
158
|
+
end
|
159
|
+
|
160
|
+
response['error'] = Error.parameter_missing('connectionType') if connection_type.nil?
|
161
|
+
|
162
|
+
response['successful'] = response['error'].nil?
|
163
|
+
|
164
|
+
if response['successful']
|
165
|
+
@engine.connect(response['clientId'], message['advice']) do |events|
|
166
|
+
callback.call([response] + events)
|
167
|
+
end
|
168
|
+
else
|
169
|
+
response.delete('clientId')
|
170
|
+
callback.call(response)
|
171
|
+
end
|
172
|
+
end
|
209
173
|
end
|
210
174
|
|
211
175
|
# MUST contain * clientId
|
212
176
|
# MAY contain * ext
|
213
177
|
# * id
|
214
|
-
def disconnect(message, local = false)
|
215
|
-
response = make_response(message)
|
216
|
-
|
178
|
+
def disconnect(message, local = false, &callback)
|
179
|
+
response = make_response(message)
|
217
180
|
client_id = message['clientId']
|
218
|
-
connection = client_id ? @connections[client_id] : nil
|
219
|
-
|
220
|
-
response['error'] = Error.client_unknown(client_id) if connection.nil?
|
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
|
-
return response unless response['successful']
|
226
181
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
182
|
+
@engine.client_exists(client_id) do |exists|
|
183
|
+
response['error'] = Error.client_unknown(client_id) unless exists
|
184
|
+
response['error'] = Error.parameter_missing('clientId') if client_id.nil?
|
185
|
+
|
186
|
+
response['successful'] = response['error'].nil?
|
187
|
+
response.delete('clientId') unless response['successful']
|
188
|
+
|
189
|
+
@engine.destroy_client(client_id) if response['successful']
|
190
|
+
callback.call(response)
|
191
|
+
end
|
232
192
|
end
|
233
193
|
|
234
194
|
# MUST contain * clientId
|
235
195
|
# * subscription
|
236
196
|
# MAY contain * ext
|
237
197
|
# * id
|
238
|
-
def subscribe(message, local = false)
|
239
|
-
response
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
|
248
|
-
|
249
|
-
response['subscription'] = subscription.compact
|
250
|
-
|
251
|
-
subscription.each do |channel_name|
|
252
|
-
next if response['error']
|
253
|
-
response['error'] = Error.channel_forbidden(channel_name) unless local or Channel.subscribable?(channel_name)
|
254
|
-
response['error'] = Error.channel_invalid(channel_name) unless Channel.valid?(channel_name)
|
198
|
+
def subscribe(message, local = false, &callback)
|
199
|
+
response = make_response(message)
|
200
|
+
client_id = message['clientId']
|
201
|
+
subscription = [message['subscription']].flatten
|
202
|
+
|
203
|
+
@engine.client_exists(client_id) do |exists|
|
204
|
+
response['error'] = Error.client_unknown(client_id) unless exists
|
205
|
+
response['error'] = Error.parameter_missing('clientId') if client_id.nil?
|
206
|
+
response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
|
255
207
|
|
256
|
-
|
257
|
-
channel = @channels[channel_name] ||= Channel.new(channel_name)
|
208
|
+
response['subscription'] = message['subscription'] || []
|
258
209
|
|
259
|
-
|
260
|
-
|
210
|
+
subscription.each do |channel|
|
211
|
+
next if response['error']
|
212
|
+
response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
|
213
|
+
response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
|
214
|
+
|
215
|
+
next if response['error']
|
216
|
+
@engine.subscribe(client_id, channel)
|
217
|
+
end
|
218
|
+
|
219
|
+
response['successful'] = response['error'].nil?
|
220
|
+
callback.call(response)
|
261
221
|
end
|
262
|
-
|
263
|
-
response['successful'] = response['error'].nil?
|
264
|
-
response
|
265
222
|
end
|
266
223
|
|
267
224
|
# MUST contain * clientId
|
268
225
|
# * subscription
|
269
226
|
# MAY contain * ext
|
270
227
|
# * id
|
271
|
-
def unsubscribe(message, local = false)
|
272
|
-
response
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
|
281
|
-
|
282
|
-
response['subscription'] = subscription.compact
|
283
|
-
|
284
|
-
subscription.each do |channel_name|
|
285
|
-
next if response['error']
|
228
|
+
def unsubscribe(message, local = false, &callback)
|
229
|
+
response = make_response(message)
|
230
|
+
client_id = message['clientId']
|
231
|
+
subscription = [message['subscription']].flatten
|
232
|
+
|
233
|
+
@engine.client_exists(client_id) do |exists|
|
234
|
+
response['error'] = Error.client_unknown(client_id) unless exists
|
235
|
+
response['error'] = Error.parameter_missing('clientId') if client_id.nil?
|
236
|
+
response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
|
286
237
|
|
287
|
-
|
288
|
-
response['error'] = Error.channel_invalid(channel_name)
|
289
|
-
next
|
290
|
-
end
|
238
|
+
response['subscription'] = message['subscription'] || []
|
291
239
|
|
292
|
-
|
293
|
-
|
240
|
+
subscription.each do |channel|
|
241
|
+
next if response['error']
|
242
|
+
response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
|
243
|
+
response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
|
244
|
+
|
245
|
+
next if response['error']
|
246
|
+
@engine.unsubscribe(client_id, channel)
|
247
|
+
end
|
294
248
|
|
295
|
-
|
296
|
-
|
297
|
-
@channels.remove(channel_name) if channel.unused?
|
249
|
+
response['successful'] = response['error'].nil?
|
250
|
+
callback.call(response)
|
298
251
|
end
|
299
|
-
|
300
|
-
response['successful'] = response['error'].nil?
|
301
|
-
response
|
302
252
|
end
|
303
253
|
|
304
254
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'em-http'
|
2
|
+
require 'em-http/version'
|
3
|
+
|
4
|
+
module Faye
|
5
|
+
|
6
|
+
class Transport::Http < Transport
|
7
|
+
def self.usable?(endpoint)
|
8
|
+
endpoint.is_a?(String)
|
9
|
+
end
|
10
|
+
|
11
|
+
def request(message, timeout)
|
12
|
+
retry_block = retry_block(message, timeout)
|
13
|
+
|
14
|
+
content = JSON.unparse(message)
|
15
|
+
params = {
|
16
|
+
:head => {
|
17
|
+
'Content-Type' => 'application/json',
|
18
|
+
'host' => URI.parse(@endpoint).host,
|
19
|
+
'Content-Length' => content.length
|
20
|
+
},
|
21
|
+
:body => content,
|
22
|
+
:timeout => -1 # for em-http-request < 1.0
|
23
|
+
}
|
24
|
+
if EventMachine::HttpRequest::VERSION.split('.')[0].to_i >= 1
|
25
|
+
options = { # for em-http-request >= 1.0
|
26
|
+
:inactivity_timeout => 0, # connection inactivity (post-setup) timeout (0 = disable timeout)
|
27
|
+
}
|
28
|
+
request = EventMachine::HttpRequest.new(@endpoint, options).post(params)
|
29
|
+
else
|
30
|
+
request = EventMachine::HttpRequest.new(@endpoint).post(params)
|
31
|
+
end
|
32
|
+
request.callback do
|
33
|
+
begin
|
34
|
+
receive(JSON.parse(request.response))
|
35
|
+
rescue
|
36
|
+
retry_block.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
request.errback { retry_block.call }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Transport.register 'long-polling', Transport::Http
|
44
|
+
|
45
|
+
end
|