ably-rest 1.2.4 → 1.2.6
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/README.md +11 -8
- data/lib/submodules/ably-ruby/.github/workflows/check.yml +2 -2
- data/lib/submodules/ably-ruby/CHANGELOG.md +27 -0
- data/lib/submodules/ably-ruby/README.md +24 -7
- data/lib/submodules/ably-ruby/SPEC.md +1722 -853
- data/lib/submodules/ably-ruby/ably.gemspec +2 -2
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +19 -11
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +5 -26
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +4 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +51 -48
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_properties.rb +9 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +4 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +20 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -13
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +14 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +21 -22
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +78 -110
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +41 -92
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +12 -17
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +15 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/push.rb +0 -27
- data/lib/submodules/ably-ruby/lib/ably/realtime/recovery_key_context.rb +36 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +4 -6
- data/lib/submodules/ably-ruby/lib/ably/rest/push/admin.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/push.rb +0 -19
- data/lib/submodules/ably-ruby/lib/ably/util/ably_extensions.rb +29 -0
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/version.rb +5 -7
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +8 -12
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +474 -300
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +8 -25
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +33 -125
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +23 -52
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +123 -92
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +9 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +8 -11
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +20 -15
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +0 -78
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +4 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +0 -30
- data/lib/submodules/ably-ruby/spec/unit/realtime/recovery_key_context_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +15 -15
- metadata +20 -5
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_spec.rb +0 -27
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_spec.rb +0 -25
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'eventmachine', '~> 1.2.6'
|
22
|
-
spec.add_runtime_dependency 'em-http-request', '~> 1.1'
|
22
|
+
spec.add_runtime_dependency 'ably-em-http-request', '~> 1.1.8'
|
23
23
|
spec.add_runtime_dependency 'statesman', '~> 9.0'
|
24
24
|
spec.add_runtime_dependency 'faraday', '~> 2.2'
|
25
25
|
spec.add_runtime_dependency 'faraday-typhoeus', '~> 0.2.0'
|
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
|
|
38
38
|
spec.add_development_dependency 'rspec-instafail', '~> 1.0'
|
39
39
|
spec.add_development_dependency 'bundler', '>= 1.3.0'
|
40
40
|
spec.add_development_dependency 'webmock', '~> 3.11'
|
41
|
-
spec.add_development_dependency 'simplecov', '~> 0.
|
41
|
+
spec.add_development_dependency 'simplecov', '~> 0.22.0'
|
42
42
|
spec.add_development_dependency 'simplecov-lcov', '~> 0.8.0'
|
43
43
|
spec.add_development_dependency 'parallel_tests', '~> 3.8'
|
44
44
|
spec.add_development_dependency 'pry', '~> 0.14.1'
|
@@ -414,13 +414,20 @@ module Ably
|
|
414
414
|
#
|
415
415
|
# @return [Hash] headers
|
416
416
|
def extra_auth_headers
|
417
|
-
if
|
418
|
-
{ 'X-Ably-ClientId' => Base64.urlsafe_encode64(
|
417
|
+
if client_id_for_request
|
418
|
+
{ 'X-Ably-ClientId' => Base64.urlsafe_encode64(client_id_for_request) }
|
419
419
|
else
|
420
420
|
{}
|
421
421
|
end
|
422
422
|
end
|
423
423
|
|
424
|
+
# ClientId that needs to be included with every rest/realtime request
|
425
|
+
# spec - RSA7e
|
426
|
+
# @return string
|
427
|
+
def client_id_for_request
|
428
|
+
options[:client_id]
|
429
|
+
end
|
430
|
+
|
424
431
|
# Auth params used in URI endpoint for Realtime connections
|
425
432
|
# Will reauthorize implicitly if required and capable
|
426
433
|
#
|
@@ -482,15 +489,16 @@ module Ably
|
|
482
489
|
#
|
483
490
|
# @api private
|
484
491
|
def configure_client_id(new_client_id)
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
492
|
+
if has_client_id?
|
493
|
+
# If new client ID from Ably is a wildcard, but preconfigured clientId is set, then keep the existing clientId
|
494
|
+
if new_client_id == "*"
|
495
|
+
@client_id_validated = true
|
496
|
+
return
|
497
|
+
end
|
498
|
+
# If client_id is defined and not a wildcard, prevent it changing, this is not supported
|
499
|
+
if new_client_id != client_id
|
500
|
+
raise Ably::Exceptions::IncompatibleClientId.new("Client ID is immutable once configured for a client. Client ID cannot be changed to '#{new_client_id}'")
|
501
|
+
end
|
494
502
|
end
|
495
503
|
@client_id_validated = true
|
496
504
|
@client_id = new_client_id
|
@@ -20,8 +20,6 @@ module Ably::Models
|
|
20
20
|
# @return [String] Contains a serial number for a message on the current channel
|
21
21
|
# @!attribute [r] connection_id
|
22
22
|
# @return [String] Contains a string private connection key used to recover this connection
|
23
|
-
# @!attribute [r] connection_serial
|
24
|
-
# @return [Bignum] Contains a serial number for a message sent from the server to the client
|
25
23
|
# @!attribute [r] message_serial
|
26
24
|
# @return [Bignum] Contains a serial number for a message sent from the client to the server
|
27
25
|
# @!attribute [r] timestamp
|
@@ -129,12 +127,6 @@ module Ably::Models
|
|
129
127
|
raise TypeError, "msg_serial '#{attributes[:msg_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
130
128
|
end
|
131
129
|
|
132
|
-
def connection_serial
|
133
|
-
Integer(attributes[:connection_serial])
|
134
|
-
rescue TypeError
|
135
|
-
raise TypeError, "connection_serial '#{attributes[:connection_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
|
136
|
-
end
|
137
|
-
|
138
130
|
def count
|
139
131
|
[1, attributes[:count].to_i].max
|
140
132
|
end
|
@@ -146,26 +138,12 @@ module Ably::Models
|
|
146
138
|
false
|
147
139
|
end
|
148
140
|
|
149
|
-
|
150
|
-
|
151
|
-
connection_serial && true
|
141
|
+
def has_channel_serial?
|
142
|
+
channel_serial && true
|
152
143
|
rescue TypeError
|
153
144
|
false
|
154
145
|
end
|
155
146
|
|
156
|
-
def serial
|
157
|
-
if has_connection_serial?
|
158
|
-
connection_serial
|
159
|
-
else
|
160
|
-
message_serial
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# @api private
|
165
|
-
def has_serial?
|
166
|
-
has_connection_serial? || has_message_serial?
|
167
|
-
end
|
168
|
-
|
169
147
|
def messages
|
170
148
|
@messages ||=
|
171
149
|
Array(attributes[:messages]).map do |message|
|
@@ -271,7 +249,7 @@ module Ably::Models
|
|
271
249
|
# Return a JSON ready object from the underlying #attributes using Ably naming conventions for keys
|
272
250
|
def as_json(*args)
|
273
251
|
raise TypeError, ':action is missing, cannot generate a valid Hash for ProtocolMessage' unless action
|
274
|
-
raise TypeError, ':msg_serial
|
252
|
+
raise TypeError, ':msg_serial is missing, cannot generate a valid Hash for ProtocolMessage' if ack_required? && !has_message_serial?
|
275
253
|
|
276
254
|
attributes.dup.tap do |hash_object|
|
277
255
|
hash_object['action'] = action.to_i
|
@@ -296,11 +274,12 @@ module Ably::Models
|
|
296
274
|
end
|
297
275
|
|
298
276
|
# True if the ProtocolMessage appears to be invalid, however this is not a guarantee
|
277
|
+
# Used for validating incoming protocol messages, so no need to add unnecessary checks
|
299
278
|
# @return [Boolean]
|
300
279
|
# @api private
|
301
280
|
def invalid?
|
302
281
|
action_enum = action rescue nil
|
303
|
-
!action_enum
|
282
|
+
!action_enum
|
304
283
|
end
|
305
284
|
|
306
285
|
# @!attribute [r] logger
|
@@ -39,7 +39,7 @@ module Ably::Modules
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
# Mark the Deferrable as succeeded and trigger all callbacks.
|
42
|
+
# Mark the Deferrable as succeeded and trigger all success callbacks.
|
43
43
|
# See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#succeed-instance_method
|
44
44
|
#
|
45
45
|
# @return [void]
|
@@ -48,7 +48,7 @@ module Ably::Modules
|
|
48
48
|
super(*args)
|
49
49
|
end
|
50
50
|
|
51
|
-
# Mark the Deferrable as failed and trigger all callbacks.
|
51
|
+
# Mark the Deferrable as failed and trigger all error callbacks.
|
52
52
|
# See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#fail-instance_method
|
53
53
|
#
|
54
54
|
# @return [void]
|
@@ -3,7 +3,7 @@ module Ably::Modules
|
|
3
3
|
# the instance variable @state is used exclusively, the {Enum} STATE is defined prior to inclusion of this
|
4
4
|
# module, and the class is an {EventEmitter}. It then emits state changes.
|
5
5
|
#
|
6
|
-
# It also ensures the EventEmitter is configured to
|
6
|
+
# It also ensures the EventEmitter is configured to restrict permitted events to the
|
7
7
|
# the available STATEs or EVENTs if defined i.e. if EVENTs includes an additional type such as
|
8
8
|
# :update, then it will support all EVENTs being emitted. EVENTs must be a superset of STATEs
|
9
9
|
#
|
@@ -226,6 +226,10 @@ module Ably
|
|
226
226
|
auth_sync.auth_header
|
227
227
|
end
|
228
228
|
|
229
|
+
def client_id_for_request_sync
|
230
|
+
auth_sync.client_id_for_request
|
231
|
+
end
|
232
|
+
|
229
233
|
# Auth params used in URI endpoint for Realtime connections
|
230
234
|
# Will reauthorize implicitly if required and capable
|
231
235
|
#
|
@@ -18,7 +18,7 @@ module Ably::Realtime
|
|
18
18
|
def attach
|
19
19
|
if can_transition_to?(:attached)
|
20
20
|
connect_if_connection_initialized
|
21
|
-
send_attach_protocol_message
|
21
|
+
send_attach_protocol_message if connection.state?(:connected) # RTL4i
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -34,9 +34,9 @@ module Ably::Realtime
|
|
34
34
|
# Channel is attached, notify presence if sync is expected
|
35
35
|
def attached(attached_protocol_message)
|
36
36
|
# If no attached ProtocolMessage then this attached request was triggered by the client
|
37
|
-
# library, such as returning to attached
|
37
|
+
# library, such as returning to attached when detach has failed
|
38
38
|
if attached_protocol_message
|
39
|
-
|
39
|
+
channel.presence.manager.on_attach attached_protocol_message.has_presence_flag?
|
40
40
|
channel.properties.set_attach_serial(attached_protocol_message.channel_serial)
|
41
41
|
channel.options.set_modes_from_flags(attached_protocol_message.flags)
|
42
42
|
channel.options.set_params(attached_protocol_message.params)
|
@@ -49,17 +49,16 @@ module Ably::Realtime
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Request channel to be reattached by sending an attach protocol message
|
52
|
-
# @param [
|
53
|
-
|
54
|
-
def request_reattach(options = {})
|
55
|
-
reason = options[:reason]
|
56
|
-
send_attach_protocol_message
|
57
|
-
logger.debug { "Explicit channel reattach request sent to Ably due to #{reason}" }
|
52
|
+
# @param [Ably::Models::ErrorInfo] reason
|
53
|
+
def request_reattach(reason = nil)
|
58
54
|
channel.set_channel_error_reason(reason) if reason
|
59
55
|
channel.transition_state_machine! :attaching, reason: reason unless channel.attaching?
|
56
|
+
send_attach_protocol_message
|
57
|
+
logger.debug { "Explicit channel reattach request sent to Ably due to #{reason}" }
|
60
58
|
end
|
61
59
|
|
62
60
|
def duplicate_attached_received(protocol_message)
|
61
|
+
logger.debug { "Server initiated ATTACHED message received for channel '#{channel.name}' with state #{channel.state}" }
|
63
62
|
if protocol_message.error
|
64
63
|
channel.set_channel_error_reason protocol_message.error
|
65
64
|
log_channel_error protocol_message.error
|
@@ -68,9 +67,7 @@ module Ably::Realtime
|
|
68
67
|
channel.properties.set_attach_serial(protocol_message.channel_serial)
|
69
68
|
channel.options.set_modes_from_flags(protocol_message.flags)
|
70
69
|
|
71
|
-
|
72
|
-
logger.debug { "ChannelManager: Additional resumed ATTACHED message received for #{channel.state} channel '#{channel.name}'" }
|
73
|
-
else
|
70
|
+
unless protocol_message.has_channel_resumed_flag?
|
74
71
|
channel.emit :update, Ably::Models::ChannelStateChange.new(
|
75
72
|
current: channel.state,
|
76
73
|
previous: channel.state,
|
@@ -78,7 +75,7 @@ module Ably::Realtime
|
|
78
75
|
reason: protocol_message.error,
|
79
76
|
resumed: false,
|
80
77
|
)
|
81
|
-
|
78
|
+
channel.presence.manager.on_attach protocol_message.has_presence_flag?
|
82
79
|
end
|
83
80
|
end
|
84
81
|
|
@@ -170,6 +167,12 @@ module Ably::Realtime
|
|
170
167
|
end
|
171
168
|
end
|
172
169
|
|
170
|
+
# RTL13c
|
171
|
+
def notify_state_change
|
172
|
+
@pending_state_change_timer.cancel if @pending_state_change_timer
|
173
|
+
@pending_state_change_timer = nil
|
174
|
+
end
|
175
|
+
|
173
176
|
private
|
174
177
|
attr_reader :pending_state_change_timer
|
175
178
|
|
@@ -209,56 +212,56 @@ module Ably::Realtime
|
|
209
212
|
message_options[:flags] = message_options[:flags].to_i | Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[:resume]
|
210
213
|
end
|
211
214
|
|
212
|
-
|
213
|
-
end
|
214
|
-
|
215
|
-
def send_detach_protocol_message(previous_state)
|
216
|
-
send_state_change_protocol_message Ably::Models::ProtocolMessage::ACTION.Detach, previous_state # return to previous state if failed
|
217
|
-
end
|
215
|
+
message_options[:channelSerial] = channel.properties.channel_serial # RTL4c1
|
218
216
|
|
219
|
-
def send_state_change_protocol_message(new_state, state_if_failed, message_options = {})
|
220
217
|
state_at_time_of_request = channel.state
|
218
|
+
attach_action = Ably::Models::ProtocolMessage::ACTION.Attach
|
219
|
+
# RTL4f
|
221
220
|
@pending_state_change_timer = EventMachine::Timer.new(realtime_request_timeout) do
|
222
221
|
if channel.state == state_at_time_of_request
|
223
|
-
error = Ably::Models::ErrorInfo.new(code: Ably::Exceptions::Codes::CHANNEL_OPERATION_FAILED_NO_RESPONSE_FROM_SERVER, message: "Channel #{
|
224
|
-
channel.transition_state_machine
|
222
|
+
error = Ably::Models::ErrorInfo.new(code: Ably::Exceptions::Codes::CHANNEL_OPERATION_FAILED_NO_RESPONSE_FROM_SERVER, message: "Channel #{attach_action} operation failed (timed out)")
|
223
|
+
channel.transition_state_machine :suspended, reason: error # return to suspended state if failed
|
225
224
|
end
|
226
225
|
end
|
226
|
+
# Shouldn't queue attach message as per RTL4i, so message is added top of the queue
|
227
|
+
# to be sent immediately while processing next message
|
228
|
+
connection.send_protocol_message_immediately(
|
229
|
+
action: attach_action.to_i,
|
230
|
+
channel: channel.name,
|
231
|
+
**message_options.to_h
|
232
|
+
)
|
233
|
+
end
|
227
234
|
|
228
|
-
|
229
|
-
|
230
|
-
|
235
|
+
def send_detach_protocol_message(previous_state)
|
236
|
+
state_at_time_of_request = channel.state
|
237
|
+
detach_action = Ably::Models::ProtocolMessage::ACTION.Detach
|
238
|
+
|
239
|
+
@pending_state_change_timer = EventMachine::Timer.new(realtime_request_timeout) do
|
240
|
+
if channel.state == state_at_time_of_request
|
241
|
+
error = Ably::Models::ErrorInfo.new(code: Ably::Exceptions::Codes::CHANNEL_OPERATION_FAILED_NO_RESPONSE_FROM_SERVER, message: "Channel #{detach_action} operation failed (timed out)")
|
242
|
+
channel.transition_state_machine previous_state, reason: error # return to previous state if failed
|
243
|
+
end
|
231
244
|
end
|
232
245
|
|
233
|
-
|
246
|
+
on_disconnected_and_connected = lambda do |&block|
|
234
247
|
connection.unsafe_once(:disconnected) do
|
235
|
-
next unless pending_state_change_timer
|
236
248
|
connection.unsafe_once(:connected) do
|
237
|
-
|
238
|
-
|
239
|
-
action: new_state.to_i,
|
240
|
-
channel: channel.name,
|
241
|
-
**message_options.to_h
|
242
|
-
)
|
243
|
-
resend_if_disconnected_and_connected.call
|
244
|
-
end
|
249
|
+
block.call if pending_state_change_timer
|
250
|
+
end if pending_state_change_timer
|
245
251
|
end
|
246
252
|
end
|
247
|
-
resend_if_disconnected_and_connected.call
|
248
|
-
|
249
|
-
connection.send_protocol_message(
|
250
|
-
action: new_state.to_i,
|
251
|
-
channel: channel.name,
|
252
|
-
**message_options.to_h
|
253
|
-
)
|
254
|
-
end
|
255
253
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
254
|
+
send_detach_message = lambda do
|
255
|
+
on_disconnected_and_connected.call do
|
256
|
+
send_detach_message.call
|
257
|
+
end
|
258
|
+
connection.send_protocol_message(
|
259
|
+
action: detach_action.to_i,
|
260
|
+
channel: channel.name
|
261
|
+
)
|
261
262
|
end
|
263
|
+
|
264
|
+
send_detach_message.call
|
262
265
|
end
|
263
266
|
|
264
267
|
def logger
|
@@ -18,6 +18,15 @@ module Ably::Realtime
|
|
18
18
|
#
|
19
19
|
attr_reader :attach_serial
|
20
20
|
|
21
|
+
# ChannelSerial contains the channelSerial from latest ProtocolMessage of action type
|
22
|
+
# Message/PresenceMessage received on the channel.
|
23
|
+
#
|
24
|
+
# @spec CP2b, RTL15b
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
attr_accessor :channel_serial
|
29
|
+
|
21
30
|
def initialize(channel)
|
22
31
|
@channel = channel
|
23
32
|
end
|
@@ -29,6 +29,7 @@ module Ably::Realtime
|
|
29
29
|
transition :from => :failed, :to => [:attaching, :initialized]
|
30
30
|
|
31
31
|
after_transition do |channel, transition|
|
32
|
+
channel.manager.notify_state_change # RTL13c
|
32
33
|
channel.synchronize_state_with_statemachine
|
33
34
|
end
|
34
35
|
|
@@ -55,6 +56,7 @@ module Ably::Realtime
|
|
55
56
|
end
|
56
57
|
|
57
58
|
after_transition(to: [:detached, :failed, :suspended]) do |channel, current_transition|
|
59
|
+
channel.properties.channel_serial = nil # RTP5a1
|
58
60
|
err = error_from_state_change(current_transition)
|
59
61
|
channel.manager.fail_queued_messages(err) if channel.failed? or channel.suspended? #RTL11
|
60
62
|
channel.manager.log_channel_error err if err
|
@@ -42,7 +42,7 @@ module Ably
|
|
42
42
|
#
|
43
43
|
# @spec RTL2b
|
44
44
|
#
|
45
|
-
# The
|
45
|
+
# The permitted states for this channel
|
46
46
|
STATE = ruby_enum('STATE',
|
47
47
|
:initialized,
|
48
48
|
:attaching,
|
@@ -332,6 +332,7 @@ module Ably
|
|
332
332
|
# @return [Ably::Util::SafeDeferrable]
|
333
333
|
#
|
334
334
|
def history(options = {}, &callback)
|
335
|
+
# RTL10b
|
335
336
|
if options.delete(:until_attach)
|
336
337
|
unless attached?
|
337
338
|
error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached' )
|
@@ -363,8 +364,8 @@ module Ably
|
|
363
364
|
# @return [Ably::Models::ChannelOptions]
|
364
365
|
def set_options(channel_options)
|
365
366
|
@options = Ably::Models::ChannelOptions(channel_options)
|
366
|
-
|
367
|
-
manager.request_reattach if need_reattach?
|
367
|
+
# RTL4i
|
368
|
+
manager.request_reattach if (need_reattach? and connection.state?(:connected))
|
368
369
|
end
|
369
370
|
alias options= set_options
|
370
371
|
|
@@ -46,6 +46,26 @@ module Ably
|
|
46
46
|
@channels.delete(channel)
|
47
47
|
end if @channels.has_key?(channel)
|
48
48
|
end
|
49
|
+
|
50
|
+
# Sets channel serial to each channel from given serials hashmap
|
51
|
+
# @param [Hash] serials - map of channel name to respective channel serial
|
52
|
+
# @api private
|
53
|
+
def set_channel_serials(serials)
|
54
|
+
serials.each do |channel_name, channel_serial|
|
55
|
+
get(channel_name).properties.channel_serial = channel_serial
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Hash] serials - map of channel name to respective channel serial
|
60
|
+
# @api private
|
61
|
+
def get_channel_serials
|
62
|
+
channel_serials = {}
|
63
|
+
self.each do |channel|
|
64
|
+
channel_serials[channel.name] = channel.properties.channel_serial if channel.state == :attached
|
65
|
+
end
|
66
|
+
channel_serials
|
67
|
+
end
|
68
|
+
|
49
69
|
end
|
50
70
|
end
|
51
71
|
end
|
@@ -43,19 +43,24 @@ module Ably::Realtime
|
|
43
43
|
raise ArgumentError, "Expected a ProtocolMessage. Received #{protocol_message}"
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
# RTL15b
|
47
|
+
if protocol_message.has_channel_serial? &&
|
48
|
+
(
|
49
|
+
protocol_message.action == :message ||
|
50
|
+
protocol_message.action == :presence ||
|
51
|
+
protocol_message.action == :attached
|
52
|
+
)
|
53
|
+
get_channel(protocol_message.channel).tap do |channel|
|
54
|
+
logger.info "Setting channel serial for channel #{channel.name}, " <<
|
55
|
+
"Previous: #{channel.properties.channel_serial}, New: #{protocol_message.channel_serial}"
|
56
|
+
channel.properties.channel_serial = protocol_message.channel_serial
|
57
|
+
end
|
48
58
|
end
|
49
59
|
|
50
|
-
|
51
|
-
|
52
|
-
error_message = "Protocol error, duplicate message received for serial #{protocol_message.connection_serial}"
|
53
|
-
logger.error error_message
|
54
|
-
return
|
55
|
-
end
|
60
|
+
unless protocol_message.action.match_any?(:nack, :error)
|
61
|
+
logger.debug { "#{protocol_message.action} received: #{protocol_message}" }
|
56
62
|
end
|
57
63
|
|
58
|
-
update_connection_recovery_info protocol_message
|
59
64
|
connection.set_connection_confirmed_alive
|
60
65
|
|
61
66
|
case protocol_message.action
|
@@ -172,10 +177,6 @@ module Ably::Realtime
|
|
172
177
|
end
|
173
178
|
end
|
174
179
|
|
175
|
-
def update_connection_recovery_info(protocol_message)
|
176
|
-
connection.update_connection_serial protocol_message.connection_serial if protocol_message.has_connection_serial?
|
177
|
-
end
|
178
|
-
|
179
180
|
def ack_pending_queue_for_message_serial(ack_protocol_message)
|
180
181
|
drop_pending_queue_from_ack(ack_protocol_message) do |protocol_message|
|
181
182
|
ack_messages protocol_message.messages
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'ably/realtime/channel/publisher'
|
3
|
+
require 'ably/realtime/recovery_key_context'
|
3
4
|
|
4
5
|
module Ably
|
5
6
|
module Realtime
|
@@ -11,6 +12,7 @@ module Ably
|
|
11
12
|
include Ably::Modules::Conversions
|
12
13
|
|
13
14
|
extend Forwardable
|
15
|
+
using Ably::Util::AblyExtensions
|
14
16
|
|
15
17
|
DOMAIN = 'realtime.ably.io'
|
16
18
|
|
@@ -120,17 +122,23 @@ module Ably
|
|
120
122
|
acc[key.to_s] = value.to_s
|
121
123
|
end
|
122
124
|
@rest_client = Ably::Rest::Client.new(options.merge(realtime_client: self))
|
123
|
-
@echo_messages = rest_client.options.
|
124
|
-
@queue_messages = rest_client.options.
|
125
|
+
@echo_messages = rest_client.options.fetch_with_default(:echo_messages, true)
|
126
|
+
@queue_messages = rest_client.options.fetch_with_default(:queue_messages, true)
|
125
127
|
@custom_realtime_host = rest_client.options[:realtime_host] || rest_client.options[:ws_host]
|
126
|
-
@auto_connect = rest_client.options.
|
127
|
-
@recover = rest_client.options
|
128
|
-
|
129
|
-
raise ArgumentError, "Recovery key '#{recover}' is invalid" if recover && !recover.match(Connection::RECOVER_REGEX)
|
128
|
+
@auto_connect = rest_client.options.fetch_with_default(:auto_connect, true)
|
129
|
+
@recover = rest_client.options.fetch_with_default(:recover, '')
|
130
130
|
|
131
131
|
@auth = Ably::Realtime::Auth.new(self)
|
132
132
|
@channels = Ably::Realtime::Channels.new(self)
|
133
133
|
@connection = Ably::Realtime::Connection.new(self, options)
|
134
|
+
|
135
|
+
unless @recover.nil_or_empty?
|
136
|
+
recovery_context = RecoveryKeyContext.from_json(@recover, logger)
|
137
|
+
unless recovery_context.nil?
|
138
|
+
@channels.set_channel_serials recovery_context.channel_serials # RTN16j
|
139
|
+
@connection.set_msg_serial_from_recover = recovery_context.msg_serial # RTN16f
|
140
|
+
end
|
141
|
+
end
|
134
142
|
end
|
135
143
|
|
136
144
|
# Return a {Ably::Realtime::Channel Realtime Channel} for the given name
|
@@ -14,12 +14,14 @@ module Ably::Realtime
|
|
14
14
|
RESOLVABLE_ERROR_CODES = {
|
15
15
|
token_expired: Ably::Exceptions::TOKEN_EXPIRED_CODE
|
16
16
|
}
|
17
|
+
using Ably::Util::AblyExtensions
|
17
18
|
|
18
19
|
def initialize(connection)
|
19
20
|
@connection = connection
|
20
21
|
@timers = Hash.new { |hash, key| hash[key] = [] }
|
21
22
|
|
22
|
-
|
23
|
+
# RTN8c, RTN9c
|
24
|
+
connection.unsafe_on(:closing, :closed, :suspended, :failed) do
|
23
25
|
connection.reset_resume_info
|
24
26
|
end
|
25
27
|
|
@@ -111,23 +113,28 @@ module Ably::Realtime
|
|
111
113
|
# Update the connection details and any associated defaults
|
112
114
|
connection.set_connection_details protocol_message.connection_details
|
113
115
|
|
116
|
+
is_connection_resume_or_recover_attempt = !connection.key.nil_or_empty? || !client.recover.nil_or_empty?
|
117
|
+
|
118
|
+
# RTN15c7, RTN16d
|
119
|
+
failed_resume_or_recover = !protocol_message.connection_id == connection.id && !protocol_message.error.nil?
|
120
|
+
if is_connection_resume_or_recover_attempt and failed_resume_or_recover # RTN15c7
|
121
|
+
connection.reset_client_msg_serial
|
122
|
+
end
|
123
|
+
client.disable_automatic_connection_recovery # RTN16k, explicitly setting null, so it won't be used for subsequent connection requests
|
124
|
+
|
114
125
|
if connection.key
|
115
126
|
if protocol_message.connection_id == connection.id
|
116
127
|
logger.debug { "ConnectionManager: Connection resumed successfully - ID #{connection.id} and key #{connection.key}" }
|
117
|
-
EventMachine.next_tick { connection.trigger_resumed }
|
118
128
|
resend_pending_message_ack_queue
|
119
129
|
else
|
120
|
-
logger.debug { "ConnectionManager: Connection was not resumed, old connection ID #{connection.id} has been updated with new connection ID #{protocol_message.connection_id} and key #{protocol_message.connection_details.connection_key}" }
|
121
130
|
nack_messages_on_all_channels protocol_message.error
|
122
|
-
force_reattach_on_channels protocol_message.error
|
123
131
|
end
|
124
132
|
else
|
125
133
|
logger.debug { "ConnectionManager: New connection created with ID #{protocol_message.connection_id} and key #{protocol_message.connection_details.connection_key}" }
|
126
134
|
end
|
127
135
|
|
128
|
-
|
129
|
-
|
130
|
-
connection.configure_new protocol_message.connection_id, protocol_message.connection_details.connection_key, protocol_message.connection_serial
|
136
|
+
connection.configure_new protocol_message.connection_id, protocol_message.connection_details.connection_key
|
137
|
+
force_reattach_on_channels protocol_message.error # irrespective of connection success/failure, reattach channels
|
131
138
|
end
|
132
139
|
|
133
140
|
# When connection is CONNECTED and receives an update
|
@@ -139,7 +146,7 @@ module Ably::Realtime
|
|
139
146
|
# Update the connection details and any associated defaults
|
140
147
|
connection.set_connection_details protocol_message.connection_details
|
141
148
|
|
142
|
-
connection.configure_new protocol_message.connection_id, protocol_message.connection_details.connection_key
|
149
|
+
connection.configure_new protocol_message.connection_id, protocol_message.connection_details.connection_key
|
143
150
|
|
144
151
|
state_change = Ably::Models::ConnectionStateChange.new(
|
145
152
|
current: connection.state,
|
@@ -281,7 +288,7 @@ module Ably::Realtime
|
|
281
288
|
# Any message sent before an ACK/NACK was received on the previous transport
|
282
289
|
# need to be resent to the Ably service so that a subsequent ACK/NACK is received.
|
283
290
|
# It is up to Ably to ensure that duplicate messages are not retransmitted on the channel
|
284
|
-
#
|
291
|
+
# based on the message serial numbers
|
285
292
|
#
|
286
293
|
# @api private
|
287
294
|
def resend_pending_message_ack_queue
|
@@ -568,22 +575,14 @@ module Ably::Realtime
|
|
568
575
|
client.auth.authorization_in_flight?
|
569
576
|
end
|
570
577
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
end.each do |channel|
|
575
|
-
channel.transition_state_machine :attaching
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
# When continuity on a connection is lost all messages
|
580
|
-
# Channels in the ATTACHED or ATTACHING state should explicitly be re-attached
|
581
|
-
# by sending a new ATTACH to Ably
|
578
|
+
# When reconnected if channel is in ATTACHING, ATTACHED or SUSPENDED state
|
579
|
+
# it should explicitly be re-attached by sending a new ATTACH to Ably
|
580
|
+
# Spec : RTN15c6, RTN15c7
|
582
581
|
def force_reattach_on_channels(error)
|
583
582
|
channels.select do |channel|
|
584
|
-
channel.attached? || channel.attaching?
|
583
|
+
channel.attached? || channel.attaching? || channel.suspended?
|
585
584
|
end.each do |channel|
|
586
|
-
channel.manager.request_reattach
|
585
|
+
channel.manager.request_reattach error
|
587
586
|
end
|
588
587
|
end
|
589
588
|
|