pubnub 4.6.2 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pubnub might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +3 -1
- data/.github/workflows/commands-handler.yml +27 -0
- data/.github/workflows/release/versions.json +17 -0
- data/.github/workflows/release.yml +58 -0
- data/.github/workflows/run_acceptance_tests.yml +46 -0
- data/.gitignore +18 -0
- data/.pubnub.yml +30 -4
- data/.travis.yml +0 -4
- data/CHANGELOG.md +35 -8
- data/Gemfile +2 -0
- data/Gemfile.lock +42 -1
- data/VERSION +1 -1
- data/config/cucumber.yml +7 -0
- data/features/step_definitions/access_steps.rb +258 -0
- data/features/support/env.rb +15 -0
- data/features/support/helper.rb +39 -0
- data/features/support/hooks.rb +98 -0
- data/lib/pubnub/cbor.rb +182 -0
- data/lib/pubnub/client/events.rb +1 -1
- data/lib/pubnub/client/getters_setters.rb +5 -0
- data/lib/pubnub/client/paged_history.rb +144 -17
- data/lib/pubnub/client.rb +14 -5
- data/lib/pubnub/constants.rb +10 -7
- data/lib/pubnub/event.rb +21 -10
- data/lib/pubnub/events/grant_token.rb +172 -0
- data/lib/pubnub/events/revoke_token.rb +66 -0
- data/lib/pubnub/validators/client.rb +1 -0
- data/lib/pubnub/validators/common_validator.rb +17 -0
- data/lib/pubnub/validators/grant_token.rb +51 -0
- data/lib/pubnub/validators/revoke_token.rb +31 -0
- data/lib/pubnub/version.rb +1 -1
- data/lib/pubnub.rb +1 -0
- metadata +22 -4
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'pubnub'
|
2
|
+
|
3
|
+
token_with_all = "qEF2AkF0GmEI03xDdHRsGDxDcmVzpURjaGFuoWljaGFubmVsLTEY70NncnChb2NoYW5uZWxfZ3JvdXAtMQVDdXNyoENzcGOgRHV1aWShZnV1aWQtMRhoQ3BhdKVEY2hhbqFtXmNoYW5uZWwtXFMqJBjvQ2dycKF0XjpjaGFubmVsX2dyb3VwLVxTKiQFQ3VzcqBDc3BjoER1dWlkoWpedXVpZC1cUyokGGhEbWV0YaBEdXVpZHR0ZXN0LWF1dGhvcml6ZWQtdXVpZENzaWdYIPpU-vCe9rkpYs87YUrFNWkyNq8CVvmKwEjVinnDrJJc"
|
4
|
+
|
5
|
+
Given('I have a keyset with access manager enabled') do
|
6
|
+
expect(ENV['PAM_SUB_KEY']).not_to be_nil
|
7
|
+
expect(ENV['PAM_PUB_KEY']).not_to be_nil
|
8
|
+
expect(ENV['PAM_SEC_KEY']).not_to be_nil
|
9
|
+
@pn_configuration['subscribe_key'] = ENV['PAM_SUB_KEY']
|
10
|
+
@pn_configuration['publish_key'] = ENV['PAM_PUB_KEY']
|
11
|
+
@pn_configuration['secret_key'] = ENV['PAM_SEC_KEY']
|
12
|
+
logger = Logger.new(STDOUT)
|
13
|
+
logger.level = Logger::DEBUG
|
14
|
+
@pn_configuration['logger'] = logger
|
15
|
+
@pn_configuration['uuid'] = Pubnub::UUID.generate
|
16
|
+
|
17
|
+
@pubnub = Pubnub.new(@pn_configuration)
|
18
|
+
end
|
19
|
+
|
20
|
+
Given('I have a keyset with access manager enabled - without secret key') do
|
21
|
+
expect(ENV['PAM_SUB_KEY']).not_to be_nil
|
22
|
+
expect(ENV['PAM_PUB_KEY']).not_to be_nil
|
23
|
+
@pn_configuration['subscribe_key'] = ENV['PAM_SUB_KEY']
|
24
|
+
@pn_configuration['publish_key'] = ENV['PAM_PUB_KEY']
|
25
|
+
logger = Logger.new(STDOUT)
|
26
|
+
logger.level = Logger::DEBUG
|
27
|
+
@pn_configuration['logger'] = logger
|
28
|
+
@pn_configuration['uuid'] = Pubnub::UUID.generate
|
29
|
+
|
30
|
+
@pubnub = Pubnub.new(@pn_configuration)
|
31
|
+
end
|
32
|
+
|
33
|
+
Given('the authorized UUID {string}') do |uuid|
|
34
|
+
@grant_token_state[:authorized_uuid] = uuid
|
35
|
+
end
|
36
|
+
|
37
|
+
Given('the TTL {int}') do |ttl|
|
38
|
+
@grant_token_state[:ttl] = ttl
|
39
|
+
end
|
40
|
+
|
41
|
+
Given('the {string} {resourceType} resource access permissions') do |name, resource_type|
|
42
|
+
@grant_token_state[:current_name] = name
|
43
|
+
|
44
|
+
@grant_token_state[:current_grant][name] = {
|
45
|
+
:name => name,
|
46
|
+
:resource_type => resource_type,
|
47
|
+
:permission_type => []
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
And('grant resource permission {permissionType}') do |permission_type|
|
52
|
+
current_name = @grant_token_state[:current_name]
|
53
|
+
@grant_token_state[:current_grant][current_name][:permission_type].push(permission_type)
|
54
|
+
end
|
55
|
+
|
56
|
+
Given('the {string} {resourceType} pattern access permissions') do |pattern, resource_type|
|
57
|
+
@grant_token_state[:current_pattern] = pattern
|
58
|
+
@grant_token_state[:current_grant][pattern] = {
|
59
|
+
:pattern => pattern,
|
60
|
+
:resource_type => resource_type,
|
61
|
+
:permission_type => []
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
Given('a valid token with permissions to publish with channel {string}') do |string|
|
66
|
+
@grant_token_state[:token] = token_with_all
|
67
|
+
end
|
68
|
+
|
69
|
+
Given('a token') do
|
70
|
+
@grant_token_state[:token] = token_with_all
|
71
|
+
end
|
72
|
+
|
73
|
+
Given('an expired token with permissions to publish with channel {string}') do |string|
|
74
|
+
@grant_token_state[:token] = token_with_all
|
75
|
+
end
|
76
|
+
|
77
|
+
And('grant pattern permission {permissionType}') do |permission_type|
|
78
|
+
current_pattern = @grant_token_state[:current_pattern]
|
79
|
+
@grant_token_state[:current_grant][current_pattern][:permission_type].push(permission_type)
|
80
|
+
end
|
81
|
+
|
82
|
+
When('I grant a token specifying those permissions') do
|
83
|
+
res = call_grant_token(@pubnub, @grant_token_state)
|
84
|
+
@grant_token_state[:parsed_token] = @pubnub.parse_token(res.result[:data]["token"])
|
85
|
+
end
|
86
|
+
|
87
|
+
When('I publish a message using that auth token with channel {string}') do |channel|
|
88
|
+
@pubnub.set_token(@grant_token_state[:token])
|
89
|
+
res = @pubnub.publish(
|
90
|
+
message: "This is message",
|
91
|
+
channel: channel,
|
92
|
+
http_sync: true
|
93
|
+
)
|
94
|
+
@global_state[:last_call_res] = res
|
95
|
+
end
|
96
|
+
|
97
|
+
When('I attempt to publish a message using that auth token with channel {string}') do |channel|
|
98
|
+
@pubnub.set_token(@grant_token_state[:token])
|
99
|
+
res = @pubnub.publish(
|
100
|
+
message: "This is message",
|
101
|
+
channel: channel,
|
102
|
+
http_sync: true
|
103
|
+
)
|
104
|
+
@global_state[:last_call_res] = res
|
105
|
+
end
|
106
|
+
|
107
|
+
When('I revoke a token') do
|
108
|
+
res = @pubnub.revoke_token(
|
109
|
+
token: @grant_token_state[:token],
|
110
|
+
http_sync: true
|
111
|
+
)
|
112
|
+
@global_state[:last_call_res] = res
|
113
|
+
end
|
114
|
+
|
115
|
+
Then('the token contains the authorized UUID {string}') do |expected_uuid|
|
116
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq expected_uuid
|
117
|
+
end
|
118
|
+
|
119
|
+
Then('the token contains the TTL {int}') do |ttl|
|
120
|
+
expect(@grant_token_state[:parsed_token]["ttl"]).to eq ttl
|
121
|
+
end
|
122
|
+
|
123
|
+
Then('the token has {string} {resourceType} resource access permissions') do |resource, resource_type|
|
124
|
+
case resource_type
|
125
|
+
when "CHANNEL"
|
126
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["chan"][resource]
|
127
|
+
when "CHANNEL_GROUP"
|
128
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["grp"][resource]
|
129
|
+
when "UUID"
|
130
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["res"]["uuid"][resource]
|
131
|
+
else
|
132
|
+
fail
|
133
|
+
end
|
134
|
+
|
135
|
+
expect(@grant_token_state[:token_resource]).not_to eq nil
|
136
|
+
end
|
137
|
+
|
138
|
+
Then('token resource permission {permissionType}') do |permission_type|
|
139
|
+
expect(has_permission(@grant_token_state[:token_resource], permission_type)).to eq true
|
140
|
+
end
|
141
|
+
|
142
|
+
Then('the token has {string} {resourceType} pattern access permissions') do |pattern, resourceType|
|
143
|
+
case resourceType
|
144
|
+
when "CHANNEL"
|
145
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["chan"][pattern]
|
146
|
+
when "CHANNEL_GROUP"
|
147
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["grp"][pattern]
|
148
|
+
when "UUID"
|
149
|
+
@grant_token_state[:token_resource] = @grant_token_state[:parsed_token]["pat"]["uuid"][pattern]
|
150
|
+
else
|
151
|
+
fail
|
152
|
+
end
|
153
|
+
|
154
|
+
expect(@grant_token_state[:token_resource]).not_to eq nil
|
155
|
+
end
|
156
|
+
|
157
|
+
And('token pattern permission {permissionType}') do |permission_type|
|
158
|
+
expect(has_permission(@grant_token_state[:token_resource], permission_type)).to eq true
|
159
|
+
end
|
160
|
+
|
161
|
+
Then('the token does not contain an authorized uuid') do
|
162
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq nil
|
163
|
+
end
|
164
|
+
|
165
|
+
Given('deny resource permission {permissionType}') do |permission_type|
|
166
|
+
current_name = @grant_token_state[:current_name]
|
167
|
+
@grant_token_state[:current_grant][current_name][:permission_type].filter { |p| p != permission_type }
|
168
|
+
end
|
169
|
+
|
170
|
+
When('I attempt to grant a token specifying those permissions') do
|
171
|
+
@global_state[:last_call_res] = call_grant_token(@pubnub, @grant_token_state)
|
172
|
+
end
|
173
|
+
|
174
|
+
Then('an error is returned') do
|
175
|
+
envelope = @global_state[:last_call_res]
|
176
|
+
expect(envelope).not_to eq nil
|
177
|
+
expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq true
|
178
|
+
end
|
179
|
+
|
180
|
+
Then('the error status code is {int}') do |code|
|
181
|
+
expect(@global_state[:last_call_res].status[:code]).to eq code
|
182
|
+
end
|
183
|
+
|
184
|
+
Then('the error message is {string}') do |error_message|
|
185
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["message"]).to eq error_message
|
186
|
+
end
|
187
|
+
|
188
|
+
Then('the error source is {string}') do |error_source|
|
189
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["source"]).to eq error_source
|
190
|
+
end
|
191
|
+
|
192
|
+
Then('the error detail message is {string}') do |details_message|
|
193
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["details"][0]["message"]).to eq details_message
|
194
|
+
end
|
195
|
+
|
196
|
+
Then('the error detail location is {string}') do |details_location|
|
197
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["details"][0]["location"]).to eq details_location
|
198
|
+
end
|
199
|
+
|
200
|
+
Then('the error detail location type is {string}') do |location_type|
|
201
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["details"][0]["locationType"]).to eq location_type
|
202
|
+
end
|
203
|
+
|
204
|
+
Given('I have a known token containing an authorized UUID') do
|
205
|
+
@grant_token_state[:token] = token_with_all
|
206
|
+
end
|
207
|
+
|
208
|
+
When('I parse the token') do
|
209
|
+
@grant_token_state[:parsed_token] = @pubnub.parse_token(@grant_token_state[:token])
|
210
|
+
end
|
211
|
+
|
212
|
+
Then('the parsed token output contains the authorized UUID {string}') do |expected_uuid|
|
213
|
+
expect(@grant_token_state[:parsed_token]["uuid"]).to eq expected_uuid
|
214
|
+
end
|
215
|
+
|
216
|
+
Given('I have a known token containing UUID resource permissions') do
|
217
|
+
@grant_token_state[:token] = token_with_all
|
218
|
+
end
|
219
|
+
|
220
|
+
Given('I have a known token containing UUID pattern Permissions') do
|
221
|
+
@grant_token_state[:token] = token_with_all
|
222
|
+
end
|
223
|
+
|
224
|
+
Given('the token string {string}') do |token|
|
225
|
+
@grant_token_state[:token] = token
|
226
|
+
end
|
227
|
+
|
228
|
+
Then('the result is successful') do
|
229
|
+
envelope = @global_state[:last_call_res]
|
230
|
+
expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false
|
231
|
+
expect(envelope.status[:code]).to eq 200
|
232
|
+
end
|
233
|
+
|
234
|
+
Then('an auth error is returned') do
|
235
|
+
envelope = @global_state[:last_call_res]
|
236
|
+
expect(envelope).not_to eq nil
|
237
|
+
expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq true
|
238
|
+
end
|
239
|
+
|
240
|
+
Then('the auth error message is {string}') do |error_message|
|
241
|
+
expect(parse_error_body(@global_state[:last_call_res])["message"]).to eq error_message
|
242
|
+
end
|
243
|
+
|
244
|
+
Then('the error service is {string}') do |service|
|
245
|
+
expect(parse_error_body(@global_state[:last_call_res])["service"]).to eq service
|
246
|
+
end
|
247
|
+
|
248
|
+
Then('I get confirmation that token has been revoked') do
|
249
|
+
envelope = @global_state[:last_call_res]
|
250
|
+
expect(envelope.is_a?(Pubnub::ErrorEnvelope)).to eq false
|
251
|
+
expect(envelope.status[:code]).to eq 200
|
252
|
+
end
|
253
|
+
|
254
|
+
Then('the error detail message is not empty') do
|
255
|
+
expect(parse_error_body(@global_state[:last_call_res])["error"]["message"].empty?).to eq false
|
256
|
+
end
|
257
|
+
|
258
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ParameterType(
|
2
|
+
name: 'resourceType',
|
3
|
+
regexp: /CHANNEL|CHANNEL_GROUP|UUID/,
|
4
|
+
type: String,
|
5
|
+
transformer: ->(s) { s }
|
6
|
+
)
|
7
|
+
|
8
|
+
ParameterType(
|
9
|
+
name: 'permissionType',
|
10
|
+
regexp: /READ|WRITE|GET|MANAGE|UPDATE|JOIN|DELETE|/,
|
11
|
+
type: String,
|
12
|
+
transformer: ->(s) { s }
|
13
|
+
)
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
def has_permission(perms, permission_type)
|
2
|
+
current_perm = nil
|
3
|
+
eval "current_perm = Pubnub::Permissions.res(#{permission_type.downcase}: true).calculate_bitmask"
|
4
|
+
perms & current_perm != 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def prepare_permissions_map(grants, resource_type)
|
8
|
+
grants
|
9
|
+
.select { |_, value|
|
10
|
+
value[:resource_type] == resource_type
|
11
|
+
}
|
12
|
+
.to_h { |name, value|
|
13
|
+
empty_permissions = if value[:pattern]
|
14
|
+
Pubnub::Permissions.pat
|
15
|
+
else
|
16
|
+
Pubnub::Permissions.res
|
17
|
+
end
|
18
|
+
permissions = value[:permission_type].reduce(empty_permissions) { |accumulated_permissions, string_perm|
|
19
|
+
accumulated_permissions.instance_variable_set("@" + string_perm.downcase, true)
|
20
|
+
accumulated_permissions
|
21
|
+
}
|
22
|
+
[name, permissions]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def call_grant_token(pubnub, grant_token_state)
|
27
|
+
pubnub.grant_token(
|
28
|
+
ttl: grant_token_state[:ttl],
|
29
|
+
http_sync: true,
|
30
|
+
channels: prepare_permissions_map(grant_token_state[:current_grant], "CHANNEL"),
|
31
|
+
channel_groups: prepare_permissions_map(grant_token_state[:current_grant], "CHANNEL_GROUP"),
|
32
|
+
uuids: prepare_permissions_map(grant_token_state[:current_grant], "UUID"),
|
33
|
+
authorized_uuid: grant_token_state[:authorized_uuid]
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_error_body(error_response)
|
38
|
+
Pubnub::Formatter.parse_json(error_response.status[:server_response].body)[0]
|
39
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
Before do |scenario|
|
5
|
+
@grant_token_state = {}
|
6
|
+
@global_state = {}
|
7
|
+
@grant_token_state[:current_grant] = {}
|
8
|
+
@pn_configuration = {}
|
9
|
+
|
10
|
+
when_mock_server_used {
|
11
|
+
puts "Using mock"
|
12
|
+
expect(ENV['SERVER_HOST']).not_to be_nil
|
13
|
+
expect(ENV['SERVER_PORT']).not_to be_nil
|
14
|
+
@pn_configuration = {
|
15
|
+
origin: ENV['SERVER_HOST'] + ":" + ENV['SERVER_PORT'],
|
16
|
+
isSecure: false,
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
when_mock_server_used {
|
21
|
+
init_mock(scenario)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
After do |scenario|
|
26
|
+
when_mock_server_used {
|
27
|
+
expect_mock(scenario)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def when_mock_server_used(&block)
|
32
|
+
if ENV['SERVER_MOCK']&.to_s&.downcase == 'true'
|
33
|
+
block.call
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def init_mock(scenario)
|
38
|
+
contract_name = contract_name(scenario)
|
39
|
+
if contract_name
|
40
|
+
call_init_endpoint(contract_name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_init_endpoint(contract_name)
|
45
|
+
Net::HTTP.get_response("#{ENV['SERVER_HOST']}", "/init?__contract__script__=#{contract_name}", "#{ENV['SERVER_PORT']}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def expect_mock(scenario)
|
49
|
+
contract_name = contract_name(scenario)
|
50
|
+
if contract_name
|
51
|
+
expect_result = call_expect_endpoint()
|
52
|
+
print(expect_result)
|
53
|
+
if still_pending?(expect_result) || some_failed?(expect_result)
|
54
|
+
message = "Scenario #{extract_contract(expect_result)} considered failure:
|
55
|
+
pending - #{extract_pending(expect_result)},
|
56
|
+
failed - #{extract_failed(expect_result)}"
|
57
|
+
RSpec::Expectations.fail_with(message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_contract(expect_result)
|
63
|
+
expect_result['contract']
|
64
|
+
end
|
65
|
+
|
66
|
+
def extract_pending(expect_result)
|
67
|
+
expect_result['expectations']['pending'].join(", ")
|
68
|
+
end
|
69
|
+
|
70
|
+
def extract_failed(expect_result)
|
71
|
+
expect_result['expectations']['failed'].join(", ")
|
72
|
+
end
|
73
|
+
|
74
|
+
def still_pending?(expect_result)
|
75
|
+
pending = expect_result['expectations']['pending']
|
76
|
+
not pending.empty?
|
77
|
+
end
|
78
|
+
|
79
|
+
def some_failed?(expect_result)
|
80
|
+
pending = expect_result['expectations']['failed']
|
81
|
+
not pending.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
def call_expect_endpoint
|
85
|
+
expect_response = Net::HTTP.get_response("#{ENV['SERVER_HOST']}", "/expect", "#{ENV['SERVER_PORT']}")
|
86
|
+
|
87
|
+
if expect_response == nil || expect_response.body == nil
|
88
|
+
RSpec::Expectations.fail_with("Expect response body is null")
|
89
|
+
else
|
90
|
+
JSON.parse(expect_response.body)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def contract_name(scenario)
|
95
|
+
scenario.source_tag_names&.
|
96
|
+
select { |tagName| tagName.start_with?("@contract") }&.
|
97
|
+
map { |tagName| tagName.split("=")[1] }&.first
|
98
|
+
end
|
data/lib/pubnub/cbor.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
module Pubnub
|
2
|
+
class Cbor
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
TYPE_MASK = 0b11100000
|
7
|
+
ADDITIONAL_MASK = 0b00011111
|
8
|
+
|
9
|
+
TYPE_UNSIGNED_INT = 0b00000000
|
10
|
+
TYPE_NEGATIVE_INT = 0b00100000
|
11
|
+
TYPE_BYTE_STRING = 0b01000000
|
12
|
+
TYPE_TEXT_STRING = 0b01100000
|
13
|
+
TYPE_ARRAY = 0b10000000
|
14
|
+
TYPE_HASHMAP = 0b10100000
|
15
|
+
TYPE_TAG = 0b11000000
|
16
|
+
TYPE_FLOAT = 0b11100000
|
17
|
+
|
18
|
+
ADDITIONAL_TYPE_INDEFINITE = 31
|
19
|
+
INDEFINITE_BREAK = 0b11111111
|
20
|
+
|
21
|
+
ADDITIONAL_LENGTH_1B = 24
|
22
|
+
ADDITIONAL_LENGTH_2B = 25
|
23
|
+
ADDITIONAL_LENGTH_4B = 26
|
24
|
+
ADDITIONAL_LENGTH_8B = 27
|
25
|
+
|
26
|
+
ADDITIONAL_LENGTH_BYTES = {
|
27
|
+
ADDITIONAL_LENGTH_1B => 1,
|
28
|
+
ADDITIONAL_LENGTH_2B => 2,
|
29
|
+
ADDITIONAL_LENGTH_4B => 4,
|
30
|
+
ADDITIONAL_LENGTH_8B => 8,
|
31
|
+
}
|
32
|
+
|
33
|
+
SIMPLE_VALUE_FALSE = 0xF4
|
34
|
+
SIMPLE_VALUE_TRUE = 0xF5
|
35
|
+
SIMPLE_VALUE_NULL = 0xF6
|
36
|
+
SIMPLE_VALUE_UNDEF = 0xF7
|
37
|
+
|
38
|
+
def bytearray_to_i(byte_array)
|
39
|
+
byte_array.reverse_each.map.with_index do |b, i|
|
40
|
+
b << 8 * i
|
41
|
+
end.reduce(0) do |reduced, byte|
|
42
|
+
reduced | byte
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def decode_integer(data, additional)
|
47
|
+
if ADDITIONAL_LENGTH_BYTES.member?(additional)
|
48
|
+
bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
49
|
+
else
|
50
|
+
additional
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decode_float(data, additional)
|
55
|
+
if additional <= 23
|
56
|
+
additional
|
57
|
+
else
|
58
|
+
bytes = bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
59
|
+
case (additional)
|
60
|
+
when ADDITIONAL_LENGTH_1B
|
61
|
+
bytes
|
62
|
+
when ADDITIONAL_LENGTH_2B
|
63
|
+
sign = (bytes >> 15) != 0 ? -1 : 1
|
64
|
+
exp = (bytes & 0b0111110000000000) >> 10
|
65
|
+
mant = bytes & 0b1111111111
|
66
|
+
if exp == 0
|
67
|
+
result = (2 ** -14) * (mant / 1024.to_f)
|
68
|
+
elsif exp == 0b11111
|
69
|
+
result = Float::INFINITY
|
70
|
+
else
|
71
|
+
result = (2 ** (exp - 15)) * (1 + mant / 1024.to_f)
|
72
|
+
end
|
73
|
+
sign * result
|
74
|
+
when ADDITIONAL_LENGTH_4B
|
75
|
+
sign = (bytes >> 31) != 0 ? -1 : 1
|
76
|
+
x = (bytes & ((1 << 23) - 1)) + (1 << 23)
|
77
|
+
exp = (bytes >> 23 & 0xFF) - 127
|
78
|
+
x * (2 ** (exp - 23)) * sign
|
79
|
+
when ADDITIONAL_LENGTH_8B
|
80
|
+
sign = (bytes >> 63) != 0 ? -1 : 1
|
81
|
+
exp = (bytes >> 52) & 0x7FF
|
82
|
+
|
83
|
+
mant = bytes & 0xFFFFFFFFFFFFF
|
84
|
+
|
85
|
+
if 0 == exp
|
86
|
+
val = mant * 2 ** (-(1022 + 52))
|
87
|
+
elsif 0b11111111111 != exp
|
88
|
+
val = (mant + (1 << 52)) * 2 ** (exp - (1023 + 52))
|
89
|
+
else
|
90
|
+
val = 0 == mant ? Float::INFINITY : Float::NAN
|
91
|
+
end
|
92
|
+
sign * val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def indefinite_data(data)
|
98
|
+
result = []
|
99
|
+
|
100
|
+
loop do
|
101
|
+
byte = data.shift
|
102
|
+
break if byte == INDEFINITE_BREAK
|
103
|
+
result.append(byte)
|
104
|
+
break if data.empty?
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def compute_length(data, additional)
|
110
|
+
if ADDITIONAL_LENGTH_BYTES.member?(additional)
|
111
|
+
bytearray_to_i(data.shift(ADDITIONAL_LENGTH_BYTES[additional]))
|
112
|
+
else
|
113
|
+
additional
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def decode_string(data, additional)
|
118
|
+
if additional == ADDITIONAL_TYPE_INDEFINITE
|
119
|
+
indefinite_data(data).pack('C*').force_encoding('UTF-8')
|
120
|
+
else
|
121
|
+
length = compute_length(data, additional)
|
122
|
+
data.shift(length).pack('C*').force_encoding('UTF-8')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def decode_map(data, additional)
|
127
|
+
length = compute_length(data, additional)
|
128
|
+
result = Hash.new
|
129
|
+
(1..length).each { result.store(parse_data(data), parse_data(data)) }
|
130
|
+
result
|
131
|
+
end
|
132
|
+
|
133
|
+
def decode_array(data, additional)
|
134
|
+
length = compute_length(data, additional)
|
135
|
+
(1..length).map { parse_data(data) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_data(data)
|
139
|
+
byte = data.shift
|
140
|
+
|
141
|
+
case (byte)
|
142
|
+
when SIMPLE_VALUE_NULL
|
143
|
+
return nil
|
144
|
+
when SIMPLE_VALUE_TRUE
|
145
|
+
return true
|
146
|
+
when SIMPLE_VALUE_FALSE
|
147
|
+
return false
|
148
|
+
when SIMPLE_VALUE_UNDEF
|
149
|
+
return nil
|
150
|
+
else
|
151
|
+
type = byte & TYPE_MASK
|
152
|
+
additional = byte & ADDITIONAL_MASK
|
153
|
+
|
154
|
+
case (type)
|
155
|
+
when TYPE_NEGATIVE_INT
|
156
|
+
-1 - decode_integer(data, additional)
|
157
|
+
when TYPE_UNSIGNED_INT
|
158
|
+
decode_integer(data, additional)
|
159
|
+
when TYPE_FLOAT
|
160
|
+
decode_float(data, additional).to_f
|
161
|
+
when TYPE_BYTE_STRING
|
162
|
+
decode_string(data, additional)
|
163
|
+
when TYPE_TEXT_STRING
|
164
|
+
decode_string(data, additional)
|
165
|
+
when TYPE_ARRAY
|
166
|
+
decode_array(data, additional)
|
167
|
+
when TYPE_HASHMAP
|
168
|
+
decode_map(data, additional)
|
169
|
+
else
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
public
|
176
|
+
|
177
|
+
def decode(value)
|
178
|
+
parse_data(value)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
data/lib/pubnub/client/events.rb
CHANGED
@@ -4,7 +4,7 @@ module Pubnub
|
|
4
4
|
class Client
|
5
5
|
# Module that holds generator for all events
|
6
6
|
module Events
|
7
|
-
EVENTS = %w[publish subscribe presence leave history here_now audit grant delete_messages
|
7
|
+
EVENTS = %w[publish subscribe presence leave history here_now audit grant grant_token revoke_token delete_messages
|
8
8
|
revoke time heartbeat where_now set_state state channel_registration message_counts signal
|
9
9
|
add_channels_to_push list_push_provisions remove_channels_from_push remove_device_from_push
|
10
10
|
set_uuid_metadata set_channel_metadata remove_uuid_metadata remove_channel_metadata
|
@@ -1,9 +1,12 @@
|
|
1
|
+
require 'pubnub/validators/common_validator'
|
2
|
+
|
1
3
|
# Toplevel Pubnub module.
|
2
4
|
module Pubnub
|
3
5
|
# Pubnub client Class
|
4
6
|
class Client
|
5
7
|
# Module that holds some getters and setters
|
6
8
|
module GettersSetters
|
9
|
+
|
7
10
|
def sdk_version
|
8
11
|
"PubNub-Ruby/#{Pubnub::VERSION}"
|
9
12
|
end
|
@@ -25,6 +28,8 @@ module Pubnub
|
|
25
28
|
def change_uuid(uuid)
|
26
29
|
Pubnub.logger.debug('Pubnub::Client') { 'Changing uuid' }
|
27
30
|
raise('Cannot change UUID while subscribed.') if subscribed?
|
31
|
+
Validator::Client.validate_uuid uuid
|
32
|
+
|
28
33
|
@env[:uuid] = uuid
|
29
34
|
end
|
30
35
|
|