slanger 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of slanger might be problematic. Click here for more details.
- data/README.md +7 -0
- data/bin/slanger +9 -0
- data/lib/slanger/api_server.rb +3 -2
- data/lib/slanger/channel.rb +16 -1
- data/lib/slanger/config.rb +2 -1
- data/lib/slanger/connection.rb +34 -0
- data/lib/slanger/handler.rb +44 -120
- data/lib/slanger/presence_channel.rb +5 -5
- data/lib/slanger/presence_subscription.rb +33 -0
- data/lib/slanger/private_subscription.rb +9 -0
- data/lib/slanger/redis.rb +2 -2
- data/lib/slanger/service.rb +1 -0
- data/lib/slanger/subscription.rb +51 -0
- data/lib/slanger/web_socket_server.rb +4 -1
- data/slanger.rb +4 -0
- metadata +57 -30
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Slanger
|
2
|
+
[![Build Status](https://secure.travis-ci.org/stevegraham/slanger.png?branch=master)](http://travis-ci.org/stevegraham/slanger)
|
2
3
|
|
3
4
|
Slanger is an open source server implementation of the Pusher protocol written in Ruby. It is designed to scale horizontally across N nodes and to be agnostic as to which Slanger node a subscriber is connected to, i.e subscribers to the same channel are NOT required to be connected to the same Slanger node. Multiple Slanger nodes can sit behind a load balancer with no special configuration. In essence it was designed to be very easy to scale.
|
4
5
|
|
@@ -96,6 +97,12 @@ Slanger supports several configuration options, which can be supplied as command
|
|
96
97
|
|
97
98
|
-w or --websocket_host This is the address that Slanger will bind the WebSocket part of the service to. This is an optional argument and defaults to 0.0.0.0:8080
|
98
99
|
|
100
|
+
-i or --require Require an additional file before starting Slanger to tune it to your needs. This is an optional argument
|
101
|
+
|
102
|
+
-p or --private_key_file Private key file for SSL support. This argument is optional, if given, SSL will be enabled
|
103
|
+
|
104
|
+
-c or --cert_chain_file Certificate chain file for SSL support. This argument is optional, if given, SSL will be enabled
|
105
|
+
|
99
106
|
-v or --[no-]verbose This makes Slanger run verbosely, meaning WebSocket frames will be echoed to STDOUT. Useful for debugging
|
100
107
|
</pre>
|
101
108
|
|
data/bin/slanger
CHANGED
@@ -11,6 +11,7 @@ options = {
|
|
11
11
|
|
12
12
|
OptionParser.new do |opts|
|
13
13
|
opts.on '-h', '--help', 'Display this screen' do
|
14
|
+
puts opts
|
14
15
|
exit
|
15
16
|
end
|
16
17
|
|
@@ -34,6 +35,11 @@ OptionParser.new do |opts|
|
|
34
35
|
options[:websocket_host], options[:websocket_port] = p.split(':')
|
35
36
|
end
|
36
37
|
|
38
|
+
opts.on '-i', '--require FILE', "Require a file before starting Slanger" do |p|
|
39
|
+
options[:require] ||= []
|
40
|
+
options[:require] << p
|
41
|
+
end
|
42
|
+
|
37
43
|
opts.on '-p', '--private_key_file FILE', "Private key file for SSL transport" do |p|
|
38
44
|
options[:tls_options] ||= {}
|
39
45
|
options[:tls_options][:private_key_file] = p
|
@@ -60,6 +66,9 @@ if options[:tls_options]
|
|
60
66
|
end
|
61
67
|
end
|
62
68
|
|
69
|
+
EM.epoll
|
70
|
+
EM.kqueue
|
71
|
+
|
63
72
|
EM.run do
|
64
73
|
File.tap { |f| require f.expand_path(f.join(f.dirname(__FILE__),'..', 'slanger.rb')) }
|
65
74
|
Slanger::Config.load options
|
data/lib/slanger/api_server.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'sinatra/base'
|
2
3
|
require 'signature'
|
3
|
-
require '
|
4
|
+
require 'json'
|
4
5
|
require 'active_support/core_ext/hash'
|
5
6
|
require 'eventmachine'
|
6
7
|
require 'em-hiredis'
|
@@ -35,7 +36,7 @@ module Slanger
|
|
35
36
|
|
36
37
|
def payload
|
37
38
|
payload = {
|
38
|
-
event: params['name'], data: request.body.read, channel: params[:channel_id], socket_id: params[:socket_id]
|
39
|
+
event: params['name'], data: request.body.read.tap { |s| s.force_encoding('utf-8') }, channel: params[:channel_id], socket_id: params[:socket_id]
|
39
40
|
}
|
40
41
|
Hash[payload.reject { |k,v| v.nil? }].to_json
|
41
42
|
end
|
data/lib/slanger/channel.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Channel class.
|
2
2
|
#
|
3
3
|
# Uses an EventMachine channel to let clients interact with the
|
4
|
-
# Pusher channel. Relay events received from Redis into the
|
4
|
+
# Pusher channel. Relay events received from Redis into the
|
5
5
|
# EM channel.
|
6
6
|
#
|
7
7
|
|
@@ -16,6 +16,21 @@ module Slanger
|
|
16
16
|
|
17
17
|
def_delegators :channel, :subscribe, :unsubscribe, :push
|
18
18
|
|
19
|
+
class << self
|
20
|
+
def from channel_id
|
21
|
+
klass = channel_id[/^presence-/] ? PresenceChannel : Channel
|
22
|
+
klass.find_or_create_by_channel_id channel_id
|
23
|
+
end
|
24
|
+
|
25
|
+
def unsubscribe channel_id, subscription_id
|
26
|
+
from(channel_id).try :unsubscribe, subscription_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_client_message msg
|
30
|
+
from(msg['channel']).try :send_client_message, msg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
19
34
|
def initialize(attrs)
|
20
35
|
super
|
21
36
|
Slanger::Redis.subscribe channel_id
|
data/lib/slanger/config.rb
CHANGED
@@ -13,7 +13,8 @@ module Slanger
|
|
13
13
|
def options
|
14
14
|
@options ||= {
|
15
15
|
api_host: '0.0.0.0', api_port: '4567', websocket_host: '0.0.0.0',
|
16
|
-
websocket_port: '8080', debug: false, redis_address: 'redis://0.0.0.0:6379/0'
|
16
|
+
websocket_port: '8080', debug: false, redis_address: 'redis://0.0.0.0:6379/0',
|
17
|
+
socket_handler: Slanger::Handler, require: []
|
17
18
|
}
|
18
19
|
end
|
19
20
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Slanger
|
2
|
+
class Connection
|
3
|
+
attr_accessor :socket, :socket_id
|
4
|
+
|
5
|
+
def initialize socket, socket_id=nil
|
6
|
+
@socket, @socket_id = socket, socket_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def send_message m
|
10
|
+
msg = JSON.parse m
|
11
|
+
s = msg.delete 'socket_id'
|
12
|
+
socket.send msg.to_json unless s == socket_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_payload *args
|
16
|
+
socket.send format(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def error e
|
20
|
+
send_payload nil, 'pusher:error', e
|
21
|
+
end
|
22
|
+
|
23
|
+
def establish
|
24
|
+
@socket_id = SecureRandom.uuid
|
25
|
+
send_payload nil, 'pusher:connection_established', { socket_id: @socket_id }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def format(channel_id, event_name, payload = {})
|
31
|
+
{ channel: channel_id, event: event_name, data: payload }.to_json
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/slanger/handler.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Handler class.
|
2
2
|
# Handles a client connected via a websocket connection.
|
3
3
|
|
4
|
-
require 'active_support/json'
|
5
4
|
require 'active_support/core_ext/hash'
|
6
5
|
require 'securerandom'
|
7
6
|
require 'signature'
|
@@ -9,156 +8,81 @@ require 'fiber'
|
|
9
8
|
|
10
9
|
module Slanger
|
11
10
|
class Handler
|
11
|
+
|
12
|
+
attr_accessor :connection
|
13
|
+
delegate :error, :establish, :send_payload, to: :connection
|
14
|
+
|
12
15
|
def initialize(socket)
|
13
16
|
@socket = socket
|
17
|
+
@connection = Connection.new(@socket)
|
14
18
|
@subscriptions = {}
|
15
19
|
authenticate
|
16
20
|
end
|
17
21
|
|
18
|
-
# Dispatches message handling to method with same name as
|
22
|
+
# Dispatches message handling to method with same name as
|
23
|
+
# the event name
|
19
24
|
def onmessage(msg)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
channel.try :send_client_message, msg
|
31
|
-
end
|
32
|
-
rescue Exception => e
|
33
|
-
handle_error(e)
|
34
|
-
end
|
25
|
+
begin
|
26
|
+
msg = JSON.parse msg
|
27
|
+
event = msg['event'].gsub(/^pusher:/, 'pusher_')
|
28
|
+
|
29
|
+
if event =~ /^client-/
|
30
|
+
msg['socket_id'] = @socket_id
|
31
|
+
Channel.send_client_message msg
|
32
|
+
elsif respond_to? event, true
|
33
|
+
send event, msg
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
rescue Exception => e
|
37
|
+
case e
|
38
|
+
when JSON::ParserError
|
39
|
+
error({ code: 5001, message: "Invalid JSON" })
|
40
|
+
else
|
41
|
+
error({ code: 500, message: "#{e.message}\n #{e.backtrace}" })
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
def find_channel(channel_id)
|
47
|
-
channel_class(channel_id).find_by_channel_id(channel_id)
|
48
|
-
end
|
49
|
-
|
50
|
-
def channel_class(channel_name)
|
51
|
-
channel_name =~ /^presence-/ ? PresenceChannel : Channel
|
46
|
+
def onclose
|
47
|
+
@subscriptions.each { |c, s| Channel.unsubscribe c, s }
|
52
48
|
end
|
53
49
|
|
54
|
-
# Verify app key. Send connection_established message to connection if it checks out. Send error message and disconnect if invalid.
|
55
50
|
def authenticate
|
56
|
-
|
57
|
-
if app_key == Slanger::Config.app_key
|
58
|
-
@socket_id = SecureRandom.uuid
|
59
|
-
@socket.send(payload nil, 'pusher:connection_established', { socket_id: @socket_id })
|
60
|
-
else
|
61
|
-
@socket.send(payload nil, 'pusher:error', { code: '4001', message: "Could not find app by key #{app_key}" })
|
62
|
-
@socket.close_websocket
|
63
|
-
end
|
64
|
-
end
|
51
|
+
return establish if valid_app_key? app_key
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
channel_id = msg['data']['channel']
|
69
|
-
subscription_id = if match = channel_id.match(/^((private)|(presence))-/)
|
70
|
-
send "handle_#{match.captures[0]}_subscription", msg
|
71
|
-
else
|
72
|
-
subscribe_channel channel_id
|
73
|
-
end
|
74
|
-
@subscriptions[channel_id] = subscription_id
|
53
|
+
error({ code: '4001', message: "Could not find app by key #{app_key}" })
|
54
|
+
@socket.close_websocket
|
75
55
|
end
|
76
56
|
|
77
57
|
def pusher_ping(msg)
|
78
|
-
|
58
|
+
send_payload nil, 'pusher:ping'
|
79
59
|
end
|
80
60
|
|
81
61
|
def pusher_pong msg; end
|
82
62
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
channel = Slanger::Channel.find_or_create_by_channel_id(channel_id)
|
87
|
-
@socket.send(payload channel_id, 'pusher_internal:subscription_succeeded')
|
88
|
-
# Subscribe to the channel and have the events received from it
|
89
|
-
# sent to the client's socket.
|
90
|
-
subscription_id = channel.subscribe do |msg|
|
91
|
-
msg = JSON.parse(msg)
|
92
|
-
# Don't send the event if it was sent by the client
|
93
|
-
socket_id = msg.delete 'socket_id'
|
94
|
-
@socket.send msg.to_json unless socket_id == @socket_id
|
95
|
-
end
|
96
|
-
end
|
63
|
+
def pusher_subscribe(msg)
|
64
|
+
channel_id = msg['data']['channel']
|
65
|
+
klass = subscription_klass channel_id
|
97
66
|
|
98
|
-
|
99
|
-
def handle_private_subscription(msg)
|
100
|
-
channel = msg['data']['channel']
|
101
|
-
if msg['data']['auth'] && token(channel, msg['data']['channel_data']) != msg['data']['auth'].split(':')[1]
|
102
|
-
@socket.send(payload nil, 'pusher:error', {
|
103
|
-
message: "Invalid signature: Expected HMAC SHA256 hex digest of #{@socket_id}:#{channel}, but got #{msg['data']['auth']}"
|
104
|
-
})
|
105
|
-
else
|
106
|
-
subscribe_channel channel
|
107
|
-
end
|
67
|
+
@subscriptions[channel_id] = klass.new(connection.socket, connection.socket_id, msg).subscribe
|
108
68
|
end
|
109
69
|
|
110
|
-
|
111
|
-
def handle_presence_subscription(msg)
|
112
|
-
channel_id = msg['data']['channel']
|
113
|
-
if token(channel_id, msg['data']['channel_data']) != msg['data']['auth'].split(':')[1]
|
114
|
-
@socket.send(payload nil, 'pusher:error', {
|
115
|
-
message: "Invalid signature: Expected HMAC SHA256 hex digest of #{@socket_id}:#{msg['data']['channel']}, but got #{msg['data']['auth']}"
|
116
|
-
})
|
117
|
-
elsif !msg['data']['channel_data']
|
118
|
-
@socket.send(payload nil, 'pusher:error', {
|
119
|
-
message: "presence-channel is a presence channel and subscription must include channel_data"
|
120
|
-
})
|
121
|
-
else
|
122
|
-
channel = Slanger::PresenceChannel.find_or_create_by_channel_id(channel_id)
|
123
|
-
callback = Proc.new {
|
124
|
-
@socket.send(payload channel_id, 'pusher_internal:subscription_succeeded', {
|
125
|
-
presence: {
|
126
|
-
count: channel.subscribers.size,
|
127
|
-
ids: channel.ids,
|
128
|
-
hash: channel.subscribers
|
129
|
-
}
|
130
|
-
})
|
131
|
-
}
|
132
|
-
# Subscribe to channel, call callback when done to send a
|
133
|
-
# subscription_succeeded event to the client.
|
134
|
-
channel.subscribe(msg, callback) do |msg|
|
135
|
-
# Send channel messages to the client, unless it is the
|
136
|
-
# sender of the event.
|
137
|
-
msg = JSON.parse(msg)
|
138
|
-
socket_id = msg.delete 'socket_id'
|
139
|
-
@socket.send msg.to_json unless socket_id == @socket_id
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
70
|
+
private
|
143
71
|
|
144
|
-
|
145
|
-
|
146
|
-
{ channel: channel_id, event: event_name, data: payload }.to_json
|
72
|
+
def app_key
|
73
|
+
@socket.request['path'].split(/\W/)[2]
|
147
74
|
end
|
148
75
|
|
149
|
-
|
150
|
-
|
151
|
-
string_to_sign = [@socket_id, channel_id, params].compact.join ':'
|
152
|
-
HMAC::SHA256.hexdigest(Slanger::Config.secret, string_to_sign)
|
76
|
+
def valid_app_key? app_key
|
77
|
+
Slanger::Config.app_key == app_key
|
153
78
|
end
|
154
79
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
@socket.send(payload nil, 'pusher:error', { code: '5001', message: "Invalid JSON" })
|
159
|
-
else
|
160
|
-
@socket.send(payload nil, 'pusher:error', { code: '5000', message: "Internal Server error" })
|
80
|
+
def subscription_klass channel_id
|
81
|
+
klass = channel_id.match(/^(private|presence)-/) do |match|
|
82
|
+
Slanger.const_get "#{match[1]}_subscription".classify
|
161
83
|
end
|
84
|
+
|
85
|
+
klass || Slanger::Subscription
|
162
86
|
end
|
163
87
|
end
|
164
88
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# PresenceChannel class.
|
2
2
|
#
|
3
3
|
# Uses an EventMachine channel to let handlers interact with the
|
4
|
-
# Pusher channel. Relay events received from Redis into the
|
4
|
+
# Pusher channel. Relay events received from Redis into the
|
5
5
|
# EM channel. Keeps data on the subscribers to send it to clients.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
|
8
8
|
require 'glamazon'
|
9
9
|
require 'eventmachine'
|
@@ -57,11 +57,11 @@ module Slanger
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def ids
|
60
|
-
subscriptions.map { |
|
60
|
+
subscriptions.map { |_,v| v['user_id'] }
|
61
61
|
end
|
62
62
|
|
63
63
|
def subscribers
|
64
|
-
Hash[subscriptions.map { |
|
64
|
+
Hash[subscriptions.map { |_,v| [v['user_id'], v['user_info']] }]
|
65
65
|
end
|
66
66
|
|
67
67
|
def unsubscribe(public_subscription_id)
|
@@ -105,7 +105,7 @@ module Slanger
|
|
105
105
|
# This is the state of the presence channel across the system. kept in sync
|
106
106
|
# with redis pubsub
|
107
107
|
def subscriptions
|
108
|
-
@subscriptions
|
108
|
+
@subscriptions ||= get_roster || {}
|
109
109
|
end
|
110
110
|
|
111
111
|
# This is used map public subscription ids to em channel subscription ids.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Slanger
|
2
|
+
class PresenceSubscription < Subscription
|
3
|
+
def subscribe
|
4
|
+
return handle_invalid_signature if invalid_signature?
|
5
|
+
|
6
|
+
unless channel_data?
|
7
|
+
return connection.error({
|
8
|
+
message: "presence-channel is a presence channel and subscription must include channel_data"
|
9
|
+
})
|
10
|
+
end
|
11
|
+
|
12
|
+
channel.subscribe(@msg, callback) { |m| connection.send_message m }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def channel_data?
|
18
|
+
@msg['data']['channel_data']
|
19
|
+
end
|
20
|
+
|
21
|
+
def callback
|
22
|
+
Proc.new {
|
23
|
+
connection.send_payload(channel_id, 'pusher_internal:subscription_succeeded', {
|
24
|
+
presence: {
|
25
|
+
count: channel.subscribers.size,
|
26
|
+
ids: channel.ids,
|
27
|
+
hash: channel.subscribers
|
28
|
+
}
|
29
|
+
})
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/slanger/redis.rb
CHANGED
@@ -11,8 +11,8 @@ module Slanger
|
|
11
11
|
# Dispatch messages received from Redis to their destination channel.
|
12
12
|
base.on(:message) do |channel, message|
|
13
13
|
message = JSON.parse message
|
14
|
-
|
15
|
-
|
14
|
+
c = Channel.from message['channel']
|
15
|
+
c.dispatch message, channel
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
data/lib/slanger/service.rb
CHANGED
@@ -4,6 +4,7 @@ require 'rack'
|
|
4
4
|
module Slanger
|
5
5
|
module Service
|
6
6
|
def run
|
7
|
+
Slanger::Config[:require].each { |f| require f }
|
7
8
|
Thin::Logging.silent = true
|
8
9
|
Rack::Handler::Thin.run Slanger::ApiServer, Host: Slanger::Config.api_host, Port: Slanger::Config.api_port
|
9
10
|
Slanger::WebSocketServer.run
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Slanger
|
2
|
+
class Subscription
|
3
|
+
attr_accessor :connection, :socket
|
4
|
+
delegate :send_payload, :send_message, :error, :socket_id, to: :connection
|
5
|
+
|
6
|
+
def initialize socket, socket_id, msg
|
7
|
+
@connection = Connection.new socket, socket_id
|
8
|
+
@msg = msg
|
9
|
+
end
|
10
|
+
|
11
|
+
def subscribe
|
12
|
+
send_payload channel_id, 'pusher_internal:subscription_succeeded'
|
13
|
+
|
14
|
+
channel.subscribe { |m| send_message m }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def channel
|
20
|
+
Channel.from channel_id
|
21
|
+
end
|
22
|
+
|
23
|
+
def channel_id
|
24
|
+
@msg['data']['channel']
|
25
|
+
end
|
26
|
+
|
27
|
+
def token(channel_id, params=nil)
|
28
|
+
to_sign = [socket_id, channel_id, params].compact.join ':'
|
29
|
+
HMAC::SHA256.hexdigest Slanger::Config.secret, to_sign
|
30
|
+
end
|
31
|
+
|
32
|
+
def invalid_signature?
|
33
|
+
token(channel_id, data) != auth.split(':')[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def auth
|
37
|
+
@msg['data']['auth']
|
38
|
+
end
|
39
|
+
|
40
|
+
def data
|
41
|
+
@msg['data']['channel_data']
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_invalid_signature
|
45
|
+
message = "Invalid signature: Expected HMAC SHA256 hex digest of "
|
46
|
+
message << "#{socket_id}:#{channel_id}, but got #{auth}"
|
47
|
+
|
48
|
+
error({ message: message})
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -4,6 +4,9 @@ require 'em-websocket'
|
|
4
4
|
module Slanger
|
5
5
|
module WebSocketServer
|
6
6
|
def run
|
7
|
+
EM.epoll
|
8
|
+
EM.kqueue
|
9
|
+
|
7
10
|
EM.run do
|
8
11
|
options = {
|
9
12
|
host: Slanger::Config[:websocket_host],
|
@@ -21,7 +24,7 @@ module Slanger
|
|
21
24
|
# Keep track of handler instance in instance of EM::Connection to ensure a unique handler instance is used per connection.
|
22
25
|
ws.class_eval { attr_accessor :connection_handler }
|
23
26
|
# Delegate connection management to handler instance.
|
24
|
-
ws.onopen { ws.connection_handler = Slanger::
|
27
|
+
ws.onopen { ws.connection_handler = Slanger::Config.socket_handler.new ws }
|
25
28
|
ws.onmessage { |msg| ws.connection_handler.onmessage msg }
|
26
29
|
ws.onclose { ws.connection_handler.onclose }
|
27
30
|
end
|
data/slanger.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'bundler/setup'
|
2
3
|
|
3
4
|
require 'eventmachine'
|
@@ -7,6 +8,9 @@ require 'active_support/core_ext/string'
|
|
7
8
|
|
8
9
|
module Slanger; end
|
9
10
|
|
11
|
+
EM.epoll
|
12
|
+
EM.kqueue
|
13
|
+
|
10
14
|
EM.run do
|
11
15
|
File.tap do |f|
|
12
16
|
Dir[f.expand_path(f.join(f.dirname(__FILE__),'lib', 'slanger', '*.rb'))].each do |file|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slanger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement: &
|
16
|
+
requirement: &70166097146020 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.12.10
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70166097146020
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: em-hiredis
|
27
|
-
requirement: &
|
27
|
+
requirement: &70166097145140 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.1.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70166097145140
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: em-websocket
|
38
|
-
requirement: &
|
38
|
+
requirement: &70166097144120 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.3.5
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70166097144120
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rack
|
49
|
-
requirement: &
|
49
|
+
requirement: &70166097142720 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.3.3
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70166097142720
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rack-fiber_pool
|
60
|
-
requirement: &
|
60
|
+
requirement: &70166097141600 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - =
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.9.1
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70166097141600
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: signature
|
71
|
-
requirement: &
|
71
|
+
requirement: &70166097140380 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.1.2
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70166097140380
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: activesupport
|
82
|
-
requirement: &
|
82
|
+
requirement: &70166097138660 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 3.1.0
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70166097138660
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: glamazon
|
93
|
-
requirement: &
|
93
|
+
requirement: &70166097137500 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: 0.3.1
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70166097137500
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: sinatra
|
104
|
-
requirement: &
|
104
|
+
requirement: &70166097136360 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ~>
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: 1.2.6
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70166097136360
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: thin
|
115
|
-
requirement: &
|
115
|
+
requirement: &70166097135480 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ~>
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: 1.2.11
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70166097135480
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: em-http-request
|
126
|
-
requirement: &
|
126
|
+
requirement: &70166097134400 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ~>
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: 0.3.0
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70166097134400
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: rspec
|
137
|
-
requirement: &
|
137
|
+
requirement: &70166097133020 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ~>
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: 2.6.0
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *70166097133020
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: pusher
|
148
|
-
requirement: &
|
148
|
+
requirement: &70166097131960 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ~>
|
@@ -153,10 +153,10 @@ dependencies:
|
|
153
153
|
version: 0.8.2
|
154
154
|
type: :development
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *70166097131960
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: haml
|
159
|
-
requirement: &
|
159
|
+
requirement: &70166097131100 !ruby/object:Gem::Requirement
|
160
160
|
none: false
|
161
161
|
requirements:
|
162
162
|
- - ~>
|
@@ -164,7 +164,29 @@ dependencies:
|
|
164
164
|
version: 3.1.2
|
165
165
|
type: :development
|
166
166
|
prerelease: false
|
167
|
-
version_requirements: *
|
167
|
+
version_requirements: *70166097131100
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: rake
|
170
|
+
requirement: &70166097129660 !ruby/object:Gem::Requirement
|
171
|
+
none: false
|
172
|
+
requirements:
|
173
|
+
- - ! '>='
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
type: :development
|
177
|
+
prerelease: false
|
178
|
+
version_requirements: *70166097129660
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: debugger
|
181
|
+
requirement: &70166097128500 !ruby/object:Gem::Requirement
|
182
|
+
none: false
|
183
|
+
requirements:
|
184
|
+
- - ! '>='
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
type: :development
|
188
|
+
prerelease: false
|
189
|
+
version_requirements: *70166097128500
|
168
190
|
description: A websocket service compatible with Pusher libraries
|
169
191
|
email: sjtgraham@mac.com
|
170
192
|
executables:
|
@@ -176,11 +198,15 @@ files:
|
|
176
198
|
- lib/slanger/api_server.rb
|
177
199
|
- lib/slanger/channel.rb
|
178
200
|
- lib/slanger/config.rb
|
201
|
+
- lib/slanger/connection.rb
|
179
202
|
- lib/slanger/handler.rb
|
180
203
|
- lib/slanger/logger.rb
|
181
204
|
- lib/slanger/presence_channel.rb
|
205
|
+
- lib/slanger/presence_subscription.rb
|
206
|
+
- lib/slanger/private_subscription.rb
|
182
207
|
- lib/slanger/redis.rb
|
183
208
|
- lib/slanger/service.rb
|
209
|
+
- lib/slanger/subscription.rb
|
184
210
|
- lib/slanger/web_socket_server.rb
|
185
211
|
- slanger.rb
|
186
212
|
- !binary |-
|
@@ -210,3 +236,4 @@ signing_key:
|
|
210
236
|
specification_version: 3
|
211
237
|
summary: A websocket service compatible with Pusher libraries
|
212
238
|
test_files: []
|
239
|
+
has_rdoc:
|