pomelo-citrus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -0
  3. data/Rakefile +0 -0
  4. data/citrus.gemspec +35 -0
  5. data/lib/citrus.rb +18 -0
  6. data/lib/citrus/application.rb +237 -0
  7. data/lib/citrus/citrus.rb +27 -0
  8. data/lib/citrus/common/remote/backend/msg_remote.rb +57 -0
  9. data/lib/citrus/common/remote/frontend/channel_remote.rb +73 -0
  10. data/lib/citrus/common/remote/frontend/session_remote.rb +108 -0
  11. data/lib/citrus/common/service/backend_session_service.rb +265 -0
  12. data/lib/citrus/common/service/channel_service.rb +485 -0
  13. data/lib/citrus/common/service/connection_service.rb +71 -0
  14. data/lib/citrus/common/service/filter_service.rb +92 -0
  15. data/lib/citrus/common/service/handler_service.rb +63 -0
  16. data/lib/citrus/common/service/session_service.rb +446 -0
  17. data/lib/citrus/components/backend_session.rb +32 -0
  18. data/lib/citrus/components/channel.rb +33 -0
  19. data/lib/citrus/components/component.rb +19 -0
  20. data/lib/citrus/components/connection.rb +48 -0
  21. data/lib/citrus/components/connector.rb +265 -0
  22. data/lib/citrus/components/master.rb +40 -0
  23. data/lib/citrus/components/monitor.rb +48 -0
  24. data/lib/citrus/components/proxy.rb +195 -0
  25. data/lib/citrus/components/push_scheduler.rb +74 -0
  26. data/lib/citrus/components/remote.rb +71 -0
  27. data/lib/citrus/components/server.rb +61 -0
  28. data/lib/citrus/components/session.rb +41 -0
  29. data/lib/citrus/connectors/commands/handshake.rb +22 -0
  30. data/lib/citrus/connectors/commands/heartbeat.rb +22 -0
  31. data/lib/citrus/connectors/commands/kick.rb +22 -0
  32. data/lib/citrus/connectors/common/coder.rb +21 -0
  33. data/lib/citrus/connectors/common/handler.rb +21 -0
  34. data/lib/citrus/connectors/ws_connector.rb +110 -0
  35. data/lib/citrus/connectors/ws_socket.rb +75 -0
  36. data/lib/citrus/filters/handler/handler_filter.rb +19 -0
  37. data/lib/citrus/filters/handler/too_busy.rb +16 -0
  38. data/lib/citrus/filters/rpc/rpc_filter.rb +19 -0
  39. data/lib/citrus/filters/rpc/too_busy.rb +16 -0
  40. data/lib/citrus/master/master.rb +60 -0
  41. data/lib/citrus/master/starter.rb +73 -0
  42. data/lib/citrus/master/watchdog.rb +83 -0
  43. data/lib/citrus/modules/console.rb +45 -0
  44. data/lib/citrus/modules/console_module.rb +35 -0
  45. data/lib/citrus/modules/master_watcher.rb +88 -0
  46. data/lib/citrus/modules/monitor_watcher.rb +86 -0
  47. data/lib/citrus/monitor/monitor.rb +61 -0
  48. data/lib/citrus/push_schedulers/buffer.rb +16 -0
  49. data/lib/citrus/push_schedulers/direct.rb +76 -0
  50. data/lib/citrus/server/server.rb +327 -0
  51. data/lib/citrus/util/app_util.rb +203 -0
  52. data/lib/citrus/util/constants.rb +19 -0
  53. data/lib/citrus/util/countdown_latch.rb +42 -0
  54. data/lib/citrus/util/events.rb +14 -0
  55. data/lib/citrus/util/module_util.rb +68 -0
  56. data/lib/citrus/util/path_util.rb +50 -0
  57. data/lib/citrus/util/utils.rb +49 -0
  58. data/lib/citrus/version.rb +7 -0
  59. metadata +241 -0
@@ -0,0 +1,71 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 25 July 2014
4
+
5
+ module Citrus
6
+ # Common
7
+ #
8
+ #
9
+ module Common
10
+ # Service
11
+ #
12
+ #
13
+ module Service
14
+ # ConnectionService
15
+ #
16
+ #
17
+ class ConnectionService
18
+ # Initialize the service
19
+ #
20
+ # @param [Object] app
21
+ def initialize app
22
+ @server_id = app.server_id
23
+ @conn_count = 0
24
+ @logined_count = 0
25
+ @logined = {}
26
+ end
27
+
28
+ # Add logined user
29
+ #
30
+ # @param [String] uid
31
+ # @param [Hash] info
32
+ def add_logined_user uid, info={}
33
+ @logined_count += 1 unless @logined[uid]
34
+ info[:uid] = uid
35
+ @logined[uid] = info
36
+ end
37
+
38
+ # Increase connection count
39
+ def increase_conn_count
40
+ @conn_count += 1
41
+ end
42
+
43
+ # Remove logined user
44
+ #
45
+ # @param [String] uid
46
+ def remove_logined_user uid
47
+ @logined_count -= 1 if @logined[uid]
48
+ @logined.delete uid
49
+ end
50
+
51
+ # Decrease connection count
52
+ #
53
+ # @param [String] uid
54
+ def decrease_conn_count uid
55
+ @conn_count -= 1 if @conn_count > 0
56
+ remove_logined_user uid unless uid.empty?
57
+ end
58
+
59
+ # Get statistics info
60
+ def get_statistics_info
61
+ {
62
+ :server_id => @server_id,
63
+ :conn_count => @conn_count,
64
+ :logined_count => @logined_count,
65
+ :logined_list => @logined.values
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,92 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 29 July 2014
4
+
5
+ module Citrus
6
+ # Common
7
+ #
8
+ #
9
+ module Common
10
+ # Service
11
+ #
12
+ #
13
+ module Service
14
+ # FilterService
15
+ #
16
+ #
17
+ class FilterService
18
+ # Initialize the service
19
+ def initialize
20
+ @befores = []
21
+ @afters = []
22
+ end
23
+
24
+ # Add before filter into the filter chain
25
+ #
26
+ # @param [#call] filter
27
+ def before filter
28
+ @befores << filter
29
+ end
30
+
31
+ # Add after filter into the filter chain
32
+ #
33
+ # @param [#call] filter
34
+ def after filter
35
+ @afters.unshift filter
36
+ end
37
+
38
+ # Do the before filter chain
39
+ #
40
+ # @param [Hash] msg
41
+ # @param [Object] session
42
+ def before_filter msg, session, &block
43
+ index = 0
44
+
45
+ next_p = Proc.new { |err, resp, args|
46
+ if err || index >= @befores.length
47
+ block_given? and yield err, resp, args
48
+ return
49
+ end
50
+
51
+ handler = @befores[index]
52
+ index += 1
53
+
54
+ if handler.respond_to? :call
55
+ handler.call msg, session, &next_p
56
+ else
57
+ next_p.call Exception.new 'invalid before filter'
58
+ end
59
+ }
60
+ next_p.call
61
+ end
62
+
63
+ # Do the after filter chain
64
+ #
65
+ # @param [Object] err
66
+ # @param [Hash] msg
67
+ # @param [Object] session
68
+ # @param [Hash] resp
69
+ def after_filter err, msg, session, resp, &block
70
+ index = 0
71
+
72
+ next_p = Proc.new { |err|
73
+ if index >= @afters.length
74
+ block_given? and yield err
75
+ return
76
+ end
77
+
78
+ handler = @afters[index]
79
+ index += 1
80
+
81
+ if handler.respond_to? :call
82
+ handler.call err, msg, session, resp, &next_p
83
+ else
84
+ next_p.call Exception.new 'invalid after filter'
85
+ end
86
+ }
87
+ next_p.call err
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,63 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 29 July 2014
4
+
5
+ module Citrus
6
+ # Common
7
+ #
8
+ #
9
+ module Common
10
+ # Service
11
+ #
12
+ #
13
+ module Service
14
+ # HandlerService
15
+ #
16
+ #
17
+ class HandlerService
18
+ # Initialize the service
19
+ #
20
+ # @param [Object] app
21
+ # @param [Hash] handlers
22
+ def initialize app, handlers={}
23
+ @app = app
24
+ @handlers = handlers
25
+ end
26
+
27
+ # Handle message from the client
28
+ #
29
+ # @param [Hash] route_record
30
+ # @param [Hash] msg
31
+ # @param [Object] session
32
+ def handle route_record, msg, session, &block
33
+ handler = get_handler route_record
34
+ unless handler
35
+ block_given? and yield Exception.new 'failed to find the handler'
36
+ return
37
+ end
38
+ handler.send(route_record['method'], msg, session) { |err, resp, args|
39
+ block_given? and yield err, resp, args
40
+ }
41
+ end
42
+
43
+ private
44
+
45
+ # Get handler by route record
46
+ #
47
+ # @param [Hash] route_record
48
+ #
49
+ # @private
50
+ def get_handler route_record
51
+ handler = @handlers[route_record['handler']]
52
+ unless handler
53
+ return nil
54
+ end
55
+ unless handler.respond_to? route_record['method']
56
+ return nil
57
+ end
58
+ handler
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,446 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 19 July 2014
4
+
5
+ module Citrus
6
+ # Common
7
+ #
8
+ #
9
+ module Common
10
+ # Service
11
+ #
12
+ #
13
+ module Service
14
+ # SessionService
15
+ #
16
+ #
17
+ class SessionService
18
+ #
19
+ #
20
+ #
21
+ FRONTEND_SESSION_FIELDS = ['id', 'frontend_id', 'uid', 'session_service']
22
+ EXPORTED_SESSION_FIELDS = ['id', 'frontend_id', 'uid', 'settings']
23
+
24
+ attr_reader :sessions
25
+
26
+ # Initialize the service
27
+ #
28
+ # @param [Hash] args
29
+ def initialize args={}
30
+ @single_session = args[:single_session] || false
31
+ @sessions = {}
32
+ @uid_map = {}
33
+ end
34
+
35
+ # Create a new session
36
+ #
37
+ # @param [Integer] sid
38
+ # @param [String] frontend_id
39
+ # @param [Object] socket
40
+ def create sid, frontend_id, socket
41
+ session = Session.new sid, frontend_id, socket, self
42
+ @sessions[session.id] = session
43
+ session
44
+ end
45
+
46
+ # Bind the session with a user id
47
+ #
48
+ # @param [Integer] sid
49
+ # @param [String] user_id
50
+ def bind sid, user_id, &block
51
+ session = @sessions[sid]
52
+ unless session
53
+ EM.next_tick {
54
+ block_given? and yield Exception.new 'session does not exist, sid: ' + sid
55
+ }
56
+ return
57
+ end
58
+
59
+ if session.uid
60
+ if session.uid == uid
61
+ # already bound with the same uid
62
+ block_given? and yield
63
+ return
64
+ end
65
+
66
+ # already bound with another uid
67
+ EM.next_tick {
68
+ block_given? and yield Exception.new 'session has already bound with ' + session.uid
69
+ }
70
+ return
71
+ end
72
+
73
+ sessions = @uid_map[uid]
74
+
75
+ if sessions && @single_session
76
+ EM.next_tick {
77
+ block_given? and yield Exception.new 'single_session is enabled and session has already bound with uid: ' + uid
78
+ }
79
+ return
80
+ end
81
+
82
+ sessions = @uid_map[uid] = [] unless sessions
83
+
84
+ sessions.each { |s|
85
+ # session has binded with the uid
86
+ if s.id == session.id
87
+ EM.next_tick { block_given? and yield }
88
+ return
89
+ end
90
+ }
91
+ sessions << session
92
+
93
+ session.bind uid
94
+
95
+ EM.next_tick { yield } if block_given?
96
+ end
97
+
98
+ # Unbind a session with the user id
99
+ #
100
+ # @param [Integer] sid
101
+ # @param [String] uid
102
+ def unbind sid, uid, &block
103
+ session = @sessions[sid]
104
+ unless session
105
+ EM.next_tick {
106
+ block_given? and yield Exception.new 'session does not exist, sid: ' + sid
107
+ }
108
+ return
109
+ end
110
+
111
+ unless session.uid && session.uid == uid
112
+ EM.next_tick {
113
+ block_given? and yield Exception.new 'session has not bind with ' + uid
114
+ }
115
+ return
116
+ end
117
+
118
+ sessions = @uid_map[uid]
119
+ if sessions
120
+ sessions.each { |s|
121
+ if s.id == sid
122
+ sessions.delete s
123
+ break
124
+ end
125
+ }
126
+
127
+ if sessions.length == 0
128
+ @uid_map.delete uid
129
+ end
130
+ end
131
+ session.unbind uid
132
+
133
+ EM.next_tick { yield } if block_given?
134
+ end
135
+
136
+ # Get session by id
137
+ #
138
+ # @param [Integer] sid
139
+ def get sid
140
+ @sessions[sid]
141
+ end
142
+
143
+ # Get sessions by user id
144
+ #
145
+ # @param [String] uid
146
+ def get_by_uid uid
147
+ @uid_map[uid]
148
+ end
149
+
150
+ # Remove session by session id
151
+ #
152
+ # @param [Integer] sid
153
+ def remove sid
154
+ if session = @sessions[sid]
155
+ uid = session.uid
156
+ @sessions.delete session.id
157
+
158
+ sessions = @uid_map[uid]
159
+ return unless sessions
160
+
161
+ sessions.each { |s|
162
+ if s.id == sid
163
+ sessions.delete s
164
+ @uid_map.delete uid if sessions.length == 0
165
+ end
166
+ }
167
+ end
168
+ end
169
+
170
+ # Import the key/value into session
171
+ #
172
+ # @param [Integer] sid
173
+ # @param [String] key
174
+ # @param [Hash] value
175
+ def import sid, key, value, &block
176
+ session = @sessions[sid]
177
+ unless session
178
+ block_given? and yield 'session does not exist, sid: ' + sid
179
+ return
180
+ end
181
+ session.set key, value
182
+ block_given? and yield
183
+ end
184
+
185
+ # Import new value for the existed session
186
+ #
187
+ # @param [Integer] sid
188
+ # @param [Hash] settings
189
+ def import_all sid, settings, &block
190
+ session = @sessions[sid]
191
+ unless session
192
+ block_given? and yield 'session does not exist, sid: ' + sid
193
+ return
194
+ end
195
+
196
+ settings.each_pair { |key, value| session.set key, value }
197
+ block_given? and yield
198
+ end
199
+
200
+ # Kick all the sessions offline under the user id
201
+ #
202
+ # @param [String] uid
203
+ # @param [String] reason
204
+ def kick uid, reason='', &block
205
+ if sessions = get_by_uid(uid)
206
+ # notify client
207
+ sids = []
208
+ sessions.each { |session| sids << session.id }
209
+ sids.each { |sid| @sessions[sid].closed reason }
210
+ end
211
+ EM.next_tick { block_given? and yield }
212
+ end
213
+
214
+ # Kick a user offline by session id
215
+ #
216
+ # @param [Integer] sid
217
+ def kick_by_session_id sid, &block
218
+ session = get sid
219
+ session.closed 'kick' if session
220
+ EM.next_tick { block_given? and yield }
221
+ end
222
+
223
+ # Get client remote address by session id
224
+ #
225
+ # @param [Integer] sid
226
+ def get_client_address_by_session_id sid
227
+ session = get sid
228
+ return session.socket.remote_addres if session
229
+ return nil
230
+ end
231
+
232
+ # Send message to the client by session id
233
+ #
234
+ # @param [Integer] sid
235
+ # @param [Hash] msg
236
+ def send_message sid, msg
237
+ session = @sessions[sid]
238
+
239
+ unless session
240
+ return false
241
+ end
242
+
243
+ send session, msg
244
+ end
245
+
246
+ # Send message to the client by user id
247
+ #
248
+ # @param [String] uid
249
+ # @param [Hash] msg
250
+ def send_message_by_uid uid, msg
251
+ sessions = @uid_map[uid]
252
+
253
+ unless sessions
254
+ return false
255
+ end
256
+
257
+ sessions.each { |session|
258
+ send session, msg
259
+ }
260
+ end
261
+
262
+ private
263
+
264
+ # Send message to the client that associated with the session
265
+ #
266
+ # @param [Object] session
267
+ # @param [Hash] msg
268
+ #
269
+ # @private
270
+ def send session, msg
271
+ session.send msg; true
272
+ end
273
+
274
+ # Session
275
+ #
276
+ #
277
+ class Session
278
+ include Utils::EventEmitter
279
+
280
+ attr_reader :id, :frontend_id, :uid, :settings, :session_service
281
+
282
+ # Create a new session
283
+ #
284
+ # @param [Integer] sid
285
+ # @param [String] frontend_id
286
+ # @param [Object] socket
287
+ # @param [Object] service
288
+ def initialize sid, frontend_id, socket, service
289
+ @id = sid
290
+ @frontend_id = frontend_id
291
+ @uid = nil
292
+ @settings = {}
293
+
294
+ @socket = socket
295
+ @session_service = service
296
+ @state = :state_inited
297
+ end
298
+
299
+ # Export current session as frontend session
300
+ def to_frontend_session
301
+ FrontendSession.new self
302
+ end
303
+
304
+ # Bind the session with the uid
305
+ #
306
+ # @param [String] uid
307
+ def bind uid
308
+ @uid = uid
309
+ emit :bind, uid
310
+ end
311
+
312
+ # Unbind the session with the uid
313
+ #
314
+ # @param [String] uid
315
+ def unbind uid
316
+ @uid = nil
317
+ emit :unbind, uid
318
+ end
319
+
320
+ # Set value for the session
321
+ #
322
+ # @param [String] key
323
+ # @param [Hash] value
324
+ def set key, value
325
+ @settings[key] = value
326
+ end
327
+
328
+ # Get value from the session
329
+ #
330
+ # @param [String] key
331
+ def get key
332
+ @settings[key]
333
+ end
334
+
335
+ # Send message to the session
336
+ #
337
+ # @param [Hash] msg
338
+ def send msg
339
+ @socket.send msg
340
+ end
341
+
342
+ # Send message to the session in batch
343
+ #
344
+ # @param [Array] msgs
345
+ def send_batch msgs
346
+ @socket.send_batch msgs
347
+ end
348
+
349
+ # Closed callback for the session which would disconnect client in next tick
350
+ #
351
+ # @param [String] reason
352
+ def closed reason=''
353
+ return if @state == :state_closed
354
+
355
+ @state = :state_closed
356
+ @service.remove @id
357
+
358
+ emit :closed, to_frontend_session, reason
359
+ @socket.emit :closing, reason
360
+
361
+ EM.next_tick { @socket.disconnect }
362
+ end
363
+ end
364
+
365
+ # FrontendSession
366
+ #
367
+ #
368
+ class FrontendSession
369
+ include Utils::EventEmitter
370
+
371
+ # Create a new frontend session
372
+ #
373
+ # @param [Object] session
374
+ def initialize session
375
+ FRONTEND_SESSION_FIELDS.each { |field|
376
+ instance_eval %Q{ @#{field} = session.#{field} }
377
+ }
378
+ # deep copy for settings
379
+ @settings = session.settings.dup
380
+ @session = session
381
+ end
382
+
383
+ # Bind the frontend session with the uid
384
+ #
385
+ # @param [String] uid
386
+ def bind uid, &block
387
+ @session_service.bind(@id, uid) { |err|
388
+ unless err
389
+ @uid = uid
390
+ end
391
+ block_given? and yield err
392
+ }
393
+ end
394
+
395
+ # Unbind the session with the uid
396
+ #
397
+ # @param [String] uid
398
+ def unbind uid, &block
399
+ @session_service.unbind(@id, uid) { |err|
400
+ unless err
401
+ @uid = nil
402
+ end
403
+ block_given? and yield err
404
+ }
405
+ end
406
+
407
+ # Set value for the frontend session
408
+ #
409
+ # @param [String] key
410
+ # @param [Hash] value
411
+ def set key, value
412
+ @settings[key] = value
413
+ end
414
+
415
+ # Get value from the frontend session
416
+ #
417
+ # @param [String] key
418
+ def get key
419
+ @settings[key]
420
+ end
421
+
422
+ # Push value to the internal session
423
+ #
424
+ # @param [String] key
425
+ def push key, &block
426
+ @session_service.import @id, key, get(key), &block
427
+ end
428
+
429
+ # Push all the key/value pairs to the internal session
430
+ def push_all &block
431
+ @session_service.import_all @id, @settings, &block
432
+ end
433
+
434
+ # Export the key/values for serialization
435
+ def export
436
+ res = {}
437
+ EXPORTED_SESSION_FIELDS.each { |field|
438
+ instance_eval %Q{ res['#{field}'] = @#{field} }
439
+ }
440
+ res
441
+ end
442
+ end
443
+ end
444
+ end
445
+ end
446
+ end