ably-rest 0.8.5 → 0.8.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 +1 -1
- data/SPEC.md +1380 -631
- data/ably-rest.gemspec +11 -5
- data/lib/submodules/ably-ruby/.travis.yml +1 -1
- data/lib/submodules/ably-ruby/CHANGELOG.md +42 -48
- data/lib/submodules/ably-ruby/ably.gemspec +7 -1
- data/lib/submodules/ably-ruby/lib/ably.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +155 -47
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +2 -3
- data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +54 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +14 -4
- data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +13 -7
- data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +1 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +3 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +1 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +6 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +15 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +2 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +10 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +62 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +58 -54
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +18 -5
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +9 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +32 -14
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +251 -11
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +12 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +316 -24
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +93 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +177 -86
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +284 -60
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +45 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +4 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +181 -49
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +13 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +222 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +132 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +129 -28
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +7 -7
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +41 -17
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +1 -0
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +16 -0
- data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +60 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +45 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +6 -5
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +5 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +5 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +5 -1
- metadata +57 -13
@@ -8,18 +8,6 @@ module Ably::Realtime
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
class ConnectionManager
|
11
|
-
# Configuration for automatic recovery of failed connection attempts
|
12
|
-
CONNECT_RETRY_CONFIG = {
|
13
|
-
disconnected: { retry_every: 15, max_time_in_state: 120 },
|
14
|
-
suspended: { retry_every: 120, max_time_in_state: Float::INFINITY }
|
15
|
-
}.freeze
|
16
|
-
|
17
|
-
# Time to wait following a connection state request before it's considered a failure
|
18
|
-
TIMEOUTS = {
|
19
|
-
open: 15,
|
20
|
-
close: 10
|
21
|
-
}
|
22
|
-
|
23
11
|
# Error codes from the server that can potentially be resolved
|
24
12
|
RESOLVABLE_ERROR_CODES = {
|
25
13
|
token_expired: 40140
|
@@ -69,9 +57,9 @@ module Ably::Realtime
|
|
69
57
|
end
|
70
58
|
end
|
71
59
|
|
72
|
-
logger.debug "ConnectionManager: Setting up automatic connection timeout timer for #{
|
73
|
-
create_timeout_timer_whilst_in_state(:connect,
|
74
|
-
connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{
|
60
|
+
logger.debug "ConnectionManager: Setting up automatic connection timeout timer for #{realtime_request_timeout}s"
|
61
|
+
create_timeout_timer_whilst_in_state(:connect, realtime_request_timeout) do
|
62
|
+
connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{realtime_request_timeout}s", nil, 80014)
|
75
63
|
end
|
76
64
|
end
|
77
65
|
|
@@ -79,6 +67,11 @@ module Ably::Realtime
|
|
79
67
|
#
|
80
68
|
# @api private
|
81
69
|
def connection_opening_failed(error)
|
70
|
+
if error.kind_of?(Ably::Exceptions::IncompatibleClientId)
|
71
|
+
client.connection.transition_state_machine :failed, reason: error
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
82
75
|
logger.warn "ConnectionManager: Connection to #{connection.current_host}:#{connection.port} failed; #{error.message}"
|
83
76
|
next_state = get_next_retry_state_info
|
84
77
|
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)
|
@@ -89,18 +82,17 @@ module Ably::Realtime
|
|
89
82
|
# @api private
|
90
83
|
def connected(protocol_message)
|
91
84
|
if connection.key
|
92
|
-
if
|
85
|
+
if protocol_message.connection_id == connection.id
|
93
86
|
logger.debug "ConnectionManager: Connection resumed successfully - ID #{connection.id} and key #{connection.key}"
|
94
87
|
EventMachine.next_tick { connection.resumed }
|
95
88
|
else
|
96
89
|
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}"
|
97
90
|
detach_attached_channels protocol_message.error
|
98
|
-
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
99
91
|
end
|
100
92
|
else
|
101
93
|
logger.debug "ConnectionManager: New connection created with ID #{protocol_message.connection_id} and key #{protocol_message.connection_key}"
|
102
|
-
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
103
94
|
end
|
95
|
+
connection.configure_new protocol_message.connection_id, protocol_message.connection_key, protocol_message.connection_serial
|
104
96
|
end
|
105
97
|
|
106
98
|
# Ensures the underlying transport has been disconnected and all event emitter callbacks removed
|
@@ -131,7 +123,7 @@ module Ably::Realtime
|
|
131
123
|
def close_connection
|
132
124
|
connection.send_protocol_message(action: Ably::Models::ProtocolMessage::ACTION.Close)
|
133
125
|
|
134
|
-
create_timeout_timer_whilst_in_state(:close,
|
126
|
+
create_timeout_timer_whilst_in_state(:close, realtime_request_timeout) do
|
135
127
|
force_close_connection if connection.closing?
|
136
128
|
end
|
137
129
|
end
|
@@ -158,19 +150,23 @@ module Ably::Realtime
|
|
158
150
|
# @api private
|
159
151
|
def respond_to_transport_disconnected_when_connecting(error)
|
160
152
|
return unless connection.disconnected? || connection.suspended? # do nothing if state has changed through an explicit request
|
161
|
-
return unless
|
153
|
+
return unless can_retry_connection? # do not always reattempt connection or change state as client may be re-authorising
|
162
154
|
|
163
155
|
if error.kind_of?(Ably::Models::ErrorInfo)
|
164
|
-
|
165
|
-
|
156
|
+
if error.code == RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
157
|
+
next_state = get_next_retry_state_info
|
158
|
+
logger.debug "ConnectionManager: Transport disconnected because of token expiry, pausing #{next_state.fetch(:pause)}s before reattempting to connect"
|
159
|
+
EventMachine.add_timer(next_state.fetch(:pause)) { renew_token_and_reconnect error }
|
160
|
+
return
|
161
|
+
end
|
166
162
|
end
|
167
163
|
|
168
|
-
|
164
|
+
if connection.state == :suspended
|
165
|
+
return if connection_retry_for(:suspended)
|
166
|
+
elsif connection.state == :disconnected
|
169
167
|
return if connection_retry_for(:disconnected)
|
170
168
|
end
|
171
169
|
|
172
|
-
return if connection_retry_for(:suspended)
|
173
|
-
|
174
170
|
# Fallback if no other criteria met
|
175
171
|
connection.transition_state_machine :failed, reason: error
|
176
172
|
end
|
@@ -179,7 +175,11 @@ module Ably::Realtime
|
|
179
175
|
#
|
180
176
|
# @api private
|
181
177
|
def respond_to_transport_disconnected_whilst_connected(error)
|
182
|
-
|
178
|
+
unless connection.disconnected? || connection.suspended?
|
179
|
+
logger.warn "ConnectionManager: Connection #{"to #{connection.transport.url}" if connection.transport} was disconnected unexpectedly"
|
180
|
+
else
|
181
|
+
logger.debug "ConnectionManager: Transport disconnected whilst connection in #{connection.state} state"
|
182
|
+
end
|
183
183
|
|
184
184
|
if error.kind_of?(Ably::Models::ErrorInfo) && error.code != RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
185
185
|
connection.emit :error, error
|
@@ -197,10 +197,8 @@ module Ably::Realtime
|
|
197
197
|
def error_received_from_server(error)
|
198
198
|
case error.code
|
199
199
|
when RESOLVABLE_ERROR_CODES.fetch(:token_expired)
|
200
|
-
|
201
|
-
connection.
|
202
|
-
renew_token_and_reconnect error
|
203
|
-
end
|
200
|
+
next_state = get_next_retry_state_info
|
201
|
+
connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: error
|
204
202
|
else
|
205
203
|
logger.error "ConnectionManager: Error #{error.class.name} code #{error.code} received from server '#{error.message}', transitioning to failed state"
|
206
204
|
connection.transition_state_machine :failed, reason: error
|
@@ -233,10 +231,16 @@ module Ably::Realtime
|
|
233
231
|
client.channels
|
234
232
|
end
|
235
233
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
234
|
+
def realtime_request_timeout
|
235
|
+
connection.defaults.fetch(:realtime_request_timeout)
|
236
|
+
end
|
237
|
+
|
238
|
+
def retry_timeout_for(state)
|
239
|
+
connection.defaults.fetch("#{state}_retry_timeout".to_sym) { raise ArgumentError.new("#{state} does not have a configured retry timeout") }
|
240
|
+
end
|
241
|
+
|
242
|
+
def state_has_retry_timeout?(state)
|
243
|
+
connection.defaults.has_key?("#{state}_retry_timeout".to_sym)
|
240
244
|
end
|
241
245
|
|
242
246
|
# Create a timer that will execute in timeout_in seconds.
|
@@ -267,12 +271,12 @@ module Ably::Realtime
|
|
267
271
|
end
|
268
272
|
|
269
273
|
def next_retry_pause(retry_state)
|
270
|
-
return nil unless
|
274
|
+
return nil unless state_has_retry_timeout?(retry_state)
|
271
275
|
|
272
276
|
if retries_for_state(retry_state, ignore_states: [:connecting]).empty?
|
273
277
|
0
|
274
278
|
else
|
275
|
-
|
279
|
+
retry_timeout_for(retry_state)
|
276
280
|
end
|
277
281
|
end
|
278
282
|
|
@@ -284,20 +288,18 @@ module Ably::Realtime
|
|
284
288
|
time_spent_attempting_state(:disconnected, ignore_states: [:connecting])
|
285
289
|
end
|
286
290
|
|
287
|
-
# Reattempt a connection with a delay based on the
|
291
|
+
# Reattempt a connection with a delay based on the configured retry timeout for +from_state+
|
288
292
|
#
|
289
293
|
# @return [Boolean] True if a connection attempt has been set up, false if no further connection attempts can be made for this state
|
290
294
|
#
|
291
295
|
def connection_retry_for(from_state)
|
292
|
-
retry_params = CONNECT_RETRY_CONFIG.fetch(from_state)
|
293
|
-
|
294
296
|
if can_reattempt_connect_for_state?(from_state)
|
295
|
-
if retries_for_state(from_state, ignore_states: [:connecting]).empty?
|
296
|
-
logger.debug "ConnectionManager: Will attempt reconnect immediately as no previous reconnect attempts made in
|
297
|
+
if connection.state == :disconnected && retries_for_state(from_state, ignore_states: [:connecting]).empty?
|
298
|
+
logger.debug "ConnectionManager: Will attempt reconnect immediately as no previous reconnect attempts made in state #{from_state}"
|
297
299
|
EventMachine.next_tick { connection.connect }
|
298
300
|
else
|
299
|
-
logger.debug "ConnectionManager: Pausing for #{
|
300
|
-
create_timeout_timer_whilst_in_state(
|
301
|
+
logger.debug "ConnectionManager: Pausing for #{retry_timeout_for(from_state)}s before attempting to reconnect"
|
302
|
+
create_timeout_timer_whilst_in_state(from_state, retry_timeout_for(from_state)) do
|
301
303
|
connection.connect if connection.state == from_state
|
302
304
|
end
|
303
305
|
end
|
@@ -309,8 +311,14 @@ module Ably::Realtime
|
|
309
311
|
# For example, if the state is disconnected, and has been in a cycle of disconnected > connect > disconnected
|
310
312
|
# so long as the time in this cycle of states is less than max_time_in_state, this will return true
|
311
313
|
def can_reattempt_connect_for_state?(state)
|
312
|
-
|
313
|
-
|
314
|
+
case state
|
315
|
+
when :disconnected
|
316
|
+
time_spent_attempting_state(:disconnected, ignore_states: [:connecting]) < connection.defaults.fetch(:connection_state_ttl)
|
317
|
+
when :suspended
|
318
|
+
true # suspended state remains indefinitely
|
319
|
+
else
|
320
|
+
raise ArgumentError, "Connections in state '#{state}' cannot be reattempted"
|
321
|
+
end
|
314
322
|
end
|
315
323
|
|
316
324
|
# Returns a float representing the amount of time passed since the first consecutive attempt of this state
|
@@ -380,23 +388,18 @@ module Ably::Realtime
|
|
380
388
|
def renew_token_and_reconnect(error)
|
381
389
|
if client.auth.token_renewable?
|
382
390
|
if @renewing_token
|
383
|
-
|
391
|
+
logger.error 'ConnectionManager: Attempting to renew token whilst another token renewal is underway. Aborting current renew token request'
|
384
392
|
return
|
385
393
|
end
|
386
394
|
|
387
395
|
@renewing_token = true
|
388
396
|
logger.info "ConnectionManager: Token has expired and is renewable, renewing token now"
|
389
397
|
|
390
|
-
client.auth.authorise.tap do |authorise_deferrable|
|
398
|
+
client.auth.authorise({}, force: true).tap do |authorise_deferrable|
|
391
399
|
authorise_deferrable.callback do |token_details|
|
392
400
|
logger.info 'ConnectionManager: Token renewed succesfully following expiration'
|
393
401
|
|
394
|
-
|
395
|
-
@renewing_token = false
|
396
|
-
connection.off &state_changed_callback
|
397
|
-
end
|
398
|
-
|
399
|
-
connection.unsafe_once :connected, :closed, :failed, &state_changed_callback
|
402
|
+
connection.once_state_changed { @renewing_token = false }
|
400
403
|
|
401
404
|
if token_details && !token_details.expired?
|
402
405
|
connection.connect
|
@@ -406,6 +409,7 @@ module Ably::Realtime
|
|
406
409
|
end
|
407
410
|
|
408
411
|
authorise_deferrable.errback do |auth_error|
|
412
|
+
@renewing_token = false
|
409
413
|
logger.error "ConnectionManager: Error authorising following token expiry: #{auth_error}"
|
410
414
|
connection.transition_state_machine :failed, reason: auth_error
|
411
415
|
end
|
@@ -428,7 +432,7 @@ module Ably::Realtime
|
|
428
432
|
end
|
429
433
|
end
|
430
434
|
|
431
|
-
def
|
435
|
+
def can_retry_connection?
|
432
436
|
!@renewing_token
|
433
437
|
end
|
434
438
|
|
@@ -74,8 +74,8 @@ module Ably::Realtime
|
|
74
74
|
data = options.fetch(:data, nil)
|
75
75
|
deferrable = create_deferrable
|
76
76
|
|
77
|
+
ensure_supported_client_id client_id
|
77
78
|
ensure_supported_payload data unless data.nil?
|
78
|
-
raise Ably::Exceptions::Standard.new('Unable to enter presence channel without a client_id', 400, 91000) unless client_id
|
79
79
|
|
80
80
|
@data = data
|
81
81
|
@client_id = client_id
|
@@ -120,8 +120,8 @@ module Ably::Realtime
|
|
120
120
|
#
|
121
121
|
def enter_client(client_id, options = {}, &success_block)
|
122
122
|
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
123
|
+
ensure_supported_client_id client_id
|
123
124
|
ensure_supported_payload options[:data] if options.has_key?(:data)
|
124
|
-
raise Ably::Exceptions::Standard.new('Unable to enter presence channel without a client_id', 400, 91000) unless client_id
|
125
125
|
|
126
126
|
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Enter, client_id, options, &success_block)
|
127
127
|
end
|
@@ -139,6 +139,7 @@ module Ably::Realtime
|
|
139
139
|
data = options.fetch(:data, self.data) # nil value defaults leave data to existing value
|
140
140
|
deferrable = create_deferrable
|
141
141
|
|
142
|
+
ensure_supported_client_id client_id
|
142
143
|
ensure_supported_payload data unless data.nil?
|
143
144
|
raise Ably::Exceptions::Standard.new('Unable to leave presence channel that is not entered', 400, 91002) unless able_to_leave?
|
144
145
|
|
@@ -178,8 +179,8 @@ module Ably::Realtime
|
|
178
179
|
#
|
179
180
|
def leave_client(client_id, options = {}, &success_block)
|
180
181
|
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
182
|
+
ensure_supported_client_id client_id
|
181
183
|
ensure_supported_payload options[:data] if options.has_key?(:data)
|
182
|
-
raise Ably::Exceptions::Standard.new('Unable to leave presence channel without a client_id', 400, 91000) unless client_id
|
183
184
|
|
184
185
|
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Leave, client_id, options, &success_block)
|
185
186
|
end
|
@@ -198,8 +199,8 @@ module Ably::Realtime
|
|
198
199
|
data = options.fetch(:data, nil)
|
199
200
|
deferrable = create_deferrable
|
200
201
|
|
202
|
+
ensure_supported_client_id client_id
|
201
203
|
ensure_supported_payload data unless data.nil?
|
202
|
-
raise Ably::Exceptions::Standard.new('Unable to update presence channel without a client_id', 400, 91000) unless client_id
|
203
204
|
|
204
205
|
@data = data
|
205
206
|
|
@@ -230,8 +231,8 @@ module Ably::Realtime
|
|
230
231
|
#
|
231
232
|
def update_client(client_id, options = {}, &success_block)
|
232
233
|
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
234
|
+
ensure_supported_client_id client_id
|
233
235
|
ensure_supported_payload options[:data] if options.has_key?(:data)
|
234
|
-
raise Ably::Exceptions::Standard.new('Unable to enter presence channel without a client_id', 400, 91000) unless client_id
|
235
236
|
|
236
237
|
send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Update, client_id, options, &success_block)
|
237
238
|
end
|
@@ -387,6 +388,18 @@ module Ably::Realtime
|
|
387
388
|
deferrable
|
388
389
|
end
|
389
390
|
|
391
|
+
def ensure_supported_client_id(check_client_id)
|
392
|
+
unless check_client_id
|
393
|
+
raise Ably::Exceptions::IncompatibleClientId.new('Unable to enter/update/leave presence channel without a client_id', 400, 40012)
|
394
|
+
end
|
395
|
+
if check_client_id == '*'
|
396
|
+
raise Ably::Exceptions::IncompatibleClientId.new('Unable to enter/update/leave presence channel with the reserved wildcard client_id', 400, 40012)
|
397
|
+
end
|
398
|
+
unless client.auth.can_assume_client_id?(check_client_id)
|
399
|
+
raise Ably::Exceptions::IncompatibleClientId.new("Cannot enter with provided client_id '#{check_client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'", 400, 40012)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
390
403
|
def send_protocol_message_and_transition_state_to(action, options = {}, &success_block)
|
391
404
|
deferrable = options.fetch(:deferrable) { raise ArgumentError, 'option :deferrable is required' }
|
392
405
|
client_id = options.fetch(:client_id) { raise ArgumentError, 'option :client_id is required' }
|
@@ -67,6 +67,14 @@ module Ably
|
|
67
67
|
payload = messages.map do |message|
|
68
68
|
Ably::Models::Message(message.dup).tap do |message|
|
69
69
|
message.encode self
|
70
|
+
|
71
|
+
next if message.client_id.nil?
|
72
|
+
if message.client_id == '*'
|
73
|
+
raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages', 400, 40012)
|
74
|
+
end
|
75
|
+
unless client.auth.can_assume_client_id?(message.client_id)
|
76
|
+
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}'", 400, 40012)
|
77
|
+
end
|
70
78
|
end.as_json
|
71
79
|
end
|
72
80
|
|
@@ -123,7 +131,7 @@ module Ably
|
|
123
131
|
|
124
132
|
private
|
125
133
|
def base_path
|
126
|
-
"/channels/#{
|
134
|
+
"/channels/#{Addressable::URI.encode(name)}"
|
127
135
|
end
|
128
136
|
|
129
137
|
def decode_message(message)
|
@@ -21,12 +21,12 @@ module Ably
|
|
21
21
|
# Default Ably domain for REST
|
22
22
|
DOMAIN = 'rest.ably.io'
|
23
23
|
|
24
|
-
# Configuration for
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
# Configuration for HTTP timeouts and HTTP request reattempts to fallback hosts
|
25
|
+
HTTP_DEFAULTS = {
|
26
|
+
open_timeout: 4,
|
27
|
+
request_timeout: 15,
|
28
|
+
max_retry_duration: 10,
|
29
|
+
max_retry_count: 3
|
30
30
|
}.freeze
|
31
31
|
|
32
32
|
def_delegators :auth, :client_id, :auth_options
|
@@ -51,18 +51,23 @@ module Ably
|
|
51
51
|
# @return [Logger::Severity]
|
52
52
|
attr_reader :log_level
|
53
53
|
|
54
|
-
# The custom host that is being used if it was provided with the option
|
54
|
+
# The custom host that is being used if it was provided with the option +:rest_host+ when the {Client} was created
|
55
55
|
# @return [String,Nil]
|
56
56
|
attr_reader :custom_host
|
57
57
|
|
58
|
-
# The custom port for non-TLS requests if it was provided with the option
|
58
|
+
# The custom port for non-TLS requests if it was provided with the option +:port+ when the {Client} was created
|
59
59
|
# @return [Integer,Nil]
|
60
60
|
attr_reader :custom_port
|
61
61
|
|
62
|
-
# The custom TLS port for TLS requests if it was provided with the option
|
62
|
+
# The custom TLS port for TLS requests if it was provided with the option +:tls_port+ when the {Client} was created
|
63
63
|
# @return [Integer,Nil]
|
64
64
|
attr_reader :custom_tls_port
|
65
65
|
|
66
|
+
# The immutable configured HTTP defaults for this client.
|
67
|
+
# See {#initialize} for the configurable HTTP defaults prefixed with +http_+
|
68
|
+
# @return [Hash]
|
69
|
+
attr_reader :http_defaults
|
70
|
+
|
66
71
|
# The registered encoders that are used to encode and decode message payloads
|
67
72
|
# @return [Array<Ably::Models::MessageEncoder::Base>]
|
68
73
|
# @api private
|
@@ -96,6 +101,11 @@ module Ably
|
|
96
101
|
# @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
|
97
102
|
# @option options [Hash] :token_params convenience to pass in +token_params+ that will be used as a default for all token requests. See {Auth#create_token_request}
|
98
103
|
#
|
104
|
+
# @option options [Integer] :http_open_timeout (4 seconds) timeout in seconds for opening an HTTP connection for all HTTP requests
|
105
|
+
# @option options [Integer] :http_request_timeout (15 seconds) timeout in seconds for any single complete HTTP request and response
|
106
|
+
# @option options [Integer] :http_max_retry_count (3) maximum number of fallback host retries for HTTP requests that fail due to network issues or server problems
|
107
|
+
# @option options [Integer] :http_max_retry_duration (10 seconds) maximum elapsed time in which fallback host retries for HTTP requests will be attempted i.e. if the first default host attempt takes 5s, and then the subsequent fallback retry attempt takes 7s, no further fallback host attempts will be made as the total elapsed time of 12s exceeds the default 10s limit
|
108
|
+
#
|
99
109
|
# @return [Ably::Rest::Client]
|
100
110
|
#
|
101
111
|
# @example
|
@@ -127,6 +137,14 @@ module Ably
|
|
127
137
|
@custom_port = options.delete(:port)
|
128
138
|
@custom_tls_port = options.delete(:tls_port)
|
129
139
|
|
140
|
+
@http_defaults = HTTP_DEFAULTS.dup
|
141
|
+
options.each do |key, val|
|
142
|
+
if http_key = key[/^http_(.+)/, 1]
|
143
|
+
@http_defaults[http_key.to_sym] = val if val && @http_defaults.has_key?(http_key.to_sym)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
@http_defaults.freeze
|
147
|
+
|
130
148
|
if @log_level == :none
|
131
149
|
@custom_logger = Ably::Models::NilLogger.new
|
132
150
|
else
|
@@ -322,8 +340,8 @@ module Ably
|
|
322
340
|
# Sends HTTP request to connection end point
|
323
341
|
# Connection failures will automatically be reattempted until thresholds are met
|
324
342
|
def send_request(method, path, params, options)
|
325
|
-
|
326
|
-
|
343
|
+
max_retry_count = http_defaults.fetch(:max_retry_count)
|
344
|
+
max_retry_duration = http_defaults.fetch(:max_retry_duration)
|
327
345
|
requested_at = Time.now
|
328
346
|
retry_count = 0
|
329
347
|
|
@@ -338,7 +356,7 @@ module Ably
|
|
338
356
|
|
339
357
|
rescue Faraday::TimeoutError, Faraday::ClientError, Ably::Exceptions::ServerError => error
|
340
358
|
time_passed = Time.now - requested_at
|
341
|
-
if can_fallback_to_alternate_ably_host? && retry_count <
|
359
|
+
if can_fallback_to_alternate_ably_host? && retry_count < max_retry_count && time_passed <= max_retry_duration
|
342
360
|
retry_count += 1
|
343
361
|
retry
|
344
362
|
end
|
@@ -395,8 +413,8 @@ module Ably
|
|
395
413
|
user_agent: user_agent
|
396
414
|
},
|
397
415
|
request: {
|
398
|
-
open_timeout:
|
399
|
-
timeout:
|
416
|
+
open_timeout: http_defaults.fetch(:open_timeout),
|
417
|
+
timeout: http_defaults.fetch(:request_timeout)
|
400
418
|
}
|
401
419
|
}
|
402
420
|
end
|