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