ably-rest 0.7.1 → 0.7.3
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 +13 -5
- data/.gitmodules +1 -1
- data/.rspec +1 -0
- data/.travis.yml +7 -3
- data/SPEC.md +495 -419
- data/ably-rest.gemspec +19 -5
- data/lib/ably-rest.rb +9 -1
- data/lib/submodules/ably-ruby/.gitignore +6 -0
- data/lib/submodules/ably-ruby/.rspec +1 -0
- data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -0
- data/lib/submodules/ably-ruby/Gemfile +4 -0
- data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
- data/lib/submodules/ably-ruby/README.md +122 -0
- data/lib/submodules/ably-ruby/Rakefile +34 -0
- data/lib/submodules/ably-ruby/SPEC.md +1794 -0
- data/lib/submodules/ably-ruby/ably.gemspec +36 -0
- data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
- data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
- data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
- data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
- data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
- data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
- data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
- metadata +182 -27
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
|
|
7
|
+
describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
8
|
+
vary_by_protocol do
|
|
9
|
+
let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) }
|
|
10
|
+
let(:client_options) { default_options }
|
|
11
|
+
let(:client) do
|
|
12
|
+
Ably::Realtime::Client.new(client_options)
|
|
13
|
+
end
|
|
14
|
+
let(:channel) { client.channel(channel_name) }
|
|
15
|
+
|
|
16
|
+
let(:other_client) do
|
|
17
|
+
Ably::Realtime::Client.new(client_options)
|
|
18
|
+
end
|
|
19
|
+
let(:other_client_channel) { other_client.channel(channel_name) }
|
|
20
|
+
|
|
21
|
+
let(:channel_name) { "subscribe_send_text-#{random_str}" }
|
|
22
|
+
let(:options) { { :protocol => :json } }
|
|
23
|
+
let(:payload) { 'Test message (subscribe_send_text)' }
|
|
24
|
+
|
|
25
|
+
it 'sends a String data payload' do
|
|
26
|
+
channel.attach
|
|
27
|
+
channel.on(:attached) do
|
|
28
|
+
channel.publish('test_event', payload) do |message|
|
|
29
|
+
expect(message.data).to eql(payload)
|
|
30
|
+
stop_reactor
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context 'with ASCII_8BIT message name' do
|
|
36
|
+
let(:message_name) { random_str.encode(Encoding::ASCII_8BIT) }
|
|
37
|
+
it 'is converted into UTF_8' do
|
|
38
|
+
channel.attach do
|
|
39
|
+
channel.publish message_name, payload
|
|
40
|
+
end
|
|
41
|
+
channel.subscribe do |message|
|
|
42
|
+
expect(message.name.encoding).to eql(Encoding::UTF_8)
|
|
43
|
+
expect(message.name.encode(Encoding::ASCII_8BIT)).to eql(message_name)
|
|
44
|
+
stop_reactor
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'when the message publisher has a client_id' do
|
|
50
|
+
let(:client_id) { random_str }
|
|
51
|
+
let(:client_options) { default_options.merge(client_id: client_id) }
|
|
52
|
+
|
|
53
|
+
it 'contains a #client_id attribute' do
|
|
54
|
+
when_all(channel.attach, other_client_channel.attach) do
|
|
55
|
+
other_client_channel.subscribe('event') do |message|
|
|
56
|
+
expect(message.client_id).to eql(client_id)
|
|
57
|
+
stop_reactor
|
|
58
|
+
end
|
|
59
|
+
channel.publish('event', payload)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe '#connection_id attribute' do
|
|
65
|
+
context 'over realtime' do
|
|
66
|
+
it 'matches the sender connection#id' do
|
|
67
|
+
when_all(channel.attach, other_client_channel.attach) do
|
|
68
|
+
other_client_channel.subscribe('event') do |message|
|
|
69
|
+
expect(message.connection_id).to eql(client.connection.id)
|
|
70
|
+
stop_reactor
|
|
71
|
+
end
|
|
72
|
+
channel.publish('event', payload)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'when retrieved over REST' do
|
|
78
|
+
it 'matches the sender connection#id' do
|
|
79
|
+
channel.publish('event', payload) do
|
|
80
|
+
channel.history do |messages|
|
|
81
|
+
expect(messages.first.connection_id).to eql(client.connection.id)
|
|
82
|
+
stop_reactor
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe 'local echo when published' do
|
|
90
|
+
it 'is enabled by default' do
|
|
91
|
+
channel.attach do
|
|
92
|
+
channel.publish 'test_event', payload
|
|
93
|
+
channel.subscribe('test_event') do |message|
|
|
94
|
+
expect(message.data).to eql(payload)
|
|
95
|
+
stop_reactor
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'with :echo_messages option set to false' do
|
|
101
|
+
let(:no_echo_client) do
|
|
102
|
+
Ably::Realtime::Client.new(default_options.merge(echo_messages: false))
|
|
103
|
+
end
|
|
104
|
+
let(:no_echo_channel) { no_echo_client.channel(channel_name) }
|
|
105
|
+
|
|
106
|
+
it 'will not echo messages to the client but will still broadcast messages to other connected clients', em_timeout: 10 do
|
|
107
|
+
channel.attach do |echo_channel|
|
|
108
|
+
no_echo_channel.attach do
|
|
109
|
+
no_echo_channel.publish 'test_event', payload
|
|
110
|
+
|
|
111
|
+
no_echo_channel.subscribe('test_event') do |message|
|
|
112
|
+
fail "Message should not have been echoed back"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
echo_channel.subscribe('test_event') do |message|
|
|
116
|
+
expect(message.data).to eql(payload)
|
|
117
|
+
EventMachine.add_timer(1) do
|
|
118
|
+
stop_reactor
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context 'publishing lots of messages across two connections' do
|
|
128
|
+
let(:send_count) { 30 }
|
|
129
|
+
let(:expected_echos) { send_count * 2 }
|
|
130
|
+
let(:channel_name) { random_str }
|
|
131
|
+
let(:echos) do
|
|
132
|
+
{ client: 0, other: 0 }
|
|
133
|
+
end
|
|
134
|
+
let(:callbacks) do
|
|
135
|
+
{ client: 0, other: 0 }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'sends and receives the messages on both opened connections and calls the success callbacks for each message published', em_timeout: 10 do
|
|
139
|
+
check_message_and_callback_counts = Proc.new do
|
|
140
|
+
if echos[:client] == expected_echos && echos[:other] == expected_echos
|
|
141
|
+
# Wait for message backlog to clear
|
|
142
|
+
EventMachine.add_timer(0.5) do
|
|
143
|
+
expect(echos[:client]).to eql(expected_echos)
|
|
144
|
+
expect(echos[:other]).to eql(expected_echos)
|
|
145
|
+
|
|
146
|
+
expect(callbacks[:client]).to eql(send_count)
|
|
147
|
+
expect(callbacks[:other]).to eql(send_count)
|
|
148
|
+
|
|
149
|
+
stop_reactor
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
channel.subscribe('test_event') do |message|
|
|
155
|
+
echos[:client] += 1
|
|
156
|
+
check_message_and_callback_counts.call
|
|
157
|
+
end
|
|
158
|
+
other_client_channel.subscribe('test_event') do |message|
|
|
159
|
+
echos[:other] += 1
|
|
160
|
+
check_message_and_callback_counts.call
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
when_all(channel.attach, other_client_channel.attach) do
|
|
164
|
+
send_count.times do |index|
|
|
165
|
+
channel.publish('test_event', "#{index}: #{payload}") do
|
|
166
|
+
callbacks[:client] += 1
|
|
167
|
+
end
|
|
168
|
+
other_client_channel.publish('test_event', "#{index}: #{payload}") do
|
|
169
|
+
callbacks[:other] += 1
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context 'without suitable publishing permissions' do
|
|
177
|
+
let(:restricted_client) do
|
|
178
|
+
Ably::Realtime::Client.new(options.merge(api_key: restricted_api_key, environment: environment, protocol: protocol))
|
|
179
|
+
end
|
|
180
|
+
let(:restricted_channel) { restricted_client.channel("cansubscribe:example") }
|
|
181
|
+
let(:payload) { 'Test message without permission to publish' }
|
|
182
|
+
|
|
183
|
+
it 'calls the error callback' do
|
|
184
|
+
restricted_channel.attach do
|
|
185
|
+
deferrable = restricted_channel.publish('test_event', payload)
|
|
186
|
+
deferrable.errback do |message, error|
|
|
187
|
+
expect(message.data).to eql(payload)
|
|
188
|
+
expect(error.status).to eql(401)
|
|
189
|
+
stop_reactor
|
|
190
|
+
end
|
|
191
|
+
deferrable.callback do |message|
|
|
192
|
+
fail 'Success callback should not have been called'
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context 'encoding and decoding encrypted messages' do
|
|
199
|
+
shared_examples 'an Ably encrypter and decrypter' do |item, data|
|
|
200
|
+
let(:algorithm) { data['algorithm'].upcase }
|
|
201
|
+
let(:mode) { data['mode'].upcase }
|
|
202
|
+
let(:key_length) { data['keylength'] }
|
|
203
|
+
let(:secret_key) { Base64.decode64(data['key']) }
|
|
204
|
+
let(:iv) { Base64.decode64(data['iv']) }
|
|
205
|
+
|
|
206
|
+
let(:cipher_options) { { key: secret_key, iv: iv, algorithm: algorithm, mode: mode, key_length: key_length } }
|
|
207
|
+
|
|
208
|
+
context 'with #publish and #subscribe' do
|
|
209
|
+
let(:encoded) { item['encoded'] }
|
|
210
|
+
let(:encoded_data) { encoded['data'] }
|
|
211
|
+
let(:encoded_encoding) { encoded['encoding'] }
|
|
212
|
+
let(:encoded_data_decoded) do
|
|
213
|
+
if encoded_encoding == 'json'
|
|
214
|
+
JSON.parse(encoded_data)
|
|
215
|
+
elsif encoded_encoding == 'base64'
|
|
216
|
+
Base64.decode64(encoded_data)
|
|
217
|
+
else
|
|
218
|
+
encoded_data
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
let(:encrypted) { item['encrypted'] }
|
|
223
|
+
let(:encrypted_data) { encrypted['data'] }
|
|
224
|
+
let(:encrypted_encoding) { encrypted['encoding'] }
|
|
225
|
+
let(:encrypted_data_decoded) do
|
|
226
|
+
if encrypted_encoding.match(%r{/base64$})
|
|
227
|
+
Base64.decode64(encrypted_data)
|
|
228
|
+
else
|
|
229
|
+
encrypted_data
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
let(:encrypted_channel) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
234
|
+
|
|
235
|
+
it 'encrypts message automatically before they are pushed to the server' do
|
|
236
|
+
encrypted_channel.__incoming_msgbus__.unsubscribe # remove all subscribe callbacks that could decrypt the message
|
|
237
|
+
|
|
238
|
+
encrypted_channel.__incoming_msgbus__.subscribe(:message) do |message|
|
|
239
|
+
if protocol == :json
|
|
240
|
+
expect(message['encoding']).to eql(encrypted_encoding)
|
|
241
|
+
expect(message['data']).to eql(encrypted_data)
|
|
242
|
+
else
|
|
243
|
+
# Messages received over binary protocol will not have Base64 encoded data
|
|
244
|
+
expect(message['encoding']).to eql(encrypted_encoding.gsub(%r{/base64$}, ''))
|
|
245
|
+
expect(message['data']).to eql(encrypted_data_decoded)
|
|
246
|
+
end
|
|
247
|
+
stop_reactor
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
encrypted_channel.publish 'example', encoded_data_decoded
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it 'sends and receives messages that are encrypted & decrypted by the Ably library' do
|
|
254
|
+
encrypted_channel.publish 'example', encoded_data_decoded
|
|
255
|
+
encrypted_channel.subscribe do |message|
|
|
256
|
+
expect(message.data).to eql(encoded_data_decoded)
|
|
257
|
+
expect(message.encoding).to be_nil
|
|
258
|
+
stop_reactor
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
resources_root = File.expand_path('../../../resources', __FILE__)
|
|
265
|
+
|
|
266
|
+
def self.add_tests_for_data(data)
|
|
267
|
+
data['items'].each_with_index do |item, index|
|
|
268
|
+
context "item #{index} with encrypted encoding #{item['encrypted']['encoding']}" do
|
|
269
|
+
it_behaves_like 'an Ably encrypter and decrypter', item, data
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
context 'with AES-128-CBC using crypto-data-128.json fixtures' do
|
|
275
|
+
data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-128.json')))
|
|
276
|
+
add_tests_for_data data
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
context 'with AES-256-CBC using crypto-data-256.json fixtures' do
|
|
280
|
+
data = JSON.parse(File.read(File.join(resources_root, 'crypto-data-256.json')))
|
|
281
|
+
add_tests_for_data data
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
context 'with multiple sends from one client to another' do
|
|
285
|
+
let(:cipher_options) { { key: random_str(32) } }
|
|
286
|
+
let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
287
|
+
let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
288
|
+
|
|
289
|
+
let(:data) { MessagePack.pack({ 'key' => random_str }) }
|
|
290
|
+
let(:message_count) { 50 }
|
|
291
|
+
|
|
292
|
+
it 'encrypts and decrypts all messages' do
|
|
293
|
+
messages_received = {
|
|
294
|
+
decrypted: 0,
|
|
295
|
+
encrypted: 0
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
encrypted_channel_client2.attach do
|
|
299
|
+
encrypted_channel_client2.subscribe do |message|
|
|
300
|
+
expect(message.data).to eql("#{message.name}-#{data}")
|
|
301
|
+
expect(message.encoding).to be_nil
|
|
302
|
+
messages_received[:decrypted] += 1
|
|
303
|
+
stop_reactor if messages_received[:decrypted] == message_count
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
encrypted_channel_client1.__incoming_msgbus__.subscribe(:message) do |message|
|
|
307
|
+
expect(message['encoding']).to match(/cipher\+/)
|
|
308
|
+
messages_received[:encrypted] += 1
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
message_count.times do |index|
|
|
313
|
+
encrypted_channel_client2.publish index.to_s, "#{index}-#{data}"
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
context 'subscribing with a different transport protocol' do
|
|
319
|
+
let(:other_protocol) { protocol == :msgpack ? :json : :msgpack }
|
|
320
|
+
let(:other_client) do
|
|
321
|
+
Ably::Realtime::Client.new(default_options.merge(protocol: other_protocol))
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
325
|
+
let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
326
|
+
let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
327
|
+
|
|
328
|
+
before do
|
|
329
|
+
expect(other_client.protocol_binary?).to_not eql(client.protocol_binary?)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
[MessagePack.pack({ 'key' => SecureRandom.hex }), 'ã unicode', { 'key' => SecureRandom.hex }].each do |payload|
|
|
333
|
+
payload_description = "#{payload.class}#{" #{payload.encoding}" if payload.kind_of?(String)}"
|
|
334
|
+
|
|
335
|
+
it "delivers a #{payload_description} payload to the receiver" do
|
|
336
|
+
encrypted_channel_client1.publish 'example', payload
|
|
337
|
+
encrypted_channel_client2.subscribe do |message|
|
|
338
|
+
expect(message.data).to eql(payload)
|
|
339
|
+
expect(message.encoding).to be_nil
|
|
340
|
+
stop_reactor
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
context 'publishing on an unencrypted channel and subscribing on an encrypted channel with another client' do
|
|
347
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
348
|
+
let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
349
|
+
let(:unencrypted_channel_client1) { client.channel(channel_name) }
|
|
350
|
+
let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
351
|
+
|
|
352
|
+
let(:payload) { MessagePack.pack({ 'key' => random_str }) }
|
|
353
|
+
|
|
354
|
+
it 'does not attempt to decrypt the message' do
|
|
355
|
+
unencrypted_channel_client1.publish 'example', payload
|
|
356
|
+
encrypted_channel_client2.subscribe do |message|
|
|
357
|
+
expect(message.data).to eql(payload)
|
|
358
|
+
expect(message.encoding).to be_nil
|
|
359
|
+
stop_reactor
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
context 'publishing on an encrypted channel and subscribing on an unencrypted channel with another client' do
|
|
365
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
366
|
+
let(:cipher_options) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
367
|
+
let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
|
368
|
+
let(:unencrypted_channel_client2) { other_client.channel(channel_name) }
|
|
369
|
+
|
|
370
|
+
let(:payload) { MessagePack.pack({ 'key' => random_str }) }
|
|
371
|
+
|
|
372
|
+
it 'delivers the message but still encrypted with a value in the #encoding attribute' do
|
|
373
|
+
encrypted_channel_client1.publish 'example', payload
|
|
374
|
+
unencrypted_channel_client2.subscribe do |message|
|
|
375
|
+
expect(message.data).to_not eql(payload)
|
|
376
|
+
expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
|
|
377
|
+
stop_reactor
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it 'triggers a Cipher error on the channel' do
|
|
382
|
+
unencrypted_channel_client2.attach do
|
|
383
|
+
encrypted_channel_client1.publish 'example', payload
|
|
384
|
+
unencrypted_channel_client2.on(:error) do |error|
|
|
385
|
+
expect(error).to be_a(Ably::Exceptions::CipherError)
|
|
386
|
+
expect(error.code).to eql(92001)
|
|
387
|
+
expect(error.message).to match(/Message cannot be decrypted/)
|
|
388
|
+
stop_reactor
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
context 'publishing on an encrypted channel and subscribing with a different algorithm on another client' do
|
|
395
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
396
|
+
let(:cipher_options_client1) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
397
|
+
let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) }
|
|
398
|
+
let(:cipher_options_client2) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
|
399
|
+
let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) }
|
|
400
|
+
|
|
401
|
+
let(:payload) { MessagePack.pack({ 'key' => random_str }) }
|
|
402
|
+
|
|
403
|
+
it 'delivers the message but still encrypted with the cipher detials in the #encoding attribute' do
|
|
404
|
+
encrypted_channel_client1.publish 'example', payload
|
|
405
|
+
encrypted_channel_client2.subscribe do |message|
|
|
406
|
+
expect(message.data).to_not eql(payload)
|
|
407
|
+
expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
|
|
408
|
+
stop_reactor
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it 'triggers a Cipher error on the channel' do
|
|
413
|
+
encrypted_channel_client2.attach do
|
|
414
|
+
encrypted_channel_client1.publish 'example', payload
|
|
415
|
+
encrypted_channel_client2.on(:error) do |error|
|
|
416
|
+
expect(error).to be_a(Ably::Exceptions::CipherError)
|
|
417
|
+
expect(error.code).to eql(92002)
|
|
418
|
+
expect(error.message).to match(/Cipher algorithm [\w-]+ does not match/)
|
|
419
|
+
stop_reactor
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
context 'publishing on an encrypted channel and subscribing with a different key on another client' do
|
|
426
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
427
|
+
let(:cipher_options_client1) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
428
|
+
let(:encrypted_channel_client1) { client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client1) }
|
|
429
|
+
let(:cipher_options_client2) { { key: random_str(32), algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
|
430
|
+
let(:encrypted_channel_client2) { other_client.channel(channel_name, encrypted: true, cipher_params: cipher_options_client2) }
|
|
431
|
+
|
|
432
|
+
let(:payload) { MessagePack.pack({ 'key' => random_str }) }
|
|
433
|
+
|
|
434
|
+
it 'delivers the message but still encrypted with the cipher details in the #encoding attribute' do
|
|
435
|
+
encrypted_channel_client1.publish 'example', payload
|
|
436
|
+
encrypted_channel_client2.subscribe do |message|
|
|
437
|
+
expect(message.data).to_not eql(payload)
|
|
438
|
+
expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
|
|
439
|
+
stop_reactor
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
it 'triggers a Cipher error on the channel' do
|
|
444
|
+
encrypted_channel_client2.attach do
|
|
445
|
+
encrypted_channel_client1.publish 'example', payload
|
|
446
|
+
encrypted_channel_client2.on(:error) do |error|
|
|
447
|
+
expect(error).to be_a(Ably::Exceptions::CipherError)
|
|
448
|
+
expect(error.code).to eql(92003)
|
|
449
|
+
expect(error.message).to match(/CipherError decrypting data/)
|
|
450
|
+
stop_reactor
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
end
|