ably-rest 1.0.6 → 1.1.4.rc
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 +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +23 -15
- data/ably-rest.gemspec +6 -6
- data/lib/submodules/ably-ruby/.editorconfig +14 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -8
- data/lib/submodules/ably-ruby/CHANGELOG.md +75 -3
- data/lib/submodules/ably-ruby/LICENSE +1 -3
- data/lib/submodules/ably-ruby/README.md +12 -7
- data/lib/submodules/ably-ruby/Rakefile +32 -0
- data/lib/submodules/ably-ruby/SPEC.md +1277 -835
- data/lib/submodules/ably-ruby/ably.gemspec +15 -10
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +30 -4
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +10 -4
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +7 -1
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/device_details.rb +87 -0
- data/lib/submodules/ably-ruby/lib/ably/models/device_push_details.rb +86 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +23 -2
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +32 -2
- data/lib/submodules/ably-ruby/lib/ably/models/push_channel_subscription.rb +89 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +15 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -102
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/publisher.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +91 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +34 -20
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +25 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/push.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/admin.rb +61 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +53 -17
- data/lib/submodules/ably-ruby/lib/ably/rest/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +162 -35
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push.rb +42 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/admin.rb +54 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/device_registrations.rb +103 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +7 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +245 -17
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +26 -20
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +177 -59
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +153 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +72 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +129 -18
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +36 -34
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +201 -167
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +736 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +41 -3
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +79 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +6 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -10
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +158 -6
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +952 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/run_parallel_tests +33 -0
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +9 -5
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +10 -3
- data/lib/submodules/ably-ruby/spec/unit/models/device_details_spec.rb +102 -0
- data/lib/submodules/ably-ruby/spec/unit/models/device_push_details_spec.rb +101 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +51 -3
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +17 -2
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/push_channel_subscription_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +13 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/push_channel_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +8 -1
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/push_channel_spec.rb +36 -0
- metadata +46 -21
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Ably::Realtime::Push, :event_machine do
|
5
|
+
vary_by_protocol do
|
6
|
+
let(:default_options) { { key: api_key, environment: environment, protocol: protocol} }
|
7
|
+
let(:client_options) { default_options }
|
8
|
+
let(:client) do
|
9
|
+
Ably::Realtime::Client.new(client_options)
|
10
|
+
end
|
11
|
+
subject { client.push }
|
12
|
+
|
13
|
+
describe '#activate' do
|
14
|
+
it 'raises an unsupported exception' do
|
15
|
+
expect { subject.activate('foo') }.to raise_error(Ably::Exceptions::PushNotificationsNotSupported)
|
16
|
+
stop_reactor
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#deactivate' do
|
21
|
+
it 'raises an unsupported exception' do
|
22
|
+
expect { subject.deactivate('foo') }.to raise_error(Ably::Exceptions::PushNotificationsNotSupported)
|
23
|
+
stop_reactor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -758,9 +758,10 @@ describe Ably::Auth do
|
|
758
758
|
end
|
759
759
|
|
760
760
|
it 'updates the persisted auth options that are then used for subsequent authorize requests' do
|
761
|
-
|
762
|
-
auth.
|
763
|
-
|
761
|
+
auth_url = "https://echo.ably.io/?type=text&body=#{auth.request_token.token}"
|
762
|
+
expect(auth.options[:auth_url]).to be_nil
|
763
|
+
auth.authorize({}, auth_url: auth_url)
|
764
|
+
expect(auth.options[:auth_url]).to eql(auth_url)
|
764
765
|
end
|
765
766
|
|
766
767
|
context 'with a lambda for the :auth_callback option' do
|
@@ -1338,5 +1339,42 @@ describe Ably::Auth do
|
|
1338
1339
|
expect(response).to be_a(Ably::Models::TokenDetails)
|
1339
1340
|
end
|
1340
1341
|
end
|
1342
|
+
|
1343
|
+
# RSC1, RSC1a, RSA3c, RSA3d
|
1344
|
+
context 'when using JWT' do
|
1345
|
+
let(:auth_url) { 'https://echo.ably.io/createJWT' }
|
1346
|
+
let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
|
1347
|
+
let(:client) { Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol) }
|
1348
|
+
|
1349
|
+
it 'authenticates correctly using the JWT token generated by the echo server' do
|
1350
|
+
expect(client.stats).to_not be_nil()
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
context 'when the JWT embeds an Ably token' do
|
1354
|
+
let(:token) { Faraday.post(auth_url, { keyName: key_name, keySecret: key_secret, jwtType: :embedded }).body }
|
1355
|
+
|
1356
|
+
it 'authenticates correctly using the embedded token' do
|
1357
|
+
expect(client.stats).to_not be_nil()
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
context 'and the requested token is encrypted' do
|
1361
|
+
let(:token) { Faraday.post(auth_url, { keyName: key_name, keySecret: key_secret, jwtType: :embedded, encrypted: 1 }).body }
|
1362
|
+
|
1363
|
+
it 'authenticates correctly using the embedded token' do
|
1364
|
+
expect(client.stats).to_not be_nil()
|
1365
|
+
end
|
1366
|
+
end
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
# RSA4f, RSA8c
|
1370
|
+
context 'when the token requested is returned with application/jwt content type' do
|
1371
|
+
let(:auth_rest_client) { Ably::Rest::Client.new(default_options.merge(key: api_key)) }
|
1372
|
+
let(:auth_params) { { keyName: key_name, keySecret: key_secret, returnType: 'jwt' } }
|
1373
|
+
let(:token) { auth_rest_client.auth.request_token({ }, { auth_url: auth_url, auth_params: auth_params }).token }
|
1374
|
+
it 'authenticates correctly and pulls stats' do
|
1375
|
+
expect(client.stats).to_not be_nil()
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
end
|
1341
1379
|
end
|
1342
1380
|
end
|
@@ -7,7 +7,7 @@ describe Ably::Rest do
|
|
7
7
|
|
8
8
|
let(:client_options) { {} }
|
9
9
|
let(:client) do
|
10
|
-
Ably::Rest::Client.new(client_options.merge(key: 'appid.keyuid:keysecret'))
|
10
|
+
Ably::Rest::Client.new(client_options.merge(key: 'appid.keyuid:keysecret', log_retries_as_info: true))
|
11
11
|
end
|
12
12
|
|
13
13
|
let(:now) { Time.now - 1000 }
|
@@ -67,7 +67,7 @@ describe Ably::Rest do
|
|
67
67
|
|
68
68
|
vary_by_protocol do
|
69
69
|
let(:client) do
|
70
|
-
Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
|
70
|
+
Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol, log_retries_as_info: true)
|
71
71
|
end
|
72
72
|
|
73
73
|
describe 'failed requests' do
|
@@ -40,7 +40,7 @@ describe Ably::Rest::Channel do
|
|
40
40
|
|
41
41
|
it 'publishes the message without a client_id' do
|
42
42
|
expect(client).to receive(:post).
|
43
|
-
with("/channels/#{channel_name}/publish", hash_excluding(client_id: client_id)).
|
43
|
+
with("/channels/#{channel_name}/publish", hash_excluding(client_id: client_id), {}).
|
44
44
|
and_return(double('response', status: 201))
|
45
45
|
|
46
46
|
expect(channel.publish(name, data)).to eql(true)
|
@@ -82,6 +82,44 @@ describe Ably::Rest::Channel do
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
+
context 'with a Message object' do
|
86
|
+
let(:name) { random_str }
|
87
|
+
|
88
|
+
let(:message) do
|
89
|
+
Ably::Models::Message(name: name, data: data)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'publishes the message' do
|
93
|
+
expect(client).to receive(:post).once.and_call_original
|
94
|
+
expect(channel.publish(message)).to eql(true)
|
95
|
+
expect(channel.history.items.first.name).to eql(name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'with a Message object and query params' do
|
100
|
+
let(:message) do
|
101
|
+
Ably::Models::Message(name: name, data: data)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should fail to publish the message (RSL1l1)' do
|
105
|
+
expect(client).to receive(:post).once.and_call_original
|
106
|
+
expect { channel.publish(message, { _forceNack: 'true' }) }.to raise_error(Ably::Exceptions::InvalidRequest, /40099/)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'with Messages and query params' do
|
111
|
+
let(:messages) do
|
112
|
+
10.times.map do |index|
|
113
|
+
{ name: index.to_s, data: { "index" => index + 10 } }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should fail to publish the message (RSL1l1)' do
|
118
|
+
expect(client).to receive(:post).once.and_call_original
|
119
|
+
expect { channel.publish(messages, { _forceNack: 'true' }) }.to raise_error(Ably::Exceptions::InvalidRequest, /40099/)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
85
123
|
context 'without adequate permissions on the channel' do
|
86
124
|
let(:capability) { { onlyChannel: ['subscribe'] } }
|
87
125
|
let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { capability: capability }) }
|
@@ -96,7 +134,7 @@ describe Ably::Rest::Channel do
|
|
96
134
|
let(:data) { random_str }
|
97
135
|
|
98
136
|
it 'publishes the message without a name attribute in the payload' do
|
99
|
-
expect(client).to receive(:post).with(anything, { "data" => data }).once.and_call_original
|
137
|
+
expect(client).to receive(:post).with(anything, { "data" => data }, {}).once.and_call_original
|
100
138
|
expect(channel.publish(nil, data)).to eql(true)
|
101
139
|
expect(channel.history.items.first.name).to be_nil
|
102
140
|
expect(channel.history.items.first.data).to eql(data)
|
@@ -107,7 +145,7 @@ describe Ably::Rest::Channel do
|
|
107
145
|
let(:name) { random_str }
|
108
146
|
|
109
147
|
it 'publishes the message without a data attribute in the payload' do
|
110
|
-
expect(client).to receive(:post).with(anything, { "name" => name }).once.and_call_original
|
148
|
+
expect(client).to receive(:post).with(anything, { "name" => name }, {}).once.and_call_original
|
111
149
|
expect(channel.publish(name)).to eql(true)
|
112
150
|
expect(channel.history.items.first.name).to eql(name)
|
113
151
|
expect(channel.history.items.first.data).to be_nil
|
@@ -118,7 +156,7 @@ describe Ably::Rest::Channel do
|
|
118
156
|
let(:name) { random_str }
|
119
157
|
|
120
158
|
it 'publishes the message without any attributes in the payload' do
|
121
|
-
expect(client).to receive(:post).with(anything, {}).once.and_call_original
|
159
|
+
expect(client).to receive(:post).with(anything, {}, {}).once.and_call_original
|
122
160
|
expect(channel.publish(nil)).to eql(true)
|
123
161
|
expect(channel.history.items.first.name).to be_nil
|
124
162
|
expect(channel.history.items.first.data).to be_nil
|
@@ -275,6 +313,43 @@ describe Ably::Rest::Channel do
|
|
275
313
|
end
|
276
314
|
end
|
277
315
|
end
|
316
|
+
|
317
|
+
context 'with a frozen message event name' do
|
318
|
+
let(:event_name) { random_str.freeze }
|
319
|
+
|
320
|
+
it 'succeeds and publishes with an implicit client_id' do
|
321
|
+
channel.publish([name: event_name])
|
322
|
+
channel.publish(event_name)
|
323
|
+
|
324
|
+
if !(RUBY_VERSION.match(/^1\./) || RUBY_VERSION.match(/^2\.[012]/))
|
325
|
+
channel.publish(+'foo-bar') # new style freeze, see https://github.com/ably/ably-ruby/issues/132
|
326
|
+
else
|
327
|
+
channel.publish('foo-bar'.freeze) # new + style not supported until Ruby 2.3
|
328
|
+
end
|
329
|
+
|
330
|
+
channel.history do |messages|
|
331
|
+
expect(messages.length).to eql(3)
|
332
|
+
expect(messages.first.name).to eql(event_name)
|
333
|
+
expect(messages[1].name).to eql(event_name)
|
334
|
+
expect(messages.last.name).to eql('foo-bar')
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context 'with a frozen payload' do
|
340
|
+
let(:payload) { { foo: random_str.freeze }.freeze }
|
341
|
+
|
342
|
+
it 'succeeds and publishes with an implicit client_id' do
|
343
|
+
channel.publish([data: payload])
|
344
|
+
channel.publish(nil, payload)
|
345
|
+
|
346
|
+
channel.history do |messages|
|
347
|
+
expect(messages.length).to eql(2)
|
348
|
+
expect(messages.first.data).to eql(payload)
|
349
|
+
expect(messages.last.data).to eql(payload)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
278
353
|
end
|
279
354
|
|
280
355
|
describe '#history' do
|
@@ -60,5 +60,11 @@ describe Ably::Rest::Channels do
|
|
60
60
|
let(:channel_with_options) { client.channels[channel_name, options] }
|
61
61
|
it_behaves_like 'a channel'
|
62
62
|
end
|
63
|
+
|
64
|
+
describe 'using a frozen channel name' do
|
65
|
+
let(:channel) { client.channels[channel_name.freeze] }
|
66
|
+
let(:channel_with_options) { client.channels[channel_name.freeze, options] }
|
67
|
+
it_behaves_like 'a channel'
|
68
|
+
end
|
63
69
|
end
|
64
70
|
end
|
@@ -4,7 +4,7 @@ require 'webrick'
|
|
4
4
|
|
5
5
|
describe Ably::Rest::Client do
|
6
6
|
vary_by_protocol do
|
7
|
-
let(:default_options) { { environment: environment, protocol: protocol } }
|
7
|
+
let(:default_options) { { environment: environment, protocol: protocol, log_retries_as_info: true } }
|
8
8
|
let(:client_options) { default_options }
|
9
9
|
|
10
10
|
let(:client) { Ably::Rest::Client.new(client_options) }
|
@@ -27,6 +27,19 @@ describe Ably::Rest::Client do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
context 'with an invalid API key' do
|
31
|
+
let(:client) { Ably::Rest::Client.new(client_options.merge(key: 'app.key:secret', log_level: :fatal)) }
|
32
|
+
|
33
|
+
it 'logs an entry with a help href url matching the code #TI5' do
|
34
|
+
begin
|
35
|
+
client.channels.get('foo').publish('test')
|
36
|
+
raise 'Expected Ably::Exceptions::ResourceMissing'
|
37
|
+
rescue Ably::Exceptions::ResourceMissing => err
|
38
|
+
expect err.to_s.match(%r{https://help.ably.io/error/40400})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
30
43
|
context 'with an explicit string :token' do
|
31
44
|
let(:client) { Ably::Rest::Client.new(client_options.merge(token: random_str)) }
|
32
45
|
|
@@ -710,6 +723,109 @@ describe Ably::Rest::Client do
|
|
710
723
|
expect(@fallback_request_count).to eql(2)
|
711
724
|
end
|
712
725
|
end
|
726
|
+
|
727
|
+
context 'to fail the primary host, allow a fallback to succeed, then later trigger a fallback to the primary host (#RSC15f)' do
|
728
|
+
before do
|
729
|
+
@request_count = 0
|
730
|
+
@primary_host_request_count = 0
|
731
|
+
@web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false, :AccessLog => [], Logger: WEBrick::Log.new("/dev/null"))
|
732
|
+
@web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
|
733
|
+
@request_count += 1
|
734
|
+
if req.header["host"].first.include?(primary_host)
|
735
|
+
@primary_host_request_count += 1
|
736
|
+
# Fail all requests to the primary host so that a fallback is used
|
737
|
+
# Except request 6 which should suceed and clear the fallback host preference
|
738
|
+
if @request_count == 6
|
739
|
+
res.status = 200
|
740
|
+
res['Content-Type'] = 'application/json'
|
741
|
+
res.body = '{}'
|
742
|
+
else
|
743
|
+
res.status = 500
|
744
|
+
end
|
745
|
+
else
|
746
|
+
# Fail the second request (first failed fallback of first request)
|
747
|
+
# Fail the third request on the previously succeeded fallback host to trigger an attempt on the primary host
|
748
|
+
if [2, 5].include?(@request_count)
|
749
|
+
res.status = 500
|
750
|
+
else
|
751
|
+
res.status = 200
|
752
|
+
res['Content-Type'] = 'application/json'
|
753
|
+
res.body = '{}'
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
Thread.new do
|
759
|
+
@web_server.start
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
let(:client_options) do
|
764
|
+
default_options.merge(
|
765
|
+
rest_host: primary_host,
|
766
|
+
fallback_hosts: fallbacks,
|
767
|
+
token: 'fake.token',
|
768
|
+
port: port,
|
769
|
+
tls: false,
|
770
|
+
log_level: :error
|
771
|
+
).merge(additional_client_options)
|
772
|
+
end
|
773
|
+
|
774
|
+
let (:additional_client_options) { {} }
|
775
|
+
|
776
|
+
it 'succeeds and remembers fallback host preferences across requests' do
|
777
|
+
# Send a request, expect primary endpoint to fail, one fallback to fail, second fallback to succeed
|
778
|
+
client.channel(channel_name).publish('event', 'data')
|
779
|
+
expect(@request_count).to eql(3)
|
780
|
+
expect(fallbacks).to include(client.using_preferred_fallback_host?)
|
781
|
+
successfull_fallback = client.using_preferred_fallback_host?
|
782
|
+
expect(@primary_host_request_count).to eql(1)
|
783
|
+
|
784
|
+
# Send another request, which should go straight to the fallback as it succeeded previously
|
785
|
+
client.channel(channel_name).publish('event', 'data')
|
786
|
+
expect(@request_count).to eql(4)
|
787
|
+
expect(successfull_fallback).to eql(client.using_preferred_fallback_host?)
|
788
|
+
expect(@primary_host_request_count).to eql(1)
|
789
|
+
|
790
|
+
# A subsequent request should fail to the fallback, go the primary host and succeed
|
791
|
+
client.channel(channel_name).publish('event', 'data')
|
792
|
+
expect(@request_count).to eql(6)
|
793
|
+
expect(client.using_preferred_fallback_host?).to be_falsey
|
794
|
+
expect(@primary_host_request_count).to eql(2)
|
795
|
+
|
796
|
+
# A subsequent request will fail on the primary endpoint, and we expect the fallback to be used again
|
797
|
+
client.channel(channel_name).publish('event', 'data')
|
798
|
+
expect(@request_count).to eql(8)
|
799
|
+
expect(fallbacks).to include(client.using_preferred_fallback_host?)
|
800
|
+
successfull_fallback = client.using_preferred_fallback_host?
|
801
|
+
expect(@primary_host_request_count).to eql(3)
|
802
|
+
|
803
|
+
# Send another request, which should go straight to the fallback as it succeeded previously
|
804
|
+
client.channel(channel_name).publish('event', 'data')
|
805
|
+
expect(@request_count).to eql(9)
|
806
|
+
expect(successfull_fallback).to eql(client.using_preferred_fallback_host?)
|
807
|
+
expect(@primary_host_request_count).to eql(3)
|
808
|
+
end
|
809
|
+
|
810
|
+
context 'with custom :fallback_retry_timeout' do
|
811
|
+
let (:additional_client_options) { { fallback_retry_timeout: 5 } }
|
812
|
+
|
813
|
+
it 'stops using the preferred fallback after this time' do
|
814
|
+
# Send a request, expect primary endpoint to fail, one fallback to fail, second fallback to succeed
|
815
|
+
client.channel(channel_name).publish('event', 'data')
|
816
|
+
expect(@request_count).to eql(3)
|
817
|
+
expect(fallbacks).to include(client.using_preferred_fallback_host?)
|
818
|
+
expect(@primary_host_request_count).to eql(1)
|
819
|
+
|
820
|
+
# Wait for the preferred fallback cache to expire
|
821
|
+
sleep 5
|
822
|
+
|
823
|
+
# Send another request, which should go straight to the primary host again as fallback host is expired
|
824
|
+
client.channel(channel_name).publish('event', 'data')
|
825
|
+
expect(@primary_host_request_count).to eql(2)
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
713
829
|
end
|
714
830
|
end
|
715
831
|
|
@@ -724,7 +840,8 @@ describe Ably::Rest::Client do
|
|
724
840
|
environment: env,
|
725
841
|
key: api_key,
|
726
842
|
http_max_retry_duration: max_retry_duration,
|
727
|
-
http_max_retry_count: max_retry_count
|
843
|
+
http_max_retry_count: max_retry_count,
|
844
|
+
log_level: :fatal,
|
728
845
|
)
|
729
846
|
end
|
730
847
|
|
@@ -751,7 +868,7 @@ describe Ably::Rest::Client do
|
|
751
868
|
end
|
752
869
|
|
753
870
|
let(:client_options) {
|
754
|
-
production_options.merge(fallback_hosts: custom_hosts, log_level: :
|
871
|
+
production_options.merge(fallback_hosts: custom_hosts, log_level: :fatal)
|
755
872
|
}
|
756
873
|
|
757
874
|
it 'attempts the fallback hosts as this is not an authentication failure' do
|
@@ -764,7 +881,7 @@ describe Ably::Rest::Client do
|
|
764
881
|
|
765
882
|
context 'with an empty array of fallback hosts provided (#RSC15b, #TO3k6)' do
|
766
883
|
let(:client_options) {
|
767
|
-
production_options.merge(fallback_hosts: [])
|
884
|
+
production_options.merge(fallback_hosts: [], log_level: :fatal)
|
768
885
|
}
|
769
886
|
|
770
887
|
it 'does not attempt the fallback hosts as this is an authentication failure' do
|
@@ -789,7 +906,7 @@ describe Ably::Rest::Client do
|
|
789
906
|
end
|
790
907
|
|
791
908
|
let(:client_options) {
|
792
|
-
production_options.merge(fallback_hosts: custom_hosts, log_level: :
|
909
|
+
production_options.merge(fallback_hosts: custom_hosts, log_level: :fatal)
|
793
910
|
}
|
794
911
|
|
795
912
|
it 'attempts the default fallback hosts as this is an authentication failure' do
|
@@ -966,7 +1083,7 @@ describe Ably::Rest::Client do
|
|
966
1083
|
it 'sends a protocol version and lib version header (#G4, #RSC7a, #RSC7b)' do
|
967
1084
|
client.channels.get('foo').publish("event")
|
968
1085
|
expect(publish_message_stub).to have_been_requested
|
969
|
-
expect(Ably::PROTOCOL_VERSION).to eql('1.
|
1086
|
+
expect(Ably::PROTOCOL_VERSION).to eql('1.1')
|
970
1087
|
end
|
971
1088
|
end
|
972
1089
|
end
|
@@ -1084,7 +1201,7 @@ describe Ably::Rest::Client do
|
|
1084
1201
|
end
|
1085
1202
|
|
1086
1203
|
context 'option add_request_ids: true and specified fallback hosts', :webmock do
|
1087
|
-
let(:client_options) { { key: api_key, fallback_hosts_use_default: true, add_request_ids: true, log_level: :error } }
|
1204
|
+
let(:client_options) { { key: api_key, fallback_hosts_use_default: true, add_request_ids: true, log_level: :error, log_retries_as_info: true } }
|
1088
1205
|
let(:requests) { [] }
|
1089
1206
|
|
1090
1207
|
before do
|
@@ -1140,7 +1257,7 @@ describe Ably::Rest::Client do
|
|
1140
1257
|
|
1141
1258
|
context 'failed request logging', :prevent_log_stubbing do
|
1142
1259
|
let(:custom_logger) { TestLogger.new }
|
1143
|
-
let(:client_options) { default_options.merge(key: api_key, logger: custom_logger) }
|
1260
|
+
let(:client_options) { default_options.merge(key: api_key, logger: custom_logger, log_retries_as_info: false) }
|
1144
1261
|
|
1145
1262
|
it 'is absent when requests do not fail' do
|
1146
1263
|
client.time
|
@@ -1153,7 +1270,8 @@ describe Ably::Rest::Client do
|
|
1153
1270
|
rest_host: 'non.existent.domain.local',
|
1154
1271
|
fallback_hosts: [[environment, Ably::Rest::Client::DOMAIN].join('-')],
|
1155
1272
|
key: api_key,
|
1156
|
-
logger: custom_logger
|
1273
|
+
logger: custom_logger,
|
1274
|
+
log_retries_as_info: false)
|
1157
1275
|
end
|
1158
1276
|
|
1159
1277
|
it 'is present with success message when requests do not actually fail' do
|
@@ -1169,7 +1287,8 @@ describe Ably::Rest::Client do
|
|
1169
1287
|
rest_host: 'non.existent.domain.local',
|
1170
1288
|
fallback_hosts: ['non2.existent.domain.local'],
|
1171
1289
|
key: api_key,
|
1172
|
-
logger: custom_logger
|
1290
|
+
logger: custom_logger,
|
1291
|
+
log_retries_as_info: false)
|
1173
1292
|
end
|
1174
1293
|
|
1175
1294
|
it 'is present when all requests fail' do
|