ably-rest 0.8.3 → 0.8.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 +4 -4
- data/.travis.yml +1 -0
- data/lib/submodules/ably-ruby/CHANGELOG.md +10 -0
- data/lib/submodules/ably-ruby/README.md +1 -1
- data/lib/submodules/ably-ruby/Rakefile +1 -1
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -20
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +3 -0
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +2 -1
- data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +8 -6
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +4 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +4 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +35 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +13 -13
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +13 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +27 -7
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +22 -12
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +16 -10
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +42 -24
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +25 -17
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +9 -9
- 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 +168 -21
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +6 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +6 -5
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +29 -19
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +150 -35
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +146 -23
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +44 -24
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +77 -46
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +31 -3
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +15 -5
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +9 -7
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +30 -4
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +9 -6
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +44 -0
- data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +54 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +8 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +18 -0
- metadata +6 -2
|
@@ -65,9 +65,9 @@ module Ably
|
|
|
65
65
|
# token_details #=> Ably::Models::TokenDetails
|
|
66
66
|
# end
|
|
67
67
|
#
|
|
68
|
-
def authorise(
|
|
68
|
+
def authorise(token_params = {}, auth_options = {}, &success_callback)
|
|
69
69
|
async_wrap(success_callback) do
|
|
70
|
-
auth_sync.authorise(
|
|
70
|
+
auth_sync.authorise(token_params, auth_options)
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -76,8 +76,8 @@ module Ably
|
|
|
76
76
|
# @option (see Ably::Auth#authorise)
|
|
77
77
|
# @return [Ably::Models::TokenDetails]
|
|
78
78
|
#
|
|
79
|
-
def authorise_sync(
|
|
80
|
-
auth_sync.authorise(
|
|
79
|
+
def authorise_sync(token_params = {}, auth_options = {})
|
|
80
|
+
auth_sync.authorise(token_params, auth_options)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
# def_delegator :auth_sync, :request_token, :request_token_sync
|
|
@@ -100,9 +100,9 @@ module Ably
|
|
|
100
100
|
# token_details #=> Ably::Models::TokenDetails
|
|
101
101
|
# end
|
|
102
102
|
#
|
|
103
|
-
def request_token(
|
|
103
|
+
def request_token(token_params = {}, auth_options = {}, &success_callback)
|
|
104
104
|
async_wrap(success_callback) do
|
|
105
|
-
request_token_sync(
|
|
105
|
+
request_token_sync(token_params, auth_options)
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
|
|
@@ -111,8 +111,8 @@ module Ably
|
|
|
111
111
|
# @option (see Ably::Auth#authorise)
|
|
112
112
|
# @return [Ably::Models::TokenDetails]
|
|
113
113
|
#
|
|
114
|
-
def request_token_sync(
|
|
115
|
-
auth_sync.request_token(
|
|
114
|
+
def request_token_sync(token_params = {}, auth_options = {})
|
|
115
|
+
auth_sync.request_token(token_params, auth_options)
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# Creates and signs a token request that can then subsequently be used by any client to request a token
|
|
@@ -124,12 +124,12 @@ module Ably
|
|
|
124
124
|
# @yield [Models::TokenRequest]
|
|
125
125
|
#
|
|
126
126
|
# @example
|
|
127
|
-
# client.auth.create_token_request(id: 'asd.asd'
|
|
127
|
+
# client.auth.create_token_request({ ttl: 3600 }, id: 'asd.asd') do |token_request|
|
|
128
128
|
# token_request #=> Ably::Models::TokenRequest
|
|
129
129
|
# end
|
|
130
|
-
def create_token_request(
|
|
130
|
+
def create_token_request(token_params = {}, auth_options = {}, &success_callback)
|
|
131
131
|
async_wrap(success_callback) do
|
|
132
|
-
create_token_request_sync(
|
|
132
|
+
create_token_request_sync(token_params, auth_options)
|
|
133
133
|
end
|
|
134
134
|
end
|
|
135
135
|
|
|
@@ -138,8 +138,8 @@ module Ably
|
|
|
138
138
|
# @option (see Ably::Auth#authorise)
|
|
139
139
|
# @return [Ably::Models::TokenRequest]
|
|
140
140
|
#
|
|
141
|
-
def create_token_request_sync(
|
|
142
|
-
auth_sync.create_token_request(
|
|
141
|
+
def create_token_request_sync(token_params = {}, auth_options = {})
|
|
142
|
+
auth_sync.create_token_request(token_params, auth_options)
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
# Auth header string used in HTTP requests to Ably
|
|
@@ -46,6 +46,7 @@ module Ably
|
|
|
46
46
|
)
|
|
47
47
|
include Ably::Modules::StateEmitter
|
|
48
48
|
include Ably::Modules::UsesStateMachine
|
|
49
|
+
ensure_state_machine_emits 'Ably::Models::ChannelStateChange'
|
|
49
50
|
|
|
50
51
|
# Max number of messages to bundle in a single ProtocolMessage
|
|
51
52
|
MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
|
|
@@ -106,6 +107,7 @@ module Ably
|
|
|
106
107
|
#
|
|
107
108
|
# @param name [String, Array<Ably::Models::Message|Hash>, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with +:name+ and +:data+ pairs
|
|
108
109
|
# @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
|
|
110
|
+
# @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
|
|
109
111
|
#
|
|
110
112
|
# @yield [Ably::Models::Message,Array<Ably::Models::Message>] On success, will call the block with the {Ably::Models::Message} if a single message is publishde, or an Array of {Ably::Models::Message} when multiple messages are published
|
|
111
113
|
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
|
@@ -132,11 +134,11 @@ module Ably
|
|
|
132
134
|
# puts "#{message.name} event received with #{message.data}"
|
|
133
135
|
# end
|
|
134
136
|
#
|
|
135
|
-
# channel.publish('click', 'body').errback do |
|
|
137
|
+
# channel.publish('click', 'body').errback do |error, message|
|
|
136
138
|
# puts "#{message.name} was not received, error #{error.message}"
|
|
137
139
|
# end
|
|
138
140
|
#
|
|
139
|
-
def publish(name, data = nil, &success_block)
|
|
141
|
+
def publish(name, data = nil, attributes = {}, &success_block)
|
|
140
142
|
raise Ably::Exceptions::ChannelInactive.new('Cannot publish messages on a detached channel') if detached? || detaching?
|
|
141
143
|
raise Ably::Exceptions::ChannelInactive.new('Cannot publish messages on a failed channel') if failed?
|
|
142
144
|
|
|
@@ -149,11 +151,11 @@ module Ably
|
|
|
149
151
|
else
|
|
150
152
|
ensure_utf_8 :name, name, allow_nil: true
|
|
151
153
|
ensure_supported_payload data
|
|
152
|
-
[{ name: name, data: data }]
|
|
154
|
+
[{ name: name, data: data }.merge(attributes)]
|
|
153
155
|
end
|
|
154
156
|
|
|
155
157
|
queue_messages(messages).tap do |deferrable|
|
|
156
|
-
deferrable.callback
|
|
158
|
+
deferrable.callback(&success_block) if block_given?
|
|
157
159
|
end
|
|
158
160
|
end
|
|
159
161
|
|
|
@@ -190,7 +192,9 @@ module Ably
|
|
|
190
192
|
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callback
|
|
191
193
|
#
|
|
192
194
|
def attach(&success_block)
|
|
193
|
-
|
|
195
|
+
if connection.closing? || connection.closed? || connection.suspended? || connection.failed?
|
|
196
|
+
raise Ably::Exceptions::InvalidStateChange.new("Cannot ATTACH channel when the connection is in a closed, suspended or failed state. Connection state: #{connection.state}")
|
|
197
|
+
end
|
|
194
198
|
|
|
195
199
|
transition_state_machine :attaching if can_transition_to?(:attaching)
|
|
196
200
|
deferrable_for_state_change_to(STATE.Attached, &success_block)
|
|
@@ -285,6 +289,10 @@ module Ably
|
|
|
285
289
|
client.logger
|
|
286
290
|
end
|
|
287
291
|
|
|
292
|
+
# As we are using a state machine, do not allow change_state to be used
|
|
293
|
+
# #transition_state_machine must be used instead
|
|
294
|
+
private :change_state
|
|
295
|
+
|
|
288
296
|
private
|
|
289
297
|
attr_reader :queue
|
|
290
298
|
|
|
@@ -27,7 +27,7 @@ module Ably::Realtime
|
|
|
27
27
|
# Commence attachment
|
|
28
28
|
def detach(error = nil)
|
|
29
29
|
if connection.closed? || connection.connecting? || connection.suspended?
|
|
30
|
-
channel.transition_state_machine :detached, error
|
|
30
|
+
channel.transition_state_machine :detached, reason: error
|
|
31
31
|
elsif can_transition_to?(:detached)
|
|
32
32
|
send_detach_protocol_message
|
|
33
33
|
end
|
|
@@ -51,7 +51,7 @@ module Ably::Realtime
|
|
|
51
51
|
|
|
52
52
|
# Detach a channel as a result of an error
|
|
53
53
|
def suspend(error)
|
|
54
|
-
channel.transition_state_machine! :detaching, error
|
|
54
|
+
channel.transition_state_machine! :detaching, reason: error
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# When a channel is no longer attached or has failed,
|
|
@@ -77,10 +77,10 @@ module Ably::Realtime
|
|
|
77
77
|
def nack_messages(protocol_message, error)
|
|
78
78
|
(protocol_message.messages + protocol_message.presence).each do |message|
|
|
79
79
|
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.to_json}, protocol message: #{protocol_message}"
|
|
80
|
-
message.fail
|
|
80
|
+
message.fail error
|
|
81
81
|
end
|
|
82
82
|
logger.debug "Calling NACK failure callbacks for #{protocol_message.class.name} - #{protocol_message.to_json}"
|
|
83
|
-
protocol_message.fail
|
|
83
|
+
protocol_message.fail error
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def drop_pending_queue_from_ack(ack_protocol_message)
|
|
@@ -118,6 +118,22 @@ module Ably::Realtime
|
|
|
118
118
|
)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
# Any message sent before an ACK/NACK was received on the previous transport
|
|
122
|
+
# needs to be resent to the Ably service so that a subsequent ACK/NACK is received.
|
|
123
|
+
# It is up to Ably to ensure that duplicate messages are not retransmitted on the channel
|
|
124
|
+
# base on the serial numbers
|
|
125
|
+
#
|
|
126
|
+
# @api private
|
|
127
|
+
def resend_pending_message_ack_queue
|
|
128
|
+
connection.__pending_message_ack_queue__.delete_if do |protocol_message|
|
|
129
|
+
if protocol_message.channel == channel.name
|
|
130
|
+
connection.__outgoing_message_queue__ << protocol_message
|
|
131
|
+
connection.__outgoing_protocol_msgbus__.publish :protocol_message
|
|
132
|
+
true
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
121
137
|
def setup_connection_event_handlers
|
|
122
138
|
connection.unsafe_on(:closed) do
|
|
123
139
|
channel.transition_state_machine :detaching if can_transition_to?(:detaching)
|
|
@@ -125,15 +141,19 @@ module Ably::Realtime
|
|
|
125
141
|
|
|
126
142
|
connection.unsafe_on(:suspended) do |error|
|
|
127
143
|
if can_transition_to?(:detaching)
|
|
128
|
-
channel.transition_state_machine :detaching, Ably::Exceptions::ConnectionSuspended.new('Connection suspended', nil, 80002, error)
|
|
144
|
+
channel.transition_state_machine :detaching, reason: Ably::Exceptions::ConnectionSuspended.new('Connection suspended', nil, 80002, error)
|
|
129
145
|
end
|
|
130
146
|
end
|
|
131
147
|
|
|
132
148
|
connection.unsafe_on(:failed) do |error|
|
|
133
|
-
if can_transition_to?(:failed)
|
|
134
|
-
channel.transition_state_machine :failed, Ably::Exceptions::ConnectionFailed.new('Connection failed', nil, 80002, error)
|
|
149
|
+
if can_transition_to?(:failed) && !channel.detached?
|
|
150
|
+
channel.transition_state_machine :failed, reason: Ably::Exceptions::ConnectionFailed.new('Connection failed', nil, 80002, error)
|
|
135
151
|
end
|
|
136
152
|
end
|
|
153
|
+
|
|
154
|
+
connection.unsafe_on(:connected) do |error|
|
|
155
|
+
resend_pending_message_ack_queue
|
|
156
|
+
end
|
|
137
157
|
end
|
|
138
158
|
|
|
139
159
|
def logger
|
|
@@ -22,8 +22,9 @@ module Ably::Realtime
|
|
|
22
22
|
|
|
23
23
|
transition :from => :initialized, :to => [:attaching]
|
|
24
24
|
transition :from => :attaching, :to => [:attached, :detaching, :failed]
|
|
25
|
-
transition :from => :attached, :to => [:detaching, :failed]
|
|
25
|
+
transition :from => :attached, :to => [:detaching, :detached, :failed]
|
|
26
26
|
transition :from => :detaching, :to => [:detached, :attaching, :failed]
|
|
27
|
+
transition :from => :detached, :to => [:attaching, :attached, :failed]
|
|
27
28
|
transition :from => :failed, :to => [:attaching]
|
|
28
29
|
|
|
29
30
|
after_transition do |channel, transition|
|
|
@@ -35,39 +36,48 @@ module Ably::Realtime
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
before_transition(to: [:attached]) do |channel, current_transition|
|
|
38
|
-
channel.manager.attached current_transition.metadata
|
|
39
|
+
channel.manager.attached current_transition.metadata.protocol_message
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
after_transition(to: [:detaching]) do |channel, current_transition|
|
|
42
|
-
|
|
43
|
+
err = error_from_state_change(current_transition)
|
|
44
|
+
channel.manager.detach err
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
after_transition(to: [:detached]) do |channel, current_transition|
|
|
46
|
-
|
|
47
|
-
channel.manager.
|
|
48
|
+
err = error_from_state_change(current_transition)
|
|
49
|
+
channel.manager.fail_messages_awaiting_ack err
|
|
50
|
+
channel.manager.emit_error err if err
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
after_transition(to: [:failed]) do |channel, current_transition|
|
|
51
|
-
|
|
52
|
-
channel.manager.
|
|
54
|
+
err = error_from_state_change(current_transition)
|
|
55
|
+
channel.manager.fail_messages_awaiting_ack err
|
|
56
|
+
channel.manager.emit_error err if err
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
# Transitions responsible for updating channel#error_reason
|
|
56
60
|
before_transition(to: [:failed]) do |channel, current_transition|
|
|
57
|
-
|
|
61
|
+
err = error_from_state_change(current_transition)
|
|
62
|
+
channel.set_failed_channel_error_reason err if err
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
before_transition(to: [:attached, :detached]) do |channel, current_transition|
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
err = error_from_state_change(current_transition)
|
|
67
|
+
if err
|
|
68
|
+
channel.set_failed_channel_error_reason err
|
|
63
69
|
else
|
|
64
70
|
# Attached & Detached are "healthy" final states so reset the error reason
|
|
65
71
|
channel.clear_error_reason
|
|
66
72
|
end
|
|
67
73
|
end
|
|
68
74
|
|
|
69
|
-
def self.
|
|
70
|
-
|
|
75
|
+
def self.error_from_state_change(current_transition)
|
|
76
|
+
# ChannelStateChange object is always passed in current_transition metadata object
|
|
77
|
+
connection_state_change = current_transition.metadata
|
|
78
|
+
# Reason attribute contains errors
|
|
79
|
+
err = connection_state_change && connection_state_change.reason
|
|
80
|
+
err if is_error_type?(err)
|
|
71
81
|
end
|
|
72
82
|
|
|
73
83
|
private
|
|
@@ -68,10 +68,16 @@ module Ably::Realtime
|
|
|
68
68
|
|
|
69
69
|
when ACTION.Connect
|
|
70
70
|
when ACTION.Connected
|
|
71
|
-
connection.
|
|
71
|
+
if connection.disconnected? || connection.closing? || connection.closed? || connection.failed?
|
|
72
|
+
logger.debug "Incoming CONNECTED ProtocolMessage discarded as connection has moved on and is in state: #{connection.state}"
|
|
73
|
+
elsif connection.connected?
|
|
74
|
+
logger.error "CONNECTED ProtocolMessage should not have been received when the connection is in the CONNECTED state"
|
|
75
|
+
else
|
|
76
|
+
connection.transition_state_machine :connected, reason: protocol_message.error, protocol_message: protocol_message
|
|
77
|
+
end
|
|
72
78
|
|
|
73
79
|
when ACTION.Disconnect, ACTION.Disconnected
|
|
74
|
-
connection.transition_state_machine :disconnected, protocol_message.error unless connection.disconnected?
|
|
80
|
+
connection.transition_state_machine :disconnected, reason: protocol_message.error unless connection.disconnected?
|
|
75
81
|
|
|
76
82
|
when ACTION.Close
|
|
77
83
|
when ACTION.Closed
|
|
@@ -87,7 +93,7 @@ module Ably::Realtime
|
|
|
87
93
|
when ACTION.Attach
|
|
88
94
|
when ACTION.Attached
|
|
89
95
|
get_channel(protocol_message.channel).tap do |channel|
|
|
90
|
-
channel.transition_state_machine :attached, protocol_message unless channel.attached?
|
|
96
|
+
channel.transition_state_machine :attached, reason: protocol_message.error, protocol_message: protocol_message unless channel.attached?
|
|
91
97
|
end
|
|
92
98
|
|
|
93
99
|
when ACTION.Detach
|
|
@@ -125,7 +131,7 @@ module Ably::Realtime
|
|
|
125
131
|
def dispatch_channel_error(protocol_message)
|
|
126
132
|
logger.warn "Channel Error message received: #{protocol_message.error}"
|
|
127
133
|
if !protocol_message.has_message_serial?
|
|
128
|
-
get_channel(protocol_message.channel).transition_state_machine :failed, protocol_message.error
|
|
134
|
+
get_channel(protocol_message.channel).transition_state_machine :failed, reason: protocol_message.error
|
|
129
135
|
else
|
|
130
136
|
logger.fatal "Cannot process ProtocolMessage as not yet implemented: #{protocol_message}"
|
|
131
137
|
end
|
|
@@ -163,17 +169,17 @@ module Ably::Realtime
|
|
|
163
169
|
def nack_messages(messages, protocol_message)
|
|
164
170
|
messages.each do |message|
|
|
165
171
|
logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.to_json}, protocol message: #{protocol_message}"
|
|
166
|
-
message.fail
|
|
172
|
+
message.fail protocol_message.error
|
|
167
173
|
end
|
|
168
174
|
end
|
|
169
175
|
|
|
170
176
|
def drop_pending_queue_from_ack(ack_protocol_message)
|
|
171
177
|
message_serial_up_to = ack_protocol_message.message_serial + ack_protocol_message.count - 1
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
|
|
179
|
+
while !connection.__pending_message_ack_queue__.empty?
|
|
180
|
+
next_message = connection.__pending_message_ack_queue__.first
|
|
181
|
+
return if next_message.message_serial > message_serial_up_to
|
|
182
|
+
yield connection.__pending_message_ack_queue__.shift
|
|
177
183
|
end
|
|
178
184
|
end
|
|
179
185
|
|
|
@@ -44,6 +44,12 @@ module Ably::Realtime
|
|
|
44
44
|
|
|
45
45
|
non_blocking_loop_while(condition) do
|
|
46
46
|
protocol_message = outgoing_queue.shift
|
|
47
|
+
|
|
48
|
+
if (!connection.transport)
|
|
49
|
+
protocol_message.fail Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, 80003)
|
|
50
|
+
next
|
|
51
|
+
end
|
|
52
|
+
|
|
47
53
|
current_transport_outgoing_message_bus.publish :protocol_message, protocol_message
|
|
48
54
|
|
|
49
55
|
if protocol_message.ack_required?
|
|
@@ -55,6 +55,7 @@ module Ably
|
|
|
55
55
|
)
|
|
56
56
|
include Ably::Modules::StateEmitter
|
|
57
57
|
include Ably::Modules::UsesStateMachine
|
|
58
|
+
ensure_state_machine_emits 'Ably::Models::ConnectionStateChange'
|
|
58
59
|
|
|
59
60
|
# Expected format for a connection recover key
|
|
60
61
|
RECOVER_REGEX = /^(?<recover>[\w-]+):(?<connection_serial>\-?\w+)$/
|
|
@@ -118,7 +119,7 @@ module Ably
|
|
|
118
119
|
# the failed state. Once closed, the library will not attempt to re-establish the
|
|
119
120
|
# connection without a call to {Connection#connect}.
|
|
120
121
|
#
|
|
121
|
-
# @yield
|
|
122
|
+
# @yield block is called as soon as this connection is in the Closed state
|
|
122
123
|
#
|
|
123
124
|
# @return [EventMachine::Deferrable]
|
|
124
125
|
#
|
|
@@ -133,7 +134,7 @@ module Ably
|
|
|
133
134
|
# Causes the library to attempt connection. If it was previously explicitly
|
|
134
135
|
# closed by the user, or was closed as a result of an unrecoverable error, a new connection will be opened.
|
|
135
136
|
#
|
|
136
|
-
# @yield
|
|
137
|
+
# @yield block is called as soon as this connection is in the Connected state
|
|
137
138
|
#
|
|
138
139
|
# @return [EventMachine::Deferrable]
|
|
139
140
|
#
|
|
@@ -189,7 +190,7 @@ module Ably
|
|
|
189
190
|
EventMachine::HttpRequest.new(url).get.tap do |http|
|
|
190
191
|
http.errback do
|
|
191
192
|
yield false if block_given?
|
|
192
|
-
deferrable.fail "Unable to connect to #{url}"
|
|
193
|
+
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unable to connect to #{url}", nil, 80000)
|
|
193
194
|
end
|
|
194
195
|
http.callback do
|
|
195
196
|
EventMachine.next_tick do
|
|
@@ -198,7 +199,7 @@ module Ably
|
|
|
198
199
|
if result
|
|
199
200
|
deferrable.succeed
|
|
200
201
|
else
|
|
201
|
-
deferrable.fail "Unexpected response from #{url} (#{http.response_header.status})"
|
|
202
|
+
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unexpected response from #{url} (#{http.response_header.status})", 400, 40000)
|
|
202
203
|
end
|
|
203
204
|
end
|
|
204
205
|
end
|
|
@@ -39,7 +39,7 @@ module Ably::Realtime
|
|
|
39
39
|
|
|
40
40
|
EventMachine.next_tick do
|
|
41
41
|
# Connect once Connection object is initialised
|
|
42
|
-
connection.connect if client.auto_connect
|
|
42
|
+
connection.connect if client.auto_connect && connection.can_transition_to?(:connecting)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -53,7 +53,7 @@ module Ably::Realtime
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
unless client.auth.authentication_security_requirements_met?
|
|
56
|
-
connection.transition_state_machine :failed, Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, 40103)
|
|
56
|
+
connection.transition_state_machine :failed, reason: Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, 40103)
|
|
57
57
|
return
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -80,7 +80,8 @@ module Ably::Realtime
|
|
|
80
80
|
# @api private
|
|
81
81
|
def connection_opening_failed(error)
|
|
82
82
|
logger.warn "ConnectionManager: Connection to #{connection.current_host}:#{connection.port} failed; #{error.message}"
|
|
83
|
-
|
|
83
|
+
next_state = get_next_retry_state_info
|
|
84
|
+
connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: Ably::Exceptions::ConnectionError.new("Connection failed: #{error.message}", nil, 80000)
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
# Called whenever a new connection is made
|
|
@@ -88,7 +89,7 @@ module Ably::Realtime
|
|
|
88
89
|
# @api private
|
|
89
90
|
def connected(protocol_message)
|
|
90
91
|
if connection.key
|
|
91
|
-
if protocol_message.connection_key == connection.key
|
|
92
|
+
if connection_key_shared(protocol_message.connection_key) == connection_key_shared(connection.key)
|
|
92
93
|
logger.debug "ConnectionManager: Connection resumed successfully - ID #{connection.id} and key #{connection.key}"
|
|
93
94
|
EventMachine.next_tick { connection.resumed }
|
|
94
95
|
else
|
|
@@ -155,11 +156,10 @@ module Ably::Realtime
|
|
|
155
156
|
# When a connection is disconnected whilst connecting, attempt reconnect and/or set state to :suspended or :failed
|
|
156
157
|
#
|
|
157
158
|
# @api private
|
|
158
|
-
def respond_to_transport_disconnected_when_connecting(
|
|
159
|
+
def respond_to_transport_disconnected_when_connecting(error)
|
|
159
160
|
return unless connection.disconnected? || connection.suspended? # do nothing if state has changed through an explicit request
|
|
160
161
|
return unless retry_connection? # do not always reattempt connection or change state as client may be re-authorising
|
|
161
162
|
|
|
162
|
-
error = current_transition.metadata
|
|
163
163
|
if error.kind_of?(Ably::Models::ErrorInfo)
|
|
164
164
|
renew_token_and_reconnect error if error.code == RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
|
165
165
|
return
|
|
@@ -172,23 +172,22 @@ module Ably::Realtime
|
|
|
172
172
|
return if connection_retry_for(:suspended)
|
|
173
173
|
|
|
174
174
|
# Fallback if no other criteria met
|
|
175
|
-
connection.transition_state_machine :failed,
|
|
175
|
+
connection.transition_state_machine :failed, reason: error
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
# When a connection is disconnected after connecting, attempt reconnect and/or set state to :suspended or :failed
|
|
179
179
|
#
|
|
180
180
|
# @api private
|
|
181
|
-
def respond_to_transport_disconnected_whilst_connected(
|
|
182
|
-
logger.warn "ConnectionManager: Connection to #{connection.transport.url} was disconnected unexpectedly"
|
|
181
|
+
def respond_to_transport_disconnected_whilst_connected(error)
|
|
182
|
+
logger.warn "ConnectionManager: Connection #{"to #{connection.transport.url}" if connection.transport} was disconnected unexpectedly"
|
|
183
183
|
|
|
184
|
-
error = current_transition.metadata
|
|
185
184
|
if error.kind_of?(Ably::Models::ErrorInfo) && error.code != RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
|
186
185
|
connection.emit :error, error
|
|
187
186
|
logger.error "ConnectionManager: Error in Disconnected ProtocolMessage received from the server - #{error}"
|
|
188
187
|
end
|
|
189
188
|
|
|
190
189
|
destroy_transport
|
|
191
|
-
respond_to_transport_disconnected_when_connecting
|
|
190
|
+
respond_to_transport_disconnected_when_connecting error
|
|
192
191
|
end
|
|
193
192
|
|
|
194
193
|
# {Ably::Models::ProtocolMessage ProtocolMessage Error} received from server.
|
|
@@ -198,13 +197,13 @@ module Ably::Realtime
|
|
|
198
197
|
def error_received_from_server(error)
|
|
199
198
|
case error.code
|
|
200
199
|
when RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
|
201
|
-
connection.transition_state_machine :disconnected
|
|
200
|
+
connection.transition_state_machine :disconnected, retry_in: 0
|
|
202
201
|
connection.unsafe_once_or_if(:disconnected) do
|
|
203
202
|
renew_token_and_reconnect error
|
|
204
203
|
end
|
|
205
204
|
else
|
|
206
205
|
logger.error "ConnectionManager: Error #{error.class.name} code #{error.code} received from server '#{error.message}', transitioning to failed state"
|
|
207
|
-
connection.transition_state_machine :failed, error
|
|
206
|
+
connection.transition_state_machine :failed, reason: error
|
|
208
207
|
end
|
|
209
208
|
end
|
|
210
209
|
|
|
@@ -234,6 +233,12 @@ module Ably::Realtime
|
|
|
234
233
|
client.channels
|
|
235
234
|
end
|
|
236
235
|
|
|
236
|
+
# Connection key left part is consistent between connection resumes
|
|
237
|
+
# i.e. wVIsgTHAB1UvXh7z-1991d8586 becomes wVIsgTHAB1UvXh7z-1990d8586 after a resume
|
|
238
|
+
def connection_key_shared(connection_key)
|
|
239
|
+
(connection_key || '')[/^\w{5,}-/, 0]
|
|
240
|
+
end
|
|
241
|
+
|
|
237
242
|
# Create a timer that will execute in timeout_in seconds.
|
|
238
243
|
# If the connection state changes however, cancel the timer
|
|
239
244
|
def create_timeout_timer_whilst_in_state(timer_id, timeout_in)
|
|
@@ -249,12 +254,26 @@ module Ably::Realtime
|
|
|
249
254
|
timers.fetch(key, []).each(&:cancel)
|
|
250
255
|
end
|
|
251
256
|
|
|
252
|
-
def
|
|
253
|
-
if connection_retry_from_suspended_state? ||
|
|
257
|
+
def get_next_retry_state_info
|
|
258
|
+
retry_state = if connection_retry_from_suspended_state? || !can_reattempt_connect_for_state?(:disconnected)
|
|
254
259
|
:suspended
|
|
255
260
|
else
|
|
256
261
|
:disconnected
|
|
257
262
|
end
|
|
263
|
+
{
|
|
264
|
+
state: retry_state,
|
|
265
|
+
pause: next_retry_pause(retry_state)
|
|
266
|
+
}
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def next_retry_pause(retry_state)
|
|
270
|
+
return nil unless CONNECT_RETRY_CONFIG.fetch(retry_state)
|
|
271
|
+
|
|
272
|
+
if retries_for_state(retry_state, ignore_states: [:connecting]).empty?
|
|
273
|
+
0
|
|
274
|
+
else
|
|
275
|
+
CONNECT_RETRY_CONFIG.fetch(retry_state).fetch(:retry_every)
|
|
276
|
+
end
|
|
258
277
|
end
|
|
259
278
|
|
|
260
279
|
def connection_retry_from_suspended_state?
|
|
@@ -348,13 +367,12 @@ module Ably::Realtime
|
|
|
348
367
|
connection.transition_state_machine :closed
|
|
349
368
|
elsif !connection.closed? && !connection.disconnected?
|
|
350
369
|
exception = if reason
|
|
351
|
-
Ably::Exceptions::
|
|
352
|
-
end
|
|
353
|
-
if connection_retry_from_suspended_state? || !can_reattempt_connect_for_state?(:disconnected)
|
|
354
|
-
connection.transition_state_machine :suspended, exception
|
|
370
|
+
Ably::Exceptions::TransportClosed.new(reason, nil, 80003)
|
|
355
371
|
else
|
|
356
|
-
|
|
372
|
+
Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, 80003)
|
|
357
373
|
end
|
|
374
|
+
next_state = get_next_retry_state_info
|
|
375
|
+
connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: exception
|
|
358
376
|
end
|
|
359
377
|
end
|
|
360
378
|
end
|
|
@@ -362,7 +380,7 @@ module Ably::Realtime
|
|
|
362
380
|
def renew_token_and_reconnect(error)
|
|
363
381
|
if client.auth.token_renewable?
|
|
364
382
|
if @renewing_token
|
|
365
|
-
connection.transition_state_machine :failed, error
|
|
383
|
+
connection.transition_state_machine :failed, reason: error
|
|
366
384
|
return
|
|
367
385
|
end
|
|
368
386
|
|
|
@@ -383,18 +401,18 @@ module Ably::Realtime
|
|
|
383
401
|
if token_details && !token_details.expired?
|
|
384
402
|
connection.connect
|
|
385
403
|
else
|
|
386
|
-
connection.transition_state_machine :failed, error unless connection.failed?
|
|
404
|
+
connection.transition_state_machine :failed, reason: error unless connection.failed?
|
|
387
405
|
end
|
|
388
406
|
end
|
|
389
407
|
|
|
390
408
|
authorise_deferrable.errback do |auth_error|
|
|
391
409
|
logger.error "ConnectionManager: Error authorising following token expiry: #{auth_error}"
|
|
392
|
-
connection.transition_state_machine :failed, auth_error
|
|
410
|
+
connection.transition_state_machine :failed, reason: auth_error
|
|
393
411
|
end
|
|
394
412
|
end
|
|
395
413
|
else
|
|
396
414
|
logger.error "ConnectionManager: Token has expired and is not renewable - #{error}"
|
|
397
|
-
connection.transition_state_machine :failed, error
|
|
415
|
+
connection.transition_state_machine :failed, reason: error
|
|
398
416
|
end
|
|
399
417
|
end
|
|
400
418
|
|