ably 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -1
- data/ably.gemspec +4 -3
- data/lib/ably/auth.rb +4 -4
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +8 -8
- data/lib/ably/models/message.rb +6 -4
- data/lib/ably/models/presence_message.rb +6 -4
- data/lib/ably/modules/async_wrapper.rb +2 -2
- data/lib/ably/modules/conversions.rb +1 -1
- data/lib/ably/modules/encodeable.rb +1 -1
- data/lib/ably/modules/event_emitter.rb +2 -2
- data/lib/ably/modules/safe_deferrable.rb +1 -1
- data/lib/ably/modules/safe_yield.rb +1 -1
- data/lib/ably/modules/state_emitter.rb +5 -5
- data/lib/ably/realtime/auth.rb +1 -1
- data/lib/ably/realtime/channel.rb +3 -3
- data/lib/ably/realtime/channel/channel_manager.rb +2 -2
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +3 -2
- data/lib/ably/realtime/connection.rb +11 -6
- data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
- data/lib/ably/realtime/presence.rb +3 -3
- data/lib/ably/realtime/presence/members_map.rb +6 -6
- data/lib/ably/rest/channel.rb +2 -2
- data/lib/ably/rest/client.rb +20 -12
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/auth_spec.rb +13 -37
- data/spec/acceptance/realtime/channel_history_spec.rb +7 -1
- data/spec/acceptance/realtime/channel_spec.rb +3 -3
- data/spec/acceptance/realtime/client_spec.rb +2 -2
- data/spec/acceptance/realtime/connection_failures_spec.rb +221 -7
- data/spec/acceptance/realtime/connection_spec.rb +13 -21
- data/spec/acceptance/realtime/message_spec.rb +2 -2
- data/spec/acceptance/realtime/presence_history_spec.rb +12 -3
- data/spec/acceptance/realtime/presence_spec.rb +10 -10
- data/spec/acceptance/rest/auth_spec.rb +21 -48
- data/spec/acceptance/rest/client_spec.rb +193 -68
- data/spec/shared/client_initializer_behaviour.rb +1 -9
- data/spec/spec_helper.rb +2 -0
- data/spec/support/event_emitter_helper.rb +31 -0
- data/spec/support/event_machine_helper.rb +1 -1
- data/spec/support/test_logger_helper.rb +42 -0
- data/spec/unit/logger_spec.rb +1 -9
- data/spec/unit/modules/async_wrapper_spec.rb +2 -2
- data/spec/unit/modules/event_emitter_spec.rb +3 -3
- data/spec/unit/modules/state_emitter_spec.rb +10 -10
- data/spec/unit/realtime/channel_spec.rb +1 -1
- data/spec/unit/realtime/connection_spec.rb +1 -1
- data/spec/unit/realtime/presence_spec.rb +1 -1
- data/spec/unit/rest/channel_spec.rb +22 -0
- data/spec/unit/util/pub_sub_spec.rb +3 -3
- metadata +26 -8
@@ -71,7 +71,7 @@ module Ably::Realtime
|
|
71
71
|
|
72
72
|
ensure_channel_attached(deferrable) do
|
73
73
|
if entering?
|
74
|
-
once_or_if(STATE.Entered, else:
|
74
|
+
once_or_if(STATE.Entered, else: lambda { |args| deferrable_fail deferrable, *args }) do
|
75
75
|
deferrable_succeed deferrable, &success_block
|
76
76
|
end
|
77
77
|
else
|
@@ -132,7 +132,7 @@ module Ably::Realtime
|
|
132
132
|
|
133
133
|
ensure_channel_attached(deferrable) do
|
134
134
|
if leaving?
|
135
|
-
once_or_if(STATE.Left, else:
|
135
|
+
once_or_if(STATE.Left, else: lambda { |error|deferrable_fail deferrable, *args }) do
|
136
136
|
deferrable_succeed deferrable, &success_block
|
137
137
|
end
|
138
138
|
else
|
@@ -310,7 +310,7 @@ module Ably::Realtime
|
|
310
310
|
# @api private
|
311
311
|
def __incoming_msgbus__
|
312
312
|
@__incoming_msgbus__ ||= Ably::Util::PubSub.new(
|
313
|
-
coerce_into:
|
313
|
+
coerce_into: lambda { |event| Ably::Models::ProtocolMessage::ACTION(event) }
|
314
314
|
)
|
315
315
|
end
|
316
316
|
|
@@ -94,7 +94,7 @@ module Ably::Realtime
|
|
94
94
|
wait_for_sync = options.fetch(:wait_for_sync, true)
|
95
95
|
deferrable = Ably::Util::SafeDeferrable.new(logger)
|
96
96
|
|
97
|
-
result_block =
|
97
|
+
result_block = lambda do
|
98
98
|
present_members.tap do |members|
|
99
99
|
members.keep_if { |member| member.connection_id == options[:connection_id] } if options[:connection_id]
|
100
100
|
members.keep_if { |member| member.client_id == options[:client_id] } if options[:client_id]
|
@@ -110,17 +110,17 @@ module Ably::Realtime
|
|
110
110
|
# Must be defined before subsequent procs reference this callback
|
111
111
|
reset_callbacks = nil
|
112
112
|
|
113
|
-
in_sync_callback =
|
114
|
-
reset_callbacks
|
113
|
+
in_sync_callback = lambda do
|
114
|
+
reset_callbacks.call if reset_callbacks
|
115
115
|
result_block.call
|
116
116
|
end
|
117
117
|
|
118
|
-
failed_callback =
|
119
|
-
reset_callbacks
|
118
|
+
failed_callback = lambda do |error|
|
119
|
+
reset_callbacks.call if reset_callbacks
|
120
120
|
deferrable.fail error
|
121
121
|
end
|
122
122
|
|
123
|
-
reset_callbacks =
|
123
|
+
reset_callbacks = lambda do
|
124
124
|
off(&in_sync_callback)
|
125
125
|
off(&failed_callback)
|
126
126
|
channel.off(&failed_callback)
|
data/lib/ably/rest/channel.rb
CHANGED
@@ -22,7 +22,7 @@ module Ably
|
|
22
22
|
# @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
|
23
23
|
#
|
24
24
|
def initialize(client, name, channel_options = {})
|
25
|
-
ensure_utf_8 :name, name
|
25
|
+
name = (ensure_utf_8 :name, name)
|
26
26
|
|
27
27
|
update_options channel_options
|
28
28
|
@client = client
|
@@ -58,7 +58,7 @@ module Ably
|
|
58
58
|
messages = if name.kind_of?(Enumerable)
|
59
59
|
name
|
60
60
|
else
|
61
|
-
ensure_utf_8
|
61
|
+
name = ensure_utf_8(:name, name, allow_nil: true)
|
62
62
|
ensure_supported_payload data
|
63
63
|
[{ name: name, data: data }.merge(attributes)]
|
64
64
|
end
|
data/lib/ably/rest/client.rb
CHANGED
@@ -439,22 +439,15 @@ module Ably
|
|
439
439
|
max_retry_duration = http_defaults.fetch(:max_retry_duration)
|
440
440
|
requested_at = Time.now
|
441
441
|
retry_count = 0
|
442
|
-
|
443
|
-
if add_request_ids
|
444
|
-
params = if params.nil?
|
445
|
-
{}
|
446
|
-
else
|
447
|
-
params.dup
|
448
|
-
end
|
449
|
-
request_id = SecureRandom.urlsafe_base64(10)
|
450
|
-
params[:request_id] = request_id
|
451
|
-
end
|
442
|
+
retry_sequence_id = nil
|
443
|
+
request_id = SecureRandom.urlsafe_base64(10) if add_request_ids
|
452
444
|
|
453
445
|
begin
|
454
446
|
use_fallback = can_fallback_to_alternate_ably_host? && retry_count > 0
|
455
447
|
|
456
448
|
connection(use_fallback: use_fallback).send(method, path, params) do |request|
|
457
449
|
if add_request_ids
|
450
|
+
request.params[:request_id] = request_id
|
458
451
|
request.options.context = {} if request.options.context.nil?
|
459
452
|
request.options.context[:request_id] = request_id
|
460
453
|
end
|
@@ -466,15 +459,30 @@ module Ably
|
|
466
459
|
end
|
467
460
|
end
|
468
461
|
end
|
462
|
+
end.tap do
|
463
|
+
if retry_count > 0
|
464
|
+
logger.warn do
|
465
|
+
"Ably::Rest::Client - Request SUCCEEDED after #{retry_count} #{retry_count > 1 ? 'retries' : 'retry' } for" \
|
466
|
+
" #{method} #{path} #{params} (seq ##{retry_sequence_id}, time elapsed #{(Time.now.to_f - requested_at.to_f).round(2)}s)"
|
467
|
+
end
|
468
|
+
end
|
469
469
|
end
|
470
470
|
|
471
471
|
rescue Faraday::TimeoutError, Faraday::ClientError, Ably::Exceptions::ServerError => error
|
472
|
+
retry_sequence_id ||= SecureRandom.urlsafe_base64(4)
|
472
473
|
time_passed = Time.now - requested_at
|
474
|
+
|
473
475
|
if can_fallback_to_alternate_ably_host? && retry_count < max_retry_count && time_passed <= max_retry_duration
|
474
476
|
retry_count += 1
|
475
|
-
logger.warn { "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}" }
|
477
|
+
logger.warn { "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed (seq ##{retry_sequence_id}): #{error}" }
|
476
478
|
retry
|
477
479
|
end
|
480
|
+
|
481
|
+
logger.error do
|
482
|
+
"Ably::Rest::Client - Request FAILED after #{retry_count} #{retry_count > 1 ? 'retries' : 'retry' } for" \
|
483
|
+
" #{method} #{path} #{params} (seq ##{retry_sequence_id}, time elapsed #{(Time.now.to_f - requested_at.to_f).round(2)}s)"
|
484
|
+
end
|
485
|
+
|
478
486
|
case error
|
479
487
|
when Faraday::TimeoutError
|
480
488
|
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, { request_id: request_id })
|
@@ -549,7 +557,7 @@ module Ably
|
|
549
557
|
setup_incoming_middleware builder, logger, fail_if_unsupported_mime_type: true
|
550
558
|
|
551
559
|
# Set Faraday's HTTP adapter
|
552
|
-
builder.adapter
|
560
|
+
builder.adapter :excon
|
553
561
|
end
|
554
562
|
end
|
555
563
|
|
data/lib/ably/version.rb
CHANGED
@@ -206,7 +206,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
206
206
|
|
207
207
|
context 'with a slow auth callback response' do
|
208
208
|
let(:auth_callback) do
|
209
|
-
|
209
|
+
lambda do |token_params|
|
210
210
|
sleep pause
|
211
211
|
rest_auth_client.auth.request_token
|
212
212
|
end
|
@@ -214,7 +214,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
214
214
|
|
215
215
|
it 'asynchronously authenticates' do
|
216
216
|
timers_called = 0
|
217
|
-
block =
|
217
|
+
block = lambda do
|
218
218
|
timers_called += 1
|
219
219
|
EventMachine.add_timer(0.5, &block)
|
220
220
|
end
|
@@ -230,7 +230,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
230
230
|
|
231
231
|
context 'when implicitly called, with an explicit ClientOptions client_id' do
|
232
232
|
let(:client_id) { random_str }
|
233
|
-
let(:client_options) { default_options.merge(auth_callback:
|
233
|
+
let(:client_options) { default_options.merge(auth_callback: lambda { |token_params| auth_token_object }, client_id: client_id, log_level: :none) }
|
234
234
|
let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key, client_id: 'invalid')) }
|
235
235
|
|
236
236
|
context 'and an incompatible client_id in a TokenDetails object passed to the auth callback' do
|
@@ -268,7 +268,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
268
268
|
|
269
269
|
context 'when explicitly called, with an explicit ClientOptions client_id' do
|
270
270
|
let(:auth_proc) do
|
271
|
-
|
271
|
+
lambda do |token_params|
|
272
272
|
if !@requested
|
273
273
|
@requested = true
|
274
274
|
valid_auth_token
|
@@ -304,18 +304,18 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
304
304
|
let(:rest_client) { Ably::Rest::Client.new(default_options) }
|
305
305
|
let(:client_publisher) { auto_close Ably::Realtime::Client.new(default_options) }
|
306
306
|
let(:basic_capability) { JSON.dump("foo" => ["subscribe"]) }
|
307
|
-
let(:basic_token_cb) {
|
307
|
+
let(:basic_token_cb) { lambda do |token_params|
|
308
308
|
rest_client.auth.create_token_request({ capability: basic_capability })
|
309
309
|
end }
|
310
310
|
let(:upgraded_capability) { JSON.dump({ "foo" => ["subscribe", "publish"] }) }
|
311
|
-
let(:upgraded_token_cb) {
|
311
|
+
let(:upgraded_token_cb) { lambda do |token_params|
|
312
312
|
rest_client.auth.create_token_request({ capability: upgraded_capability })
|
313
313
|
end }
|
314
|
-
let(:identified_token_cb) {
|
314
|
+
let(:identified_token_cb) { lambda do |token_params|
|
315
315
|
rest_client.auth.create_token_request({ client_id: 'bob' })
|
316
316
|
end }
|
317
317
|
let(:downgraded_capability) { JSON.dump({ "bar" => ["subscribe"] }) }
|
318
|
-
let(:downgraded_token_cb) {
|
318
|
+
let(:downgraded_token_cb) { lambda do |token_params|
|
319
319
|
rest_client.auth.create_token_request({ capability: downgraded_capability })
|
320
320
|
end }
|
321
321
|
|
@@ -589,7 +589,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
589
589
|
context 'when client is identified' do
|
590
590
|
let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
|
591
591
|
|
592
|
-
let(:basic_token_cb) {
|
592
|
+
let(:basic_token_cb) { lambda do |token_params|
|
593
593
|
rest_client.auth.create_token_request({ client_id: 'mike', capability: basic_capability })
|
594
594
|
end }
|
595
595
|
|
@@ -612,7 +612,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
612
612
|
connection_failed = false
|
613
613
|
|
614
614
|
client.connection.once(:connected) do
|
615
|
-
client.auth.authorize(nil, auth_callback:
|
615
|
+
client.auth.authorize(nil, auth_callback: lambda { |token_params| 'invalid.token:will.cause.failure' }).tap do |deferrable|
|
616
616
|
deferrable.errback do |error|
|
617
617
|
EventMachine.add_timer(0.2) do
|
618
618
|
expect(connection_failed).to eql(true)
|
@@ -638,7 +638,7 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
638
638
|
|
639
639
|
it 'calls the error callback of authorize and leaves the connection intact (#RSA4c3)' do
|
640
640
|
client.connection.once(:connected) do
|
641
|
-
client.auth.authorize(nil, auth_callback:
|
641
|
+
client.auth.authorize(nil, auth_callback: lambda { |token_params| raise 'Exception raised' }).errback do |error|
|
642
642
|
EventMachine.add_timer(0.2) do
|
643
643
|
expect(connection).to be_connected
|
644
644
|
expect(error.message).to match(/Exception raised/i)
|
@@ -1014,33 +1014,9 @@ describe Ably::Realtime::Auth, :event_machine do
|
|
1014
1014
|
end
|
1015
1015
|
end
|
1016
1016
|
|
1017
|
-
context 'deprecated #authorise' do
|
1017
|
+
context 'deprecated #authorise', :prevent_log_stubbing do
|
1018
1018
|
let(:client_options) { default_options.merge(key: api_key, logger: custom_logger_object, use_token_auth: true) }
|
1019
|
-
let(:
|
1020
|
-
Class.new do
|
1021
|
-
def initialize
|
1022
|
-
@messages = []
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
[:fatal, :error, :warn, :info, :debug].each do |severity|
|
1026
|
-
define_method severity do |message|
|
1027
|
-
@messages << [severity, message]
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
|
1031
|
-
def logs
|
1032
|
-
@messages
|
1033
|
-
end
|
1034
|
-
|
1035
|
-
def level
|
1036
|
-
1
|
1037
|
-
end
|
1038
|
-
|
1039
|
-
def level=(new_level)
|
1040
|
-
end
|
1041
|
-
end
|
1042
|
-
end
|
1043
|
-
let(:custom_logger_object) { custom_logger.new }
|
1019
|
+
let(:custom_logger_object) { TestLogger.new }
|
1044
1020
|
|
1045
1021
|
it 'logs a deprecation warning (#RSA10l)' do
|
1046
1022
|
client.auth.authorise
|
@@ -77,7 +77,13 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
|
|
77
77
|
limit.times do |index|
|
78
78
|
expect(next_page.items[index].data).to eql("history#{index + limit}")
|
79
79
|
end
|
80
|
-
|
80
|
+
if next_page.has_next?
|
81
|
+
next_page.next do |last|
|
82
|
+
expect(last.items.length).to eql(0)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
expect(next_page).to be_last
|
86
|
+
end
|
81
87
|
|
82
88
|
stop_reactor
|
83
89
|
end
|
@@ -1342,7 +1342,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1342
1342
|
|
1343
1343
|
context 'many times with different event names' do
|
1344
1344
|
it 'filters events accordingly to each callback' do
|
1345
|
-
click_callback =
|
1345
|
+
click_callback = lambda { |message| messages << message }
|
1346
1346
|
|
1347
1347
|
channel.subscribe('click', &click_callback)
|
1348
1348
|
channel.subscribe('move', &click_callback)
|
@@ -1958,7 +1958,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
1958
1958
|
specify 'all queued messages fail with NACK (#RTL11)' do
|
1959
1959
|
channel.attach do
|
1960
1960
|
# Move to disconnected
|
1961
|
-
disconnect_transport_proc =
|
1961
|
+
disconnect_transport_proc = lambda do
|
1962
1962
|
if connection.transport
|
1963
1963
|
connection.transport.close_connection_after_writing
|
1964
1964
|
else
|
@@ -2090,7 +2090,7 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
2090
2090
|
it 'will move to the SUSPENDED state and then attempt to ATTACH with the ATTACHING state (#RTL13b)' do
|
2091
2091
|
connection.once(:connected) do
|
2092
2092
|
# Prevent any incoming or outgoing ATTACH/ATTACHED message from Ably
|
2093
|
-
prevent_protocol_messages_proc =
|
2093
|
+
prevent_protocol_messages_proc = lambda do
|
2094
2094
|
if client.connection.transport
|
2095
2095
|
client.connection.transport.__incoming_protocol_msgbus__.unsubscribe
|
2096
2096
|
client.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
@@ -134,7 +134,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
134
134
|
|
135
135
|
context 'with a wildcard client_id token' do
|
136
136
|
subject { auto_close Ably::Realtime::Client.new(client_options) }
|
137
|
-
let(:client_options) { default_options.merge(auth_callback:
|
137
|
+
let(:client_options) { default_options.merge(auth_callback: lambda { |token_params| auth_token_object }, client_id: client_id) }
|
138
138
|
let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key)) }
|
139
139
|
let(:auth_token_object) { rest_auth_client.auth.request_token(client_id: '*') }
|
140
140
|
|
@@ -155,7 +155,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
155
155
|
end
|
156
156
|
|
157
157
|
context 'and client_id omitted in ClientOptions' do
|
158
|
-
let(:client_options) { default_options.merge(auth_callback:
|
158
|
+
let(:client_options) { default_options.merge(auth_callback: lambda { |token_params| auth_token_object }) }
|
159
159
|
|
160
160
|
it 'uses the token provided clientId in the connection' do
|
161
161
|
connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
@@ -143,7 +143,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
143
143
|
context 'with auth_callback' do
|
144
144
|
context 'opening a new connection' do
|
145
145
|
context 'when callback fails due to an exception' do
|
146
|
-
let(:client_options) { default_options.reject { |k, v| k == :key }.merge(auth_callback:
|
146
|
+
let(:client_options) { default_options.reject { |k, v| k == :key }.merge(auth_callback: lambda { |token_params| raise "Cannot issue token" }, log_level: :fatal) }
|
147
147
|
|
148
148
|
it 'the connection moves to the disconnected state and tries again, returning again to the disconnected state (#RSA4c, #RSA4c1, #RSA4c2)' do
|
149
149
|
states = Hash.new { |hash, key| hash[key] = [] }
|
@@ -174,7 +174,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
174
174
|
use_token_auth: true,
|
175
175
|
log_level: :fatal)
|
176
176
|
}
|
177
|
-
let(:auth_options) { { auth_callback:
|
177
|
+
let(:auth_options) { { auth_callback: lambda { |token_params| sleep 10 }, } }
|
178
178
|
|
179
179
|
it 'the authorization request fails as configured in the realtime_request_timeout (#RSA4c, #RSA4c1, #RSA4c3)' do
|
180
180
|
connection.once(:connected) do
|
@@ -514,6 +514,216 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
514
514
|
end
|
515
515
|
end
|
516
516
|
|
517
|
+
context 'connection state freshness is monitored' do
|
518
|
+
it 'resumes connections when disconnected within the connection_state_ttl period (#RTN15g)' do
|
519
|
+
connection.once(:connected) do
|
520
|
+
connection_id = connection.id
|
521
|
+
reconnected_with_resume = false
|
522
|
+
|
523
|
+
# Make sure the next connect has the resume param
|
524
|
+
allow(EventMachine).to receive(:connect).and_wrap_original do |original, *args, &block|
|
525
|
+
url = args[4]
|
526
|
+
uri = URI.parse(url)
|
527
|
+
expect(CGI::parse(uri.query)['resume'][0]).to_not be_empty
|
528
|
+
reconnected_with_resume = true
|
529
|
+
original.call(*args, &block)
|
530
|
+
end
|
531
|
+
|
532
|
+
connection.once(:disconnected) do
|
533
|
+
disconnected_at = Time.now
|
534
|
+
|
535
|
+
connection.once(:connecting) do
|
536
|
+
expect(Time.now.to_f - disconnected_at.to_f).to be < connection.connection_state_ttl
|
537
|
+
connection.once(:connected) do |state_change|
|
538
|
+
expect(connection.id).to eql(connection_id)
|
539
|
+
expect(reconnected_with_resume).to be_truthy
|
540
|
+
stop_reactor
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
connection.transport.unbind
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context 'when connection_state_ttl period has passed since being disconnected' do
|
550
|
+
let(:client_options) do
|
551
|
+
default_options.merge(
|
552
|
+
disconnected_retry_timeout: 4,
|
553
|
+
suspended_retry_timeout: 8,
|
554
|
+
max_connection_state_ttl: 2,
|
555
|
+
)
|
556
|
+
end
|
557
|
+
|
558
|
+
it 'clears the local connection state and uses a new connection when the connection_state_ttl period has passed (#RTN15g)' do
|
559
|
+
connection.once(:connected) do
|
560
|
+
connection_id = connection.id
|
561
|
+
resumed_with_clean_connection = false
|
562
|
+
|
563
|
+
connection.once(:disconnected) do
|
564
|
+
disconnected_at = Time.now
|
565
|
+
|
566
|
+
connection.once(:connecting) do
|
567
|
+
connection.once(:disconnected) do
|
568
|
+
# Make sure the next connect does not have the resume param
|
569
|
+
allow(EventMachine).to receive(:connect).and_wrap_original do |original, *args, &block|
|
570
|
+
url = args[4]
|
571
|
+
uri = URI.parse(url)
|
572
|
+
expect(CGI::parse(uri.query)['resume']).to be_empty
|
573
|
+
resumed_with_clean_connection = true
|
574
|
+
original.call(*args, &block)
|
575
|
+
end
|
576
|
+
|
577
|
+
allow(connection.details).to receive(:max_idle_interval).and_return(0)
|
578
|
+
connection.__incoming_protocol_msgbus__.plugin_listeners
|
579
|
+
|
580
|
+
connection.once(:connecting) do
|
581
|
+
expect(Time.now.to_f - disconnected_at.to_f).to be > connection.connection_state_ttl
|
582
|
+
connection.once(:connected) do |state_change|
|
583
|
+
expect(connection.id).to_not eql(connection_id)
|
584
|
+
expect(resumed_with_clean_connection).to be_truthy
|
585
|
+
stop_reactor
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
# Disconnect the transport and trigger a new disconnected state
|
591
|
+
wait_until(lambda { connection.transport }) do
|
592
|
+
connection.transport.unbind
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
connection.__incoming_protocol_msgbus__.unplug_listeners
|
597
|
+
end
|
598
|
+
|
599
|
+
connection.transport.unbind
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
context 'when connection_state_ttl period has passed since last activity on the connection' do
|
605
|
+
let(:client_options) do
|
606
|
+
default_options.merge(
|
607
|
+
max_connection_state_ttl: 2,
|
608
|
+
)
|
609
|
+
end
|
610
|
+
|
611
|
+
it 'does not clear the local connection state when the connection_state_ttl period has passed since last activity, but the idle timeout has not passed (#RTN15g1, #RTN15g2)' do
|
612
|
+
expect(connection.connection_state_ttl).to eql(client_options.fetch(:max_connection_state_ttl))
|
613
|
+
|
614
|
+
connection.once(:connected) do
|
615
|
+
connection_id = connection.id
|
616
|
+
resumed_connection = false
|
617
|
+
|
618
|
+
connection.once(:disconnected) do
|
619
|
+
disconnected_at = Time.now
|
620
|
+
|
621
|
+
allow(connection).to receive(:time_since_connection_confirmed_alive?).and_return(connection.connection_state_ttl + 1)
|
622
|
+
|
623
|
+
# Make sure the next connect does not have the resume param
|
624
|
+
allow(EventMachine).to receive(:connect).and_wrap_original do |original, *args, &block|
|
625
|
+
url = args[4]
|
626
|
+
uri = URI.parse(url)
|
627
|
+
expect(CGI::parse(uri.query)['resume']).to_not be_empty
|
628
|
+
resumed_connection = true
|
629
|
+
original.call(*args, &block)
|
630
|
+
end
|
631
|
+
|
632
|
+
connection.once(:connecting) do
|
633
|
+
connection.once(:connected) do |state_change|
|
634
|
+
expect(connection.id).to eql(connection_id)
|
635
|
+
expect(resumed_connection).to be_truthy
|
636
|
+
stop_reactor
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
connection.transport.unbind
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
it 'clears the local connection state and uses a new connection when the connection_state_ttl + max_idle_interval period has passed since last activity (#RTN15g1, #RTN15g2)' do
|
646
|
+
expect(connection.connection_state_ttl).to eql(client_options.fetch(:max_connection_state_ttl))
|
647
|
+
|
648
|
+
connection.once(:connected) do
|
649
|
+
connection_id = connection.id
|
650
|
+
resumed_with_clean_connection = false
|
651
|
+
|
652
|
+
connection.once(:disconnected) do
|
653
|
+
disconnected_at = Time.now
|
654
|
+
|
655
|
+
pseudo_time_passed = connection.connection_state_ttl + connection.details.max_idle_interval + 1
|
656
|
+
allow(connection).to receive(:time_since_connection_confirmed_alive?).and_return(pseudo_time_passed)
|
657
|
+
|
658
|
+
# Make sure the next connect does not have the resume param
|
659
|
+
allow(EventMachine).to receive(:connect).and_wrap_original do |original, *args, &block|
|
660
|
+
url = args[4]
|
661
|
+
uri = URI.parse(url)
|
662
|
+
expect(CGI::parse(uri.query)['resume']).to be_empty
|
663
|
+
resumed_with_clean_connection = true
|
664
|
+
original.call(*args, &block)
|
665
|
+
end
|
666
|
+
|
667
|
+
connection.once(:connecting) do
|
668
|
+
connection.once(:connected) do |state_change|
|
669
|
+
expect(connection.id).to_not eql(connection_id)
|
670
|
+
expect(resumed_with_clean_connection).to be_truthy
|
671
|
+
stop_reactor
|
672
|
+
end
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|
676
|
+
connection.transport.unbind
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
it 'still reattaches the channels automatically following a new connection being established (#RTN15g2)' do
|
681
|
+
connection.once(:connected) do
|
682
|
+
connection_id = connection.id
|
683
|
+
resumed_with_clean_connection = false
|
684
|
+
channel_emitted_an_attached = false
|
685
|
+
|
686
|
+
channel.attach do
|
687
|
+
channel.once(:attached) do |channel_state_change|
|
688
|
+
expect(channel_state_change.resumed).to be_falsey
|
689
|
+
channel_emitted_an_attached = true
|
690
|
+
end
|
691
|
+
|
692
|
+
connection.once(:disconnected) do
|
693
|
+
disconnected_at = Time.now
|
694
|
+
|
695
|
+
pseudo_time_passed = connection.connection_state_ttl + connection.details.max_idle_interval + 1
|
696
|
+
allow(connection).to receive(:time_since_connection_confirmed_alive?).and_return(pseudo_time_passed)
|
697
|
+
|
698
|
+
# Make sure the next connect does not have the resume param
|
699
|
+
allow(EventMachine).to receive(:connect).and_wrap_original do |original, *args, &block|
|
700
|
+
url = args[4]
|
701
|
+
uri = URI.parse(url)
|
702
|
+
expect(CGI::parse(uri.query)['resume']).to be_empty
|
703
|
+
resumed_with_clean_connection = true
|
704
|
+
original.call(*args, &block)
|
705
|
+
end
|
706
|
+
|
707
|
+
connection.once(:connecting) do
|
708
|
+
connection.once(:connected) do |state_change|
|
709
|
+
expect(connection.id).to_not eql(connection_id)
|
710
|
+
expect(resumed_with_clean_connection).to be_truthy
|
711
|
+
|
712
|
+
wait_until(lambda { channel.attached? }) do
|
713
|
+
expect(channel_emitted_an_attached).to be_truthy
|
714
|
+
stop_reactor
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
connection.transport.unbind
|
721
|
+
end
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
517
727
|
context 'and subsequently fails to reconnect' do
|
518
728
|
let(:retry_every) { 1.5 }
|
519
729
|
|
@@ -935,16 +1145,20 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
935
1145
|
previous_connection_id = connection.id
|
936
1146
|
previous_connection_key = connection.key
|
937
1147
|
|
938
|
-
five_minutes_time = Time.now + 5 * 60
|
939
|
-
allow(Time).to receive(:now) { five_minutes_time }
|
940
|
-
|
941
1148
|
connection.once(:connected) do
|
942
1149
|
expect(connection.key).to_not eql(previous_connection_key)
|
943
1150
|
expect(connection.id).to_not eql(previous_connection_id)
|
944
1151
|
stop_reactor
|
945
1152
|
end
|
946
1153
|
|
947
|
-
|
1154
|
+
# Wait until next tick before stubbing otherwise liveness test may
|
1155
|
+
# record the stubbed last contact time as the future time
|
1156
|
+
EventMachine.next_tick do
|
1157
|
+
five_minutes_time = Time.now + 5 * 60
|
1158
|
+
allow(Time).to receive(:now) { five_minutes_time }
|
1159
|
+
|
1160
|
+
kill_connection_transport_and_prevent_valid_resume
|
1161
|
+
end
|
948
1162
|
end
|
949
1163
|
end
|
950
1164
|
end
|
@@ -1017,7 +1231,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
1017
1231
|
}
|
1018
1232
|
|
1019
1233
|
let(:client_options) do
|
1020
|
-
default_options.merge(auth_callback:
|
1234
|
+
default_options.merge(auth_callback: lambda do |token_params|
|
1021
1235
|
@auth_requests ||= 0
|
1022
1236
|
@auth_requests += 1
|
1023
1237
|
|