slanger 0.3.4 → 0.3.5

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 CHANGED
@@ -1,12 +1,47 @@
1
1
  # Slanger
2
-
3
- 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
- Presence channel state is shared using Redis. Channels are lazily instantiated internally within a given Slanger node when the first subscriber connects. When a presence channel is instantiated within a Slanger node, it queries Redis for the global state across all nodes within the system for that channel, and then copies that state internally. Afterwards, when subscribers connect or disconnect the node publishes a presence message to all interested nodes, i.e. all nodes with at least one subscriber interested in the given channel.
6
-
7
- Slanger is smart enough to know if a new channel subscription belongs to the same user. It will not send presence messages to subscribers in this case. This happens when the user has multiple browser tabs open for example. Using a chat room backed by presence channels as a real example, one would not want "Barrington" to show up N times in the presence roster because Barrington is a retard and has the chat room open in N browser tabs.
8
-
9
- Slanger was designed to be highly available and partition tolerant with eventual consistency, which in practise is instantaneous.
2
+ ##Typical usage
3
+
4
+ Slanger is a standalone server ruby implementation of the Pusher protocol. It
5
+ is not designed to run inside a Rails or sinatra app, but it can be easily
6
+ installed as a gem.
7
+
8
+ ```
9
+ #see the section further down for How To Use It
10
+ gem install slanger
11
+ ```
12
+
13
+ ##You probably don't mean this:
14
+
15
+ Gemfile
16
+ ```
17
+ gem 'slanger'
18
+ ```
19
+
20
+ ##About
21
+ Slanger is an open source server implementation of the Pusher protocol written
22
+ in Ruby. It is designed to scale horizontally across N nodes and to be agnostic
23
+ as to which Slanger node a subscriber is connected to, i.e subscribers to the
24
+ same channel are NOT required to be connected to the same Slanger node.
25
+ Multiple Slanger nodes can sit behind a load balancer with no special
26
+ configuration. In essence it was designed to be very easy to scale.
27
+
28
+ Presence channel state is shared using Redis. Channels are lazily instantiated
29
+ internally within a given Slanger node when the first subscriber connects. When
30
+ a presence channel is instantiated within a Slanger node, it queries Redis for
31
+ the global state across all nodes within the system for that channel, and then
32
+ copies that state internally. Afterwards, when subscribers connect or
33
+ disconnect the node publishes a presence message to all interested nodes, i.e.
34
+ all nodes with at least one subscriber interested in the given channel.
35
+
36
+ Slanger is smart enough to know if a new channel subscription belongs to the
37
+ same user. It will not send presence messages to subscribers in this case. This
38
+ happens when the user has multiple browser tabs open for example. Using a chat
39
+ room backed by presence channels as a real example, one would not want
40
+ "Barrington" to show up N times in the presence roster because Barrington is a
41
+ retard and has the chat room open in N browser tabs.
42
+
43
+ Slanger was designed to be highly available and partition tolerant with
44
+ eventual consistency, which in practise is instantaneous.
10
45
 
11
46
  # How to use it
12
47
 
@@ -75,6 +110,7 @@ You will also need to do the same to the Pusher JavaScript client in your client
75
110
 
76
111
  Pusher.host = 'slanger.example.com'
77
112
  Pusher.ws_port = 8080
113
+ Pusher.wss_port = 8080
78
114
 
79
115
  </script>
80
116
  </pre>
@@ -102,7 +138,7 @@ Slanger supports several configuration options, which can be supplied as command
102
138
 
103
139
  -b or --webhook_url URL for webhooks. This argument is optional, if given webhook callbacks will be made http://pusher.com/docs/webhooks
104
140
 
105
- -c or --cert_chain_file Certificate chain file for SSL support. This argument is optional, if given, SSL will be enabled
141
+ -c or --cert_file Certificate file for SSL support. This argument is optional, if given, SSL will be enabled
106
142
 
107
143
  -v or --[no-]verbose This makes Slanger run verbosely, meaning WebSocket frames will be echoed to STDOUT. Useful for debugging
108
144
  </pre>
data/bin/slanger CHANGED
@@ -50,9 +50,9 @@ OptionParser.new do |opts|
50
50
  options[:webhook_url] = p
51
51
  end
52
52
 
53
- opts.on '-c', '--cert_chain_file FILE', "Certificate chain file for SSL transport" do |p|
53
+ opts.on '-c', '--cert_file FILE', "Certificate file for SSL transport" do |p|
54
54
  options[:tls_options] ||= {}
55
- options[:tls_options][:cert_chain_file] = p
55
+ options[:tls_options][:cert_file] = p
56
56
  end
57
57
 
58
58
  opts.on "-v", "--[no-]verbose", "Run verbosely" do |v|
@@ -75,9 +75,9 @@ end
75
75
 
76
76
 
77
77
  if options[:tls_options]
78
- [:cert_chain_file, :private_key_file].each do |param|
78
+ [:cert_file, :private_key_file].each do |param|
79
79
  raise RuntimeError.new "--#{param} does not exist at `#{options[:tls_options][param]}`" unless File.exists? options[:tls_options][param]
80
- raise RuntimeError.new "Both --cert_chain_file and --private_key_file need to be specified" unless options[:tls_options][param]
80
+ raise RuntimeError.new "Both --cert_file and --private_key_file need to be specified" unless options[:tls_options][param]
81
81
  end
82
82
  end
83
83
 
@@ -18,31 +18,56 @@ module Slanger
18
18
  # Respond with HTTP 401 Unauthorized if request cannot be authenticated.
19
19
  error(Signature::AuthenticationError) { |c| halt 401, "401 UNAUTHORIZED\n" }
20
20
 
21
+ post '/apps/:app_id/events' do
22
+ authenticate
23
+
24
+ # Event and channel data are now serialized in the JSON data
25
+ # So, extract and use it
26
+ data = JSON.parse(request.body.read.tap{ |s| s.force_encoding('utf-8')})
27
+
28
+ # Send event to each channel
29
+ data["channels"].each { |channel| publish(channel, data['name'], data['data']) }
30
+
31
+ return {}.to_json
32
+ end
33
+
21
34
  post '/apps/:app_id/channels/:channel_id/events' do
35
+ authenticate
36
+
37
+ publish(params[:channel_id], params['name'], request.body.read.tap{ |s| s.force_encoding('utf-8') })
38
+
39
+ return {}.to_json
40
+ end
41
+
42
+ def payload(channel, event, data)
43
+ {
44
+ event: event,
45
+ data: data,
46
+ channel: channel,
47
+ socket_id: params[:socket_id]
48
+ }.select { |_,v| v }.to_json
49
+ end
50
+
51
+ def authenticate
22
52
  # authenticate request. exclude 'channel_id' and 'app_id' included by sinatra but not sent by Pusher.
23
53
  # Raises Signature::AuthenticationError if request does not authenticate.
24
54
  Signature::Request.new('POST', env['PATH_INFO'], params.except('channel_id', 'app_id')).
25
55
  authenticate { |key| Signature::Token.new key, Slanger::Config.secret }
56
+ end
26
57
 
58
+ def publish(channel, event, data)
27
59
  f = Fiber.current
60
+
28
61
  # Publish the event in Redis and translate the result into an HTTP
29
62
  # status to return to the client.
30
- Slanger::Redis.publish(params[:channel_id], payload).tap do |r|
63
+ Slanger::Redis.publish(channel, payload(channel, event, data)).tap do |r|
31
64
  r.callback { f.resume [202, {}, "202 ACCEPTED\n"] }
32
65
  r.errback { f.resume [500, {}, "500 INTERNAL SERVER ERROR\n"] }
33
66
  end
67
+
34
68
  Fiber.yield
35
69
  end
36
70
 
37
- def payload
38
- payload = {
39
- event: params['name'],
40
- data: request.body.read.tap{ |s| s.force_encoding('utf-8') },
41
- channel: params[:channel_id],
42
- socket_id: params[:socket_id]
43
- }
44
- Hash[payload.reject { |_,v| v.nil? }].to_json
45
- end
46
71
  end
47
72
  end
48
73
 
@@ -35,11 +35,14 @@ module Slanger
35
35
  rescue JSON::ParserError
36
36
  error({ code: 5001, message: "Invalid JSON" })
37
37
  rescue Exception => e
38
- error({ code: 500, message: "#{e.message}\n #{e.backtrace}" })
38
+ error({ code: 500, message: "#{e.message}\n #{e.backtrace.join "\n"}" })
39
39
  end
40
40
 
41
41
  def onclose
42
- @subscriptions.each { |channel_id, subscription_id| Channel.unsubscribe channel_id, subscription_id }
42
+ @subscriptions.select { |k,v| k && v }.
43
+ each do |channel_id, subscription_id|
44
+ Channel.unsubscribe channel_id, subscription_id
45
+ end
43
46
  end
44
47
 
45
48
  def authenticate
data/lib/slanger/redis.rb CHANGED
@@ -7,15 +7,6 @@ module Slanger
7
7
  module Redis
8
8
  extend Forwardable
9
9
 
10
- def self.extended base
11
- # Dispatch messages received from Redis to their destination channel.
12
- base.on(:message) do |channel, message|
13
- message = JSON.parse message
14
- c = Channel.from message['channel']
15
- c.dispatch message, channel
16
- end
17
- end
18
-
19
10
  def_delegator :publisher, :publish
20
11
  def_delegators :subscriber, :on, :subscribe
21
12
  def_delegators :regular_connection, :hgetall, :hdel, :hset, :hincrby
@@ -39,5 +30,12 @@ module Slanger
39
30
  end
40
31
 
41
32
  extend self
33
+
34
+ # Dispatch messages received from Redis to their destination channel.
35
+ on(:message) do |channel, message|
36
+ message = JSON.parse message
37
+ c = Channel.from message['channel']
38
+ c.dispatch message, channel
39
+ end
42
40
  end
43
41
  end
data/slanger.rb CHANGED
@@ -13,6 +13,6 @@ EM.kqueue
13
13
 
14
14
  File.tap do |f|
15
15
  Dir[f.expand_path(f.join(f.dirname(__FILE__),'lib', 'slanger', '*.rb'))].each do |file|
16
- Slanger.autoload File.basename(file, '.rb').classify, file
16
+ Slanger.autoload File.basename(file, '.rb').camelize, file
17
17
  end
18
18
  end
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.3.4
4
+ version: 0.3.5
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-09-07 00:00:00.000000000 Z
12
+ date: 2013-02-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
16
- requirement: &70128529306340 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 0.12.10
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70128529306340
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.12.10
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: em-hiredis
27
- requirement: &70128529304980 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 0.1.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70128529304980
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.1.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: em-websocket
38
- requirement: &70128529304400 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,54 +53,79 @@ dependencies:
43
53
  version: 0.3.5
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70128529304400
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.3.5
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rack
49
- requirement: &70128529303940 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ~>
53
68
  - !ruby/object:Gem::Version
54
- version: 1.3.3
69
+ version: '1.3'
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *70128529303940
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: rack-fiber_pool
60
- requirement: &70128529303480 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
- - - =
83
+ - - '='
64
84
  - !ruby/object:Gem::Version
65
85
  version: 0.9.1
66
86
  type: :runtime
67
87
  prerelease: false
68
- version_requirements: *70128529303480
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.9.1
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: signature
71
- requirement: &70128529303020 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ~>
75
100
  - !ruby/object:Gem::Version
76
- version: 0.1.3
101
+ version: 0.1.6
77
102
  type: :runtime
78
103
  prerelease: false
79
- version_requirements: *70128529303020
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 0.1.6
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: activesupport
82
- requirement: &70128529302560 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ~>
86
116
  - !ruby/object:Gem::Version
87
- version: 3.1.0
117
+ version: '3.1'
88
118
  type: :runtime
89
119
  prerelease: false
90
- version_requirements: *70128529302560
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '3.1'
91
126
  - !ruby/object:Gem::Dependency
92
127
  name: glamazon
93
- requirement: &70128529302100 !ruby/object:Gem::Requirement
128
+ requirement: !ruby/object:Gem::Requirement
94
129
  none: false
95
130
  requirements:
96
131
  - - ~>
@@ -98,10 +133,15 @@ dependencies:
98
133
  version: 0.3.1
99
134
  type: :runtime
100
135
  prerelease: false
101
- version_requirements: *70128529302100
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 0.3.1
102
142
  - !ruby/object:Gem::Dependency
103
143
  name: sinatra
104
- requirement: &70128529301580 !ruby/object:Gem::Requirement
144
+ requirement: !ruby/object:Gem::Requirement
105
145
  none: false
106
146
  requirements:
107
147
  - - ~>
@@ -109,10 +149,15 @@ dependencies:
109
149
  version: 1.2.6
110
150
  type: :runtime
111
151
  prerelease: false
112
- version_requirements: *70128529301580
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 1.2.6
113
158
  - !ruby/object:Gem::Dependency
114
159
  name: thin
115
- requirement: &70128529301120 !ruby/object:Gem::Requirement
160
+ requirement: !ruby/object:Gem::Requirement
116
161
  none: false
117
162
  requirements:
118
163
  - - ~>
@@ -120,10 +165,15 @@ dependencies:
120
165
  version: 1.2.11
121
166
  type: :runtime
122
167
  prerelease: false
123
- version_requirements: *70128529301120
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 1.2.11
124
174
  - !ruby/object:Gem::Dependency
125
175
  name: em-http-request
126
- requirement: &70128529300540 !ruby/object:Gem::Requirement
176
+ requirement: !ruby/object:Gem::Requirement
127
177
  none: false
128
178
  requirements:
129
179
  - - ~>
@@ -131,32 +181,47 @@ dependencies:
131
181
  version: 0.3.0
132
182
  type: :runtime
133
183
  prerelease: false
134
- version_requirements: *70128529300540
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ~>
188
+ - !ruby/object:Gem::Version
189
+ version: 0.3.0
135
190
  - !ruby/object:Gem::Dependency
136
191
  name: rspec
137
- requirement: &70128529299940 !ruby/object:Gem::Requirement
192
+ requirement: !ruby/object:Gem::Requirement
138
193
  none: false
139
194
  requirements:
140
195
  - - ~>
141
196
  - !ruby/object:Gem::Version
142
- version: 2.6.0
197
+ version: '2.6'
143
198
  type: :development
144
199
  prerelease: false
145
- version_requirements: *70128529299940
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ~>
204
+ - !ruby/object:Gem::Version
205
+ version: '2.6'
146
206
  - !ruby/object:Gem::Dependency
147
207
  name: pusher
148
- requirement: &70128529299260 !ruby/object:Gem::Requirement
208
+ requirement: !ruby/object:Gem::Requirement
149
209
  none: false
150
210
  requirements:
151
211
  - - ~>
152
212
  - !ruby/object:Gem::Version
153
- version: 0.9.3
213
+ version: 0.11.3
154
214
  type: :development
155
215
  prerelease: false
156
- version_requirements: *70128529299260
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ~>
220
+ - !ruby/object:Gem::Version
221
+ version: 0.11.3
157
222
  - !ruby/object:Gem::Dependency
158
223
  name: haml
159
- requirement: &70128529298000 !ruby/object:Gem::Requirement
224
+ requirement: !ruby/object:Gem::Requirement
160
225
  none: false
161
226
  requirements:
162
227
  - - ~>
@@ -164,10 +229,15 @@ dependencies:
164
229
  version: 3.1.2
165
230
  type: :development
166
231
  prerelease: false
167
- version_requirements: *70128529298000
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ none: false
234
+ requirements:
235
+ - - ~>
236
+ - !ruby/object:Gem::Version
237
+ version: 3.1.2
168
238
  - !ruby/object:Gem::Dependency
169
239
  name: rake
170
- requirement: &70128529297380 !ruby/object:Gem::Requirement
240
+ requirement: !ruby/object:Gem::Requirement
171
241
  none: false
172
242
  requirements:
173
243
  - - ! '>='
@@ -175,10 +245,15 @@ dependencies:
175
245
  version: '0'
176
246
  type: :development
177
247
  prerelease: false
178
- version_requirements: *70128529297380
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ none: false
250
+ requirements:
251
+ - - ! '>='
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
179
254
  - !ruby/object:Gem::Dependency
180
255
  name: timecop
181
- requirement: &70128529296800 !ruby/object:Gem::Requirement
256
+ requirement: !ruby/object:Gem::Requirement
182
257
  none: false
183
258
  requirements:
184
259
  - - ~>
@@ -186,10 +261,15 @@ dependencies:
186
261
  version: 0.3.5
187
262
  type: :development
188
263
  prerelease: false
189
- version_requirements: *70128529296800
264
+ version_requirements: !ruby/object:Gem::Requirement
265
+ none: false
266
+ requirements:
267
+ - - ~>
268
+ - !ruby/object:Gem::Version
269
+ version: 0.3.5
190
270
  - !ruby/object:Gem::Dependency
191
271
  name: webmock
192
- requirement: &70128529286260 !ruby/object:Gem::Requirement
272
+ requirement: !ruby/object:Gem::Requirement
193
273
  none: false
194
274
  requirements:
195
275
  - - ~>
@@ -197,10 +277,15 @@ dependencies:
197
277
  version: 1.8.7
198
278
  type: :development
199
279
  prerelease: false
200
- version_requirements: *70128529286260
280
+ version_requirements: !ruby/object:Gem::Requirement
281
+ none: false
282
+ requirements:
283
+ - - ~>
284
+ - !ruby/object:Gem::Version
285
+ version: 1.8.7
201
286
  - !ruby/object:Gem::Dependency
202
287
  name: mocha
203
- requirement: &70128529285640 !ruby/object:Gem::Requirement
288
+ requirement: !ruby/object:Gem::Requirement
204
289
  none: false
205
290
  requirements:
206
291
  - - ~>
@@ -208,7 +293,12 @@ dependencies:
208
293
  version: 0.12.0
209
294
  type: :development
210
295
  prerelease: false
211
- version_requirements: *70128529285640
296
+ version_requirements: !ruby/object:Gem::Requirement
297
+ none: false
298
+ requirements:
299
+ - - ~>
300
+ - !ruby/object:Gem::Version
301
+ version: 0.12.0
212
302
  description: A websocket service compatible with Pusher libraries
213
303
  email: sjtgraham@mac.com
214
304
  executables:
@@ -232,8 +322,7 @@ files:
232
322
  - lib/slanger/web_socket_server.rb
233
323
  - lib/slanger/webhook.rb
234
324
  - slanger.rb
235
- - !binary |-
236
- YmluL3NsYW5nZXI=
325
+ - bin/slanger
237
326
  homepage: http://github.com/stevegraham/slanger
238
327
  licenses: []
239
328
  post_install_message:
@@ -254,9 +343,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
343
  version: '0'
255
344
  requirements: []
256
345
  rubyforge_project:
257
- rubygems_version: 1.8.11
346
+ rubygems_version: 1.8.24
258
347
  signing_key:
259
348
  specification_version: 3
260
349
  summary: A websocket service compatible with Pusher libraries
261
350
  test_files: []
262
- has_rdoc: