slanger 0.4.1 → 0.4.2

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.

Potentially problematic release.


This version of slanger might be problematic. Click here for more details.

@@ -0,0 +1,12 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIBvzCCASgCCQCsMkmDVYNDETANBgkqhkiG9w0BAQUFADAkMQswCQYDVQQGEwJE
3
+ RTEVMBMGA1UEAwwMc2xhbmdlci50ZXN0MB4XDTEyMDQxMTE2NDMwNloXDTE5MDIx
4
+ NDE2NDMwNlowJDELMAkGA1UEBhMCREUxFTATBgNVBAMMDHNsYW5nZXIudGVzdDCB
5
+ nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmlTxrcqXw+hbdjnpNENgx4p6T+x7
6
+ SgN/5ti3+gr5ZJElebEdJdGymM/KK817GFhLuYSEv72oEVitC1ISCfo/iOu4S71Y
7
+ sGsrdyPVl3cDswSkvmo27J3rtAbY1fNDs68YFAQGH8wlQtPSPvd9KBKg0klafsDU
8
+ VvDYjQ+XZ2+ZKZECAwEAATANBgkqhkiG9w0BAQUFAAOBgQCHGqUddcsTfvV0Nk3F
9
+ zZ5kGvAiZ02MUourZ4GVs5uBYtkIrQ7HAlQbHAbC8d7e0UVgcTwUKgwpw/RfNR/O
10
+ Ho/zn7lPciLQ7VMnOZ2+MfbJ2HIFgZL6qH1gTcQpBW4s3gKR5hFpaGJ+8l/cmEWj
11
+ AvywaOcSLex+q0OwZaiusiDorg==
12
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXQIBAAKBgQCaVPGtypfD6Ft2Oek0Q2DHinpP7HtKA3/m2Lf6CvlkkSV5sR0l
3
+ 0bKYz8orzXsYWEu5hIS/vagRWK0LUhIJ+j+I67hLvViwayt3I9WXdwOzBKS+ajbs
4
+ neu0BtjV80OzrxgUBAYfzCVC09I+930oEqDSSVp+wNRW8NiND5dnb5kpkQIDAQAB
5
+ AoGABGzBDSGM3mIQFUCtzgDMiowO27HFCyc0iJLYG4QrCFYdA/MvCcGMZFM40a6v
6
+ g9AsQ6JoB/NRGUY4l+V/fOe+4Iuycf8+vN1mrSVR1lTjy/mOwj900pc4ff6cDv6S
7
+ bI/hm4BNiuj8OD11R+ZK07Lo1iCzBkAy53RkTFcBk74MYgECQQDMyAMT0DhlRBqD
8
+ 4vrPF+GZ+rMYpeTjuNDOZphIwzxpv70uyh2RNg+7F6U91Qxz6vpbIkz8Zf4TgdwM
9
+ u/rroktxAkEAwO6wyzidm2yrPMPtnwxDIYnH/ETdNraa3JyHsjXsQwGAIG80+hCv
10
+ QfCA/LmvNOm/Mpe1EyiAeI1/YJp4a2xwIQJBAKysFpQ1ZehVtbnxwaSwMWXiE/Q7
11
+ pjYyl7cCoXPxVFai+8WhXa8dE8Shmo75v2dbAsGnuZy177jJLiB6vYjFL7ECQQCI
12
+ Zri7lLVo8zVFasgO0F6N0ZmAMzeqvQNTwZ72UcVNwjvRso3j1fPyTJUFGEpUwIWa
13
+ wUMV3mal1HQf2lYUrL/BAkBBtXqOLFHINHUmffdRSV/2HECYXHazb6lAnL8nnQX0
14
+ vin2ujCli9mcYWnrY7zwlXdAxgQv5Q2ByQT9Fd8S7FjA
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,109 @@
1
+ module SlangerHelperMethods
2
+ def start_slanger_with_options options={}
3
+ # Fork service. Our integration tests MUST block the main thread because we want to wait for i/o to finish.
4
+ @server_pid = EM.fork_reactor do
5
+ Thin::Logging.silent = true
6
+
7
+ opts = { host: '0.0.0.0',
8
+ api_port: '4567',
9
+ websocket_port: '8080',
10
+ app_key: '765ec374ae0a69f4ce44',
11
+ secret: 'your-pusher-secret',
12
+ activity_timeout: 100
13
+ }
14
+
15
+ Slanger::Config.load opts.merge(options)
16
+
17
+ Slanger::Service.run
18
+ end
19
+ wait_for_slanger
20
+ end
21
+
22
+ alias start_slanger start_slanger_with_options
23
+
24
+ def stop_slanger
25
+ # Ensure Slanger is properly stopped. No orphaned processes allowed!
26
+ Process.kill 'SIGKILL', @server_pid
27
+ Process.wait @server_pid
28
+ end
29
+
30
+ def wait_for_slanger opts = {}
31
+ opts = { port: 8080 }.update opts
32
+ begin
33
+ TCPSocket.new('0.0.0.0', opts[:port]).close
34
+ rescue
35
+ sleep 0.005
36
+ retry
37
+ end
38
+ end
39
+
40
+ def new_websocket opts = {}
41
+ opts = { key: Pusher.key, protocol: 7 }.update opts
42
+ uri = "ws://0.0.0.0:8080/app/#{opts[:key]}?client=js&version=1.8.5&protocol=#{opts[:protocol]}"
43
+
44
+ EM::HttpRequest.new(uri).get.tap { |ws| ws.errback &errback }
45
+ end
46
+
47
+ def em_stream opts = {}
48
+ messages = []
49
+
50
+ em_thread do
51
+ websocket = new_websocket opts
52
+
53
+ stream(websocket, messages) do |message|
54
+ yield websocket, messages
55
+ end
56
+ end
57
+
58
+ return messages
59
+ end
60
+
61
+ def em_thread
62
+ Thread.new do
63
+ EM.run do
64
+ yield
65
+ end
66
+ end.join
67
+ end
68
+
69
+ def stream websocket, messages
70
+ websocket.stream do |message|
71
+ messages << JSON.parse(message)
72
+
73
+ yield message
74
+ end
75
+ end
76
+
77
+ def send_subscribe options
78
+ info = { user_id: options[:user_id], user_info: { name: options[:name] } }
79
+ socket_id = JSON.parse(options[:message]['data'])['socket_id']
80
+ to_sign = [socket_id, 'presence-channel', info.to_json].join ':'
81
+
82
+ digest = OpenSSL::Digest::SHA256.new
83
+
84
+ options[:user].send({
85
+ event: 'pusher:subscribe',
86
+ data: {
87
+ auth: [Pusher.key, OpenSSL::HMAC.hexdigest(digest, Pusher.secret, to_sign)].join(':'),
88
+ channel_data: info.to_json,
89
+ channel: 'presence-channel'
90
+ }
91
+ }.to_json)
92
+ end
93
+
94
+ def private_channel websocket, message
95
+ socket_id = JSON.parse(message['data'])['socket_id']
96
+ to_sign = [socket_id, 'private-channel'].join ':'
97
+
98
+ digest = OpenSSL::Digest::SHA256.new
99
+
100
+ websocket.send({
101
+ event: 'pusher:subscribe',
102
+ data: {
103
+ auth: [Pusher.key, OpenSSL::HMAC.hexdigest(digest, Pusher.secret, to_sign)].join(':'),
104
+ channel: 'private-channel'
105
+ }
106
+ }.to_json)
107
+
108
+ end
109
+ end
@@ -0,0 +1,43 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'active_support/json'
4
+ require 'active_support/core_ext/hash'
5
+ require 'eventmachine'
6
+ require 'em-http-request'
7
+ require 'pusher'
8
+ require 'thin'
9
+ require 'slanger_helper_methods'
10
+ require 'have_attributes'
11
+ require 'openssl'
12
+ require 'socket'
13
+ require 'timecop'
14
+ require 'pry'
15
+ require 'webmock/rspec'
16
+ require 'slanger'
17
+
18
+ WebMock.disable!
19
+
20
+ module Slanger; end
21
+
22
+ def errback
23
+ @errback ||= Proc.new { |e| fail 'cannot connect to slanger. your box might be too slow. try increasing sleep value in the before block' }
24
+ end
25
+
26
+ RSpec.configure do |config|
27
+ config.formatter = 'documentation'
28
+ config.color = true
29
+ config.mock_framework = :mocha
30
+ config.order = 'random'
31
+ config.include SlangerHelperMethods
32
+ config.fail_fast = true
33
+ config.after(:each) { stop_slanger if @server_pid }
34
+ config.before :all do
35
+ Pusher.tap do |p|
36
+ p.host = '0.0.0.0'
37
+ p.port = 4567
38
+ p.app_id = 'your-pusher-app-id'
39
+ p.secret = 'your-pusher-secret'
40
+ p.key = '765ec374ae0a69f4ce44'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'slanger'
3
+
4
+ def clear_redis_connections
5
+ Slanger::Redis.instance_variables.each do |ivar|
6
+ Slanger::Redis.send :remove_instance_variable, ivar
7
+ end
8
+ end
9
+
10
+ describe 'Slanger::Channel' do
11
+ let(:channel) { Slanger::Channel.create channel_id: 'test' }
12
+
13
+ before(:each) do
14
+ EM::Hiredis.stubs(:connect).returns stub_everything('redis', :pubsub => stub_everything('redis'))
15
+ clear_redis_connections
16
+ end
17
+
18
+ after(:each) do
19
+ clear_redis_connections
20
+ EM::Hiredis.unstub(:connect)
21
+ end
22
+
23
+ describe '#unsubscribe' do
24
+ it 'decrements channel subscribers on Redis' do
25
+ Slanger::Redis.expects(:hincrby).
26
+ with('channel_subscriber_count', channel.channel_id, -1).
27
+ once.returns mock { expects(:callback).once.yields(2) }
28
+
29
+ channel.unsubscribe 1
30
+ end
31
+
32
+ it 'activates a webhook when the last subscriber of a channel unsubscribes' do
33
+ Slanger::Webhook.expects(:post).
34
+ with(name: 'channel_vacated', channel: channel.channel_id).
35
+ once
36
+
37
+ Slanger::Redis.expects(:hincrby).
38
+ with('channel_subscriber_count', channel.channel_id, -1).
39
+ times(3).returns mock {
40
+ expects(:callback).times(3).yields(2).then.yields(1).then.yields(0)
41
+ }
42
+
43
+ 3.times { |i| channel.unsubscribe i + 1 }
44
+ end
45
+ end
46
+
47
+ describe '#subscribe' do
48
+ it 'increments channel subscribers on Redis' do
49
+ Slanger::Redis.expects(:hincrby).
50
+ with('channel_subscriber_count', channel.channel_id, 1).
51
+ once.returns mock { expects(:callback).once.yields(2) }
52
+ channel.subscribe { |m| nil }
53
+ end
54
+
55
+ it 'activates a webhook when the first subscriber of a channel joins' do
56
+ Slanger::Webhook.expects(:post).
57
+ with(name: 'channel_occupied', channel: channel.channel_id).
58
+ once
59
+
60
+ Slanger::Redis.expects(:hincrby).
61
+ with('channel_subscriber_count', channel.channel_id, 1).
62
+ times(3).returns mock {
63
+ expects(:callback).times(3).yields(1).then.yields(2).then.yields(3)
64
+ }
65
+
66
+ 3.times { channel.subscribe { |m| nil } }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,64 @@
1
+ #encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Slanger::Api::RequestValidation do
5
+ describe '#socket_id' do
6
+ it 'validation' do
7
+ socket_id = "POST\n/apps/99759/events\nauth_key=840543d97de9803651b1&auth_timestamp=123&auth_version=1.0&body_md5=some_md5&dummy="
8
+
9
+ expect {validate(nil) }.not_to raise_error Slanger::Api::InvalidRequest
10
+ expect {validate("1234.123455") }.not_to raise_error Slanger::Api::InvalidRequest
11
+
12
+ expect {validate(socket_id) }.to raise_error Slanger::Api::InvalidRequest
13
+ expect {validate("something 123")}.to raise_error Slanger::Api::InvalidRequest
14
+
15
+ expect {validate("1234.12345 ") }.to raise_error Slanger::Api::InvalidRequest
16
+ expect {validate(" 1234.12345") }.to raise_error Slanger::Api::InvalidRequest
17
+ expect {validate("hello\n1234.123456\nhomakov") }.to raise_error Slanger::Api::InvalidRequest
18
+ end
19
+ end
20
+
21
+ before do
22
+ request = mock("request")
23
+ request.expects(:authenticate).times(0..2)
24
+ Signature::Request.expects(:new).times(0..2).returns request
25
+ end
26
+
27
+ describe "#channels" do
28
+ let(:body) { {socket_id: "1234.5678", channels: channels}.to_json }
29
+
30
+ context "with valid channels" do
31
+ let(:channels) { ["MY_CHANNEL", "presence-abcd"] }
32
+
33
+ it "returns an array of valid channel_id values" do
34
+ rv = Slanger::Api::RequestValidation.new(body, {}, "")
35
+
36
+ expect(rv.channels).to eq ["MY_CHANNEL", "presence-abcd"]
37
+ end
38
+ end
39
+
40
+ context "with invalid channels" do
41
+ let(:channels) { ["MY_CHANNEL:presence-abcd", "presence-abcd"] }
42
+
43
+ it "rejects invalid channels" do
44
+ expect{ Slanger::Api::RequestValidation.new(body, {}, "")}.to raise_error Slanger::Api::InvalidRequest
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#socket_id" do
50
+ it do
51
+ rv = Slanger::Api::RequestValidation.new(body("1234.5678"), {}, "")
52
+ expect(rv.socket_id).to eq "1234.5678"
53
+ end
54
+ end
55
+
56
+ def validate(socket_id)
57
+ Slanger::Api::RequestValidation.new(body(socket_id), {}, "").socket_id
58
+ end
59
+
60
+ def body(socket_id)
61
+ {socket_id: socket_id}.to_json
62
+ end
63
+ end
64
+
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'lib/slanger/webhook'
3
+
4
+ describe 'Slanger::Webhook' do
5
+
6
+ around do |example|
7
+ Slanger::Config.load webhook_url: 'https://example.com/pusher',
8
+ app_key: 'PUSHER_APP_KEY', secret: 'secret'
9
+ WebMock.enable!
10
+ Timecop.freeze(Time.now) { example.run }
11
+ WebMock.disable!
12
+ Slanger::Config.load webhook_url: nil
13
+ end
14
+
15
+ describe '.post' do
16
+ it 'make a POST request to the endpoint' do
17
+ payload = {
18
+ time_ms: Time.now.strftime('%s%L'),
19
+ events: [{ name: 'channel_occupied', channel: 'test channel' }]
20
+ }.to_json
21
+
22
+ digest = OpenSSL::Digest::SHA256.new
23
+ hmac = OpenSSL::HMAC.hexdigest(digest, Slanger::Config.secret, payload)
24
+
25
+ stub_request(:post, Slanger::Config.webhook_url).
26
+ with(body: payload, headers: {
27
+ "X-Pusher-Key" => Slanger::Config.app_key,
28
+ "X-Pusher-Signature" => hmac,
29
+ "Content-Type" => 'application/json'
30
+ }).
31
+ to_return(:status => 200, :body => {}.to_json, :headers => {})
32
+
33
+ Slanger::Webhook.post name: 'channel_occupied', channel: 'test channel'
34
+
35
+ WebMock.should have_requested(:post, Slanger::Config.webhook_url).
36
+ with(body: payload, headers: {
37
+ "X-Pusher-Key" => Slanger::Config.app_key,
38
+ "X-Pusher-Signature" => hmac,
39
+ "Content-Type" => 'application/json'
40
+ })
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slanger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stevie Graham
8
+ - Mark Burns
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-02-20 00:00:00.000000000 Z
12
+ date: 2015-05-16 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: eventmachine
@@ -100,28 +101,14 @@ dependencies:
100
101
  requirements:
101
102
  - - "~>"
102
103
  - !ruby/object:Gem::Version
103
- version: '3.1'
104
+ version: 4.2.1
104
105
  type: :runtime
105
106
  prerelease: false
106
107
  version_requirements: !ruby/object:Gem::Requirement
107
108
  requirements:
108
109
  - - "~>"
109
110
  - !ruby/object:Gem::Version
110
- version: '3.1'
111
- - !ruby/object:Gem::Dependency
112
- name: glamazon
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: 0.3.1
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: 0.3.1
111
+ version: 4.2.1
125
112
  - !ruby/object:Gem::Dependency
126
113
  name: sinatra
127
114
  requirement: !ruby/object:Gem::Requirement
@@ -206,20 +193,6 @@ dependencies:
206
193
  - - "~>"
207
194
  - !ruby/object:Gem::Version
208
195
  version: 3.1.2
209
- - !ruby/object:Gem::Dependency
210
- name: rake
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - ">="
214
- - !ruby/object:Gem::Version
215
- version: '0'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - ">="
221
- - !ruby/object:Gem::Version
222
- version: '0'
223
196
  - !ruby/object:Gem::Dependency
224
197
  name: timecop
225
198
  requirement: !ruby/object:Gem::Requirement
@@ -276,8 +249,52 @@ dependencies:
276
249
  - - "~>"
277
250
  - !ruby/object:Gem::Version
278
251
  version: 0.10.1
252
+ - !ruby/object:Gem::Dependency
253
+ name: pry-byebug
254
+ requirement: !ruby/object:Gem::Requirement
255
+ requirements:
256
+ - - "~>"
257
+ - !ruby/object:Gem::Version
258
+ version: 2.0.0
259
+ type: :development
260
+ prerelease: false
261
+ version_requirements: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - "~>"
264
+ - !ruby/object:Gem::Version
265
+ version: 2.0.0
266
+ - !ruby/object:Gem::Dependency
267
+ name: bundler
268
+ requirement: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - "~>"
271
+ - !ruby/object:Gem::Version
272
+ version: '1.5'
273
+ type: :development
274
+ prerelease: false
275
+ version_requirements: !ruby/object:Gem::Requirement
276
+ requirements:
277
+ - - "~>"
278
+ - !ruby/object:Gem::Version
279
+ version: '1.5'
280
+ - !ruby/object:Gem::Dependency
281
+ name: rake
282
+ requirement: !ruby/object:Gem::Requirement
283
+ requirements:
284
+ - - "~>"
285
+ - !ruby/object:Gem::Version
286
+ version: 10.4.2
287
+ type: :development
288
+ prerelease: false
289
+ version_requirements: !ruby/object:Gem::Requirement
290
+ requirements:
291
+ - - "~>"
292
+ - !ruby/object:Gem::Version
293
+ version: 10.4.2
279
294
  description: A websocket service compatible with Pusher libraries
280
- email: sjtgraham@mac.com
295
+ email:
296
+ - sjtgraham@mac.com
297
+ - markthedeveloper@gmail.com
281
298
  executables:
282
299
  - slanger
283
300
  extensions: []
@@ -285,7 +302,11 @@ extra_rdoc_files: []
285
302
  files:
286
303
  - README.md
287
304
  - bin/slanger
288
- - lib/slanger/api_server.rb
305
+ - lib/slanger/api.rb
306
+ - lib/slanger/api/event.rb
307
+ - lib/slanger/api/event_publisher.rb
308
+ - lib/slanger/api/request_validation.rb
309
+ - lib/slanger/api/server.rb
289
310
  - lib/slanger/channel.rb
290
311
  - lib/slanger/config.rb
291
312
  - lib/slanger/connection.rb
@@ -301,8 +322,23 @@ files:
301
322
  - lib/slanger/web_socket_server.rb
302
323
  - lib/slanger/webhook.rb
303
324
  - slanger.rb
304
- homepage: http://github.com/stevegraham/slanger
305
- licenses: []
325
+ - spec/have_attributes.rb
326
+ - spec/integration/channel_spec.rb
327
+ - spec/integration/integration_spec.rb
328
+ - spec/integration/presence_channel_spec.rb
329
+ - spec/integration/private_channel_spec.rb
330
+ - spec/integration/replaced_handler_spec.rb
331
+ - spec/integration/ssl_spec.rb
332
+ - spec/server.crt
333
+ - spec/server.key
334
+ - spec/slanger_helper_methods.rb
335
+ - spec/spec_helper.rb
336
+ - spec/unit/channel_spec.rb
337
+ - spec/unit/request_validation_spec.rb
338
+ - spec/unit/webhook_spec.rb
339
+ homepage: https://github.com/stevegraham/slanger
340
+ licenses:
341
+ - MIT
306
342
  metadata: {}
307
343
  post_install_message:
308
344
  rdoc_options: []
@@ -320,8 +356,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
320
356
  version: '0'
321
357
  requirements: []
322
358
  rubyforge_project:
323
- rubygems_version: 2.2.2
359
+ rubygems_version: 2.4.5
324
360
  signing_key:
325
361
  specification_version: 4
326
362
  summary: A websocket service compatible with Pusher libraries
327
- test_files: []
363
+ test_files:
364
+ - spec/have_attributes.rb
365
+ - spec/integration/channel_spec.rb
366
+ - spec/integration/integration_spec.rb
367
+ - spec/integration/presence_channel_spec.rb
368
+ - spec/integration/private_channel_spec.rb
369
+ - spec/integration/replaced_handler_spec.rb
370
+ - spec/integration/ssl_spec.rb
371
+ - spec/server.crt
372
+ - spec/server.key
373
+ - spec/slanger_helper_methods.rb
374
+ - spec/spec_helper.rb
375
+ - spec/unit/channel_spec.rb
376
+ - spec/unit/request_validation_spec.rb
377
+ - spec/unit/webhook_spec.rb