faye-huboard 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ module Faye
2
+ module Extensible
3
+ include Logging
4
+
5
+ def add_extension(extension)
6
+ @extensions ||= []
7
+ @extensions << extension
8
+ extension.added(self) if extension.respond_to?(:added)
9
+ end
10
+
11
+ def remove_extension(extension)
12
+ return unless @extensions
13
+ @extensions.delete_if do |ext|
14
+ next false unless ext == extension
15
+ extension.removed(self) if extension.respond_to?(:removed)
16
+ true
17
+ end
18
+ end
19
+
20
+ def pipe_through_extensions(stage, message, env, &callback)
21
+ debug('Passing through ? extensions: ?', stage, message)
22
+
23
+ return callback.call(message) unless @extensions
24
+ extensions = @extensions.dup
25
+
26
+ pipe = lambda do |message|
27
+ next callback.call(message) unless message
28
+
29
+ extension = extensions.shift
30
+ next callback.call(message) unless extension
31
+
32
+ next pipe.call(message) unless extension.respond_to?(stage)
33
+
34
+ arity = extension.method(stage).arity
35
+ if arity >= 3
36
+ extension.__send__(stage, message, env, pipe)
37
+ else
38
+ extension.__send__(stage, message, pipe)
39
+ end
40
+ end
41
+ pipe.call(message)
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ module Faye
2
+ module Grammar
3
+
4
+ def self.rule(&block)
5
+ source = instance_eval(&block)
6
+ %r{^#{string(source)}$}
7
+ end
8
+
9
+ def self.choice(*list)
10
+ '(' + list.map(&method(:string)) * '|' + ')'
11
+ end
12
+
13
+ def self.repeat(*pattern)
14
+ '(' + string(pattern) + ')*'
15
+ end
16
+
17
+ def self.oneormore(*pattern)
18
+ '(' + string(pattern) + ')+'
19
+ end
20
+
21
+ def self.string(item)
22
+ return item.map(&method(:string)) * '' if Array === item
23
+ String === item ? item : item.source.gsub(/^\^/, '').gsub(/\$$/, '')
24
+ end
25
+
26
+ LOWALPHA = rule {[ '[a-z]' ]}
27
+ UPALPHA = rule {[ '[A-Z]' ]}
28
+ ALPHA = rule {[ choice(LOWALPHA, UPALPHA) ]}
29
+ DIGIT = rule {[ '[0-9]' ]}
30
+ ALPHANUM = rule {[ choice(ALPHA, DIGIT) ]}
31
+ MARK = rule {[ choice(*%w[\\- \\_ \\! \\~ \\( \\) \\$ \\@]) ]}
32
+ STRING = rule {[ repeat(choice(ALPHANUM, MARK, ' ', '\\/', '\\*', '\\.')) ]}
33
+ TOKEN = rule {[ oneormore(choice(ALPHANUM, MARK)) ]}
34
+ INTEGER = rule {[ oneormore(DIGIT) ]}
35
+
36
+ CHANNEL_SEGMENT = rule {[ TOKEN ]}
37
+ CHANNEL_SEGMENTS = rule {[ CHANNEL_SEGMENT, repeat('\\/', CHANNEL_SEGMENT) ]}
38
+ CHANNEL_NAME = rule {[ '\\/', CHANNEL_SEGMENTS ]}
39
+
40
+ WILD_CARD = rule {[ '\\*{1,2}' ]}
41
+ CHANNEL_PATTERN = rule {[ repeat('\\/', CHANNEL_SEGMENT), '\\/', WILD_CARD ]}
42
+
43
+ VERSION_ELEMENT = rule {[ ALPHANUM, repeat(choice(ALPHANUM, '\\-', '\\_')) ]}
44
+ VERSION = rule {[ INTEGER, repeat('\\.', VERSION_ELEMENT) ]}
45
+
46
+ CLIENT_ID = rule {[ oneormore(ALPHANUM) ]}
47
+
48
+ ID = rule {[ oneormore(ALPHANUM) ]}
49
+
50
+ ERROR_MESSAGE = rule {[ STRING ]}
51
+ ERROR_ARGS = rule {[ STRING, repeat(',', STRING) ]}
52
+ ERROR_CODE = rule {[ DIGIT, DIGIT, DIGIT ]}
53
+ ERROR = rule {[ choice(string([ERROR_CODE, ':', ERROR_ARGS, ':', ERROR_MESSAGE]),
54
+ string([ERROR_CODE, ':', ':', ERROR_MESSAGE])) ]}
55
+
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ module Faye
2
+ class Publication
3
+ include Deferrable
4
+ end
5
+ end
@@ -0,0 +1,291 @@
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 close
23
+ @engine.close
24
+ end
25
+
26
+ def open_socket(client_id, socket, env)
27
+ return unless client_id and socket
28
+ @engine.open_socket(client_id, Socket.new(self, socket, env))
29
+ end
30
+
31
+ def close_socket(client_id, close = true)
32
+ @engine.flush_connection(client_id, close)
33
+ end
34
+
35
+ def process(messages, env, &callback)
36
+ local = env.nil?
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, env) 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, env) 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
+
108
+ unless META_METHODS.include?(method)
109
+ response = make_response(message)
110
+ response['error'] = Faye::Error.channel_forbidden(message['channel'])
111
+ response['successful'] = false
112
+ return callback.call([response])
113
+ end
114
+
115
+ __send__(method, message, local) do |responses|
116
+ responses = [responses].flatten
117
+ responses.each { |r| advize(r, message['connectionType']) }
118
+ callback.call(responses)
119
+ end
120
+ end
121
+
122
+ def advize(response, connection_type)
123
+ return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
124
+
125
+ if connection_type == 'eventsource'
126
+ interval = (@engine.timeout * 1000).floor
127
+ timeout = 0
128
+ else
129
+ interval = (@engine.interval * 1000).floor
130
+ timeout = (@engine.timeout * 1000).floor
131
+ end
132
+
133
+ advice = response['advice'] ||= {}
134
+ if response['error']
135
+ advice['reconnect'] ||= 'handshake'
136
+ else
137
+ advice['reconnect'] ||= 'retry'
138
+ advice['interval'] ||= interval
139
+ advice['timeout'] ||= timeout
140
+ end
141
+ end
142
+
143
+ # MUST contain * version
144
+ # * supportedConnectionTypes
145
+ # MAY contain * minimumVersion
146
+ # * ext
147
+ # * id
148
+ def handshake(message, local = false, &callback)
149
+ response = make_response(message)
150
+ response['version'] = BAYEUX_VERSION
151
+
152
+ response['error'] = Error.parameter_missing('version') if message['version'].nil?
153
+
154
+ client_conns = message['supportedConnectionTypes']
155
+
156
+ response['supportedConnectionTypes'] = CONNECTION_TYPES
157
+
158
+ if client_conns
159
+ common_conns = client_conns.select { |c| CONNECTION_TYPES.include?(c) }
160
+ response['error'] = Error.conntype_mismatch(*client_conns) if common_conns.empty?
161
+ else
162
+ response['error'] = Error.parameter_missing('supportedConnectionTypes')
163
+ end
164
+
165
+ response['successful'] = response['error'].nil?
166
+ return callback.call(response) unless response['successful']
167
+
168
+ @engine.create_client do |client_id|
169
+ response['clientId'] = client_id
170
+ callback.call(response)
171
+ end
172
+ end
173
+
174
+ # MUST contain * clientId
175
+ # * connectionType
176
+ # MAY contain * ext
177
+ # * id
178
+ def connect(message, local = false, &callback)
179
+ response = make_response(message)
180
+ client_id = message['clientId']
181
+ connection_type = message['connectionType']
182
+
183
+ @engine.client_exists(client_id) do |exists|
184
+ response['error'] = Error.client_unknown(client_id) unless exists
185
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
186
+
187
+ unless CONNECTION_TYPES.include?(connection_type)
188
+ response['error'] = Error.conntype_mismatch(connection_type)
189
+ end
190
+
191
+ response['error'] = Error.parameter_missing('connectionType') if connection_type.nil?
192
+
193
+ response['successful'] = response['error'].nil?
194
+
195
+ if !response['successful']
196
+ response.delete('clientId')
197
+ next callback.call(response)
198
+ end
199
+
200
+ if message['connectionType'] == 'eventsource'
201
+ message['advice'] ||= {}
202
+ message['advice']['timeout'] = 0
203
+ end
204
+
205
+ @engine.connect(response['clientId'], message['advice']) do |events|
206
+ callback.call([response] + events)
207
+ end
208
+ end
209
+ end
210
+
211
+ # MUST contain * clientId
212
+ # MAY contain * ext
213
+ # * id
214
+ def disconnect(message, local = false, &callback)
215
+ response = make_response(message)
216
+ client_id = message['clientId']
217
+
218
+ @engine.client_exists(client_id) do |exists|
219
+ response['error'] = Error.client_unknown(client_id) unless exists
220
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
221
+
222
+ response['successful'] = response['error'].nil?
223
+ response.delete('clientId') unless response['successful']
224
+
225
+ @engine.destroy_client(client_id) if response['successful']
226
+ callback.call(response)
227
+ end
228
+ end
229
+
230
+ # MUST contain * clientId
231
+ # * subscription
232
+ # MAY contain * ext
233
+ # * id
234
+ def subscribe(message, local = false, &callback)
235
+ response = make_response(message)
236
+ client_id = message['clientId']
237
+ subscription = [message['subscription']].flatten
238
+
239
+ @engine.client_exists(client_id) do |exists|
240
+ response['error'] = Error.client_unknown(client_id) unless exists
241
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
242
+ response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
243
+
244
+ response['subscription'] = message['subscription'] || []
245
+
246
+ subscription.each do |channel|
247
+ next if response['error']
248
+ response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
249
+ response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
250
+
251
+ next if response['error']
252
+ @engine.subscribe(client_id, channel)
253
+ end
254
+
255
+ response['successful'] = response['error'].nil?
256
+ callback.call(response)
257
+ end
258
+ end
259
+
260
+ # MUST contain * clientId
261
+ # * subscription
262
+ # MAY contain * ext
263
+ # * id
264
+ def unsubscribe(message, local = false, &callback)
265
+ response = make_response(message)
266
+ client_id = message['clientId']
267
+ subscription = [message['subscription']].flatten
268
+
269
+ @engine.client_exists(client_id) do |exists|
270
+ response['error'] = Error.client_unknown(client_id) unless exists
271
+ response['error'] = Error.parameter_missing('clientId') if client_id.nil?
272
+ response['error'] = Error.parameter_missing('subscription') if message['subscription'].nil?
273
+
274
+ response['subscription'] = message['subscription'] || []
275
+
276
+ subscription.each do |channel|
277
+ next if response['error']
278
+ response['error'] = Error.channel_forbidden(channel) unless local or Channel.subscribable?(channel)
279
+ response['error'] = Error.channel_invalid(channel) unless Channel.valid?(channel)
280
+
281
+ next if response['error']
282
+ @engine.unsubscribe(client_id, channel)
283
+ end
284
+
285
+ response['successful'] = response['error'].nil?
286
+ callback.call(response)
287
+ end
288
+ end
289
+
290
+ end
291
+ end
@@ -0,0 +1,24 @@
1
+ module Faye
2
+ class Server
3
+
4
+ class Socket
5
+ def initialize(server, socket, env)
6
+ @server = server
7
+ @socket = socket
8
+ @env = env
9
+ end
10
+
11
+ def send(message)
12
+ @server.pipe_through_extensions(:outgoing, message, @env) do |piped_message|
13
+ @socket.send(Faye.to_json([piped_message])) if @socket
14
+ end
15
+ end
16
+
17
+ def close
18
+ @socket.close if @socket
19
+ @socket = nil
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ module Faye
2
+ class Subscription
3
+ include 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