ably 0.8.15 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGELOG.md +6 -2
- data/README.md +5 -1
- data/SPEC.md +1473 -852
- data/ably.gemspec +11 -8
- data/lib/ably/auth.rb +90 -53
- data/lib/ably/exceptions.rb +37 -8
- data/lib/ably/logger.rb +10 -1
- data/lib/ably/models/auth_details.rb +42 -0
- data/lib/ably/models/channel_state_change.rb +18 -4
- data/lib/ably/models/connection_details.rb +6 -3
- data/lib/ably/models/connection_state_change.rb +4 -3
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/message.rb +17 -1
- data/lib/ably/models/message_encoders/base.rb +103 -82
- data/lib/ably/models/message_encoders/base64.rb +1 -1
- data/lib/ably/models/presence_message.rb +16 -1
- data/lib/ably/models/protocol_message.rb +20 -3
- data/lib/ably/models/token_details.rb +11 -1
- data/lib/ably/models/token_request.rb +16 -6
- data/lib/ably/modules/async_wrapper.rb +7 -3
- data/lib/ably/modules/encodeable.rb +51 -12
- data/lib/ably/modules/enum.rb +17 -7
- data/lib/ably/modules/event_emitter.rb +29 -14
- data/lib/ably/modules/model_common.rb +13 -21
- data/lib/ably/modules/state_emitter.rb +7 -4
- data/lib/ably/modules/state_machine.rb +2 -4
- data/lib/ably/modules/uses_state_machine.rb +7 -3
- data/lib/ably/realtime.rb +2 -0
- data/lib/ably/realtime/auth.rb +102 -42
- data/lib/ably/realtime/channel.rb +68 -26
- data/lib/ably/realtime/channel/channel_manager.rb +154 -65
- data/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
- data/lib/ably/realtime/client.rb +18 -3
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
- data/lib/ably/realtime/connection.rb +108 -49
- data/lib/ably/realtime/connection/connection_manager.rb +167 -61
- data/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -10
- data/lib/ably/realtime/presence.rb +70 -45
- data/lib/ably/realtime/presence/members_map.rb +201 -36
- data/lib/ably/realtime/presence/presence_manager.rb +30 -6
- data/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +5 -5
- data/lib/ably/rest/client.rb +31 -27
- data/lib/ably/rest/middleware/exceptions.rb +1 -3
- data/lib/ably/rest/middleware/logger.rb +2 -2
- data/lib/ably/rest/presence.rb +2 -2
- data/lib/ably/util/pub_sub.rb +1 -1
- data/lib/ably/util/safe_deferrable.rb +26 -0
- data/lib/ably/version.rb +2 -2
- data/spec/acceptance/realtime/auth_spec.rb +470 -111
- data/spec/acceptance/realtime/channel_history_spec.rb +5 -3
- data/spec/acceptance/realtime/channel_spec.rb +1017 -168
- data/spec/acceptance/realtime/client_spec.rb +6 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
- data/spec/acceptance/realtime/connection_spec.rb +424 -105
- data/spec/acceptance/realtime/message_spec.rb +52 -23
- data/spec/acceptance/realtime/presence_history_spec.rb +5 -3
- data/spec/acceptance/realtime/presence_spec.rb +1110 -96
- data/spec/acceptance/rest/auth_spec.rb +222 -59
- data/spec/acceptance/rest/base_spec.rb +1 -1
- data/spec/acceptance/rest/channel_spec.rb +1 -2
- data/spec/acceptance/rest/client_spec.rb +104 -48
- data/spec/acceptance/rest/message_spec.rb +42 -15
- data/spec/acceptance/rest/presence_spec.rb +4 -11
- data/spec/rspec_config.rb +2 -1
- data/spec/shared/client_initializer_behaviour.rb +2 -2
- data/spec/shared/safe_deferrable_behaviour.rb +6 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/debug_failure_helper.rb +20 -4
- data/spec/support/event_machine_helper.rb +32 -1
- data/spec/unit/auth_spec.rb +4 -11
- data/spec/unit/logger_spec.rb +28 -2
- data/spec/unit/models/auth_details_spec.rb +49 -0
- data/spec/unit/models/channel_state_change_spec.rb +23 -3
- data/spec/unit/models/connection_details_spec.rb +12 -1
- data/spec/unit/models/connection_state_change_spec.rb +15 -4
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -1
- data/spec/unit/models/message_spec.rb +153 -0
- data/spec/unit/models/presence_message_spec.rb +192 -0
- data/spec/unit/models/protocol_message_spec.rb +64 -6
- data/spec/unit/models/token_details_spec.rb +75 -0
- data/spec/unit/models/token_request_spec.rb +74 -0
- data/spec/unit/modules/async_wrapper_spec.rb +2 -1
- data/spec/unit/modules/enum_spec.rb +69 -0
- data/spec/unit/modules/event_emitter_spec.rb +149 -22
- data/spec/unit/modules/state_emitter_spec.rb +9 -3
- data/spec/unit/realtime/client_spec.rb +1 -1
- data/spec/unit/realtime/connection_spec.rb +8 -5
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
- data/spec/unit/realtime/presence_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +1 -1
- data/spec/unit/util/crypto_spec.rb +3 -3
- metadata +22 -19
@@ -4,7 +4,8 @@ module Ably::Modules
|
|
4
4
|
# module, and the class is an {EventEmitter}. It then emits state changes.
|
5
5
|
#
|
6
6
|
# It also ensures the EventEmitter is configured to retrict permitted events to the
|
7
|
-
# the available STATEs
|
7
|
+
# the available STATEs or EVENTs if defined i.e. if EVENTs includes an additional type such as
|
8
|
+
# :update, then it will support all EVENTs being emitted. EVENTs must be a superset of STATEs
|
8
9
|
#
|
9
10
|
# @note This module requires that the method #logger is defined.
|
10
11
|
#
|
@@ -51,7 +52,7 @@ module Ably::Modules
|
|
51
52
|
# @api private
|
52
53
|
def state=(new_state, *args)
|
53
54
|
if state != new_state
|
54
|
-
logger.debug
|
55
|
+
logger.debug { "#{self.class}: StateEmitter changed from #{state} => #{new_state}" } if respond_to?(:logger, true)
|
55
56
|
@state = STATE(new_state)
|
56
57
|
emit @state, *args
|
57
58
|
end
|
@@ -153,8 +154,10 @@ module Ably::Modules
|
|
153
154
|
|
154
155
|
def self.included(klass)
|
155
156
|
klass.configure_event_emitter coerce_into: Proc.new { |event|
|
156
|
-
|
157
|
-
|
157
|
+
# Special case allows EVENT instead of STATE to be emitted
|
158
|
+
# Relies on the assumption that EVENT is a superset of STATE
|
159
|
+
if klass.const_defined?(:EVENT)
|
160
|
+
klass::EVENT(event)
|
158
161
|
else
|
159
162
|
klass::STATE(event)
|
160
163
|
end
|
@@ -18,14 +18,12 @@ module Ably::Modules
|
|
18
18
|
|
19
19
|
# Alternative to Statesman's #transition_to that:
|
20
20
|
# * log state change failures to {Logger}
|
21
|
-
# * raise an exception on the {Ably::Realtime::Channel}
|
22
21
|
#
|
23
22
|
# @return [void]
|
24
23
|
def transition_state(state, *args)
|
25
|
-
unless result = transition_to(state, *args)
|
24
|
+
unless result = transition_to(state.to_sym, *args)
|
26
25
|
exception = exception_for_state_change_to(state)
|
27
|
-
|
28
|
-
logger.fatal "#{self.class}: #{exception.message}"
|
26
|
+
logger.fatal { "#{self.class}: #{exception.message}" }
|
29
27
|
end
|
30
28
|
result
|
31
29
|
end
|
@@ -67,15 +67,19 @@ module Ably::Modules
|
|
67
67
|
|
68
68
|
def log_state_machine_state_change
|
69
69
|
if state_machine.previous_state
|
70
|
-
logger.debug "#{self.class.name}: Transitioned from #{state_machine.previous_state} => #{state_machine.current_state}"
|
70
|
+
logger.debug { "#{self.class.name}: Transitioned from #{state_machine.previous_state} => #{state_machine.current_state}" }
|
71
71
|
else
|
72
|
-
logger.debug "#{self.class.name}: Transitioned to #{state_machine.current_state}"
|
72
|
+
logger.debug { "#{self.class.name}: Transitioned to #{state_machine.current_state}" }
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
def emit_object(new_state, emit_params)
|
77
77
|
if self.class.emits_klass
|
78
|
-
self.class.emits_klass.new((emit_params || {}).merge(
|
78
|
+
self.class.emits_klass.new((emit_params || {}).merge(
|
79
|
+
current: STATE(new_state),
|
80
|
+
previous: STATE(state_machine.current_state),
|
81
|
+
event: EVENT(new_state)
|
82
|
+
))
|
79
83
|
else
|
80
84
|
emit_params
|
81
85
|
end
|
data/lib/ably/realtime.rb
CHANGED
@@ -11,6 +11,8 @@ require 'ably/realtime/client'
|
|
11
11
|
require 'ably/realtime/connection'
|
12
12
|
require 'ably/realtime/presence'
|
13
13
|
|
14
|
+
require 'ably/models/message_encoders/base'
|
15
|
+
|
14
16
|
Dir.glob(File.expand_path("models/*.rb", File.dirname(__FILE__))).each do |file|
|
15
17
|
require file
|
16
18
|
end
|
data/lib/ably/realtime/auth.rb
CHANGED
@@ -44,18 +44,20 @@ module Ably
|
|
44
44
|
def_delegators :auth_sync, :using_basic_auth?, :using_token_auth?
|
45
45
|
def_delegators :auth_sync, :token_renewable?, :authentication_security_requirements_met?
|
46
46
|
def_delegators :client, :logger
|
47
|
+
def_delegators :client, :connection
|
47
48
|
|
48
49
|
def initialize(client)
|
49
50
|
@client = client
|
50
51
|
@auth_sync = client.rest_client.auth
|
51
52
|
end
|
52
53
|
|
53
|
-
#
|
54
|
+
# For new connections, ensures valid auth credentials are present for the library instance. This may rely on an already-known and valid token, and will obtain a new token if necessary.
|
55
|
+
# If a connection is already established, the connection will be upgraded with a new token
|
54
56
|
#
|
55
57
|
# In the event that a new token request is made, the provided options are used
|
56
58
|
#
|
57
|
-
# @param (see Ably::Auth#
|
58
|
-
# @option (see Ably::Auth#
|
59
|
+
# @param (see Ably::Auth#authorize)
|
60
|
+
# @option (see Ably::Auth#authorize)
|
59
61
|
#
|
60
62
|
# @return [Ably::Util::SafeDeferrable]
|
61
63
|
# @yield [Ably::Models::TokenDetails]
|
@@ -63,33 +65,90 @@ module Ably
|
|
63
65
|
# @example
|
64
66
|
# # will issue a simple token request using basic auth
|
65
67
|
# client = Ably::Rest::Client.new(key: 'key.id:secret')
|
66
|
-
# client.auth.
|
68
|
+
# client.auth.authorize do |token_details|
|
67
69
|
# token_details #=> Ably::Models::TokenDetails
|
68
70
|
# end
|
69
71
|
#
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
def authorize(token_params = nil, auth_options = nil, &success_callback)
|
73
|
+
Ably::Util::SafeDeferrable.new(logger).tap do |authorize_method_deferrable|
|
74
|
+
# Wrap the sync authorize method and wait for the result from the deferrable
|
75
|
+
async_wrap do
|
76
|
+
authorize_sync(token_params, auth_options)
|
77
|
+
end.tap do |auth_operation|
|
78
|
+
# Authorize operation succeeded and we have a new token, now let's perform inline authentication
|
79
|
+
auth_operation.callback do |token|
|
80
|
+
case connection.state.to_sym
|
81
|
+
when :initialized, :disconnected, :suspended, :closed, :closing, :failed
|
82
|
+
connection.connect
|
83
|
+
when :connected
|
84
|
+
perform_inline_auth token
|
85
|
+
when :connecting
|
86
|
+
# Fail all current connection attempts and try again with the new token, see #RTC8b
|
87
|
+
connection.manager.release_and_establish_new_transport
|
88
|
+
else
|
89
|
+
logger.fatal { "Auth#authorize: unsupported state #{connection.state}" }
|
90
|
+
authorize_method_deferrable.fail Ably::Exceptions::InvalidState.new("Unsupported state #{connection.state} for Auth#authorize")
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
# Indicate success or failure based on response from realtime, see #RTC8b1
|
95
|
+
auth_deferrable_resolved = false
|
96
|
+
|
97
|
+
connection.unsafe_once(:connected, :update) do
|
98
|
+
auth_deferrable_resolved = true
|
99
|
+
authorize_method_deferrable.succeed token
|
100
|
+
end
|
101
|
+
connection.unsafe_once(:suspended, :closed, :failed) do |state_change|
|
102
|
+
auth_deferrable_resolved = true
|
103
|
+
authorize_method_deferrable.fail state_change.reason
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Authorize failed, likely due to auth_url or auth_callback failing
|
108
|
+
auth_operation.errback do |error|
|
109
|
+
client.connection.transition_state_machine :failed, reason: error if error.kind_of?(Ably::Exceptions::IncompatibleClientId)
|
110
|
+
authorize_method_deferrable.fail error
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Call the block provided to this method upon success of this deferrable
|
115
|
+
authorize_method_deferrable.callback do |token|
|
116
|
+
yield token if block_given?
|
76
117
|
end
|
77
118
|
end
|
78
119
|
end
|
79
120
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
121
|
+
# @deprecated Use {#authorize} instead
|
122
|
+
def authorise(*args, &block)
|
123
|
+
logger.warn { "Auth#authorise is deprecated and will be removed in 1.0. Please use Auth#authorize instead" }
|
124
|
+
authorize(*args, &block)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Synchronous version of {#authorize}. See {Ably::Auth#authorize} for method definition
|
128
|
+
# Please note that authorize_sync will however not upgrade the current connection's token as this requires
|
129
|
+
# an synchronous operation to send the new authentication details to Ably over a realtime connection
|
130
|
+
#
|
131
|
+
# @param (see Ably::Auth#authorize)
|
132
|
+
# @option (see Ably::Auth#authorize)
|
83
133
|
# @return [Ably::Models::TokenDetails]
|
84
134
|
#
|
85
|
-
def
|
86
|
-
|
135
|
+
def authorize_sync(token_params = nil, auth_options = nil)
|
136
|
+
@authorization_in_flight = true
|
137
|
+
auth_sync.authorize(token_params, auth_options)
|
138
|
+
ensure
|
139
|
+
@authorization_in_flight = false
|
87
140
|
end
|
88
141
|
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
142
|
+
# @api private
|
143
|
+
def authorization_in_flight?
|
144
|
+
@authorization_in_flight
|
145
|
+
end
|
146
|
+
|
147
|
+
# @deprecated Use {#authorize_sync} instead
|
148
|
+
def authorise_sync(*args)
|
149
|
+
logger.warn { "Auth#authorise_sync is deprecated and will be removed in 1.0. Please use Auth#authorize_sync instead" }
|
150
|
+
authorize_sync(*args)
|
151
|
+
end
|
93
152
|
|
94
153
|
# Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
|
95
154
|
#
|
@@ -113,8 +172,8 @@ module Ably
|
|
113
172
|
end
|
114
173
|
|
115
174
|
# Synchronous version of {#request_token}. See {Ably::Auth#request_token} for method definition
|
116
|
-
# @param (see Ably::Auth#
|
117
|
-
# @option (see Ably::Auth#
|
175
|
+
# @param (see Ably::Auth#authorize)
|
176
|
+
# @option (see Ably::Auth#authorize)
|
118
177
|
# @return [Ably::Models::TokenDetails]
|
119
178
|
#
|
120
179
|
def request_token_sync(token_params = {}, auth_options = {})
|
@@ -140,8 +199,8 @@ module Ably
|
|
140
199
|
end
|
141
200
|
|
142
201
|
# Synchronous version of {#create_token_request}. See {Ably::Auth#create_token_request} for method definition
|
143
|
-
# @param (see Ably::Auth#
|
144
|
-
# @option (see Ably::Auth#
|
202
|
+
# @param (see Ably::Auth#authorize)
|
203
|
+
# @option (see Ably::Auth#authorize)
|
145
204
|
# @return [Ably::Models::TokenRequest]
|
146
205
|
#
|
147
206
|
def create_token_request_sync(token_params = {}, auth_options = {})
|
@@ -149,7 +208,7 @@ module Ably
|
|
149
208
|
end
|
150
209
|
|
151
210
|
# Auth header string used in HTTP requests to Ably
|
152
|
-
# Will
|
211
|
+
# Will reauthorize implicitly if required and capable
|
153
212
|
#
|
154
213
|
# @return [Ably::Util::SafeDeferrable]
|
155
214
|
# @yield [String] HTTP authentication value used in HTTP_AUTHORIZATION header
|
@@ -168,13 +227,22 @@ module Ably
|
|
168
227
|
end
|
169
228
|
|
170
229
|
# Auth params used in URI endpoint for Realtime connections
|
171
|
-
# Will
|
230
|
+
# Will reauthorize implicitly if required and capable
|
172
231
|
#
|
173
232
|
# @return [Ably::Util::SafeDeferrable]
|
174
233
|
# @yield [Hash] Auth params for a new Realtime connection
|
175
234
|
#
|
176
235
|
def auth_params(&success_callback)
|
177
|
-
|
236
|
+
fail_callback = Proc.new do |error, deferrable|
|
237
|
+
logger.error { "Failed to authenticate: #{error}" }
|
238
|
+
if error.kind_of?(Ably::Exceptions::BaseAblyException)
|
239
|
+
# Use base exception if it exists carrying forward the status codes
|
240
|
+
deferrable.fail Ably::Exceptions::AuthenticationFailed.new(error.message, nil, nil, error)
|
241
|
+
else
|
242
|
+
deferrable.fail Ably::Exceptions::AuthenticationFailed.new(error.message, 500, 80019)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
async_wrap(success_callback, fail_callback) do
|
178
246
|
auth_params_sync
|
179
247
|
end
|
180
248
|
end
|
@@ -197,22 +265,14 @@ module Ably
|
|
197
265
|
@client
|
198
266
|
end
|
199
267
|
|
200
|
-
#
|
201
|
-
#
|
202
|
-
def
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
logger.debug "Realtime::Auth - current transport disconnected"
|
209
|
-
client.connection.transport.disconnect
|
210
|
-
else
|
211
|
-
EventMachine.add_timer(0.1, &block)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
block.call
|
215
|
-
end
|
268
|
+
# Sends an AUTH ProtocolMessage on the existing connection triggering
|
269
|
+
# an inline AUTH process, see #RTC8a
|
270
|
+
def perform_inline_auth(token)
|
271
|
+
logger.debug { "Performing inline AUTH with Ably using token #{token}" }
|
272
|
+
connection.send_protocol_message(
|
273
|
+
action: Ably::Models::ProtocolMessage::ACTION.Auth.to_i,
|
274
|
+
auth: { access_token: token.token }
|
275
|
+
)
|
216
276
|
end
|
217
277
|
end
|
218
278
|
end
|
@@ -23,8 +23,6 @@ module Ably
|
|
23
23
|
# Channel::STATE.Detached
|
24
24
|
# Channel::STATE.Failed
|
25
25
|
#
|
26
|
-
# Channels emit errors - use +on(:error)+ to subscribe to errors
|
27
|
-
#
|
28
26
|
# @!attribute [r] state
|
29
27
|
# @return {Ably::Realtime::Connection::STATE} channel state
|
30
28
|
#
|
@@ -36,14 +34,24 @@ module Ably
|
|
36
34
|
include Ably::Modules::MessageEmitter
|
37
35
|
extend Ably::Modules::Enum
|
38
36
|
|
37
|
+
# ChannelState
|
38
|
+
# The permited states for this channel
|
39
39
|
STATE = ruby_enum('STATE',
|
40
40
|
:initialized,
|
41
41
|
:attaching,
|
42
42
|
:attached,
|
43
43
|
:detaching,
|
44
44
|
:detached,
|
45
|
+
:suspended,
|
45
46
|
:failed
|
46
47
|
)
|
48
|
+
|
49
|
+
# ChannelEvent
|
50
|
+
# The permitted channel events that are emitted for this channel
|
51
|
+
EVENT = ruby_enum('EVENT',
|
52
|
+
STATE.to_sym_arr + [:update]
|
53
|
+
)
|
54
|
+
|
47
55
|
include Ably::Modules::StateEmitter
|
48
56
|
include Ably::Modules::UsesStateMachine
|
49
57
|
ensure_state_machine_emits 'Ably::Models::ChannelStateChange'
|
@@ -138,11 +146,14 @@ module Ably
|
|
138
146
|
# end
|
139
147
|
#
|
140
148
|
def publish(name, data = nil, attributes = {}, &success_block)
|
141
|
-
|
142
|
-
|
149
|
+
if detached? || detaching? || failed?
|
150
|
+
error = Ably::Exceptions::ChannelInactive.new("Cannot publish messages on a channel in state #{state}")
|
151
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
152
|
+
end
|
143
153
|
|
144
154
|
if !connection.can_publish_messages?
|
145
|
-
|
155
|
+
error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is configured to disallow queueing of messages and connection is currently #{connection.state}")
|
156
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
146
157
|
end
|
147
158
|
|
148
159
|
messages = if name.kind_of?(Enumerable)
|
@@ -192,10 +203,19 @@ module Ably
|
|
192
203
|
#
|
193
204
|
def attach(&success_block)
|
194
205
|
if connection.closing? || connection.closed? || connection.suspended? || connection.failed?
|
195
|
-
|
206
|
+
error = Ably::Exceptions::InvalidStateChange.new("Cannot ATTACH channel when the connection is in a closed, suspended or failed state. Connection state: #{connection.state}")
|
207
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
208
|
+
end
|
209
|
+
|
210
|
+
if !attached?
|
211
|
+
if detaching?
|
212
|
+
# Let the pending operation complete (#RTL4h)
|
213
|
+
once_state_changed { transition_state_machine :attaching if can_transition_to?(:attaching) }
|
214
|
+
else
|
215
|
+
transition_state_machine :attaching if can_transition_to?(:attaching)
|
216
|
+
end
|
196
217
|
end
|
197
218
|
|
198
|
-
transition_state_machine :attaching if can_transition_to?(:attaching)
|
199
219
|
deferrable_for_state_change_to(STATE.Attached, &success_block)
|
200
220
|
end
|
201
221
|
|
@@ -207,14 +227,24 @@ module Ably
|
|
207
227
|
def detach(&success_block)
|
208
228
|
if initialized?
|
209
229
|
success_block.call if block_given?
|
210
|
-
return Ably::Util::SafeDeferrable.
|
211
|
-
EventMachine.next_tick { deferrable.succeed }
|
212
|
-
end
|
230
|
+
return Ably::Util::SafeDeferrable.new_and_succeed_immediately(logger)
|
213
231
|
end
|
214
232
|
|
215
|
-
|
233
|
+
if failed? || connection.closing? || connection.failed?
|
234
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, exception_for_state_change_to(:detaching))
|
235
|
+
end
|
236
|
+
|
237
|
+
if !detached?
|
238
|
+
if attaching?
|
239
|
+
# Let the pending operation complete (#RTL5i)
|
240
|
+
once_state_changed { transition_state_machine :detaching if can_transition_to?(:detaching) }
|
241
|
+
elsif can_transition_to?(:detaching)
|
242
|
+
transition_state_machine :detaching
|
243
|
+
else
|
244
|
+
transition_state_machine! :detached
|
245
|
+
end
|
246
|
+
end
|
216
247
|
|
217
|
-
transition_state_machine :detaching if can_transition_to?(:detaching)
|
218
248
|
deferrable_for_state_change_to(STATE.Detached, &success_block)
|
219
249
|
end
|
220
250
|
|
@@ -244,7 +274,10 @@ module Ably
|
|
244
274
|
#
|
245
275
|
def history(options = {}, &callback)
|
246
276
|
if options.delete(:until_attach)
|
247
|
-
|
277
|
+
unless attached?
|
278
|
+
error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached' )
|
279
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
280
|
+
end
|
248
281
|
options[:from_serial] = attached_serial
|
249
282
|
end
|
250
283
|
|
@@ -263,7 +296,7 @@ module Ably
|
|
263
296
|
end
|
264
297
|
|
265
298
|
# @api private
|
266
|
-
def
|
299
|
+
def set_channel_error_reason(error)
|
267
300
|
@error_reason = error
|
268
301
|
end
|
269
302
|
|
@@ -288,22 +321,26 @@ module Ably
|
|
288
321
|
client.logger
|
289
322
|
end
|
290
323
|
|
324
|
+
# Internal queue used for messages published that cannot yet be enqueued on the connection
|
325
|
+
# @api private
|
326
|
+
def __queue__
|
327
|
+
@queue
|
328
|
+
end
|
329
|
+
|
291
330
|
# As we are using a state machine, do not allow change_state to be used
|
292
331
|
# #transition_state_machine must be used instead
|
293
332
|
private :change_state
|
294
333
|
|
295
334
|
private
|
296
|
-
def queue
|
297
|
-
@queue
|
298
|
-
end
|
299
|
-
|
300
335
|
def setup_event_handlers
|
301
336
|
__incoming_msgbus__.subscribe(:message) do |message|
|
302
|
-
message.decode
|
337
|
+
message.decode(client.encoders, options) do |encode_error, error_message|
|
338
|
+
client.logger.error error_message
|
339
|
+
end
|
303
340
|
emit_message message.name, message
|
304
341
|
end
|
305
342
|
|
306
|
-
|
343
|
+
unsafe_on(STATE.Attached) do
|
307
344
|
process_queue
|
308
345
|
end
|
309
346
|
end
|
@@ -316,15 +353,18 @@ module Ably
|
|
316
353
|
create_message(raw_msg).tap do |message|
|
317
354
|
next if message.client_id.nil?
|
318
355
|
if message.client_id == '*'
|
319
|
-
raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages'
|
356
|
+
raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages')
|
357
|
+
end
|
358
|
+
if message.client_id && !message.client_id.kind_of?(String)
|
359
|
+
raise Ably::Exceptions::IncompatibleClientId.new('client_id must be a String when publishing messages')
|
320
360
|
end
|
321
361
|
unless client.auth.can_assume_client_id?(message.client_id)
|
322
|
-
raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{message.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'"
|
362
|
+
raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{message.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'")
|
323
363
|
end
|
324
364
|
end
|
325
365
|
end
|
326
366
|
|
327
|
-
|
367
|
+
__queue__.push(*messages)
|
328
368
|
|
329
369
|
if attached?
|
330
370
|
process_queue
|
@@ -366,14 +406,14 @@ module Ably
|
|
366
406
|
end
|
367
407
|
|
368
408
|
def messages_in_queue?
|
369
|
-
!
|
409
|
+
!__queue__.empty?
|
370
410
|
end
|
371
411
|
|
372
412
|
# Move messages from Channel Queue into Outgoing Connection Queue
|
373
413
|
def process_queue
|
374
414
|
condition = -> { attached? && messages_in_queue? }
|
375
415
|
non_blocking_loop_while(condition) do
|
376
|
-
send_messages_within_protocol_message
|
416
|
+
send_messages_within_protocol_message __queue__.shift(MAX_PROTOCOL_MESSAGE_BATCH_SIZE)
|
377
417
|
end
|
378
418
|
end
|
379
419
|
|
@@ -387,7 +427,9 @@ module Ably
|
|
387
427
|
|
388
428
|
def create_message(message)
|
389
429
|
Ably::Models::Message(message.dup).tap do |msg|
|
390
|
-
msg.encode
|
430
|
+
msg.encode(client.encoders, options) do |encode_error, error_message|
|
431
|
+
client.logger.error error_message
|
432
|
+
end
|
391
433
|
end
|
392
434
|
end
|
393
435
|
|