ably-rest 1.0.5 → 1.1.3
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 +5 -5
- data/.travis.yml +6 -3
- data/CHANGELOG.md +1 -1
- data/LICENSE +1 -1
- data/README.md +26 -7
- data/SPEC.md +2003 -1605
- data/ably-rest.gemspec +4 -2
- data/lib/submodules/ably-ruby/.editorconfig +14 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -8
- data/lib/submodules/ably-ruby/CHANGELOG.md +97 -1
- data/lib/submodules/ably-ruby/LICENSE +1 -3
- data/lib/submodules/ably-ruby/README.md +12 -7
- data/lib/submodules/ably-ruby/Rakefile +32 -0
- data/lib/submodules/ably-ruby/SPEC.md +1277 -835
- data/lib/submodules/ably-ruby/ably.gemspec +17 -11
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +34 -8
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +10 -4
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +8 -2
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/device_details.rb +87 -0
- data/lib/submodules/ably-ruby/lib/ably/models/device_push_details.rb +86 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +23 -2
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +12 -12
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +6 -4
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +6 -4
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +32 -2
- data/lib/submodules/ably-ruby/lib/ably/models/push_channel_subscription.rb +89 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +15 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +5 -5
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +27 -105
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +4 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/publisher.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +91 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +9 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +45 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +25 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +7 -7
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +9 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/push.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/admin.rb +61 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +54 -18
- data/lib/submodules/ably-ruby/lib/ably/rest/channel/push_channel.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +171 -41
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push.rb +42 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/admin.rb +54 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/push/device_registrations.rb +103 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +7 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +253 -49
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +33 -21
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +180 -62
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +155 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +293 -13
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +142 -39
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +38 -36
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +12 -3
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +207 -173
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_admin_spec.rb +736 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/push_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +62 -51
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +79 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +6 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +318 -74
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +158 -6
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_admin_spec.rb +952 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/push_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/run_parallel_tests +33 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +1 -9
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -1
- data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +9 -5
- data/lib/submodules/ably-ruby/spec/support/event_emitter_helper.rb +31 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +2 -2
- data/lib/submodules/ably-ruby/spec/support/test_logger_helper.rb +42 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +11 -12
- data/lib/submodules/ably-ruby/spec/unit/models/device_details_spec.rb +102 -0
- data/lib/submodules/ably-ruby/spec/unit/models/device_push_details_spec.rb +101 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +51 -3
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +17 -2
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/push_channel_subscription_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +10 -10
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +13 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/realtime/push_channel_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +30 -1
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/push_channel_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -0
- metadata +51 -10
@@ -0,0 +1,62 @@
|
|
1
|
+
module Ably::Realtime
|
2
|
+
class Channel
|
3
|
+
# A push channel used for push notifications
|
4
|
+
# Each PushChannel maps to exactly one Realtime Channel
|
5
|
+
#
|
6
|
+
# @!attribute [r] channel
|
7
|
+
# @return [Ably::Realtime::Channel] Underlying channel object
|
8
|
+
#
|
9
|
+
class PushChannel
|
10
|
+
attr_reader :channel
|
11
|
+
|
12
|
+
def initialize(channel)
|
13
|
+
raise ArgumentError, "Unsupported channel type '#{channel.class}'" unless channel.kind_of?(Ably::Realtime::Channel)
|
14
|
+
@channel = channel
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"<PushChannel: name=#{channel.name}>"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Subscribe local device for push notifications on this channel
|
22
|
+
#
|
23
|
+
# @note This is unsupported in the Ruby library
|
24
|
+
def subscribe_device(*args)
|
25
|
+
raise_unsupported
|
26
|
+
end
|
27
|
+
|
28
|
+
# Subscribe all devices registered to this client's authenticated client_id for push notifications on this channel
|
29
|
+
#
|
30
|
+
# @note This is unsupported in the Ruby library
|
31
|
+
def subscribe_client_id(*args)
|
32
|
+
raise_unsupported
|
33
|
+
end
|
34
|
+
|
35
|
+
# Unsubscribe local device for push notifications on this channel
|
36
|
+
#
|
37
|
+
# @note This is unsupported in the Ruby library
|
38
|
+
def unsubscribe_device(*args)
|
39
|
+
raise_unsupported
|
40
|
+
end
|
41
|
+
|
42
|
+
# Unsubscribe all devices registered to this client's authenticated client_id for push notifications on this channel
|
43
|
+
#
|
44
|
+
# @note This is unsupported in the Ruby library
|
45
|
+
def unsubscribe_client_id(*args)
|
46
|
+
raise_unsupported
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get list of subscriptions on this channel for this device or authenticate client_id
|
50
|
+
#
|
51
|
+
# @note This is unsupported in the Ruby library
|
52
|
+
def get_subscriptions(*args)
|
53
|
+
raise_unsupported
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def raise_unsupported
|
58
|
+
raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. All PushChannel methods are unavailable'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'ably/realtime/channel/publisher'
|
2
3
|
|
3
4
|
module Ably
|
4
5
|
module Realtime
|
@@ -21,6 +22,9 @@ module Ably
|
|
21
22
|
#
|
22
23
|
class Client
|
23
24
|
include Ably::Modules::AsyncWrapper
|
25
|
+
include Ably::Realtime::Channel::Publisher
|
26
|
+
include Ably::Modules::Conversions
|
27
|
+
|
24
28
|
extend Forwardable
|
25
29
|
|
26
30
|
DOMAIN = 'realtime.ably.io'
|
@@ -38,6 +42,7 @@ module Ably
|
|
38
42
|
|
39
43
|
# The {Ably::Rest::Client REST client} instantiated with the same credentials and configuration that is used for all REST operations such as authentication
|
40
44
|
# @return [Ably::Rest::Client]
|
45
|
+
# @private
|
41
46
|
attr_reader :rest_client
|
42
47
|
|
43
48
|
# When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true
|
@@ -105,9 +110,6 @@ module Ably
|
|
105
110
|
end
|
106
111
|
|
107
112
|
@rest_client = Ably::Rest::Client.new(options.merge(realtime_client: self))
|
108
|
-
@auth = Ably::Realtime::Auth.new(self)
|
109
|
-
@channels = Ably::Realtime::Channels.new(self)
|
110
|
-
@connection = Ably::Realtime::Connection.new(self, options)
|
111
113
|
@echo_messages = rest_client.options.fetch(:echo_messages, true) == false ? false : true
|
112
114
|
@queue_messages = rest_client.options.fetch(:queue_messages, true) == false ? false : true
|
113
115
|
@custom_realtime_host = rest_client.options[:realtime_host] || rest_client.options[:ws_host]
|
@@ -115,6 +117,10 @@ module Ably
|
|
115
117
|
@recover = rest_client.options[:recover]
|
116
118
|
|
117
119
|
raise ArgumentError, "Recovery key '#{recover}' is invalid" if recover && !recover.match(Connection::RECOVER_REGEX)
|
120
|
+
|
121
|
+
@auth = Ably::Realtime::Auth.new(self)
|
122
|
+
@channels = Ably::Realtime::Channels.new(self)
|
123
|
+
@connection = Ably::Realtime::Connection.new(self, options)
|
118
124
|
end
|
119
125
|
|
120
126
|
# Return a {Ably::Realtime::Channel Realtime Channel} for the given name
|
@@ -162,6 +168,12 @@ module Ably
|
|
162
168
|
connection.connect(&block)
|
163
169
|
end
|
164
170
|
|
171
|
+
# Push notification object for publishing and managing push notifications
|
172
|
+
# @return [Ably::Realtime::Push]
|
173
|
+
def push
|
174
|
+
@push ||= Push.new(self)
|
175
|
+
end
|
176
|
+
|
165
177
|
# (see Ably::Rest::Client#request)
|
166
178
|
# @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats
|
167
179
|
#
|
@@ -172,6 +184,74 @@ module Ably
|
|
172
184
|
end
|
173
185
|
end
|
174
186
|
|
187
|
+
# Publish one or more messages to the specified channel.
|
188
|
+
#
|
189
|
+
# This method allows messages to be efficiently published to Ably without instancing a {Ably::Realtime::Channel} object.
|
190
|
+
# If you want to publish a high rate of messages to Ably without instancing channels or using the REST API, then this method
|
191
|
+
# is recommended. However, channel options such as encryption are not supported with this method. If you need to specify channel options
|
192
|
+
# we recommend you use the {Ably::Realtime::Channel} +publish+ method without attaching to each channel, unless you also want to subscribe
|
193
|
+
# to published messages on that channel.
|
194
|
+
#
|
195
|
+
# Note: This feature is still in beta. As such, we cannot guarantee the API will not change in future.
|
196
|
+
#
|
197
|
+
# @param channel [String] The channel name you want to publish the message(s) to
|
198
|
+
# @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
|
199
|
+
# @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
|
200
|
+
# @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
|
201
|
+
#
|
202
|
+
# @yield [Ably::Models::Message,Array<Ably::Models::Message>] On success, will call the block with the {Ably::Models::Message} if a single message is published, or an Array of {Ably::Models::Message} when multiple messages are published
|
203
|
+
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
# # Publish a single message
|
207
|
+
# client.publish 'activityChannel', click', { x: 1, y: 2 }
|
208
|
+
#
|
209
|
+
# # Publish an array of message Hashes
|
210
|
+
# messages = [
|
211
|
+
# { name: 'click', { x: 1, y: 2 } },
|
212
|
+
# { name: 'click', { x: 2, y: 3 } }
|
213
|
+
# ]
|
214
|
+
# client.publish 'activityChannel', messages
|
215
|
+
#
|
216
|
+
# # Publish an array of Ably::Models::Message objects
|
217
|
+
# messages = [
|
218
|
+
# Ably::Models::Message(name: 'click', { x: 1, y: 2 })
|
219
|
+
# Ably::Models::Message(name: 'click', { x: 2, y: 3 })
|
220
|
+
# ]
|
221
|
+
# client.publish 'activityChannel', messages
|
222
|
+
#
|
223
|
+
# client.publish('activityChannel', 'click', 'body') do |message|
|
224
|
+
# puts "#{message.name} event received with #{message.data}"
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# client.publish('activityChannel', 'click', 'body').errback do |error, message|
|
228
|
+
# puts "#{message.name} was not received, error #{error.message}"
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
def publish(channel_name, name, data = nil, attributes = {}, &success_block)
|
232
|
+
if !connection.can_publish_messages?
|
233
|
+
error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
|
234
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
235
|
+
end
|
236
|
+
|
237
|
+
messages = if name.kind_of?(Enumerable)
|
238
|
+
name
|
239
|
+
else
|
240
|
+
name = ensure_utf_8(:name, name, allow_nil: true)
|
241
|
+
ensure_supported_payload data
|
242
|
+
[{ name: name, data: data }.merge(attributes)]
|
243
|
+
end
|
244
|
+
|
245
|
+
if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
|
246
|
+
error = Ably::Exceptions::InvalidRequest.new("It is not possible to publish more than #{Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE} messages with a single publish request.")
|
247
|
+
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
248
|
+
end
|
249
|
+
|
250
|
+
enqueue_messages_on_connection(self, messages, channel_name).tap do |deferrable|
|
251
|
+
deferrable.callback(&success_block) if block_given?
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
175
255
|
# @!attribute [r] endpoint
|
176
256
|
# @return [URI::Generic] Default Ably Realtime endpoint used for all requests
|
177
257
|
def endpoint
|
@@ -214,6 +294,14 @@ module Ably
|
|
214
294
|
@fallback_endpoints[fallback_endpoint_index % @fallback_endpoints.count]
|
215
295
|
end
|
216
296
|
|
297
|
+
# The local device detilas
|
298
|
+
# @return [Ably::Models::LocalDevice]
|
299
|
+
#
|
300
|
+
# @note This is unsupported in the Ruby library
|
301
|
+
def device
|
302
|
+
raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. The local device object is not unavailable'
|
303
|
+
end
|
304
|
+
|
217
305
|
private
|
218
306
|
def endpoint_for_host(host)
|
219
307
|
port = if use_tls?
|
@@ -56,6 +56,7 @@ module Ably::Realtime
|
|
56
56
|
end
|
57
57
|
|
58
58
|
update_connection_recovery_info protocol_message
|
59
|
+
connection.set_connection_confirmed_alive
|
59
60
|
|
60
61
|
case protocol_message.action
|
61
62
|
when ACTION.Heartbeat
|
@@ -75,8 +76,10 @@ module Ably::Realtime
|
|
75
76
|
elsif connection.connected?
|
76
77
|
logger.debug { "Updated CONNECTED ProtocolMessage received (whilst connected)" }
|
77
78
|
process_connected_update_message protocol_message
|
79
|
+
connection.set_connection_confirmed_alive # Connection protocol messages can change liveness settings such as max_idle_interval
|
78
80
|
else
|
79
81
|
process_connected_message protocol_message
|
82
|
+
connection.set_connection_confirmed_alive # Connection protocol messages can change liveness settings such as max_idle_interval
|
80
83
|
end
|
81
84
|
|
82
85
|
when ACTION.Disconnect, ACTION.Disconnected
|
@@ -99,7 +102,11 @@ module Ably::Realtime
|
|
99
102
|
if channel.attached?
|
100
103
|
channel.manager.duplicate_attached_received protocol_message
|
101
104
|
else
|
102
|
-
channel.
|
105
|
+
if channel.failed?
|
106
|
+
logger.warn "Ably::Realtime::Client::IncomingMessageDispatcher - Received an ATTACHED protocol message for FAILED channel #{channel.name}. Ignoring ATTACHED message"
|
107
|
+
else
|
108
|
+
channel.transition_state_machine :attached, reason: protocol_message.error, resumed: protocol_message.has_channel_resumed_flag?, protocol_message: protocol_message
|
109
|
+
end
|
103
110
|
end
|
104
111
|
end
|
105
112
|
|
@@ -129,11 +136,9 @@ module Ably::Realtime
|
|
129
136
|
client.auth.authorize
|
130
137
|
|
131
138
|
else
|
132
|
-
error = Ably::Exceptions::ProtocolError.new("Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher", 400,
|
139
|
+
error = Ably::Exceptions::ProtocolError.new("Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
|
133
140
|
logger.fatal error.message
|
134
141
|
end
|
135
|
-
|
136
|
-
connection.set_connection_confirmed_alive
|
137
142
|
end
|
138
143
|
|
139
144
|
def dispatch_channel_error(protocol_message)
|
@@ -52,7 +52,7 @@ module Ably::Realtime
|
|
52
52
|
protocol_message = outgoing_queue.shift
|
53
53
|
|
54
54
|
if (!connection.transport)
|
55
|
-
protocol_message.fail Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil,
|
55
|
+
protocol_message.fail Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, Ably::Exceptions::Codes::DISCONNECTED)
|
56
56
|
next
|
57
57
|
end
|
58
58
|
|
@@ -66,7 +66,7 @@ module Ably
|
|
66
66
|
ensure_state_machine_emits 'Ably::Models::ConnectionStateChange'
|
67
67
|
|
68
68
|
# Expected format for a connection recover key
|
69
|
-
RECOVER_REGEX = /^(?<recover>[
|
69
|
+
RECOVER_REGEX = /^(?<recover>[^:]+):(?<connection_serial>[^:]+):(?<msg_serial>\-?\d+)$/
|
70
70
|
|
71
71
|
# Defaults for automatic connection recovery and timeouts
|
72
72
|
DEFAULTS = {
|
@@ -79,6 +79,9 @@ module Ably
|
|
79
79
|
websocket_heartbeats_disabled: false,
|
80
80
|
}.freeze
|
81
81
|
|
82
|
+
# Max number of messages to bundle in a single ProtocolMessage
|
83
|
+
MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
|
84
|
+
|
82
85
|
# A unique public identifier for this connection, used to identify this member in presence events and messages
|
83
86
|
# @return [String]
|
84
87
|
attr_reader :id
|
@@ -134,7 +137,6 @@ module Ably
|
|
134
137
|
@client = client
|
135
138
|
@__outgoing_message_queue__ = []
|
136
139
|
@__pending_message_ack_queue__ = []
|
137
|
-
reset_client_serial
|
138
140
|
|
139
141
|
@defaults = DEFAULTS.dup
|
140
142
|
options.each do |key, val|
|
@@ -142,12 +144,25 @@ module Ably
|
|
142
144
|
end if options.kind_of?(Hash)
|
143
145
|
@defaults.freeze
|
144
146
|
|
147
|
+
# If a recover client options is provided, then we need to ensure that the msgSerial matches the
|
148
|
+
# recover serial immediately at client library instantiation. This is done immediately so that any queued
|
149
|
+
# publishes use the correct serial number for these queued messages as well.
|
150
|
+
# There is no harm if the msgSerial is higher than expected if the recover fails.
|
151
|
+
recovery_msg_serial = connection_recover_parts && connection_recover_parts[:msg_serial].to_i
|
152
|
+
if recovery_msg_serial
|
153
|
+
@client_msg_serial = recovery_msg_serial
|
154
|
+
else
|
155
|
+
reset_client_msg_serial
|
156
|
+
end
|
157
|
+
|
145
158
|
Client::IncomingMessageDispatcher.new client, self
|
146
159
|
Client::OutgoingMessageDispatcher.new client, self
|
147
160
|
|
148
161
|
@state_machine = ConnectionStateMachine.new(self)
|
149
162
|
@state = STATE(state_machine.current_state)
|
150
163
|
@manager = ConnectionManager.new(self)
|
164
|
+
|
165
|
+
@current_host = client.endpoint.host
|
151
166
|
end
|
152
167
|
|
153
168
|
# Causes the connection to close, entering the closed state, from any state except
|
@@ -225,7 +240,7 @@ module Ably
|
|
225
240
|
#
|
226
241
|
def ping(&block)
|
227
242
|
if initialized? || suspended? || closing? || closed? || failed?
|
228
|
-
error = Ably::Models::ErrorInfo.new(message: "Cannot send a ping when the connection is #{state}", code:
|
243
|
+
error = Ably::Models::ErrorInfo.new(message: "Cannot send a ping when the connection is #{state}", code: Ably::Exceptions::Codes::DISCONNECTED)
|
229
244
|
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
230
245
|
end
|
231
246
|
|
@@ -235,7 +250,7 @@ module Ably
|
|
235
250
|
ping_id = SecureRandom.hex(16)
|
236
251
|
heartbeat_action = Ably::Models::ProtocolMessage::ACTION.Heartbeat
|
237
252
|
|
238
|
-
wait_for_ping =
|
253
|
+
wait_for_ping = lambda do |protocol_message|
|
239
254
|
next if finished
|
240
255
|
if protocol_message.action == heartbeat_action && protocol_message.id == ping_id
|
241
256
|
finished = true
|
@@ -256,7 +271,7 @@ module Ably
|
|
256
271
|
once_or_if([:suspended, :closing, :closed, :failed]) do
|
257
272
|
next if finished
|
258
273
|
finished = true
|
259
|
-
deferrable.fail Ably::Models::ErrorInfo.new(message: "Ping failed as connection has changed state to #{state}", code:
|
274
|
+
deferrable.fail Ably::Models::ErrorInfo.new(message: "Ping failed as connection has changed state to #{state}", code: Ably::Exceptions::Codes::DISCONNECTED)
|
260
275
|
end
|
261
276
|
|
262
277
|
EventMachine.add_timer(defaults.fetch(:realtime_request_timeout)) do
|
@@ -265,7 +280,7 @@ module Ably
|
|
265
280
|
__incoming_protocol_msgbus__.unsubscribe(:protocol_message, &wait_for_ping)
|
266
281
|
error_msg = "Ping timed out after #{defaults.fetch(:realtime_request_timeout)}s"
|
267
282
|
logger.warn { error_msg }
|
268
|
-
deferrable.fail Ably::Models::ErrorInfo.new(message: error_msg, code:
|
283
|
+
deferrable.fail Ably::Models::ErrorInfo.new(message: error_msg, code: Ably::Exceptions::Codes::TIMEOUT_ERROR)
|
269
284
|
safe_yield block, nil if block_given?
|
270
285
|
end
|
271
286
|
end
|
@@ -280,7 +295,7 @@ module Ably
|
|
280
295
|
EventMachine::HttpRequest.new(url).get.tap do |http|
|
281
296
|
http.errback do
|
282
297
|
yield false if block_given?
|
283
|
-
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unable to connect to #{url}", nil,
|
298
|
+
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unable to connect to #{url}", nil, Ably::Exceptions::Codes::CONNECTION_FAILED)
|
284
299
|
end
|
285
300
|
http.callback do
|
286
301
|
EventMachine.next_tick do
|
@@ -289,7 +304,7 @@ module Ably
|
|
289
304
|
if result
|
290
305
|
deferrable.succeed
|
291
306
|
else
|
292
|
-
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unexpected response from #{url} (#{http.response_header.status})", 400,
|
307
|
+
deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unexpected response from #{url} (#{http.response_header.status})", 400, Ably::Exceptions::Codes::BAD_REQUEST)
|
293
308
|
end
|
294
309
|
end
|
295
310
|
end
|
@@ -300,18 +315,17 @@ module Ably
|
|
300
315
|
# @!attribute [r] recovery_key
|
301
316
|
# @return [String] recovery key that can be used by another client to recover this connection with the :recover option
|
302
317
|
def recovery_key
|
303
|
-
"#{key}:#{serial}" if connection_resumable?
|
318
|
+
"#{key}:#{serial}:#{client_msg_serial}" if connection_resumable?
|
304
319
|
end
|
305
320
|
|
306
321
|
# Following a new connection being made, the connection ID, connection key
|
307
|
-
# and
|
322
|
+
# and connection serial need to match the details provided by the server.
|
308
323
|
#
|
309
324
|
# @return [void]
|
310
325
|
# @api private
|
311
326
|
def configure_new(connection_id, connection_key, connection_serial)
|
312
327
|
@id = connection_id
|
313
328
|
@key = connection_key
|
314
|
-
@client_serial = connection_serial
|
315
329
|
|
316
330
|
update_connection_serial connection_serial
|
317
331
|
end
|
@@ -423,9 +437,12 @@ module Ably
|
|
423
437
|
lib: client.rest_client.lib_version_id,
|
424
438
|
)
|
425
439
|
|
426
|
-
# Use native websocket heartbeats if possible
|
427
|
-
|
428
|
-
|
440
|
+
# Use native websocket heartbeats if possible, but allow Ably protocol heartbeats
|
441
|
+
url_params['heartbeats'] = if defaults.fetch(:websocket_heartbeats_disabled)
|
442
|
+
'true'
|
443
|
+
else
|
444
|
+
'false'
|
445
|
+
end
|
429
446
|
|
430
447
|
url_params['clientId'] = client.auth.client_id if client.auth.has_client_id?
|
431
448
|
|
@@ -536,11 +553,11 @@ module Ably
|
|
536
553
|
defaults.fetch(:realtime_request_timeout)
|
537
554
|
end
|
538
555
|
|
539
|
-
# Resets the client serial (msgSerial) sent to Ably for each new {Ably::Models::ProtocolMessage}
|
540
|
-
# (see #
|
556
|
+
# Resets the client message serial (msgSerial) sent to Ably for each new {Ably::Models::ProtocolMessage}
|
557
|
+
# (see #client_msg_serial)
|
541
558
|
# @api private
|
542
|
-
def
|
543
|
-
@
|
559
|
+
def reset_client_msg_serial
|
560
|
+
@client_msg_serial = -1
|
544
561
|
end
|
545
562
|
|
546
563
|
# When a hearbeat or any other message from Ably is received
|
@@ -562,15 +579,15 @@ module Ably
|
|
562
579
|
|
563
580
|
private
|
564
581
|
|
565
|
-
# The client serial is incremented for every message that is published that requires an ACK.
|
582
|
+
# The client message serial (msgSerial) is incremented for every message that is published that requires an ACK.
|
566
583
|
# Note that this is different to the connection serial that contains the last known serial number
|
567
584
|
# received from the server.
|
568
585
|
#
|
569
586
|
# A message serial number does not guarantee a message has been received, only sent.
|
570
587
|
# A connection serial guarantees the server has received the message and is thus used for connection recovery and resumes.
|
571
588
|
# @return [Integer] starting at -1 indicating no messages sent, 0 when the first message is sent
|
572
|
-
def
|
573
|
-
@
|
589
|
+
def client_msg_serial
|
590
|
+
@client_msg_serial
|
574
591
|
end
|
575
592
|
|
576
593
|
def resume_callbacks
|
@@ -579,7 +596,7 @@ module Ably
|
|
579
596
|
|
580
597
|
def create_pub_sub_message_bus
|
581
598
|
Ably::Util::PubSub.new(
|
582
|
-
coerce_into:
|
599
|
+
coerce_into: lambda do |event|
|
583
600
|
raise KeyError, "Expected :protocol_message, :#{event} is disallowed" unless event == :protocol_message
|
584
601
|
:protocol_message
|
585
602
|
end
|
@@ -595,11 +612,11 @@ module Ably
|
|
595
612
|
end
|
596
613
|
|
597
614
|
def add_message_serial_to(protocol_message)
|
598
|
-
@
|
599
|
-
protocol_message[:msgSerial] =
|
615
|
+
@client_msg_serial += 1
|
616
|
+
protocol_message[:msgSerial] = client_msg_serial
|
600
617
|
yield
|
601
618
|
rescue StandardError => e
|
602
|
-
@
|
619
|
+
@client_msg_serial -= 1
|
603
620
|
raise e
|
604
621
|
end
|
605
622
|
|
@@ -615,8 +632,10 @@ module Ably
|
|
615
632
|
def connection_state_available?
|
616
633
|
return true if connected?
|
617
634
|
|
635
|
+
return false if time_since_connection_confirmed_alive? > connection_state_ttl + details.max_idle_interval
|
636
|
+
|
618
637
|
connected_last = state_history.reverse.find { |connected| connected.fetch(:state) == :connected }
|
619
|
-
if connected_last.nil?
|
638
|
+
if connected_last.nil?
|
620
639
|
false
|
621
640
|
else
|
622
641
|
true
|