ortc 0.1.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/CHANGELOG.md +4 -0
- data/README.md +7 -0
- data/example/ortc_example.rb +45 -0
- data/example/ortc_example2.rb +126 -0
- data/lib/ortc.rb +865 -0
- data/lib/ortc/ortc_extensibility.rb +40 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a624c528b6abbb1404407a0e0e36dc4dc58de25
|
4
|
+
data.tar.gz: bddc9022702a803acba26bbf73faf8d76edb0623
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 97888ea0f27025feba5595abffcd77b6fd81258118b0daee366922f19900c231595919a5939ad46c85e24c84d48a2b6981da930dbccc8322d43998178320d4cc
|
7
|
+
data.tar.gz: a7277e0bfd379b68ca4bffd25f6034450da4837c806d5ec2943db8d442b2c752d74df851e7974c147cc442092b8cd3973f240c88b04d3fd2de8e362993620780
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# ortc
|
2
|
+
|
3
|
+
The ORTC (Open Real-Time Connectivity) was developed to add a layer of abstraction to real-time full-duplex web communications platforms by making real-time web applications independent of those platforms.
|
4
|
+
|
5
|
+
ORTC provides a standard software API (Application Programming Interface) for sending and receiving data in real-time over the web.
|
6
|
+
|
7
|
+
Visit our webpage (http://framework.realtime.co/messaging/) for more details.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "ortc"
|
2
|
+
|
3
|
+
ortc_client = ORTC::OrtcClient.new
|
4
|
+
ortc_client.cluster_url = 'http://ortc-developers.realtime.co/server/2.1'
|
5
|
+
|
6
|
+
ortc_client.on_connected do |sender|
|
7
|
+
p [:Connected]
|
8
|
+
ortc_client.subscribe("blue", true) { |sender, channel, message|
|
9
|
+
puts "Message received on (#{channel}): #{message}"
|
10
|
+
ortc_client.unsubscribe(channel)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
ortc_client.on_disconnected do |sender|
|
15
|
+
p [:Disconnected]
|
16
|
+
abort()
|
17
|
+
end
|
18
|
+
|
19
|
+
ortc_client.on_exception do |sender, exception|
|
20
|
+
p [:Exception, exception]
|
21
|
+
end
|
22
|
+
|
23
|
+
ortc_client.on_subscribed do |sender, channel|
|
24
|
+
p [:Subscribed, channel]
|
25
|
+
ortc_client.send(channel, 'This is a message')
|
26
|
+
end
|
27
|
+
|
28
|
+
ortc_client.on_unsubscribed do |sender, channel|
|
29
|
+
p [:Unsubscribed, channel]
|
30
|
+
ortc_client.disconnect
|
31
|
+
end
|
32
|
+
|
33
|
+
ortc_client.on_reconnecting do |sender|
|
34
|
+
p [:Reconnecting]
|
35
|
+
end
|
36
|
+
|
37
|
+
ortc_client.on_reconnected do |sender|
|
38
|
+
p [:Reconnected]
|
39
|
+
end
|
40
|
+
|
41
|
+
ortc_client.connect 'Your_application_key', 'Your_token'
|
42
|
+
|
43
|
+
loop do
|
44
|
+
sleep 1
|
45
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require "ortc"
|
2
|
+
|
3
|
+
ortc_client = ORTC::OrtcClient.new
|
4
|
+
|
5
|
+
ortc_url = 'https://ortc-developers.realtime.co/server/ssl/2.1'
|
6
|
+
ortc_app_key = 'your_application_key'
|
7
|
+
ortc_auth_token = 'your_authentication_token' #needed only when using authentication
|
8
|
+
ortc_private_key = 'your_private_key' #needed only for 'saving authentication and enable/disable presence
|
9
|
+
|
10
|
+
ortc_client.cluster_url = ortc_url
|
11
|
+
|
12
|
+
ortc_client.on_connected do |sender|
|
13
|
+
p [:Connected]
|
14
|
+
end
|
15
|
+
|
16
|
+
ortc_client.on_disconnected do |sender|
|
17
|
+
p [:Disconnected]
|
18
|
+
end
|
19
|
+
|
20
|
+
ortc_client.on_exception do |sender, exception|
|
21
|
+
p [:Exception, exception]
|
22
|
+
end
|
23
|
+
|
24
|
+
ortc_client.on_subscribed do |sender, channel|
|
25
|
+
p [:Subscribed, channel]
|
26
|
+
end
|
27
|
+
|
28
|
+
ortc_client.on_unsubscribed do |sender, channel|
|
29
|
+
p [:Unsubscribed, channel]
|
30
|
+
end
|
31
|
+
|
32
|
+
ortc_client.on_reconnecting do |sender|
|
33
|
+
p [:Reconnecting]
|
34
|
+
end
|
35
|
+
|
36
|
+
ortc_client.on_reconnected do |sender|
|
37
|
+
p [:Reconnected]
|
38
|
+
end
|
39
|
+
|
40
|
+
command = ""
|
41
|
+
t = Thread.new {
|
42
|
+
puts "q - quit, 1 - connect, 2 - disconnect, 3 - subscribe, 4 - unsubscribe, 5 - send, 6 - save authentication, 7 - Enable presence, 8 - Disable presence, 9 - Presence"
|
43
|
+
begin
|
44
|
+
command = gets.chomp
|
45
|
+
if(command == "1")
|
46
|
+
ortc_client.connect ortc_app_key, ortc_auth_token
|
47
|
+
end
|
48
|
+
if(command == "2")
|
49
|
+
ortc_client.disconnect
|
50
|
+
end
|
51
|
+
if(command == "3")
|
52
|
+
puts "Subscribing... Channel name:"
|
53
|
+
channel_name = gets.chomp
|
54
|
+
ortc_client.subscribe(channel_name, true) { |sender, channel, message| puts "Received: #{message}" }
|
55
|
+
end
|
56
|
+
if(command == "4")
|
57
|
+
puts "Unsubscribing... Channel name:"
|
58
|
+
channel_name = gets.chomp
|
59
|
+
ortc_client.unsubscribe channel_name
|
60
|
+
end
|
61
|
+
if(command == "5")
|
62
|
+
puts "Sending message... Channel name:"
|
63
|
+
channel_name = gets.chomp
|
64
|
+
puts "Message to send:"
|
65
|
+
message = gets.chomp
|
66
|
+
ortc_client.send(channel_name, message)
|
67
|
+
end
|
68
|
+
if command == "6"
|
69
|
+
puts "Saving authentication..."
|
70
|
+
permissions = Hash.new
|
71
|
+
begin
|
72
|
+
puts "Channel name:"
|
73
|
+
channel_name = gets.chomp
|
74
|
+
puts "Permission: (r)ead, (w)rite, (p)resence"
|
75
|
+
permission = gets.chomp
|
76
|
+
permissions[channel_name] = permission
|
77
|
+
puts "Do you want to add permissions for another channel? (y/n)"
|
78
|
+
response = gets.chomp
|
79
|
+
end until response != 'y'
|
80
|
+
puts "Saving authentication..."
|
81
|
+
puts ortc_client.save_authentication(ortc_url, true, ortc_auth_token, false, ortc_app_key, 1800, ortc_private_key, permissions) ? 'Success' : 'Failed'
|
82
|
+
end
|
83
|
+
if command == "7"
|
84
|
+
puts "Enabling presence..."
|
85
|
+
puts "Channel name:"
|
86
|
+
channel_name = gets.chomp
|
87
|
+
ORTC.enable_presence(ortc_url, true, ortc_app_key, ortc_private_key, channel_name, true) { |error, result|
|
88
|
+
if error.to_s.empty?
|
89
|
+
puts "result: #{result}"
|
90
|
+
else
|
91
|
+
puts "error: #{error}"
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
if command == "8"
|
98
|
+
puts "Disabling presence..."
|
99
|
+
puts "Channel name:"
|
100
|
+
channel_name = gets.chomp
|
101
|
+
ORTC.disable_presence(ortc_url, true, ortc_app_key, ortc_private_key, channel_name) { |error, result|
|
102
|
+
if error.to_s.empty?
|
103
|
+
puts "result: #{result}"
|
104
|
+
else
|
105
|
+
puts "error: #{error}"
|
106
|
+
end
|
107
|
+
}
|
108
|
+
|
109
|
+
end
|
110
|
+
if command == "9"
|
111
|
+
puts "Presence..."
|
112
|
+
puts "Channel name:"
|
113
|
+
channel_name = gets.chomp
|
114
|
+
ORTC.presence(ortc_url, true, ortc_app_key, ortc_auth_token, channel_name) { |error, result|
|
115
|
+
if error.to_s.empty?
|
116
|
+
puts "result: #{result}"
|
117
|
+
else
|
118
|
+
puts "error: #{error}"
|
119
|
+
end
|
120
|
+
}
|
121
|
+
|
122
|
+
end
|
123
|
+
end until command == "q"
|
124
|
+
}
|
125
|
+
t.run
|
126
|
+
t.join
|
data/lib/ortc.rb
ADDED
@@ -0,0 +1,865 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'faye/websocket'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'net/http'
|
5
|
+
require 'net/https'
|
6
|
+
require 'json'
|
7
|
+
require 'ortc/ortc_extensibility'
|
8
|
+
|
9
|
+
module ORTC
|
10
|
+
|
11
|
+
MAX_CHANNEL_NAME_SIZE = 100
|
12
|
+
MAX_MESSAGE_SIZE = 800
|
13
|
+
MAX_HEARTBEAT_INTERVAL = 30
|
14
|
+
RECONNECT_INTERVAL = 5
|
15
|
+
MAX_CONNECTION_METADATA_SIZE = 255
|
16
|
+
|
17
|
+
|
18
|
+
#Enables presence for the specified channel with first 100 unique metadata if true.
|
19
|
+
#
|
20
|
+
#*Note:* This method will send your Private Key over the Internet. Make sure to use secure connection.
|
21
|
+
#- url - Server containing the presence service
|
22
|
+
#- is_cluster - Indicates whether the url is in a cluster.
|
23
|
+
#- application_key - Application key with access to presence service
|
24
|
+
#- private_key - The private key provided when the ORTC service is purchased.
|
25
|
+
#- channel - Channel to activate presence.
|
26
|
+
#- metadata - Defines if to collect first 100 unique metadata.
|
27
|
+
#- &block - Callback with error and result parameters.
|
28
|
+
#
|
29
|
+
#Usage:
|
30
|
+
# ORTC.enable_presence(ortc_url, true, ortc_app_key, ortc_private_key, channel, true) { |error, result|
|
31
|
+
# if error.to_s.empty?
|
32
|
+
# puts "result: #{result}"
|
33
|
+
# else
|
34
|
+
# puts "error: #{error}"
|
35
|
+
# end
|
36
|
+
# }
|
37
|
+
def self.enable_presence(url, is_cluster, application_key, private_key, channel, metadata, &block)
|
38
|
+
if url.to_s.empty?
|
39
|
+
block.call('URL is null or empty', nil)
|
40
|
+
elsif application_key.to_s.empty?
|
41
|
+
block.call('Application Key is null or empty', nil)
|
42
|
+
elsif private_key.to_s.empty?
|
43
|
+
block.call('Private key is null or empty', nil)
|
44
|
+
elsif channel.to_s.empty?
|
45
|
+
block.call('Channel is null or empty', nil)
|
46
|
+
elsif not channel =~ /^[\w\-:\/.]+$/
|
47
|
+
block.call('Channel has invalid characters', nil)
|
48
|
+
else
|
49
|
+
begin
|
50
|
+
r_thread = Thread.new {
|
51
|
+
server = ''
|
52
|
+
if is_cluster
|
53
|
+
server = _get_cluster(url)
|
54
|
+
begin
|
55
|
+
block.call('Can not connect with the server', nil)
|
56
|
+
r_thread.exit
|
57
|
+
end if server == ''
|
58
|
+
else
|
59
|
+
server = url.clone
|
60
|
+
end
|
61
|
+
server = server << (server.match(/\/$/) ? 'presence' : '/presence')
|
62
|
+
server = server << "/enable/#{application_key}/#{channel}"
|
63
|
+
body = "privatekey=#{private_key}&metadata=" << (metadata ? '1' : '0')
|
64
|
+
uri = URI.parse(server)
|
65
|
+
begin
|
66
|
+
if http = Net::HTTP.new(uri.host, uri.port)
|
67
|
+
if server.match /^https/
|
68
|
+
http.use_ssl = true
|
69
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
70
|
+
end
|
71
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
72
|
+
req.body = body
|
73
|
+
res = http.request(req)
|
74
|
+
if res.code == '200'
|
75
|
+
block.call(nil, res.body)
|
76
|
+
else
|
77
|
+
Block.call(res.body, nil)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue => e
|
81
|
+
block.call(e, nil)
|
82
|
+
r_thread.exit
|
83
|
+
end
|
84
|
+
}
|
85
|
+
r_thread.run
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
#Disables presence for the specified channel.
|
91
|
+
#
|
92
|
+
#*Note:* This method will send your Private Key over the Internet. Make sure to use secure connection.
|
93
|
+
#- url - Server containing the presence service
|
94
|
+
#- is_cluster - Indicates whether the url is in a cluster.
|
95
|
+
#- application_key - Application key with access to presence service
|
96
|
+
#- private_key - The private key provided when the ORTC service is purchased.
|
97
|
+
#- channel - Channel to disable presence.
|
98
|
+
#- &block - Callback with error and result parameters.
|
99
|
+
#
|
100
|
+
#Usage:
|
101
|
+
# ORTC.disable_presence(ortc_url, true, ortc_app_key, ortc_private_key, channel) { |error, result|
|
102
|
+
# if error.to_s.empty?
|
103
|
+
# puts "result: #{result}"
|
104
|
+
# else
|
105
|
+
# puts "error: #{error}"
|
106
|
+
# end
|
107
|
+
# }
|
108
|
+
def self.disable_presence(url, is_cluster, application_key, private_key, channel, &block)
|
109
|
+
if url.to_s.empty?
|
110
|
+
block.call('URL is null or empty', nil)
|
111
|
+
elsif application_key.to_s.empty?
|
112
|
+
block.call('Application Key is null or empty', nil)
|
113
|
+
elsif private_key.to_s.empty?
|
114
|
+
block.call('Private key is null or empty', nil)
|
115
|
+
elsif channel.to_s.empty?
|
116
|
+
block.call('Channel is null or empty', nil)
|
117
|
+
elsif not channel =~ /^[\w\-:\/.]+$/
|
118
|
+
block.call('Channel has invalid characters', nil)
|
119
|
+
else
|
120
|
+
begin
|
121
|
+
r_thread = Thread.new {
|
122
|
+
server = ''
|
123
|
+
if is_cluster
|
124
|
+
server = _get_cluster(url)
|
125
|
+
begin
|
126
|
+
block.call('Can not connect with the server', nil)
|
127
|
+
r_thread.exit
|
128
|
+
end if server == ''
|
129
|
+
else
|
130
|
+
server = url.clone
|
131
|
+
end
|
132
|
+
server = server << (server.match(/\/$/) ? 'presence' : '/presence')
|
133
|
+
server = server << "/disable/#{application_key}/#{channel}"
|
134
|
+
body = "privatekey=#{private_key}"
|
135
|
+
uri = URI.parse(server)
|
136
|
+
begin
|
137
|
+
if http = Net::HTTP.new(uri.host, uri.port)
|
138
|
+
if server.match /^https/
|
139
|
+
http.use_ssl = true
|
140
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
141
|
+
end
|
142
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
143
|
+
req.body = body
|
144
|
+
res = http.request(req)
|
145
|
+
if res.code == '200'
|
146
|
+
block.call(nil, res.body)
|
147
|
+
else
|
148
|
+
block.call(res.body, nil)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
rescue => e
|
152
|
+
block.call(e, nil)
|
153
|
+
r_thread.exit
|
154
|
+
end
|
155
|
+
}
|
156
|
+
r_thread.run
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#Gets a Hash table indicating the subscriptions in the specified channel and if active the first 100 unique metadata.
|
162
|
+
#- url - Server containing the presence service
|
163
|
+
#- is_cluster - Indicates whether the url is in a cluster.
|
164
|
+
#- application_key - Application key with access to presence service
|
165
|
+
#- authentication_token - Authentication token with access to presence service
|
166
|
+
#- channel - Channel to presence data active.
|
167
|
+
#- &block - Callback with error and result parameters.
|
168
|
+
#
|
169
|
+
#Usage:
|
170
|
+
# ORTC.presence(ortc_url, true, ortc_app_key, ortc_auth_token, channel) { |error, result|
|
171
|
+
# if error.to_s.empty?
|
172
|
+
# puts "result: #{result}"
|
173
|
+
# else
|
174
|
+
# puts "error: #{error}"
|
175
|
+
# end
|
176
|
+
# }
|
177
|
+
def self.presence(url, is_cluster, application_key, authentication_token, channel, &block)
|
178
|
+
if url.to_s.empty?
|
179
|
+
block.call('URL is null or empty', nil)
|
180
|
+
elsif application_key.to_s.empty?
|
181
|
+
block.call('Application Key is null or empty', nil)
|
182
|
+
elsif authentication_token.to_s.empty?
|
183
|
+
block.call('Authentication Token is null or empty', nil)
|
184
|
+
elsif channel.to_s.empty?
|
185
|
+
block.call('Channel is null or empty', nil)
|
186
|
+
elsif not channel =~ /^[\w\-:\/.]+$/
|
187
|
+
block.call('Channel has invalid characters', nil)
|
188
|
+
else
|
189
|
+
begin
|
190
|
+
r_thread = Thread.new {
|
191
|
+
server = ''
|
192
|
+
if is_cluster
|
193
|
+
server = _get_cluster(url)
|
194
|
+
begin
|
195
|
+
block.call('Can not connect with the server', nil)
|
196
|
+
r_thread.exit
|
197
|
+
end if server == ''
|
198
|
+
else
|
199
|
+
server = url.clone
|
200
|
+
end
|
201
|
+
server = server << (server.match(/\/$/) ? 'presence' : '/presence')
|
202
|
+
server = server << "/#{application_key}/#{authentication_token}/#{channel}"
|
203
|
+
#body = "privatekey=#{private_key}"
|
204
|
+
uri = URI.parse(server)
|
205
|
+
begin
|
206
|
+
if http = Net::HTTP.new(uri.host, uri.port)
|
207
|
+
if server.match /^https/
|
208
|
+
http.use_ssl = true
|
209
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
210
|
+
end
|
211
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
212
|
+
res = http.request(req)
|
213
|
+
if res.code == '200'
|
214
|
+
ret = Hash.new
|
215
|
+
ret = JSON.parse(res.body) if not res.body == 'null'
|
216
|
+
block.call(nil, ret)
|
217
|
+
else
|
218
|
+
block.call(res.body, nil)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
rescue => e
|
222
|
+
block.call(e, nil)
|
223
|
+
r_thread.exit
|
224
|
+
end
|
225
|
+
}
|
226
|
+
r_thread.run
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
def self._get_cluster(url)
|
233
|
+
begin
|
234
|
+
uri = URI.parse(url)
|
235
|
+
if http = Net::HTTP.new(uri.host, uri.port)
|
236
|
+
if url.include? 'https'
|
237
|
+
http.use_ssl = true
|
238
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
239
|
+
end
|
240
|
+
uri = URI.parse(url<< "?appkey=#{@app_key}")
|
241
|
+
http.start do |http|
|
242
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
243
|
+
response = http.request(request)
|
244
|
+
response.body.scan(/"(.*?)"/).join
|
245
|
+
end
|
246
|
+
end
|
247
|
+
rescue Timeout::ExitException
|
248
|
+
return ''
|
249
|
+
rescue Timeout::Error
|
250
|
+
return ''
|
251
|
+
rescue => e
|
252
|
+
return ''
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
#A class representing an ORTC Client
|
258
|
+
class OrtcClient
|
259
|
+
#The client announcement subchannel
|
260
|
+
attr_accessor :announcement_subchannel
|
261
|
+
#The client connection metadata
|
262
|
+
attr_accessor :connection_metadata
|
263
|
+
#The cluster server URL
|
264
|
+
attr_accessor :cluster_url
|
265
|
+
#The server URL
|
266
|
+
attr_accessor :url
|
267
|
+
#The client identifier
|
268
|
+
attr_accessor :id
|
269
|
+
#The client session identifier
|
270
|
+
attr_reader :session_id
|
271
|
+
#Indicates whether the client is connected
|
272
|
+
attr_reader:is_connected
|
273
|
+
|
274
|
+
#Creates a new instance of OrtcClient
|
275
|
+
def initialize
|
276
|
+
@app_key = nil
|
277
|
+
@auth_token = nil
|
278
|
+
@ortcserver = nil
|
279
|
+
@url = nil
|
280
|
+
@is_connected = false
|
281
|
+
@is_connecting = false
|
282
|
+
@is_disconnecting = false
|
283
|
+
@is_reconnecting = false
|
284
|
+
@str_set = [('a'..'z'),('0'..'9')].map{|i| i.to_a}.flatten
|
285
|
+
@session_id = (0...16).map{@str_set[rand(@str_set.length)]}.join
|
286
|
+
@on_connected_callback = nil
|
287
|
+
@on_disconnected_callback =nil
|
288
|
+
@on_reconnected_callback = nil
|
289
|
+
@on_reconnecting_callback = nil
|
290
|
+
@on_subscribed_callback = nil
|
291
|
+
@on_unsubscribed_callback = nil
|
292
|
+
@on_exception_callback = nil
|
293
|
+
@permissions_table = Hash.new
|
294
|
+
@client_thread = nil
|
295
|
+
@heartbeat_thread = nil
|
296
|
+
@reconnect_thread = nil
|
297
|
+
@channels = Hash.new
|
298
|
+
@messages_buffer = Hash.new
|
299
|
+
@socket = nil
|
300
|
+
@got_heartbeat = false
|
301
|
+
EM.error_handler{ |e|
|
302
|
+
raise "Error during event loop: #{e.message}"
|
303
|
+
EM.stop_event_loop
|
304
|
+
}
|
305
|
+
Thread.abort_on_exception=true
|
306
|
+
end
|
307
|
+
|
308
|
+
#Saves the channel and its permissions for the supplied application key and authentication token.
|
309
|
+
#
|
310
|
+
#*Note:* This method will send your Private Key over the Internet. Make sure to use secure connection.
|
311
|
+
#- url - The ORTC server URL.
|
312
|
+
#- is_cluster - Indicates whether the ORTC server is in a cluster.
|
313
|
+
#- authentication_token - The authentication token generated by an application server (for instance: a unique session ID).
|
314
|
+
#- is_private - Indicates whether the authentication token is private.
|
315
|
+
#- application_key - The application key provided when the ORTC service is purchased.
|
316
|
+
#- time_to_live - The authentication token time to live (TTL), in other words, the allowed activity time (in seconds).
|
317
|
+
#- private_key - The private key provided when the ORTC service is purchased.
|
318
|
+
#- channels_permissions - The hash table containing channels and their permissions ('r' - read, 'w' - write, 'p' - presence).
|
319
|
+
#Returns boolean- Indicates whether the authentication was successful.
|
320
|
+
#
|
321
|
+
#Usage:
|
322
|
+
# permissions = Hash.new
|
323
|
+
# permissions['blue'] = 'r'
|
324
|
+
# permissions['yellow'] = 'wp'
|
325
|
+
# ortc_client.save_authentication('https://ortc-developers.realtime.co/server/ssl/2.1', true, 'Your_authentication_token', false, 'Your_app_key', 1800, 'Your_private_key', permissions)
|
326
|
+
def save_authentication(url, is_cluster, authentication_token, is_private, application_key, time_to_live, private_key, channels_permissions)
|
327
|
+
unless channels_permissions.class == Hash
|
328
|
+
@on_exception_callback.call(self, 'Wrong parameter: channels_permissions') if(@on_exception_callback)
|
329
|
+
return false
|
330
|
+
end
|
331
|
+
str = "AT=#{authentication_token}&AK=#{application_key}&PK=#{private_key}&TTL=#{time_to_live}&TP=#{channels_permissions.length}&PVT=#{is_private ? "1" : "0"}"
|
332
|
+
channels_permissions.each {|channel, permission|
|
333
|
+
return false unless is_channel_valid channel, false
|
334
|
+
str << "&#{channel}=#{permission}"
|
335
|
+
}
|
336
|
+
if is_cluster
|
337
|
+
auth_server = ORTC._get_cluster(url)
|
338
|
+
begin
|
339
|
+
@on_exception_callback.call(self, 'Can not connect with the server') if(@on_exception_callback)
|
340
|
+
return false
|
341
|
+
end if auth_server == ''
|
342
|
+
end
|
343
|
+
auth_server = auth_server << (auth_server.match(/\/$/) ? 'authenticate' : '/authenticate')
|
344
|
+
uri = URI.parse(auth_server)
|
345
|
+
begin
|
346
|
+
if http = Net::HTTP.new(uri.host, uri.port)
|
347
|
+
if auth_server.match /^https/
|
348
|
+
http.use_ssl = true
|
349
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
350
|
+
end
|
351
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
352
|
+
req.body = str
|
353
|
+
res = http.request(req)
|
354
|
+
end
|
355
|
+
rescue => e
|
356
|
+
@on_exception_callback.call(self, e) if(@on_exception_callback)
|
357
|
+
return false
|
358
|
+
end
|
359
|
+
return res.code=='201' ? true : false
|
360
|
+
end
|
361
|
+
|
362
|
+
#Enables presence for the specified channel with first 100 unique metadata if true.
|
363
|
+
#
|
364
|
+
#*Note:* This method will send your Private Key over the Internet. Make sure to use secure connection.
|
365
|
+
#- private_key - The private key provided when the ORTC service is purchased.
|
366
|
+
#- channel - Channel to activate presence.
|
367
|
+
#- metadata - Defines if to collect first 100 unique metadata.
|
368
|
+
#- &block - Callback with error and result parameters.
|
369
|
+
#
|
370
|
+
#Usage:
|
371
|
+
# ortc_client.enable_presence(ortc_private_key, channel, true) { |error, result|
|
372
|
+
# if error.to_s.empty?
|
373
|
+
# puts "result: #{result}"
|
374
|
+
# else
|
375
|
+
# puts "error: #{error}"
|
376
|
+
# end
|
377
|
+
# }
|
378
|
+
def enable_presence(private_key, channel, metadata, &block)
|
379
|
+
if not @is_connected
|
380
|
+
@on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
|
381
|
+
return false
|
382
|
+
elsif @cluster_url.to_s == '' && @url.to_s == ''
|
383
|
+
@on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)
|
384
|
+
return false
|
385
|
+
else
|
386
|
+
if not @url.to_s.empty?
|
387
|
+
ORTC.enable_presence(@url, false, @app_key, private_key, channel, metadata, &block)
|
388
|
+
else
|
389
|
+
ORTC.enable_presence(@cluster_url, true, @app_key, private_key, channel, metadata, &block)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
#Disables presence for the specified channel.
|
396
|
+
#
|
397
|
+
#*Note:* This method will send your Private Key over the Internet. Make sure to use secure connection.
|
398
|
+
#- private_key - The private key provided when the ORTC service is purchased.
|
399
|
+
#- channel - Channel to disable presence.
|
400
|
+
#- &block - Callback with error and result parameters.
|
401
|
+
#
|
402
|
+
#Usage:
|
403
|
+
# ortc_client.disable_presence(ortc_private_key, channel) { |error, result|
|
404
|
+
# if error.to_s.empty?
|
405
|
+
# puts "result: #{result}"
|
406
|
+
# else
|
407
|
+
# puts "error: #{error}"
|
408
|
+
# end
|
409
|
+
# }
|
410
|
+
def disable_presence(private_key, channel, &block)
|
411
|
+
if not @is_connected
|
412
|
+
@on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
|
413
|
+
return false
|
414
|
+
elsif @cluster_url.to_s == '' && @url.to_s == ''
|
415
|
+
@on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)
|
416
|
+
return false
|
417
|
+
else
|
418
|
+
if not @url.to_s.empty?
|
419
|
+
ORTC.disable_presence(@url, false, @app_key, private_key, channel, &block)
|
420
|
+
else
|
421
|
+
ORTC.disable_presence(@cluster_url, true, @app_key, private_key, channel, &block)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
|
427
|
+
#Gets a Hash table indicating the subscriptions in the specified channel and if active the first 100 unique metadata.
|
428
|
+
#- channel - Channel to presence data active.
|
429
|
+
#- &block - Callback with error and result parameters.
|
430
|
+
#
|
431
|
+
#Usage:
|
432
|
+
# ortc_client.presence(channel) { |error, result|
|
433
|
+
# if error.to_s.empty?
|
434
|
+
# puts "result: #{result}"
|
435
|
+
# else
|
436
|
+
# puts "error: #{error}"
|
437
|
+
# end
|
438
|
+
# }
|
439
|
+
def presence(channel, &block)
|
440
|
+
if not @is_connected
|
441
|
+
@on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
|
442
|
+
return false
|
443
|
+
elsif @cluster_url.to_s == '' && @url.to_s == ''
|
444
|
+
@on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)
|
445
|
+
return false
|
446
|
+
else
|
447
|
+
if not @url.to_s.empty?
|
448
|
+
ORTC.presence(@url, false, @app_key, @auth_token, channel, &block)
|
449
|
+
else
|
450
|
+
ORTC.presence(@cluster_url, true, @app_key, @auth_token, channel, &block)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
#Indicates whether the client is subscribed to the supplied channel.
|
456
|
+
#- channel - The channel name.
|
457
|
+
#Returns boolean - Indicates whether the client is subscribed to the supplied channel.
|
458
|
+
#
|
459
|
+
#Usage:
|
460
|
+
# puts ortc_client.is_subscribed('blue')
|
461
|
+
def is_subscribed(channel)
|
462
|
+
return false unless is_channel_valid channel
|
463
|
+
ch = @channels[channel]
|
464
|
+
return false if ch == nil
|
465
|
+
return ch.is_subscribed
|
466
|
+
end
|
467
|
+
|
468
|
+
#Sets the callback which occurs when the client connects.
|
469
|
+
#- block - code to be interpreted when the client connects.
|
470
|
+
#Usage:
|
471
|
+
# ortc_client.on_connected do |sender|
|
472
|
+
# p [:Connected]
|
473
|
+
# end
|
474
|
+
def on_connected(&block) @on_connected_callback = block end
|
475
|
+
|
476
|
+
#Sets the callback which occurs when the client disconnects.
|
477
|
+
#- block - code to be interpreted when the client disconnects.
|
478
|
+
#Usage:
|
479
|
+
# ortc_client.on_disconnected do |sender|
|
480
|
+
# p [:Disconnected]
|
481
|
+
# end
|
482
|
+
def on_disconnected(&block) @on_disconnected_callback = block end
|
483
|
+
|
484
|
+
#Sets the callback which occurs when the client reconnects.
|
485
|
+
#- block - code to be interpreted when the client reconnects.
|
486
|
+
#Usage:
|
487
|
+
# ortc_client.on_reconnected do |sender|
|
488
|
+
# p [:Reconnected]
|
489
|
+
# end
|
490
|
+
def on_reconnected(&block) @on_reconnected_callback = block end
|
491
|
+
|
492
|
+
#Sets the callback which occurs when the client attempts to reconnect.
|
493
|
+
#- block - code to be interpreted when the client attempts to reconnect.
|
494
|
+
#Usage:
|
495
|
+
# ortc_client.on_reonnecting do |sender|
|
496
|
+
# p [:Reconnecting]
|
497
|
+
# end
|
498
|
+
def on_reconnecting(&block) @on_reconnecting_callback = block end
|
499
|
+
|
500
|
+
#Sets the callback which occurs when the client subscribes to a channel.
|
501
|
+
#- block - code to be interpreted when the client subscribes to a channel.
|
502
|
+
#Usage:
|
503
|
+
# ortc_client.on_subscribed do |sender, channel|
|
504
|
+
# p [:Subscribed, channel]
|
505
|
+
# end
|
506
|
+
def on_subscribed(&block) @on_subscribed_callback = block end
|
507
|
+
|
508
|
+
#Sets the callback which occurs when the client unsubscribes from a channel.
|
509
|
+
#- block - code to be interpreted when the client unsubscribes from a channel.
|
510
|
+
#Usage:
|
511
|
+
# ortc_client.on_unsubscribed do |sender, channel|
|
512
|
+
# p [:Unsubscribed, channel]
|
513
|
+
# end
|
514
|
+
def on_unsubscribed(&block) @on_unsubscribed_callback = block end
|
515
|
+
|
516
|
+
#Sets the callback which occurs when there is an exception.
|
517
|
+
#- block - code to be interpreted when there is an exception.
|
518
|
+
#Usage:
|
519
|
+
# ortc_client.on_exception do |sender, exception|
|
520
|
+
# p [:Exception, exception]
|
521
|
+
# end
|
522
|
+
def on_exception(&block) @on_exception_callback = block end
|
523
|
+
|
524
|
+
#Connects the client using the supplied application key and authentication token.
|
525
|
+
#- application_key - Your ORTC application key.
|
526
|
+
#- authentication_token - Your ORTC authentication token, this parameter is optional.
|
527
|
+
#Usage:
|
528
|
+
# ortc_client.connect 'aBc123'
|
529
|
+
# ortc_client.connect 'aBc123', 'au7h3n71ca710nT0k3n'
|
530
|
+
def connect(application_key, authentication_token='PM.Anonymous')
|
531
|
+
begin
|
532
|
+
@on_exception_callback.call(self, "Metadata exceeds the limit of #{MAX_CONNECTION_METADATA_SIZE} bytes") if(@on_exception_callback)
|
533
|
+
return
|
534
|
+
end if @connection_metadata.bytes.to_a.size > MAX_CONNECTION_METADATA_SIZE if @connection_metadata != nil
|
535
|
+
begin
|
536
|
+
@on_exception_callback.call(self, 'Wrong Applicaition Key') if(@on_exception_callback)
|
537
|
+
return
|
538
|
+
end if (!application_key.is_a?(String) || application_key.size < 1)
|
539
|
+
$1 if application_key =~ /^[\w\-:\/.]+$/ or begin @on_exception_callback.call(self, "Application key: \"#{application_key}\" has invalid characters") if (@on_exception_callback)
|
540
|
+
return end
|
541
|
+
$1 if authentication_token =~ /^[\w\-:\/.]+$/ or begin @on_exception_callback.call(self, "Authentication token: \"#{authentication_token}\" has invalid characters") if (@on_exception_callback)
|
542
|
+
return end
|
543
|
+
begin
|
544
|
+
@on_exception_callback.call(self, 'Already connected') if(@on_exception_callback)
|
545
|
+
return
|
546
|
+
end if @is_connected
|
547
|
+
begin
|
548
|
+
@on_exception_callback.call(self, 'Already trying to connect') if(@on_exception_callback)
|
549
|
+
return
|
550
|
+
end if @is_connecting
|
551
|
+
begin
|
552
|
+
@on_exception_callback.call(self, 'URL is empty') if(@on_exception_callback)
|
553
|
+
return
|
554
|
+
end if @cluster_url.to_s == '' && @url.to_s == ''
|
555
|
+
|
556
|
+
@is_connecting = true
|
557
|
+
@app_key = application_key
|
558
|
+
@auth_token = authentication_token
|
559
|
+
@client_thread = nil
|
560
|
+
@client_thread = Thread.new {
|
561
|
+
if @url.to_s == ''
|
562
|
+
$1 if @cluster_url=~ /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ or begin @on_exception_callback.call(self, "Invalid cluster URL") if (@on_exception_callback)
|
563
|
+
@client_thread.kill if @client_thread end
|
564
|
+
@ortcserver = ORTC._get_cluster(@cluster_url)
|
565
|
+
else
|
566
|
+
$1 if @url=~ /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ or begin @on_exception_callback.call(self, "Invalid URL") if (@on_exception_callback)
|
567
|
+
@client_thread.kill if @client_thread end
|
568
|
+
@ortcserver = @url.clone
|
569
|
+
end
|
570
|
+
begin
|
571
|
+
@on_exception_callback.call(self, 'There is no server available') if @on_exception_callback
|
572
|
+
@is_connecting = false
|
573
|
+
@client_thread.kill if @client_thread
|
574
|
+
end if @ortcserver.to_s == ''
|
575
|
+
@ortcserver["http"]="ws" if @ortcserver["http"]
|
576
|
+
conn_str = "#{@ortcserver}/broadcast/#{rand(1000)}/#{(0...8).map{@str_set[rand(@str_set.length)]}.join}/websocket"
|
577
|
+
|
578
|
+
EM.run {
|
579
|
+
@socket = Faye::WebSocket::Client.new(conn_str)
|
580
|
+
|
581
|
+
@socket.onopen = lambda do |event|
|
582
|
+
|
583
|
+
end
|
584
|
+
|
585
|
+
@socket.onmessage = lambda do |event|
|
586
|
+
if event.data.eql? "o"
|
587
|
+
EM.next_tick {
|
588
|
+
@socket.send "validate;#{@app_key};#{@auth_token};#{@announcement_subchannel};#{@session_id};#{@connection_metadata}".to_json
|
589
|
+
}
|
590
|
+
elsif event.data != "h"
|
591
|
+
parse_message event.data
|
592
|
+
else
|
593
|
+
@got_heartbeat = true
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
@socket.onclose = lambda do |event|
|
598
|
+
if(@is_disconnecting || @is_connecting)
|
599
|
+
@on_disconnected_callback.call(self) if(@on_disconnected_callback && @is_disconnecting)
|
600
|
+
@is_disconnecting = false
|
601
|
+
@is_connecting = false
|
602
|
+
unless @is_reconnecting
|
603
|
+
@reconnect_thread.kill if @reconnect_thread
|
604
|
+
@channels.clear
|
605
|
+
end
|
606
|
+
@heartbeat_thread.kill if @heartbeat_thread
|
607
|
+
end
|
608
|
+
@is_connected = false
|
609
|
+
EM.stop_event_loop
|
610
|
+
end
|
611
|
+
}
|
612
|
+
}
|
613
|
+
@client_thread.run
|
614
|
+
end
|
615
|
+
|
616
|
+
#Disconnects the client.
|
617
|
+
#
|
618
|
+
#Usage:
|
619
|
+
# ortc_client.disconnect
|
620
|
+
def disconnect
|
621
|
+
begin @on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
|
622
|
+
return
|
623
|
+
end unless @is_connected || @is_reconnecting
|
624
|
+
if @is_reconnecting
|
625
|
+
@reconnect_thread.kill if @reconnect_thread
|
626
|
+
@heartbeat_thread.kill if @heartbeat_thread
|
627
|
+
end
|
628
|
+
@is_connecting = false
|
629
|
+
@is_reconnecting = false
|
630
|
+
@is_disconnecting = true
|
631
|
+
@channels.clear
|
632
|
+
#@socket.close(nil, nil ,nil)
|
633
|
+
@socket.close
|
634
|
+
@client_thread.kill if @client_thread
|
635
|
+
end
|
636
|
+
|
637
|
+
#Subscribes to the supplied channel to receive messages sent to it.
|
638
|
+
#- channel - The channel name.
|
639
|
+
#- subscribeOnReconnected - Indicates whether the client should subscribe to the channel when reconnected (if it was previously subscribed when connected).
|
640
|
+
#- block - the callback called when a message arrives at the channel.
|
641
|
+
#Usage:
|
642
|
+
# ortc_client.subscribe('blue', true) { |sender, channel, message|
|
643
|
+
# puts "Message received on (#{channel}): #{message}"
|
644
|
+
# }
|
645
|
+
def subscribe(channel, subscribe_on_reconnect, &block)
|
646
|
+
return unless is_channel_valid channel
|
647
|
+
ch = @channels[channel]
|
648
|
+
if ch != nil
|
649
|
+
begin
|
650
|
+
@on_exception_callback.call(self, "Already subscribing to the channel #{channel}") if(@on_exception_callback)
|
651
|
+
return
|
652
|
+
end if ch.is_subscribing
|
653
|
+
begin
|
654
|
+
@on_exception_callback.call(self, "Already subscribed to the channel #{channel}") if(@on_exception_callback)
|
655
|
+
return
|
656
|
+
end if ch.is_subscribed
|
657
|
+
end
|
658
|
+
unless @permissions_table.empty?
|
659
|
+
hash = check_permissions 'subscribe', channel
|
660
|
+
return if hash == nil
|
661
|
+
end
|
662
|
+
|
663
|
+
@channels[channel] = Channel.new(channel, block, true, subscribe_on_reconnect) unless ch
|
664
|
+
EM.next_tick {
|
665
|
+
@socket.send "subscribe;#{@app_key};#{@auth_token};#{channel};#{hash}".to_json
|
666
|
+
}
|
667
|
+
end
|
668
|
+
|
669
|
+
#Unsubscribes from the supplied channel to stop receiving messages sent to it.
|
670
|
+
#- channel - The channel name.
|
671
|
+
#Usage:
|
672
|
+
# ortc_client.unsubscribe('blue')
|
673
|
+
def unsubscribe(channel)
|
674
|
+
return unless is_channel_valid channel
|
675
|
+
ch = @channels[channel]
|
676
|
+
if ch != nil
|
677
|
+
begin @on_exception_callback.call(self, "Not subscribed to the channel #{channel}") if(@on_exception_callback)
|
678
|
+
return end unless ch.is_subscribed
|
679
|
+
ch.subscribe_on_reconnected = false
|
680
|
+
EM.next_tick {
|
681
|
+
@socket.send "unsubscribe;#{@app_key};#{channel}".to_json
|
682
|
+
}
|
683
|
+
else
|
684
|
+
@on_exception_callback.call(self, "Not subscribed to the channel #{channel}") if(@on_exception_callback)
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
#Sends the supplied message to the supplied channel.
|
689
|
+
#- channel - The channel name.
|
690
|
+
#- message - The message to send.
|
691
|
+
#Usage:
|
692
|
+
# ortc_client.send('blue', 'This is a message')
|
693
|
+
def send(channel, message)
|
694
|
+
return false unless is_channel_valid channel
|
695
|
+
begin
|
696
|
+
@on_exception_callback.call(self, 'Message is null or empty') if(@on_exception_callback)
|
697
|
+
return
|
698
|
+
end if message.to_s.empty?
|
699
|
+
unless @permissions_table.empty?
|
700
|
+
hash = check_permissions 'send', channel
|
701
|
+
return false if hash == nil
|
702
|
+
end
|
703
|
+
message_id = (0...8).map{@str_set[rand(@str_set.length)]}.join
|
704
|
+
parts = message.bytes.to_a.each_slice(MAX_MESSAGE_SIZE).to_a
|
705
|
+
EM.next_tick {
|
706
|
+
parts.each_with_index { |part, index|
|
707
|
+
@socket.send "send;#{@app_key};#{@auth_token};#{channel};#{hash};#{message_id}_#{index+1}-#{parts.length}_#{part.pack('c*')}".to_json
|
708
|
+
}
|
709
|
+
}
|
710
|
+
return true
|
711
|
+
end
|
712
|
+
|
713
|
+
private
|
714
|
+
|
715
|
+
def reconnect
|
716
|
+
@reconnect_thread = Thread.new{
|
717
|
+
until @is_connected do
|
718
|
+
sleep RECONNECT_INTERVAL
|
719
|
+
connect @app_key, @auth_token unless @is_connected
|
720
|
+
end
|
721
|
+
}
|
722
|
+
@reconnect_thread.run
|
723
|
+
end
|
724
|
+
|
725
|
+
def check_connection_loop
|
726
|
+
@heartbeat_thread = Thread.new{
|
727
|
+
loop do
|
728
|
+
counter = 0
|
729
|
+
while counter < MAX_HEARTBEAT_INTERVAL
|
730
|
+
sleep 1
|
731
|
+
if @got_heartbeat
|
732
|
+
counter = 0
|
733
|
+
@got_heartbeat = false
|
734
|
+
end
|
735
|
+
counter += 1
|
736
|
+
end
|
737
|
+
if @is_connected
|
738
|
+
@is_connected = false
|
739
|
+
@is_disconnecting = false
|
740
|
+
@is_reconnecting = true
|
741
|
+
EM.stop_event_loop if EM.reactor_running?
|
742
|
+
@on_reconnecting_callback.call(self) if(@on_reconnecting_callback)
|
743
|
+
reconnect
|
744
|
+
@heartbeat_thread.kill if @heartbeat_thread
|
745
|
+
end
|
746
|
+
end
|
747
|
+
}
|
748
|
+
@heartbeat_thread.run
|
749
|
+
end
|
750
|
+
|
751
|
+
def check_permissions(reason, channel)
|
752
|
+
hash = @permissions_table[channel]
|
753
|
+
if hash == nil && !(channel=~ /^[\w\-\/]+:./).nil?
|
754
|
+
subchannel = channel[/^[\w\-\/]+:/]+'*'
|
755
|
+
puts "cp #{subchannel} z #{@permissions_table}"
|
756
|
+
hash = @permissions_table[subchannel]
|
757
|
+
end
|
758
|
+
if hash==nil
|
759
|
+
@on_exception_callback.call(self, "No permission found to #{reason} to the channel #{channel}") if (@on_exception_callback)
|
760
|
+
return
|
761
|
+
end
|
762
|
+
return hash
|
763
|
+
end
|
764
|
+
|
765
|
+
def is_channel_valid(channel, check_connection = true)
|
766
|
+
if check_connection
|
767
|
+
begin
|
768
|
+
@on_exception_callback.call(self, 'Not connected') if(@on_exception_callback)
|
769
|
+
return
|
770
|
+
end unless @is_connected
|
771
|
+
end
|
772
|
+
$1 if channel =~ /^[\w\-:\/.]+$/ or begin @on_exception_callback.call(self, "Channel name: #{channel} has invalid characters") if (@on_exception_callback)
|
773
|
+
return end
|
774
|
+
begin
|
775
|
+
@on_exception_callback.call(self, "Channel name size exceeds the limit of #{MAX_CHANNEL_NAME_SIZE} characters") if(@on_exception_callback)
|
776
|
+
return
|
777
|
+
end if channel.length > MAX_CHANNEL_NAME_SIZE
|
778
|
+
return true
|
779
|
+
end
|
780
|
+
|
781
|
+
|
782
|
+
|
783
|
+
def parse_message(message)
|
784
|
+
message = message.gsub("\\\"", "\"")
|
785
|
+
unless (message =~/^a?\["\{"ch":"(.*)","m":"([\s\S]*?)"\}"\]$/).nil?
|
786
|
+
channel = $1
|
787
|
+
raw_message = $2
|
788
|
+
unless (raw_message =~/^(.[^_]*)_(.[^-]*)-(.[^_]*)_([\s\S]*?)$/).nil?
|
789
|
+
if($2.to_i==1 && $3.to_i==1)
|
790
|
+
m = $4.gsub("\\\\n","\n").gsub("\\\"", "\"").gsub("\\\\\\\\", "\\").gsub(/\\"/, '"')
|
791
|
+
@channels[channel].on_received_message(self, m) if @channels[channel]
|
792
|
+
else
|
793
|
+
if @messages_buffer[$1].nil?
|
794
|
+
@messages_buffer[$1] = MultiMessage.new($3.to_i)
|
795
|
+
end
|
796
|
+
@messages_buffer[$1].set_part($2.to_i-1, $4)
|
797
|
+
if @messages_buffer[$1].is_ready
|
798
|
+
all = @messages_buffer[$1].get_all.gsub("\\\\n","\n").gsub("\\\"", "\"").gsub("\\\\\\\\", "\\").gsub(/\\"/, '"')
|
799
|
+
@messages_buffer[$1] = nil
|
800
|
+
@channels[channel].on_received_message(self, all) if @channels[channel]
|
801
|
+
end
|
802
|
+
end
|
803
|
+
return
|
804
|
+
end
|
805
|
+
@channels[channel].on_received_message(self, raw_message.gsub("\\\\n","\n").gsub("\\\"", "\"").gsub("\\\\\\\\", "\\").gsub(/\\"/, '"')) if @channels[channel]
|
806
|
+
end
|
807
|
+
unless (message =~ /^a\["\{"op":"([^"]+)",(.*)\}"\]$/).nil? #operation
|
808
|
+
operation = $1
|
809
|
+
params = $2
|
810
|
+
unless (params =~ /^\"up\":{1}(.*),\"set\":(.*)$/).nil? #validated
|
811
|
+
if $1 == 'null'
|
812
|
+
@permissions_table = Hash.new
|
813
|
+
else
|
814
|
+
@permissions_table = JSON.parse($1) if $1 != 'null'
|
815
|
+
end
|
816
|
+
#session_expiration_time = $2
|
817
|
+
check_connection_loop #starts a thread to watch for connection breaks
|
818
|
+
@is_connected = true
|
819
|
+
@is_connecting = false
|
820
|
+
if @is_reconnecting
|
821
|
+
@channels.each { |channel_name,channel|
|
822
|
+
if channel == nil
|
823
|
+
@channels.delete(channel_name)
|
824
|
+
next
|
825
|
+
end
|
826
|
+
if channel.subscribe_on_reconnected
|
827
|
+
channel.is_subscribing = true
|
828
|
+
channel.is_subscribed = false
|
829
|
+
unless @permissions_table.empty?
|
830
|
+
hash = check_permissions 'subscribe', channel.name
|
831
|
+
next if hash == nil
|
832
|
+
end
|
833
|
+
EM.next_tick {
|
834
|
+
@socket.send "subscribe;#{@app_key};#{@auth_token};#{channel_name};#{hash}".to_json
|
835
|
+
}
|
836
|
+
else
|
837
|
+
@channels.delete(channel_name)
|
838
|
+
end
|
839
|
+
}
|
840
|
+
@on_reconnected_callback.call(self) if(@on_reconnected_callback)
|
841
|
+
else
|
842
|
+
@on_connected_callback.call(self) if(@on_connected_callback)
|
843
|
+
end
|
844
|
+
@is_reconnecting = false
|
845
|
+
end
|
846
|
+
unless (params =~ /^\"ex\":(\{.*\})$/).nil? #error
|
847
|
+
error = JSON.parse($1)
|
848
|
+
@on_exception_callback.call(self, error['ex']) if(@on_exception_callback)
|
849
|
+
end
|
850
|
+
if operation=="ortc-subscribed"
|
851
|
+
channel = (JSON.parse "{#{params}}")["ch"]
|
852
|
+
@channels[channel].is_subscribing = false
|
853
|
+
@channels[channel].is_subscribed = true
|
854
|
+
@on_subscribed_callback.call(self, channel) if(@on_subscribed_callback)
|
855
|
+
end
|
856
|
+
if operation=="ortc-unsubscribed"
|
857
|
+
channel = (JSON.parse "{#{params}}")["ch"]
|
858
|
+
@channels[channel] = nil
|
859
|
+
@on_unsubscribed_callback.call(self, channel) if(@on_unsubscribed_callback)
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ORTC
|
2
|
+
|
3
|
+
class Channel
|
4
|
+
attr_accessor :is_subscribing, :is_subscribed, :subscribe_on_reconnected, :name
|
5
|
+
def initialize(name, on_message, is_subscribing, subscribe_on_reconnected = true)
|
6
|
+
@name = name
|
7
|
+
@is_subscribing = is_subscribing || false
|
8
|
+
@is_subscribed = false
|
9
|
+
@subscribe_on_reconnected = subscribe_on_reconnected
|
10
|
+
@on_message = on_message
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_received_message(sender, message)
|
14
|
+
@on_message.call(sender, @name, message) if @on_message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MultiMessage
|
19
|
+
attr_reader :total_parts, :ready_parts
|
20
|
+
def initialize(total_parts)
|
21
|
+
@total_parts = total_parts
|
22
|
+
@ready_parts = 0
|
23
|
+
@parts = Array.new total_parts
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_part(part_id, part)
|
27
|
+
@ready_parts+=1 if @parts[part_id]==nil
|
28
|
+
@parts[part_id] = part
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_ready
|
32
|
+
return true if @ready_parts == @total_parts
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_all
|
36
|
+
return @parts.join("")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ortc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Realtime.co
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: eventmachine
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.12.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.12.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.6.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faye-websocket
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.4.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.0
|
55
|
+
description: ORTC aka Realtime Cloud Messaging provides a standard software API for
|
56
|
+
sending and receiving data in real-time over the web.
|
57
|
+
email: ortc@realtime.co
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- CHANGELOG.md
|
63
|
+
- README.md
|
64
|
+
- example/ortc_example.rb
|
65
|
+
- example/ortc_example2.rb
|
66
|
+
- lib/ortc.rb
|
67
|
+
- lib/ortc/ortc_extensibility.rb
|
68
|
+
homepage: http://framework.realtime.co/messaging
|
69
|
+
licenses: []
|
70
|
+
metadata: {}
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 2.2.2
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: ORTC (Open Real-Time Connectivity) module for pub/sub communications.
|
91
|
+
test_files: []
|