ably-rest 0.7.3 → 0.7.5
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 +8 -8
- data/.travis.yml +1 -0
- data/SPEC.md +480 -472
- data/lib/ably-rest.rb +1 -1
- data/lib/submodules/ably-ruby/LICENSE.txt +1 -1
- data/lib/submodules/ably-ruby/README.md +107 -24
- data/lib/submodules/ably-ruby/SPEC.md +531 -398
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -16
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +9 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +17 -9
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +18 -10
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -4
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +0 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -29
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +7 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +41 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +74 -208
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +6 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +3 -1
- data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +32 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +96 -7
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +8 -0
- data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +13 -7
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +15 -14
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +17 -17
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +51 -20
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +52 -26
- data/lib/submodules/ably-ruby/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- data/spec/spec_helper.rb +5 -0
- metadata +12 -4
- data/lib/submodules/ably-ruby/.ruby-version.old +0 -1
@@ -101,7 +101,7 @@ module Ably
|
|
101
101
|
# Retrieve the Ably service time
|
102
102
|
#
|
103
103
|
# @yield [Time] The time as reported by the Ably service
|
104
|
-
# @return [
|
104
|
+
# @return [Ably::Util::SafeDeferrable]
|
105
105
|
#
|
106
106
|
def time(&success_callback)
|
107
107
|
async_wrap(success_callback) do
|
@@ -116,7 +116,7 @@ module Ably
|
|
116
116
|
#
|
117
117
|
# @yield [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
|
118
118
|
#
|
119
|
-
# @return [
|
119
|
+
# @return [Ably::Util::SafeDeferrable]
|
120
120
|
#
|
121
121
|
def stats(options = {}, &success_callback)
|
122
122
|
async_wrap(success_callback) do
|
@@ -129,6 +129,11 @@ module Ably
|
|
129
129
|
connection.close(&block)
|
130
130
|
end
|
131
131
|
|
132
|
+
# (see Ably::Realtime::Connection#connect)
|
133
|
+
def connect(&block)
|
134
|
+
connection.connect(&block)
|
135
|
+
end
|
136
|
+
|
132
137
|
# @!attribute [r] endpoint
|
133
138
|
# @return [URI::Generic] Default Ably Realtime endpoint used for all requests
|
134
139
|
def endpoint
|
@@ -41,6 +41,20 @@ module Ably::Realtime
|
|
41
41
|
logger.debug "#{protocol_message.action} received: #{protocol_message}"
|
42
42
|
end
|
43
43
|
|
44
|
+
if [:sync, :presence, :message].any? { |prevent_duplicate| protocol_message.action == prevent_duplicate }
|
45
|
+
if connection.serial && protocol_message.has_connection_serial? && protocol_message.connection_serial <= connection.serial
|
46
|
+
error_target = if protocol_message.channel
|
47
|
+
get_channel(protocol_message.channel)
|
48
|
+
else
|
49
|
+
connection
|
50
|
+
end
|
51
|
+
error_message = "Protocol error, duplicate message received for serial #{protocol_message.connection_serial}"
|
52
|
+
error_target.trigger :error, Ably::Exceptions::ProtocolError.new(error_message, 400, 80013)
|
53
|
+
logger.error error_message
|
54
|
+
return
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
44
58
|
update_connection_recovery_info protocol_message
|
45
59
|
|
46
60
|
case protocol_message.action
|
@@ -54,14 +68,14 @@ module Ably::Realtime
|
|
54
68
|
|
55
69
|
when ACTION.Connect
|
56
70
|
when ACTION.Connected
|
57
|
-
connection.transition_state_machine :connected, protocol_message.
|
71
|
+
connection.transition_state_machine :connected, protocol_message unless connection.connected?
|
58
72
|
|
59
73
|
when ACTION.Disconnect, ACTION.Disconnected
|
60
|
-
connection.transition_state_machine :disconnected, protocol_message.error
|
74
|
+
connection.transition_state_machine :disconnected, protocol_message.error unless connection.disconnected?
|
61
75
|
|
62
76
|
when ACTION.Close
|
63
77
|
when ACTION.Closed
|
64
|
-
connection.transition_state_machine :closed
|
78
|
+
connection.transition_state_machine :closed unless connection.closed?
|
65
79
|
|
66
80
|
when ACTION.Error
|
67
81
|
if protocol_message.channel && !protocol_message.has_message_serial?
|
@@ -72,18 +86,22 @@ module Ably::Realtime
|
|
72
86
|
|
73
87
|
when ACTION.Attach
|
74
88
|
when ACTION.Attached
|
75
|
-
get_channel(protocol_message.channel).
|
89
|
+
get_channel(protocol_message.channel).tap do |channel|
|
90
|
+
channel.transition_state_machine :attached, protocol_message unless channel.attached?
|
91
|
+
end
|
76
92
|
|
77
93
|
when ACTION.Detach
|
78
94
|
when ACTION.Detached
|
79
|
-
get_channel(protocol_message.channel).
|
95
|
+
get_channel(protocol_message.channel).tap do |channel|
|
96
|
+
channel.transition_state_machine :detached unless channel.detached?
|
97
|
+
end
|
80
98
|
|
81
99
|
when ACTION.Sync
|
82
100
|
presence = get_channel(protocol_message.channel).presence
|
83
101
|
protocol_message.presence.each do |presence_message|
|
84
102
|
presence.__incoming_msgbus__.publish :sync, presence_message
|
85
103
|
end
|
86
|
-
presence.update_sync_serial protocol_message.channel_serial
|
104
|
+
presence.members.update_sync_serial protocol_message.channel_serial
|
87
105
|
|
88
106
|
when ACTION.Presence
|
89
107
|
presence = get_channel(protocol_message.channel).presence
|
@@ -98,7 +116,9 @@ module Ably::Realtime
|
|
98
116
|
end
|
99
117
|
|
100
118
|
else
|
101
|
-
|
119
|
+
error = Ably::Exceptions::ProtocolError.new("Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher", 400, 80013)
|
120
|
+
client.connection.trigger :error, error
|
121
|
+
logger.fatal error.message
|
102
122
|
end
|
103
123
|
end
|
104
124
|
|
@@ -116,24 +136,7 @@ module Ably::Realtime
|
|
116
136
|
end
|
117
137
|
|
118
138
|
def update_connection_recovery_info(protocol_message)
|
119
|
-
|
120
|
-
logger.debug "New connection ID set to #{protocol_message.connection_id} with connection key #{protocol_message.connection_key}"
|
121
|
-
detach_attached_channels protocol_message.error if protocol_message.error
|
122
|
-
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
123
|
-
end
|
124
|
-
|
125
|
-
if protocol_message.has_connection_serial?
|
126
|
-
connection.update_connection_serial protocol_message.connection_serial
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def detach_attached_channels(error)
|
131
|
-
channels.select do |channel|
|
132
|
-
channel.attached? || channel.attaching?
|
133
|
-
end.each do |channel|
|
134
|
-
logger.warn "Detaching channel '#{channel.name}': #{error}"
|
135
|
-
channel.manager.suspend error
|
136
|
-
end
|
139
|
+
connection.update_connection_serial protocol_message.connection_serial if protocol_message.has_connection_serial?
|
137
140
|
end
|
138
141
|
|
139
142
|
def ack_pending_queue_for_message_serial(ack_protocol_message)
|
@@ -166,7 +169,7 @@ module Ably::Realtime
|
|
166
169
|
|
167
170
|
def drop_pending_queue_from_ack(ack_protocol_message)
|
168
171
|
message_serial_up_to = ack_protocol_message.message_serial + ack_protocol_message.count - 1
|
169
|
-
connection.
|
172
|
+
connection.__pending_message_ack_queue__.drop_while do |protocol_message|
|
170
173
|
if protocol_message.message_serial <= message_serial_up_to
|
171
174
|
yield protocol_message
|
172
175
|
true
|
@@ -31,8 +31,8 @@ module Ably::Realtime
|
|
31
31
|
connection.__outgoing_message_queue__
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
connection.
|
34
|
+
def pending_ack_queue
|
35
|
+
connection.__pending_message_ack_queue__
|
36
36
|
end
|
37
37
|
|
38
38
|
def current_transport_outgoing_message_bus
|
@@ -47,7 +47,7 @@ module Ably::Realtime
|
|
47
47
|
current_transport_outgoing_message_bus.publish :protocol_message, protocol_message
|
48
48
|
|
49
49
|
if protocol_message.ack_required?
|
50
|
-
|
50
|
+
pending_ack_queue << protocol_message
|
51
51
|
else
|
52
52
|
protocol_message.succeed protocol_message
|
53
53
|
end
|
@@ -61,7 +61,7 @@ module Ably::Realtime
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def setup_event_handlers
|
64
|
-
connection.
|
64
|
+
connection.unsafe_on(:connected) do
|
65
65
|
deliver_queued_protocol_messages
|
66
66
|
end
|
67
67
|
end
|
@@ -37,6 +37,7 @@ module Ably
|
|
37
37
|
class Connection
|
38
38
|
include Ably::Modules::EventEmitter
|
39
39
|
include Ably::Modules::Conversions
|
40
|
+
include Ably::Modules::SafeYield
|
40
41
|
extend Ably::Modules::Enum
|
41
42
|
|
42
43
|
# Valid Connection states
|
@@ -94,14 +95,14 @@ module Ably
|
|
94
95
|
# An internal queue used to manage sent messages. You should never interface with this array directly
|
95
96
|
# @return [Array]
|
96
97
|
# @api private
|
97
|
-
attr_reader :
|
98
|
+
attr_reader :__pending_message_ack_queue__
|
98
99
|
|
99
100
|
# @api public
|
100
101
|
def initialize(client)
|
101
|
-
@client
|
102
|
-
@client_serial
|
103
|
-
@__outgoing_message_queue__
|
104
|
-
@
|
102
|
+
@client = client
|
103
|
+
@client_serial = -1
|
104
|
+
@__outgoing_message_queue__ = []
|
105
|
+
@__pending_message_ack_queue__ = []
|
105
106
|
|
106
107
|
Client::IncomingMessageDispatcher.new client, self
|
107
108
|
Client::OutgoingMessageDispatcher.new client, self
|
@@ -156,7 +157,7 @@ module Ably
|
|
156
157
|
#
|
157
158
|
# @return [void]
|
158
159
|
#
|
159
|
-
def ping
|
160
|
+
def ping(&block)
|
160
161
|
raise RuntimeError, 'Cannot send a ping when connection is not open' if initialized?
|
161
162
|
raise RuntimeError, 'Cannot send a ping when connection is in a closed or failed state' if closed? || failed?
|
162
163
|
|
@@ -166,7 +167,7 @@ module Ably
|
|
166
167
|
if protocol_message.action == Ably::Models::ProtocolMessage::ACTION.Heartbeat
|
167
168
|
__incoming_protocol_msgbus__.unsubscribe(:protocol_message, &wait_for_ping)
|
168
169
|
time_passed = (Time.now.to_f * 1000 - started.to_f * 1000).to_i
|
169
|
-
|
170
|
+
safe_yield block, time_passed if block_given?
|
170
171
|
end
|
171
172
|
end
|
172
173
|
|
@@ -202,7 +203,7 @@ module Ably
|
|
202
203
|
"#{key}:#{serial}" if connection_resumable?
|
203
204
|
end
|
204
205
|
|
205
|
-
# Following a new connection being made,
|
206
|
+
# Following a new connection being made, the connection ID, connection key
|
206
207
|
# and message serial need to match the details provided by the server.
|
207
208
|
#
|
208
209
|
# @return [void]
|
@@ -290,7 +291,7 @@ module Ably
|
|
290
291
|
# @api private
|
291
292
|
def send_protocol_message(protocol_message)
|
292
293
|
add_message_serial_if_ack_required_to(protocol_message) do
|
293
|
-
Ably::Models::ProtocolMessage.new(protocol_message).tap do |protocol_message|
|
294
|
+
Ably::Models::ProtocolMessage.new(protocol_message, logger: logger).tap do |protocol_message|
|
294
295
|
add_message_to_outgoing_queue protocol_message
|
295
296
|
notify_message_dispatcher_of_new_message protocol_message
|
296
297
|
logger.debug("Connection: Prot msg queued =>: #{protocol_message.action} #{protocol_message}")
|
@@ -362,6 +363,29 @@ module Ably
|
|
362
363
|
@error_reason = error
|
363
364
|
end
|
364
365
|
|
366
|
+
# @api private
|
367
|
+
def clear_error_reason
|
368
|
+
@error_reason = nil
|
369
|
+
end
|
370
|
+
|
371
|
+
# Triggers registered callbacks for a successful connection resume event
|
372
|
+
# @api private
|
373
|
+
def resumed
|
374
|
+
resume_callbacks.each(&:call)
|
375
|
+
end
|
376
|
+
|
377
|
+
# Provides a simple hook to inject a callback when a connection is successfully resumed
|
378
|
+
# @api private
|
379
|
+
def on_resume(&callback)
|
380
|
+
resume_callbacks << callback
|
381
|
+
end
|
382
|
+
|
383
|
+
# Remove a registered connection resume callback
|
384
|
+
# @api private
|
385
|
+
def off_resume(&callback)
|
386
|
+
resume_callbacks.delete(callback)
|
387
|
+
end
|
388
|
+
|
365
389
|
# As we are using a state machine, do not allow change_state to be used
|
366
390
|
# #transition_state_machine must be used instead
|
367
391
|
private :change_state
|
@@ -378,6 +402,10 @@ module Ably
|
|
378
402
|
# @return [Integer] starting at -1 indicating no messages sent, 0 when the first message is sent
|
379
403
|
attr_reader :client_serial
|
380
404
|
|
405
|
+
def resume_callbacks
|
406
|
+
@resume_callbacks ||= []
|
407
|
+
end
|
408
|
+
|
381
409
|
def create_pub_sub_message_bus
|
382
410
|
Ably::Util::PubSub.new(
|
383
411
|
coerce_into: Proc.new do |event|
|
@@ -443,3 +471,7 @@ module Ably
|
|
443
471
|
end
|
444
472
|
end
|
445
473
|
end
|
474
|
+
|
475
|
+
require 'ably/realtime/connection/connection_manager'
|
476
|
+
require 'ably/realtime/connection/connection_state_machine'
|
477
|
+
require 'ably/realtime/connection/websocket_transport'
|
@@ -29,11 +29,11 @@ module Ably::Realtime
|
|
29
29
|
@connection = connection
|
30
30
|
@timers = Hash.new { |hash, key| hash[key] = [] }
|
31
31
|
|
32
|
-
connection.
|
32
|
+
connection.unsafe_on(:closed) do
|
33
33
|
connection.reset_resume_info
|
34
34
|
end
|
35
35
|
|
36
|
-
connection.
|
36
|
+
connection.unsafe_once(:connecting) do
|
37
37
|
close_connection_when_reactor_is_stopped
|
38
38
|
end
|
39
39
|
|
@@ -75,15 +75,26 @@ module Ably::Realtime
|
|
75
75
|
# @api private
|
76
76
|
def connection_opening_failed(error)
|
77
77
|
logger.warn "ConnectionManager: Connection to #{connection.current_host}:#{connection.port} failed; #{error.message}"
|
78
|
-
connection.transition_state_machine next_retry_state, Ably::Exceptions::ConnectionError.new("Connection failed
|
78
|
+
connection.transition_state_machine next_retry_state, Ably::Exceptions::ConnectionError.new("Connection failed: #{error.message}", nil, 80000)
|
79
79
|
end
|
80
80
|
|
81
|
-
# Called whenever a new connection
|
81
|
+
# Called whenever a new connection is made
|
82
82
|
#
|
83
83
|
# @api private
|
84
|
-
def
|
85
|
-
|
86
|
-
|
84
|
+
def connected(protocol_message)
|
85
|
+
if connection.key
|
86
|
+
if protocol_message.connection_key == connection.key
|
87
|
+
logger.debug "ConnectionManager: Connection resumed successfully - ID #{connection.id} and key #{connection.key}"
|
88
|
+
EventMachine.next_tick { connection.resumed }
|
89
|
+
else
|
90
|
+
logger.debug "ConnectionManager: Connection was not resumed, old connection ID #{connection.id} has been updated with new connect ID #{protocol_message.connection_id} and key #{protocol_message.connection_key}"
|
91
|
+
detach_attached_channels protocol_message.error
|
92
|
+
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
93
|
+
end
|
94
|
+
else
|
95
|
+
logger.debug "ConnectionManager: New connection created with ID #{protocol_message.connection_id} and key #{protocol_message.connection_key}"
|
96
|
+
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
97
|
+
end
|
87
98
|
end
|
88
99
|
|
89
100
|
# Ensures the underlying transport has been disconnected and all event emitter callbacks removed
|
@@ -133,7 +144,7 @@ module Ably::Realtime
|
|
133
144
|
def fail(error)
|
134
145
|
connection.logger.fatal "ConnectionManager: Connection failed - #{error}"
|
135
146
|
connection.manager.destroy_transport
|
136
|
-
connection.
|
147
|
+
connection.unsafe_once(:failed) { connection.trigger :error, error }
|
137
148
|
end
|
138
149
|
|
139
150
|
# When a connection is disconnected whilst connecting, attempt reconnect and/or set state to :suspended or :failed
|
@@ -143,6 +154,12 @@ module Ably::Realtime
|
|
143
154
|
return unless connection.disconnected? || connection.suspended? # do nothing if state has changed through an explicit request
|
144
155
|
return unless retry_connection? # do not always reattempt connection or change state as client may be re-authorising
|
145
156
|
|
157
|
+
error = current_transition.metadata
|
158
|
+
if error.kind_of?(Ably::Models::ErrorInfo)
|
159
|
+
renew_token_and_reconnect error if error.code == RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
160
|
+
return
|
161
|
+
end
|
162
|
+
|
146
163
|
unless connection_retry_from_suspended_state?
|
147
164
|
return if connection_retry_for(:disconnected, ignore_states: [:connecting])
|
148
165
|
end
|
@@ -159,9 +176,10 @@ module Ably::Realtime
|
|
159
176
|
def respond_to_transport_disconnected_whilst_connected(current_transition)
|
160
177
|
logger.warn "ConnectionManager: Connection to #{connection.transport.url} was disconnected unexpectedly"
|
161
178
|
|
162
|
-
|
163
|
-
|
164
|
-
|
179
|
+
error = current_transition.metadata
|
180
|
+
if error.kind_of?(Ably::Models::ErrorInfo) && error.code != RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
181
|
+
connection.trigger :error, error
|
182
|
+
logger.error "ConnectionManager: Error in Disconnected ProtocolMessage received from the server - #{error}"
|
165
183
|
end
|
166
184
|
|
167
185
|
destroy_transport
|
@@ -176,7 +194,7 @@ module Ably::Realtime
|
|
176
194
|
case error.code
|
177
195
|
when RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
178
196
|
connection.transition_state_machine :disconnected
|
179
|
-
connection.
|
197
|
+
connection.unsafe_once_or_if(:disconnected) do
|
180
198
|
renew_token_and_reconnect error
|
181
199
|
end
|
182
200
|
else
|
@@ -207,6 +225,10 @@ module Ably::Realtime
|
|
207
225
|
connection.client
|
208
226
|
end
|
209
227
|
|
228
|
+
def channels
|
229
|
+
client.channels
|
230
|
+
end
|
231
|
+
|
210
232
|
# Create a timer that will execute in timeout_in seconds.
|
211
233
|
# If the connection state changes however, cancel the timer
|
212
234
|
def create_timeout_timer_whilst_in_state(timer_id, timeout_in)
|
@@ -215,7 +237,7 @@ module Ably::Realtime
|
|
215
237
|
timers[timer_id] << EventMachine::Timer.new(timeout_in) do
|
216
238
|
yield
|
217
239
|
end
|
218
|
-
connection.
|
240
|
+
connection.unsafe_once_state_changed { clear_timers timer_id }
|
219
241
|
end
|
220
242
|
|
221
243
|
def clear_timers(key)
|
@@ -245,10 +267,15 @@ module Ably::Realtime
|
|
245
267
|
def connection_retry_for(from_state, options = {})
|
246
268
|
retry_params = CONNECT_RETRY_CONFIG.fetch(from_state)
|
247
269
|
|
248
|
-
if time_spent_attempting_state(from_state, options)
|
249
|
-
|
250
|
-
|
251
|
-
|
270
|
+
if time_spent_attempting_state(from_state, options) < retry_params.fetch(:max_time_in_state)
|
271
|
+
if retries_for_state(from_state, ignore_states: [:connecting]).empty?
|
272
|
+
logger.debug "ConnectionManager: Will attempt reconnect immediately as no previous reconnect attempts made in this state"
|
273
|
+
EventMachine.next_tick { connection.connect }
|
274
|
+
else
|
275
|
+
logger.debug "ConnectionManager: Pausing for #{retry_params.fetch(:retry_every)}s before attempting to reconnect"
|
276
|
+
create_timeout_timer_whilst_in_state(:reconnect, retry_params.fetch(:retry_every)) do
|
277
|
+
connection.connect if connection.state == from_state
|
278
|
+
end
|
252
279
|
end
|
253
280
|
true
|
254
281
|
end
|
@@ -282,7 +309,16 @@ module Ably::Realtime
|
|
282
309
|
ignore_states = options.fetch(:ignore_states, [])
|
283
310
|
allowed_states = Array(state) + Array(ignore_states)
|
284
311
|
|
285
|
-
connection.state_history.reverse
|
312
|
+
state_history_ordered = connection.state_history.reverse
|
313
|
+
last_state = state_history_ordered.first
|
314
|
+
|
315
|
+
# If this method is called after the transition has been persisted to memory,
|
316
|
+
# then we need to ignore the current transition when reviewing the number of retries
|
317
|
+
if last_state[:state].to_sym == state && last_state.fetch(:transitioned_at).to_f > Time.now.to_f - 0.1
|
318
|
+
state_history_ordered.shift
|
319
|
+
end
|
320
|
+
|
321
|
+
state_history_ordered.take_while do |transition|
|
286
322
|
allowed_states.include?(transition[:state].to_sym)
|
287
323
|
end.select do |transition|
|
288
324
|
transition[:state] == state
|
@@ -290,15 +326,18 @@ module Ably::Realtime
|
|
290
326
|
end
|
291
327
|
|
292
328
|
def subscribe_to_transport_events(transport)
|
293
|
-
transport.__incoming_protocol_msgbus__.
|
329
|
+
transport.__incoming_protocol_msgbus__.unsafe_on(:protocol_message) do |protocol_message|
|
294
330
|
connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
295
331
|
end
|
296
332
|
|
297
|
-
transport.
|
333
|
+
transport.unsafe_on(:disconnected) do |reason|
|
298
334
|
if connection.closing?
|
299
335
|
connection.transition_state_machine :closed
|
300
336
|
elsif !connection.closed? && !connection.disconnected?
|
301
|
-
|
337
|
+
exception = if reason
|
338
|
+
Ably::Exceptions::ConnectionClosedError.new(reason)
|
339
|
+
end
|
340
|
+
connection.transition_state_machine :disconnected, exception
|
302
341
|
end
|
303
342
|
end
|
304
343
|
end
|
@@ -328,10 +367,10 @@ module Ably::Realtime
|
|
328
367
|
connection.off &state_changed_callback
|
329
368
|
end
|
330
369
|
|
331
|
-
connection.
|
370
|
+
connection.unsafe_once :connected, :closed, :failed, &state_changed_callback
|
332
371
|
|
333
372
|
if token && !token.expired?
|
334
|
-
|
373
|
+
connection.connect
|
335
374
|
else
|
336
375
|
connection.transition_state_machine :failed, error unless connection.failed?
|
337
376
|
end
|
@@ -339,7 +378,7 @@ module Ably::Realtime
|
|
339
378
|
|
340
379
|
EventMachine.defer operation, callback
|
341
380
|
else
|
342
|
-
logger.
|
381
|
+
logger.error "ConnectionManager: Token has expired and is not renewable"
|
343
382
|
connection.transition_state_machine :failed, error
|
344
383
|
end
|
345
384
|
end
|
@@ -360,6 +399,15 @@ module Ably::Realtime
|
|
360
399
|
!@renewing_token
|
361
400
|
end
|
362
401
|
|
402
|
+
def detach_attached_channels(error)
|
403
|
+
channels.select do |channel|
|
404
|
+
channel.attached? || channel.attaching?
|
405
|
+
end.each do |channel|
|
406
|
+
logger.warn "Force detaching channel '#{channel.name}': #{error}"
|
407
|
+
channel.manager.suspend error
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
363
411
|
def logger
|
364
412
|
connection.logger
|
365
413
|
end
|