colossus 0.0.1
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 +7 -0
- data/lib/colossus.rb +95 -0
- data/lib/colossus/configuration.rb +45 -0
- data/lib/colossus/engines/memory/client_session.rb +20 -0
- data/lib/colossus/engines/memory/client_session_store.rb +55 -0
- data/lib/colossus/engines/memory/memory.rb +96 -0
- data/lib/colossus/faye/extension.rb +162 -0
- data/lib/colossus/verifier.rb +31 -0
- data/lib/colossus/version.rb +4 -0
- data/lib/colossus/writer_client.rb +62 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9ea35d4325a20e16ba03e449605e9afb9fef70cb
|
4
|
+
data.tar.gz: 4624e6e7bea9aab4bc858cd81663e7f5f4e28b42
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4d5d0f2cb89a7a033ab391d5d93c865bc743d5a19934227a134442d25508018249229334559ee48fedd57cc0d15a3fe59e781f3ac39618819ffd841682a3ea3
|
7
|
+
data.tar.gz: ab60db5e7d318ef1f32934dba3e0076773e2b50fafc01f2e3ba689ccdbc7fd5841dab8cedd7e1bc557d80e64534e1fb23328f945571afbec002368d97e3a4e1a
|
data/lib/colossus.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'observer'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
require 'colossus/configuration'
|
5
|
+
require 'colossus/verifier'
|
6
|
+
require 'colossus/writer_client'
|
7
|
+
|
8
|
+
require 'colossus/engines/memory/memory'
|
9
|
+
require 'colossus/engines/memory/client_session'
|
10
|
+
require 'colossus/engines/memory/client_session_store'
|
11
|
+
|
12
|
+
require 'colossus/faye/extension'
|
13
|
+
|
14
|
+
# Top Level Class.
|
15
|
+
# The public API of the Gem.
|
16
|
+
class Colossus
|
17
|
+
include Observable
|
18
|
+
|
19
|
+
attr_reader :engine, :verifier
|
20
|
+
|
21
|
+
# Initialize Colossus
|
22
|
+
#
|
23
|
+
# @param ttl [Integer] the seconds before a user without emitting heartbeat
|
24
|
+
# is considered disconnected
|
25
|
+
#
|
26
|
+
# @param engine [Engine] the engine which implements the needed methods
|
27
|
+
# to work with Colossus
|
28
|
+
#
|
29
|
+
# @return [Colossus]
|
30
|
+
def initialize(ttl = Colossus.config.ttl,
|
31
|
+
engine = Colossus.config.engine,
|
32
|
+
secret = Colossus.config.secret_key,
|
33
|
+
writer_token = Colossus.config.writer_token)
|
34
|
+
@engine = engine.new(ttl.to_i)
|
35
|
+
@engine.add_observer(self)
|
36
|
+
@verifier = Colossus::Verifier.new(secret, writer_token)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set the status of a user on a specificed client. A client could be
|
40
|
+
# a Websocket session (if the user has 2 tabs opened) or anything else.
|
41
|
+
#
|
42
|
+
# @param user_id [#to_s] The unique identifier of a user
|
43
|
+
# @param client_id [#to_s] The unique identifier of a client
|
44
|
+
# @param status [#to_s] The status of a the user, it can be active,
|
45
|
+
# away or disconnected.
|
46
|
+
#
|
47
|
+
# @return [Boolean] Return true if the status has changed if not false.
|
48
|
+
def set(user_id, client_id, status)
|
49
|
+
engine.set(user_id.to_s, client_id.to_s, status.to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get the status of a specified user, it analyzes the status
|
53
|
+
# of all the sessions.
|
54
|
+
# It returns :
|
55
|
+
#
|
56
|
+
# - active if one or more clients are active.
|
57
|
+
# - away if one or more clients are away.
|
58
|
+
# - disconnected.
|
59
|
+
#
|
60
|
+
# @param user_id [#to_s] The unique identifier of a user
|
61
|
+
#
|
62
|
+
# @return status [String]
|
63
|
+
def get(user_id)
|
64
|
+
engine.get(user_id.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param user_ids [Array<#to_s>] An array of user ids
|
68
|
+
# (see #get)
|
69
|
+
def get_multi(*user_ids)
|
70
|
+
engine.get_multi(user_ids.map(&:to_s))
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return Hash{String => String} User_id keys, statuses values
|
74
|
+
def get_all
|
75
|
+
engine.get_all
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reset all the data (useful for specs)
|
79
|
+
def reset!
|
80
|
+
engine.reset!
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generate a token for the given user_id
|
84
|
+
def generate_user_token(user_id)
|
85
|
+
verifier.generate_user_token(user_id)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Method used when the engine notify a change
|
89
|
+
#
|
90
|
+
# @!visibility private
|
91
|
+
def update(user_id, status)
|
92
|
+
changed
|
93
|
+
notify_observers(user_id, status)
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Main Colossus module
|
3
|
+
class Colossus
|
4
|
+
# Handles all the configuration
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :ttl, :seconds_before_ttl_check, :engine,
|
7
|
+
:secret_key, :writer_token
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@ttl = 10
|
11
|
+
@seconds_before_ttl_check = 2
|
12
|
+
@engine = Colossus::Engine::Memory
|
13
|
+
@secret_key = ''
|
14
|
+
@writer_token = ''
|
15
|
+
end
|
16
|
+
end # class Configuration
|
17
|
+
|
18
|
+
def self.configure
|
19
|
+
yield(configuration) if block_given?
|
20
|
+
configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configuration
|
24
|
+
@configuration ||= Configuration.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.config
|
28
|
+
configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
# Help ?
|
32
|
+
# rubocop:disable TrivialAccessors
|
33
|
+
def self.configuration=(configuration)
|
34
|
+
@configuration = configuration
|
35
|
+
end
|
36
|
+
# rubocop:enable
|
37
|
+
|
38
|
+
def self.config=(configuration)
|
39
|
+
self.configuration = configuration
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.reset_configuration
|
43
|
+
@configuration = Configuration.new
|
44
|
+
end
|
45
|
+
end # Colossus
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Colossus
|
2
|
+
module Engine
|
3
|
+
class Memory
|
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
|
+
@status = 'disconnected'
|
10
|
+
@last_seen = Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
def status=(given_status)
|
14
|
+
@last_seen = Time.now
|
15
|
+
@status = given_status
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Colossus
|
2
|
+
module Engine
|
3
|
+
class Memory
|
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
|
8
|
+
attr_accessor :sessions
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@sessions = Hash.new do |hash, key|
|
12
|
+
hash[key] = Colossus::Engine::Memory::ClientSession.new
|
13
|
+
end
|
14
|
+
@last_status = 'disconnected'
|
15
|
+
end
|
16
|
+
|
17
|
+
def status
|
18
|
+
sessions.values.reduce('disconnected') do |memo, session|
|
19
|
+
case session.status
|
20
|
+
when 'active'
|
21
|
+
session.status
|
22
|
+
when 'away'
|
23
|
+
memo == 'active' ? memo : session.status
|
24
|
+
else
|
25
|
+
memo
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def last_seen
|
31
|
+
sessions.values.reduce(Time.new(0)) do |memo, session|
|
32
|
+
session.last_seen > memo ? session.last_seen : memo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def status_changed?
|
37
|
+
last_status != status
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](session_id)
|
41
|
+
sessions[session_id]
|
42
|
+
end
|
43
|
+
|
44
|
+
def []=(session_id, session_status)
|
45
|
+
@last_status = status
|
46
|
+
sessions[session_id].status = session_status
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete(session_id)
|
50
|
+
sessions.delete(session_id)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class Colossus
|
2
|
+
module Engine
|
3
|
+
# The Memory Engine is a non-distributed engine.
|
4
|
+
# Based on EventMachine in order to provide the ttl to
|
5
|
+
# disconnect clients.
|
6
|
+
class Memory
|
7
|
+
include Observable
|
8
|
+
|
9
|
+
attr_reader :client_sessions, :ttl, :mutex
|
10
|
+
|
11
|
+
def initialize(ttl)
|
12
|
+
@client_sessions = Hash.new do |hash, key|
|
13
|
+
hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
|
14
|
+
end
|
15
|
+
@ttl = ttl
|
16
|
+
@mutex = Mutex.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def user_changed(user_id, status)
|
20
|
+
changed
|
21
|
+
notify_observers(user_id, status)
|
22
|
+
end
|
23
|
+
|
24
|
+
def set(user_id, client_id, given_status)
|
25
|
+
mutex.synchronize do
|
26
|
+
if given_status == 'disconnected'
|
27
|
+
client_sessions[user_id].delete(client_id)
|
28
|
+
else
|
29
|
+
client_sessions[user_id][client_id] = given_status
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if client_sessions[user_id].status_changed?
|
34
|
+
status = client_sessions[user_id].status
|
35
|
+
delete(user_id) if status == 'disconnected'
|
36
|
+
user_changed(user_id, status)
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def get(user_id)
|
44
|
+
client_sessions[user_id]
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_multi(*user_ids)
|
48
|
+
user_ids.map { |user_id| get(user_id) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_all
|
52
|
+
statuses = {}
|
53
|
+
client_sessions.each_pair do |user_id, session_store|
|
54
|
+
statuses[user_id] = session_store.status
|
55
|
+
end
|
56
|
+
statuses
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete(user_id)
|
60
|
+
mutex.synchronize do
|
61
|
+
client_sessions.delete(user_id)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset!
|
66
|
+
mutex.synchronize do
|
67
|
+
@client_sessions = Hash.new do |hash, key|
|
68
|
+
hash[key] = Colossus::Engine::Memory::ClientSessionStore.new
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def new_periodic_ttl
|
74
|
+
secs_ttl = Colossus.config.seconds_before_ttl_check
|
75
|
+
@periodic_ttl = EM::Synchrony.add_periodic_timer(secs_ttl) do
|
76
|
+
client_sessions.each_pair do |user_id, session_store|
|
77
|
+
sessions_dupped = session_store.sessions.dup
|
78
|
+
|
79
|
+
session_store.sessions.each_pair do |session_id, session|
|
80
|
+
if (session.last_seen + ttl) < Time.now
|
81
|
+
sessions_dupped.delete(session_id)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
mutex.synchronize { session_store.sessions = sessions_dupped }
|
86
|
+
|
87
|
+
if (session_store.last_seen + ttl) < Time.now
|
88
|
+
delete(user_id)
|
89
|
+
user_changed(user_id, 'disconnected')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
class Colossus
|
2
|
+
module Faye
|
3
|
+
# Faye extension implementing all the presence, authorization,
|
4
|
+
# authentification and push logic.
|
5
|
+
class Extension
|
6
|
+
attr_reader :colossus, :faye, :faye_client
|
7
|
+
|
8
|
+
VALID_STATUSES = %w(disconnected away active).freeze
|
9
|
+
|
10
|
+
def initialize(faye, colossus = Colossus.new)
|
11
|
+
@colossus = colossus
|
12
|
+
@faye = faye
|
13
|
+
faye.add_extension(self)
|
14
|
+
@faye_client = faye.get_client
|
15
|
+
colossus_faye_extension = Colossus::WriterClient::FayeExtension.
|
16
|
+
new(colossus.verifier.writer_token)
|
17
|
+
faye_client.add_extension(colossus_faye_extension)
|
18
|
+
end
|
19
|
+
|
20
|
+
def incoming(message, _request, callback)
|
21
|
+
if !acceptable?(message)
|
22
|
+
handle_invalid_token(message)
|
23
|
+
message.delete('data')
|
24
|
+
message.delete('ext')
|
25
|
+
elsif message['ext']['user_token']
|
26
|
+
handle_user_action(message)
|
27
|
+
message.delete('data')
|
28
|
+
message.delete('ext')
|
29
|
+
elsif message['ext']['writer_token']
|
30
|
+
handle_server_action(message)
|
31
|
+
message.delete('ext')
|
32
|
+
end
|
33
|
+
|
34
|
+
callback.call(message)
|
35
|
+
end
|
36
|
+
|
37
|
+
def acceptable?(message)
|
38
|
+
message['ext'] &&
|
39
|
+
(message['ext']['user_token'] ||
|
40
|
+
message['ext']['writer_token'])
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_user_action(message)
|
44
|
+
if message['channel'] == '/meta/subscribe'
|
45
|
+
handle_subscribe(message)
|
46
|
+
elsif message['channel'].start_with?('/meta/')
|
47
|
+
message
|
48
|
+
elsif message['channel'].start_with?('/users/')
|
49
|
+
handle_set_status(message)
|
50
|
+
else
|
51
|
+
message['error'] = 'Unknown Action'
|
52
|
+
end
|
53
|
+
|
54
|
+
message
|
55
|
+
end
|
56
|
+
|
57
|
+
def handle_server_action(message)
|
58
|
+
token = message['ext'] && message['ext']['writer_token']
|
59
|
+
|
60
|
+
unless token && colossus.verifier.verify_writer_token(token)
|
61
|
+
message['error'] = 'Invalid Token'
|
62
|
+
message.delete('data')
|
63
|
+
return message
|
64
|
+
end
|
65
|
+
|
66
|
+
if message['channel'].start_with?('/meta/')
|
67
|
+
message.delete('data')
|
68
|
+
message
|
69
|
+
elsif message['channel'].start_with?('/presences/request/')
|
70
|
+
handle_presence_request(message)
|
71
|
+
elsif message['channel'].start_with?('/presences/response/')
|
72
|
+
handle_presence_response(message)
|
73
|
+
elsif message['channel'].start_with?('/users/')
|
74
|
+
handle_publish(message)
|
75
|
+
else
|
76
|
+
message['error'] = 'Unknown Action'
|
77
|
+
message.delete('data')
|
78
|
+
end
|
79
|
+
|
80
|
+
message
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_presence_request(message)
|
84
|
+
user_ids = message['data'] && message['data']['user_ids']
|
85
|
+
presence_id = message['channel'].partition('/presences/request/').last
|
86
|
+
|
87
|
+
if user_ids && user_ids.is_a?(Array)
|
88
|
+
statuses = colossus.get_multi(user_ids)
|
89
|
+
message['data'].delete('user_ids')
|
90
|
+
faye_client.publish("/presences/response/#{presence_id}", { 'statuses' => Hash[user_ids.zip(statuses)] })
|
91
|
+
message
|
92
|
+
elsif user_ids == nil
|
93
|
+
statuses = colossus.get_all
|
94
|
+
faye_client.publish("/presences/response/#{presence_id}", { 'statuses' => statuses })
|
95
|
+
message
|
96
|
+
else
|
97
|
+
message.delete('data')
|
98
|
+
message['error'] = 'Invalid user_ids data'
|
99
|
+
message
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_presence_response(message)
|
104
|
+
message
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_publish(message)
|
108
|
+
message
|
109
|
+
end
|
110
|
+
|
111
|
+
def handle_invalid_token(message)
|
112
|
+
message['error'] = 'Invalid Token'
|
113
|
+
message
|
114
|
+
end
|
115
|
+
|
116
|
+
def handle_subscribe(message)
|
117
|
+
token = message['ext']['user_token']
|
118
|
+
user_id = message['subscription'].partition('/users/').last
|
119
|
+
|
120
|
+
if invalid_user_channel?(user_id)
|
121
|
+
message['error'] = 'The only accepted channel_name is users/:user_id'
|
122
|
+
return message
|
123
|
+
end
|
124
|
+
|
125
|
+
unless colossus.verifier.verify_token(token, user_id)
|
126
|
+
message['error'] = 'Invalid Token'
|
127
|
+
end
|
128
|
+
|
129
|
+
message
|
130
|
+
end
|
131
|
+
|
132
|
+
def handle_set_status(message)
|
133
|
+
token = message['ext']['user_token']
|
134
|
+
user_id = message['channel'].partition('/users/').last
|
135
|
+
status = message['data'] && message['data']['status']
|
136
|
+
|
137
|
+
if invalid_user_channel?(user_id)
|
138
|
+
message['error'] = 'The only accepted channel_name is users/:user_id'
|
139
|
+
return message
|
140
|
+
end
|
141
|
+
|
142
|
+
unless status && VALID_STATUSES.include?(status)
|
143
|
+
message['error'] = 'Invalid Status'
|
144
|
+
return message
|
145
|
+
end
|
146
|
+
|
147
|
+
unless colossus.verifier.verify_token(token, user_id)
|
148
|
+
message['error'] = 'Invalid Token'
|
149
|
+
return message
|
150
|
+
end
|
151
|
+
|
152
|
+
colossus.set(user_id, message['clientId'], status)
|
153
|
+
|
154
|
+
message
|
155
|
+
end
|
156
|
+
|
157
|
+
def invalid_user_channel?(user_id)
|
158
|
+
user_id.empty? || user_id.include?('*') || user_id.include?('/')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Colossus
|
2
|
+
# Implements the verification logic based on SHA1 in order
|
3
|
+
# to avoid timing attacks. (cf Faye doc)
|
4
|
+
class Verifier
|
5
|
+
attr_reader :sha1, :secret, :writer_token
|
6
|
+
|
7
|
+
def initialize(secret = Colossus.config.secret_key,
|
8
|
+
writer_token = Colossus.config.writer_token)
|
9
|
+
@sha1 = OpenSSL::Digest.new('sha1')
|
10
|
+
@secret = secret
|
11
|
+
@writer_token = writer_token
|
12
|
+
end
|
13
|
+
|
14
|
+
def verify_token(token_given, user_id)
|
15
|
+
expected_token = generate_user_token(user_id)
|
16
|
+
expected_hash = Digest::SHA1.hexdigest(expected_token)
|
17
|
+
actual_hash = Digest::SHA1.hexdigest(token_given)
|
18
|
+
expected_hash == actual_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def verify_writer_token(token_given)
|
22
|
+
expected_hash = Digest::SHA1.hexdigest(writer_token)
|
23
|
+
actual_hash = Digest::SHA1.hexdigest(token_given)
|
24
|
+
expected_hash == actual_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_user_token(user_id)
|
28
|
+
OpenSSL::HMAC.hexdigest(sha1, secret, user_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Colossus
|
2
|
+
class WriterClient
|
3
|
+
attr_reader :url, :writer_token, :time_out
|
4
|
+
|
5
|
+
def initialize(url, writer_token, time_out = 2)
|
6
|
+
@url = url
|
7
|
+
@writer_token = writer_token
|
8
|
+
@time_out = time_out
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_presences(optional_user_ids = nil)
|
12
|
+
user_ids = Array(optional_user_ids) if optional_user_ids
|
13
|
+
unique_token = generate_unique_token
|
14
|
+
EM.synchrony do
|
15
|
+
EM::Synchrony.add_timer(time_out) { raise "Presence request timed out" }
|
16
|
+
EM::Synchrony.sync(faye_client.subscribe("/presences/response/#{unique_token}") { |message| return message['statuses'] })
|
17
|
+
EM::Synchrony.sync(faye_client.publish("/presences/request/#{unique_token}", user_ids))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def push_message(user_ids, message)
|
22
|
+
user_ids = Array(user_ids)
|
23
|
+
EM.synchrony do
|
24
|
+
user_ids.each do |user_id|
|
25
|
+
EM::Synchrony.sync(faye_client.publish("/users/#{user_id}", message))
|
26
|
+
end
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def faye_client
|
34
|
+
faye_client = ::Faye::Client.new(url)
|
35
|
+
faye_client.add_extension(FayeExtension.new(writer_token))
|
36
|
+
faye_client
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_unique_token
|
40
|
+
SecureRandom.hex(8)
|
41
|
+
end
|
42
|
+
|
43
|
+
class FayeExtension
|
44
|
+
attr_reader :token
|
45
|
+
|
46
|
+
def initialize(token)
|
47
|
+
@token = token
|
48
|
+
end
|
49
|
+
|
50
|
+
def incoming(message, callback)
|
51
|
+
callback.call(message)
|
52
|
+
end
|
53
|
+
|
54
|
+
def outgoing(message, callback)
|
55
|
+
message['ext'] ||= {}
|
56
|
+
message['ext']['writer_token'] = 'WRITER_TOKEN'
|
57
|
+
|
58
|
+
callback.call(message)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: colossus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- antoinelyset
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faye
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: em-synchrony
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
description: Colossus is a Push and Presence pure Ruby server. It uses Faye internally.
|
42
|
+
email:
|
43
|
+
- antoinelyset+github@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/colossus.rb
|
49
|
+
- lib/colossus/configuration.rb
|
50
|
+
- lib/colossus/engines/memory/client_session.rb
|
51
|
+
- lib/colossus/engines/memory/client_session_store.rb
|
52
|
+
- lib/colossus/engines/memory/memory.rb
|
53
|
+
- lib/colossus/faye/extension.rb
|
54
|
+
- lib/colossus/verifier.rb
|
55
|
+
- lib/colossus/version.rb
|
56
|
+
- lib/colossus/writer_client.rb
|
57
|
+
homepage: https://github.com/antoinelyset/colossus
|
58
|
+
licenses: []
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.2.2
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Colossus, Web Push & Presence made easy.
|
80
|
+
test_files: []
|
81
|
+
has_rdoc:
|