slangerq 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +224 -0
- data/bin/slanger +137 -0
- data/lib/slanger/api.rb +5 -0
- data/lib/slanger/api/event.rb +17 -0
- data/lib/slanger/api/event_publisher.rb +24 -0
- data/lib/slanger/api/request_validation.rb +105 -0
- data/lib/slanger/api/server.rb +57 -0
- data/lib/slanger/channel.rb +106 -0
- data/lib/slanger/config.rb +27 -0
- data/lib/slanger/connection.rb +46 -0
- data/lib/slanger/handler.rb +121 -0
- data/lib/slanger/logger.rb +7 -0
- data/lib/slanger/presence_channel.rb +140 -0
- data/lib/slanger/presence_subscription.rb +33 -0
- data/lib/slanger/private_subscription.rb +9 -0
- data/lib/slanger/redis.rb +41 -0
- data/lib/slanger/service.rb +20 -0
- data/lib/slanger/subscription.rb +53 -0
- data/lib/slanger/version.rb +3 -0
- data/lib/slanger/web_socket_server.rb +37 -0
- data/lib/slanger/webhook.rb +31 -0
- data/slanger.rb +23 -0
- data/spec/have_attributes.rb +64 -0
- data/spec/integration/channel_spec.rb +114 -0
- data/spec/integration/integration_spec.rb +68 -0
- data/spec/integration/presence_channel_spec.rb +157 -0
- data/spec/integration/private_channel_spec.rb +79 -0
- data/spec/integration/replaced_handler_spec.rb +23 -0
- data/spec/integration/ssl_spec.rb +18 -0
- data/spec/server.crt +12 -0
- data/spec/server.key +15 -0
- data/spec/slanger_helper_methods.rb +109 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/unit/channel_spec.rb +105 -0
- data/spec/unit/request_validation_spec.rb +72 -0
- data/spec/unit/webhook_spec.rb +43 -0
- metadata +392 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'Integration' do
|
5
|
+
|
6
|
+
before(:each) { start_slanger }
|
7
|
+
|
8
|
+
context "connecting with invalid credentials" do
|
9
|
+
it "sends an error message" do
|
10
|
+
messages = em_stream(key: 'bogus_key') do |websocket, messages|
|
11
|
+
websocket.callback { EM.stop }
|
12
|
+
end
|
13
|
+
expect(messages).to have_attributes count: 1, last_event: 'pusher:error',
|
14
|
+
connection_established: false, id_present: false
|
15
|
+
messages.first['data'] == 'Could not find app by key bogus_key'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "connecting with valid credentials" do
|
20
|
+
it "should succeed and include activity_timeout value in handshake" do
|
21
|
+
messages = em_stream do |websocket, messages|
|
22
|
+
websocket.callback { EM.stop }
|
23
|
+
end
|
24
|
+
expect(messages).to have_attributes activity_timeout: Slanger::Config.activity_timeout,
|
25
|
+
connection_established: true, id_present: true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "connect with valid protocol version" do
|
30
|
+
it "should connect successfuly" do
|
31
|
+
messages = em_stream do |websocket, messages|
|
32
|
+
websocket.callback { EM.stop }
|
33
|
+
end
|
34
|
+
expect(messages).to have_attributes connection_established: true, id_present: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "connect with invalid protocol version" do
|
39
|
+
it "should not connect successfuly with version bigger than supported" do
|
40
|
+
messages = em_stream(protocol: "20") do |websocket, messages|
|
41
|
+
websocket.callback { EM.stop }
|
42
|
+
end
|
43
|
+
expect(messages).to have_attributes connection_established: false, id_present: false,
|
44
|
+
last_event: 'pusher:error'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not connect successfuly without specified version" do
|
48
|
+
messages = em_stream(protocol: nil) do |websocket, messages|
|
49
|
+
websocket.callback { EM.stop }
|
50
|
+
end
|
51
|
+
expect(messages).to have_attributes connection_established: false, id_present: false,
|
52
|
+
last_event: 'pusher:error'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "given invalid JSON as input" do
|
57
|
+
it 'should not crash' do
|
58
|
+
messages = em_stream do |websocket, messages|
|
59
|
+
websocket.callback do
|
60
|
+
websocket.send("{ event: 'pusher:subscribe', data: { channel: 'MY_CHANNEL'} }23123")
|
61
|
+
EM.next_tick { EM.stop }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
EM.run { new_websocket.tap { |u| u.stream { EM.next_tick { EM.stop } } }}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe 'Integration' do
|
6
|
+
|
7
|
+
before(:each) { start_slanger }
|
8
|
+
|
9
|
+
describe 'presence channels:' do
|
10
|
+
context 'subscribing without channel data' do
|
11
|
+
context 'and bogus authentication credentials' do
|
12
|
+
it 'sends back an error message' do
|
13
|
+
messages = em_stream do |websocket, messages|
|
14
|
+
case messages.length
|
15
|
+
when 1
|
16
|
+
websocket.send({ event: 'pusher:subscribe', data: { channel: 'presence-channel', auth: 'bogus' } }.to_json)
|
17
|
+
else
|
18
|
+
EM.stop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(messages).to have_attributes connection_established: true, id_present: true,
|
23
|
+
count: 2,
|
24
|
+
last_event: 'pusher:error'
|
25
|
+
|
26
|
+
expect(JSON.parse(messages.last['data'])['message']).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'subscribing with channel data' do
|
32
|
+
context 'and bogus authentication credentials' do
|
33
|
+
it 'sends back an error message' do
|
34
|
+
messages = em_stream do |websocket, messages|
|
35
|
+
case messages.length
|
36
|
+
when 1
|
37
|
+
websocket.send({ event: 'pusher:subscribe', data: {
|
38
|
+
channel: 'presence-lel',
|
39
|
+
auth: 'boog',
|
40
|
+
channel_data: {
|
41
|
+
user_id: "barry",
|
42
|
+
}
|
43
|
+
}.to_json }.to_json)
|
44
|
+
else
|
45
|
+
EM.stop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(messages).to have_attributes first_event: 'pusher:connection_established', count: 2,
|
50
|
+
id_present: true
|
51
|
+
|
52
|
+
# Channel id should be in the payload
|
53
|
+
expect(messages.last['event']).to eq('pusher:error')
|
54
|
+
expect(JSON.parse(messages.last['data'])['message']).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with genuine authentication credentials' do
|
59
|
+
it 'sends back a success message' do
|
60
|
+
messages = em_stream do |websocket, messages|
|
61
|
+
case messages.length
|
62
|
+
when 1
|
63
|
+
send_subscribe( user: websocket,
|
64
|
+
user_id: '0f177369a3b71275d25ab1b44db9f95f',
|
65
|
+
name: 'SG',
|
66
|
+
message: messages.first)
|
67
|
+
else
|
68
|
+
EM.stop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
expect(messages).to have_attributes connection_established: true, count: 2
|
73
|
+
|
74
|
+
expect(messages.last).to eq({"channel"=>"presence-channel",
|
75
|
+
"event" =>"pusher_internal:subscription_succeeded",
|
76
|
+
"data" => "{\"presence\":{\"count\":1,\"ids\":[\"0f177369a3b71275d25ab1b44db9f95f\"],\"hash\":{\"0f177369a3b71275d25ab1b44db9f95f\":{\"name\":\"SG\"}}}}"})
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
context 'with more than one subscriber subscribed to the channel' do
|
83
|
+
it 'sends a member added message to the existing subscribers' do
|
84
|
+
messages = em_stream do |user1, messages|
|
85
|
+
case messages.length
|
86
|
+
when 1
|
87
|
+
send_subscribe(user: user1,
|
88
|
+
user_id: '0f177369a3b71275d25ab1b44db9f95f',
|
89
|
+
name: 'SG',
|
90
|
+
message: messages.first)
|
91
|
+
|
92
|
+
when 2
|
93
|
+
new_websocket.tap do |u|
|
94
|
+
u.stream do |message|
|
95
|
+
message = JSON.parse(message)
|
96
|
+
if message['event'] == 'pusher:connection_established'
|
97
|
+
send_subscribe \
|
98
|
+
user: u, user_id: '37960509766262569d504f02a0ee986d',
|
99
|
+
name: 'CHROME', message: message
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
EM.stop
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
expect(messages).to have_attributes connection_established: true, count: 3
|
110
|
+
# Channel id should be in the payload
|
111
|
+
expect(messages[1]).to eq({"channel"=>"presence-channel", "event"=>"pusher_internal:subscription_succeeded",
|
112
|
+
"data"=>"{\"presence\":{\"count\":1,\"ids\":[\"0f177369a3b71275d25ab1b44db9f95f\"],\"hash\":{\"0f177369a3b71275d25ab1b44db9f95f\":{\"name\":\"SG\"}}}}"})
|
113
|
+
|
114
|
+
expect(messages.last).to eq({"channel"=>"presence-channel", "event"=>"pusher_internal:member_added",
|
115
|
+
"data"=>{"user_id"=>"37960509766262569d504f02a0ee986d", "user_info"=>{"name"=>"CHROME"}}})
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not send multiple member added and member removed messages if one subscriber opens multiple connections, i.e. multiple browser tabs.' do
|
119
|
+
messages = em_stream do |user1, messages|
|
120
|
+
case messages.length
|
121
|
+
when 1
|
122
|
+
send_subscribe(user: user1,
|
123
|
+
user_id: '0f177369a3b71275d25ab1b44db9f95f',
|
124
|
+
name: 'SG',
|
125
|
+
message: messages.first)
|
126
|
+
|
127
|
+
when 2
|
128
|
+
10.times do
|
129
|
+
new_websocket.tap do |u|
|
130
|
+
u.stream do |message|
|
131
|
+
# remove stream callback
|
132
|
+
## close the connection in the next tick as soon as subscription is acknowledged
|
133
|
+
u.stream { EM.next_tick { u.close_connection } }
|
134
|
+
|
135
|
+
send_subscribe({ user: u,
|
136
|
+
user_id: '37960509766262569d504f02a0ee986d',
|
137
|
+
name: 'CHROME',
|
138
|
+
message: JSON.parse(message)})
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
when 4
|
143
|
+
EM.next_tick { EM.stop }
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
# There should only be one set of presence messages sent to the refernce user for the second user.
|
149
|
+
expect(messages.one? { |message| message['event'] == 'pusher_internal:member_added' && message['data']['user_id'] == '37960509766262569d504f02a0ee986d' }).to eq(true)
|
150
|
+
expect(messages.one? { |message| message['event'] == 'pusher_internal:member_removed' && message['data']['user_id'] == '37960509766262569d504f02a0ee986d' }).to eq(true)
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'Integration' do
|
5
|
+
|
6
|
+
before(:each) { start_slanger }
|
7
|
+
|
8
|
+
describe 'private channels' do
|
9
|
+
context 'with valid authentication credentials:' do
|
10
|
+
it 'accepts the subscription request' do
|
11
|
+
messages = em_stream do |websocket, messages|
|
12
|
+
case messages.length
|
13
|
+
when 1
|
14
|
+
private_channel websocket, messages.first
|
15
|
+
else
|
16
|
+
EM.stop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(messages).to have_attributes connection_established: true,
|
21
|
+
count: 2,
|
22
|
+
id_present: true,
|
23
|
+
last_event: 'pusher_internal:subscription_succeeded'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with bogus authentication credentials:' do
|
28
|
+
it 'sends back an error message' do
|
29
|
+
messages = em_stream do |websocket, messages|
|
30
|
+
case messages.length
|
31
|
+
when 1
|
32
|
+
websocket.send({ event: 'pusher:subscribe',
|
33
|
+
data: { channel: 'private-channel',
|
34
|
+
auth: 'bogus' } }.to_json)
|
35
|
+
else
|
36
|
+
EM.stop
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(messages).to have_attributes connection_established: true, count: 2, id_present: true, last_event:
|
41
|
+
'pusher:error'
|
42
|
+
|
43
|
+
expect(JSON.parse(messages.last['data'])['message']).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'client events' do
|
48
|
+
it "sends event to other channel subscribers" do
|
49
|
+
client1_messages, client2_messages = [], []
|
50
|
+
|
51
|
+
em_thread do
|
52
|
+
client1, client2 = new_websocket, new_websocket
|
53
|
+
client2_messages, client1_messages = [], []
|
54
|
+
|
55
|
+
stream(client1, client1_messages) do |message|
|
56
|
+
case client1_messages.length
|
57
|
+
when 1
|
58
|
+
private_channel client1, client1_messages.first
|
59
|
+
when 3
|
60
|
+
EM.next_tick { EM.stop }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
stream(client2, client2_messages) do |message|
|
65
|
+
case client2_messages.length
|
66
|
+
when 1
|
67
|
+
private_channel client2, client2_messages.first
|
68
|
+
when 2
|
69
|
+
client2.send({ event: 'client-something', data: { some: 'stuff' }, channel: 'private-channel' }.to_json)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
expect(client1_messages.one? { |m| m['event'] == 'client-something' }).to eq(true)
|
75
|
+
expect(client2_messages.none? { |m| m['event'] == 'client-something' }).to eq(true)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'lib/slanger/handler.rb'
|
3
|
+
|
4
|
+
class ReplacedHandler < Slanger::Handler
|
5
|
+
def authenticate
|
6
|
+
super
|
7
|
+
send_payload nil, 'pusher:info', { message: "Welcome!" }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'Replacable handler' do
|
12
|
+
it 'says welcome' do
|
13
|
+
start_slanger_with_options socket_handler: ReplacedHandler
|
14
|
+
|
15
|
+
msgs = em_stream do |websocket, messages|
|
16
|
+
if messages.length == 2
|
17
|
+
EM.stop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
expect(msgs.last).to eq({ "event" => "pusher:info", "data" =>"{\"message\":\"Welcome!\"}" })
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Integration' do
|
4
|
+
describe 'Slanger when configured to use SSL' do
|
5
|
+
it 'encrypts the connection' do
|
6
|
+
start_slanger_with_options tls_options: {
|
7
|
+
cert_chain_file: 'spec/server.crt',
|
8
|
+
private_key_file: 'spec/server.key'
|
9
|
+
}
|
10
|
+
|
11
|
+
socket = TCPSocket.new('0.0.0.0', 8080)
|
12
|
+
expected_cert = OpenSSL::X509::Certificate.new(File.open('spec/server.crt'))
|
13
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
|
14
|
+
ssl_socket.connect
|
15
|
+
expect(ssl_socket.peer_cert.to_s).to eq(expected_cert.to_s)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/server.crt
ADDED
@@ -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-----
|
data/spec/server.key
ADDED
@@ -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
|