slangerq 0.6.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/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
|