face-faye 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +304 -0
- data/README.rdoc +83 -0
- data/lib/faye-browser-min.js +2 -0
- data/lib/faye-browser-min.js.map +8 -0
- data/lib/faye-browser.js +2194 -0
- data/lib/faye.rb +122 -0
- data/lib/faye/adapters/rack_adapter.rb +216 -0
- data/lib/faye/adapters/static_server.rb +56 -0
- data/lib/faye/engines/connection.rb +60 -0
- data/lib/faye/engines/memory.rb +112 -0
- data/lib/faye/engines/proxy.rb +121 -0
- data/lib/faye/error.rb +49 -0
- data/lib/faye/mixins/logging.rb +47 -0
- data/lib/faye/mixins/publisher.rb +30 -0
- data/lib/faye/mixins/timeouts.rb +22 -0
- data/lib/faye/protocol/channel.rb +124 -0
- data/lib/faye/protocol/client.rb +376 -0
- data/lib/faye/protocol/extensible.rb +43 -0
- data/lib/faye/protocol/grammar.rb +58 -0
- data/lib/faye/protocol/publication.rb +5 -0
- data/lib/faye/protocol/server.rb +293 -0
- data/lib/faye/protocol/socket.rb +23 -0
- data/lib/faye/protocol/subscription.rb +24 -0
- data/lib/faye/transport/http.rb +76 -0
- data/lib/faye/transport/local.rb +22 -0
- data/lib/faye/transport/transport.rb +116 -0
- data/lib/faye/transport/web_socket.rb +92 -0
- data/lib/faye/util/namespace.rb +20 -0
- data/spec/browser.html +45 -0
- data/spec/encoding_helper.rb +7 -0
- data/spec/install.sh +78 -0
- data/spec/javascript/channel_spec.js +15 -0
- data/spec/javascript/client_spec.js +729 -0
- data/spec/javascript/engine/memory_spec.js +7 -0
- data/spec/javascript/engine_spec.js +417 -0
- data/spec/javascript/faye_spec.js +34 -0
- data/spec/javascript/grammar_spec.js +66 -0
- data/spec/javascript/node_adapter_spec.js +307 -0
- data/spec/javascript/publisher_spec.js +27 -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 +145 -0
- data/spec/javascript/server/integration_spec.js +131 -0
- data/spec/javascript/server/publish_spec.js +85 -0
- data/spec/javascript/server/subscribe_spec.js +247 -0
- data/spec/javascript/server/unsubscribe_spec.js +245 -0
- data/spec/javascript/server_spec.js +121 -0
- data/spec/javascript/transport_spec.js +135 -0
- data/spec/node.js +55 -0
- data/spec/phantom.js +17 -0
- data/spec/ruby/channel_spec.rb +17 -0
- data/spec/ruby/client_spec.rb +741 -0
- data/spec/ruby/engine/memory_spec.rb +7 -0
- data/spec/ruby/engine_examples.rb +427 -0
- data/spec/ruby/faye_spec.rb +30 -0
- data/spec/ruby/grammar_spec.rb +68 -0
- data/spec/ruby/publisher_spec.rb +27 -0
- data/spec/ruby/rack_adapter_spec.rb +236 -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 +68 -0
- data/spec/ruby/server/handshake_spec.rb +143 -0
- data/spec/ruby/server/integration_spec.rb +133 -0
- data/spec/ruby/server/publish_spec.rb +81 -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 +121 -0
- data/spec/ruby/transport_spec.rb +136 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/testswarm +42 -0
- data/spec/thin_proxy.rb +37 -0
- metadata +441 -0
@@ -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
|