colossus 0.10.0 → 0.11.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 744e97003be9b4ba97c54fd8877679f042268423
4
- data.tar.gz: 831be7283ce1271cf95cb80ea751d62c4ab384e8
3
+ metadata.gz: c95d76c91b2b37ca49130fc2fbb14ded53d85620
4
+ data.tar.gz: 123c4860b3864ddfda2d505faca5292f36e9083c
5
5
  SHA512:
6
- metadata.gz: 0db8010a14269f54288633e7701c6bae7a13a27cd087e8b024faedbc307717fbc185b2e051a653575cd5abfe17ef6a0bb0898e982a5ca9b8a8c1c465ca0df9e2
7
- data.tar.gz: 287374b1f76f7b5891f4ebb5ea4a25c66c9cdb9df77d6db0231fe8369a70572238df3a3ab426b7877e019686ccae675e97b855950e4a84815d33afafa4854e85
6
+ metadata.gz: 2f998058fc924ccb6c760c7e40529a7db2db09a53c50bc4e0c059d7b80225216617347e7d1ed50a096d36671b39dcd232fe8f740bd6c44676415beac25bf5df7
7
+ data.tar.gz: 071464b4bc8a3bee161f6780b6db462b84f7a98bbcabe3f302b7d2be9075b964fc9131d618f990b30060ed319c20bcc235c6557fe64cfac47261b3266c1c812b
@@ -1,11 +1,13 @@
1
1
  require 'faye'
2
2
  require 'json'
3
3
  require 'openssl'
4
+ require 'monitor'
4
5
  require 'faraday'
5
6
  require 'faraday_middleware'
6
7
  require 'observer'
7
8
  require 'em-synchrony'
8
9
  require 'securerandom'
10
+ require 'thread_safe'
9
11
 
10
12
  require 'colossus/configuration'
11
13
  require 'colossus/verifier'
@@ -16,6 +18,12 @@ require 'colossus/engines/memory/memory'
16
18
  require 'colossus/engines/memory/client_session'
17
19
  require 'colossus/engines/memory/client_session_store'
18
20
 
21
+ require 'colossus/engines/memory_thread_safe/memory'
22
+ require 'colossus/engines/memory_thread_safe/client_session'
23
+ require 'colossus/engines/memory_thread_safe/client_session_store'
24
+
25
+ require 'colossus/engines/memory_monitor/memory'
26
+
19
27
  require 'colossus/faye/extension'
20
28
 
21
29
  # Top Level Class.
@@ -23,6 +31,10 @@ require 'colossus/faye/extension'
23
31
  class Colossus
24
32
  include Observable
25
33
 
34
+ ACTIVE = 'active'.freeze
35
+ AWAY = 'away'.freeze
36
+ DISCONNECTED = 'disconnected'.freeze
37
+
26
38
  attr_reader :engine, :verifier
27
39
 
28
40
  # Initialize Colossus
@@ -9,7 +9,7 @@ class Colossus
9
9
  def initialize
10
10
  @ttl = 10
11
11
  @seconds_before_ttl_check = 2
12
- @engine = Colossus::Engine::Memory
12
+ @engine = Colossus::Engine::MemoryThreadSafe
13
13
  @secret_key = ''
14
14
  @writer_token = ''
15
15
  end
@@ -6,7 +6,7 @@ class Colossus
6
6
  attr_reader :status, :last_seen
7
7
 
8
8
  def initialize
9
- @status = 'disconnected'
9
+ @status = DISCONNECTED
10
10
  @last_seen = Time.now
11
11
  end
12
12
 
@@ -11,16 +11,16 @@ class Colossus
11
11
  @sessions = Hash.new do |hash, key|
12
12
  hash[key] = Colossus::Engine::Memory::ClientSession.new
13
13
  end
14
- @last_status = 'disconnected'
14
+ @last_status = DISCONNECTED
15
15
  end
16
16
 
17
17
  def status
18
- sessions.values.reduce('disconnected') do |memo, session|
18
+ sessions.values.reduce(DISCONNECTED) do |memo, session|
19
19
  case session.status
20
- when 'active'
20
+ when ACTIVE
21
21
  session.status
22
- when 'away'
23
- memo == 'active' ? memo : session.status
22
+ when AWAY
23
+ memo == ACTIVE ? memo : session.status
24
24
  else
25
25
  memo
26
26
  end
@@ -1,20 +1,19 @@
1
1
  class Colossus
2
2
  module Engine
3
- # The Memory Engine is a non-distributed engine.
3
+ # The Memory Engine is a non-distributed, in process, non-threadsafe engine.
4
4
  # Based on EventMachine in order to provide the ttl to
5
5
  # disconnect clients.
6
6
  class Memory
7
7
  include Observable
8
8
 
9
- attr_reader :client_sessions, :ttl, :mutex
9
+ attr_reader :client_sessions, :ttl
10
10
 
11
11
  # @param [Integer/Float] ttl TTL in seconds
12
12
  def initialize(ttl)
13
13
  @client_sessions = Hash.new do |hash, key|
14
14
  hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
15
15
  end
16
- @ttl = ttl
17
- @mutex = Mutex.new
16
+ @ttl = ttl
18
17
  end
19
18
 
20
19
  def user_changed(user_id, status)
@@ -23,18 +22,16 @@ class Colossus
23
22
  end
24
23
 
25
24
  def set(user_id, client_id, given_status)
26
- mutex.synchronize do
27
- if given_status == 'disconnected'
28
- client_sessions[user_id].delete(client_id)
29
- else
30
- client_sessions[user_id][client_id] = given_status
31
- end
25
+ if given_status == DISCONNECTED
26
+ client_sessions[user_id].delete(client_id)
27
+ else
28
+ client_sessions[user_id][client_id] = given_status
32
29
  end
33
30
 
34
31
  if client_sessions[user_id].sessions.empty? ||
35
32
  client_sessions[user_id].status_changed?
36
33
  status = client_sessions[user_id].status
37
- delete(user_id) if status == 'disconnected'
34
+ delete(user_id) if status == DISCONNECTED
38
35
  user_changed(user_id, status)
39
36
  return true
40
37
  end
@@ -46,7 +43,7 @@ class Colossus
46
43
  if client_sessions.has_key?(user_id)
47
44
  { user_id => client_sessions[user_id].status }
48
45
  else
49
- { user_id => 'disconnected' }
46
+ { user_id => DISCONNECTED }
50
47
  end
51
48
  end
52
49
 
@@ -63,16 +60,12 @@ class Colossus
63
60
  end
64
61
 
65
62
  def delete(user_id)
66
- mutex.synchronize do
67
- client_sessions.delete(user_id)
68
- end
63
+ client_sessions.delete(user_id)
69
64
  end
70
65
 
71
66
  def reset!
72
- mutex.synchronize do
73
- @client_sessions = Hash.new do |hash, key|
74
- hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
75
- end
67
+ @client_sessions = Hash.new do |hash, key|
68
+ hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
76
69
  end
77
70
  end
78
71
 
@@ -85,16 +78,10 @@ class Colossus
85
78
  user_ids_to_delete = []
86
79
 
87
80
  client_sessions.each_pair do |user_id, session_store|
88
- sessions_dupped = session_store.sessions.dup
89
-
90
- session_store.sessions.each_pair do |session_id, session|
91
- if (session.last_seen + ttl) < Time.now
92
- sessions_dupped.delete(session_id)
93
- end
81
+ session_store.sessions.delete_if do |session_id, session|
82
+ (session.last_seen + ttl) < Time.now
94
83
  end
95
84
 
96
- mutex.synchronize { session_store.sessions = sessions_dupped }
97
-
98
85
  if (session_store.last_seen + ttl) < Time.now
99
86
  user_ids_to_delete << user_id
100
87
  end
@@ -106,7 +93,7 @@ class Colossus
106
93
  def delete_expired_users(user_ids)
107
94
  user_ids.each do |user_id|
108
95
  delete(user_id)
109
- user_changed(user_id, 'disconnected')
96
+ user_changed(user_id, DISCONNECTED)
110
97
  end
111
98
  end
112
99
  end
@@ -0,0 +1,118 @@
1
+ class Colossus
2
+ module Engine
3
+ # The Memory Engine is a non-distributed, in process, threadsafe engine.
4
+ # Based on EventMachine in order to provide the ttl to
5
+ # disconnect clients.
6
+ class MemoryMonitor
7
+ include Observable
8
+
9
+ attr_reader :client_sessions, :ttl, :monitor
10
+
11
+ # @param [Integer/Float] ttl TTL in seconds
12
+ def initialize(ttl)
13
+ @client_sessions = Hash.new do |hash, key|
14
+ hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
15
+ end
16
+ @ttl = ttl
17
+ @monitor = Monitor.new
18
+ end
19
+
20
+ def user_changed(user_id, status)
21
+ changed
22
+ notify_observers(user_id, status)
23
+ end
24
+
25
+ def set(user_id, client_id, given_status)
26
+ synchronize do
27
+ if given_status == DISCONNECTED
28
+ client_sessions[user_id].delete(client_id)
29
+ else
30
+ client_sessions[user_id][client_id] = given_status
31
+ end
32
+ end
33
+
34
+ if client_sessions[user_id].sessions.empty? ||
35
+ client_sessions[user_id].status_changed?
36
+ status = client_sessions[user_id].status
37
+ delete(user_id) if status == DISCONNECTED
38
+ user_changed(user_id, status)
39
+ return true
40
+ end
41
+
42
+ false
43
+ end
44
+
45
+ def get(user_id)
46
+ if client_sessions.has_key?(user_id)
47
+ { user_id => client_sessions[user_id].status }
48
+ else
49
+ { user_id => DISCONNECTED }
50
+ end
51
+ end
52
+
53
+ def get_multi(*user_ids)
54
+ user_ids.inject({}) { |memo, user_id| memo.merge!(get(user_id)) }
55
+ end
56
+
57
+ def get_all
58
+ statuses = {}
59
+ client_sessions.each_pair do |user_id, session_store|
60
+ statuses[user_id] = session_store.status
61
+ end
62
+ statuses
63
+ end
64
+
65
+ def delete(user_id)
66
+ synchronize do
67
+ client_sessions.delete(user_id)
68
+ end
69
+ end
70
+
71
+ def reset!
72
+ synchronize do
73
+ @client_sessions = Hash.new do |hash, key|
74
+ hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
75
+ end
76
+ end
77
+ end
78
+
79
+ def new_periodic_ttl
80
+ secs_ttl = Colossus.config.seconds_before_ttl_check
81
+ @periodic_ttl = EM::Synchrony.add_periodic_timer(secs_ttl, &method(:gc_ttl))
82
+ end
83
+
84
+ def gc_ttl
85
+ user_ids_to_delete = []
86
+
87
+ client_sessions.each_pair do |user_id, session_store|
88
+ sessions_dupped = session_store.sessions.dup
89
+
90
+ session_store.sessions.each_pair do |session_id, session|
91
+ if (session.last_seen + ttl) < Time.now
92
+ sessions_dupped.delete(session_id)
93
+ end
94
+ end
95
+
96
+ synchronize { session_store.sessions = sessions_dupped }
97
+
98
+ if (session_store.last_seen + ttl) < Time.now
99
+ user_ids_to_delete << user_id
100
+ end
101
+ end
102
+
103
+ delete_expired_users(user_ids_to_delete)
104
+ end
105
+
106
+ def delete_expired_users(user_ids)
107
+ user_ids.each do |user_id|
108
+ delete(user_id)
109
+ user_changed(user_id, DISCONNECTED)
110
+ end
111
+ end
112
+
113
+ def synchronize(&block)
114
+ monitor.synchronize(&block)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,30 @@
1
+ class Colossus
2
+ module Engine
3
+ class MemoryThreadSafe
4
+ # Represent the status and the information of a given user.
5
+ class ClientSession
6
+ attr_reader :status, :last_seen
7
+
8
+ def initialize
9
+ @data = ThreadSafe::Cache.new
10
+ @data[:status] = DISCONNECTED
11
+ @data[:last_seen] = Time.now
12
+ end
13
+
14
+ def status
15
+ @data[:status]
16
+ end
17
+
18
+ def status=(given_status)
19
+ @data[:last_seen] = Time.now
20
+ @data[:status] = given_status
21
+ end
22
+
23
+ def last_seen
24
+ @data[:last_seen]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,65 @@
1
+ class Colossus
2
+ module Engine
3
+ class MemoryThreadSafe
4
+ # Represents all the different sessions of a user. It can find
5
+ # the global status of the user given all the different status.
6
+ class ClientSessionStore
7
+ attr_reader :last_status, :ttl
8
+ attr_accessor :sessions
9
+
10
+ def initialize(ttl)
11
+ @sessions = ThreadSafe::Cache.new do |hash, key|
12
+ hash[key] = Colossus::Engine::MemoryThreadSafe::ClientSession.new
13
+ end
14
+ @last_status = DISCONNECTED
15
+ @ttl = ttl
16
+ end
17
+
18
+ def status
19
+ sessions.values.reduce(DISCONNECTED) do |memo, session|
20
+ case session.status
21
+ when ACTIVE
22
+ session.status
23
+ when AWAY
24
+ memo == ACTIVE ? memo : session.status
25
+ else
26
+ memo
27
+ end
28
+ end
29
+ end
30
+
31
+ def last_seen
32
+ sessions.values.reduce(Time.new(0)) do |memo, session|
33
+ session.last_seen > memo ? session.last_seen : memo
34
+ end
35
+ end
36
+
37
+ def status_changed?
38
+ last_status != status
39
+ end
40
+
41
+ def [](session_id)
42
+ sessions[session_id]
43
+ end
44
+
45
+ def []=(session_id, session_status)
46
+ @last_status = status
47
+ sessions[session_id].status = session_status
48
+ end
49
+
50
+ def delete(session_id)
51
+ sessions.delete(session_id)
52
+ end
53
+
54
+ def delete_expired_sessions
55
+ session_keys_to_delete = []
56
+ sessions.each_pair do |session_id, session|
57
+ session_keys_to_delete << session_id if (session.last_seen + ttl) < Time.now
58
+ end
59
+
60
+ session_keys_to_delete.each { |session_id| sessions.delete(session_id) }
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,90 @@
1
+ class Colossus
2
+ module Engine
3
+ # Threadsage with ThreadSafe
4
+ class MemoryThreadSafe
5
+ include Observable
6
+
7
+ attr_reader :client_sessions, :ttl
8
+
9
+ # @param [Integer/Float] ttl TTL in seconds
10
+ def initialize(ttl)
11
+ @client_sessions = ThreadSafe::Cache.new do |hash, key|
12
+ hash[key] = Colossus::Engine::MemoryThreadSafe::ClientSessionStore.new(ttl)
13
+ end
14
+ @ttl = ttl
15
+ end
16
+
17
+ def user_changed(user_id, status)
18
+ changed
19
+ notify_observers(user_id, status)
20
+ end
21
+
22
+ def set(user_id, client_id, given_status)
23
+ if given_status == DISCONNECTED
24
+ client_sessions[user_id].delete(client_id)
25
+ else
26
+ client_sessions[user_id][client_id] = given_status
27
+ end
28
+
29
+ if client_sessions[user_id].sessions.empty? ||
30
+ client_sessions[user_id].status_changed?
31
+ status = client_sessions[user_id].status
32
+ delete(user_id) if status == DISCONNECTED
33
+ user_changed(user_id, status)
34
+ return true
35
+ end
36
+
37
+ false
38
+ end
39
+
40
+ def get(user_id)
41
+ if client_sessions.key?(user_id)
42
+ { user_id => client_sessions[user_id].status }
43
+ else
44
+ { user_id => DISCONNECTED }
45
+ end
46
+ end
47
+
48
+ def get_multi(*user_ids)
49
+ user_ids.inject({}) { |memo, user_id| memo.merge!(get(user_id)) }
50
+ end
51
+
52
+ def get_all
53
+ statuses = {}
54
+ client_sessions.each_pair do |user_id, session_store|
55
+ statuses[user_id] = session_store.status
56
+ end
57
+ statuses
58
+ end
59
+
60
+ def delete(user_id)
61
+ client_sessions.delete(user_id)
62
+ end
63
+
64
+ def reset!
65
+ client_sessions.clear
66
+ end
67
+
68
+ def new_periodic_ttl
69
+ secs_ttl = Colossus.config.seconds_before_ttl_check
70
+ @periodic_ttl = EM::Synchrony.add_periodic_timer(secs_ttl, &method(:gc_ttl))
71
+ end
72
+
73
+ def gc_ttl
74
+ delete_expired_users
75
+ client_sessions.each_pair do |user_id, session_store|
76
+ session_store.delete_expired_sessions
77
+ end
78
+ end
79
+
80
+ def delete_expired_users
81
+ user_ids_to_delete = []
82
+ client_sessions.each_pair do |user_id, session_store|
83
+ user_ids_to_delete << user_id if (session_store.last_seen + ttl) < Time.now
84
+ end
85
+
86
+ user_ids_to_delete.each { |user_id| client_sessions.delete(user_id) }
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,12 @@
1
+ class Colossus
2
+ module Engine
3
+ # The Redis Engine is a distributed engine.
4
+ # Colossus server is now stateless.
5
+ class Redis
6
+ include Observable
7
+
8
+ def initialize(ttl)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -13,7 +13,7 @@ class Colossus
13
13
  faye.add_extension(self)
14
14
  end
15
15
 
16
- def incoming(message, _request, callback)
16
+ def incoming(message, callback)
17
17
  if !acceptable?(message)
18
18
  handle_invalid_token(message)
19
19
  message.delete('data')
@@ -1,4 +1,4 @@
1
1
  # The Version
2
2
  class Colossus
3
- VERSION = '0.10.0'
3
+ VERSION = '0.11.0'
4
4
  end
metadata CHANGED
@@ -1,69 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: colossus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - antoinelyset
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-05 00:00:00.000000000 Z
11
+ date: 2015-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faye
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: em-synchrony
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: faraday
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: faraday_middleware
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thread_safe
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  description: Colossus is a Push and Presence pure Ruby server. It uses Faye internally.
@@ -78,6 +92,11 @@ files:
78
92
  - lib/colossus/engines/memory/client_session.rb
79
93
  - lib/colossus/engines/memory/client_session_store.rb
80
94
  - lib/colossus/engines/memory/memory.rb
95
+ - lib/colossus/engines/memory_monitor/memory.rb
96
+ - lib/colossus/engines/memory_thread_safe/client_session.rb
97
+ - lib/colossus/engines/memory_thread_safe/client_session_store.rb
98
+ - lib/colossus/engines/memory_thread_safe/memory.rb
99
+ - lib/colossus/engines/redis/redis.rb
81
100
  - lib/colossus/faye/extension.rb
82
101
  - lib/colossus/http_writer_client.rb
83
102
  - lib/colossus/simple_writer_server.rb
@@ -93,17 +112,17 @@ require_paths:
93
112
  - lib
94
113
  required_ruby_version: !ruby/object:Gem::Requirement
95
114
  requirements:
96
- - - ">="
115
+ - - '>='
97
116
  - !ruby/object:Gem::Version
98
- version: '0'
117
+ version: 1.9.3
99
118
  required_rubygems_version: !ruby/object:Gem::Requirement
100
119
  requirements:
101
- - - ">="
120
+ - - '>='
102
121
  - !ruby/object:Gem::Version
103
122
  version: '0'
104
123
  requirements: []
105
124
  rubyforge_project:
106
- rubygems_version: 2.2.2
125
+ rubygems_version: 2.0.14
107
126
  signing_key:
108
127
  specification_version: 4
109
128
  summary: Colossus, Web Push & Presence made easy.