ably-pubsub-core 0.0.1.pre

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.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ably/agent.rb +8 -0
  3. data/lib/ably/auth.rb +791 -0
  4. data/lib/ably/exceptions.rb +169 -0
  5. data/lib/ably/logger.rb +119 -0
  6. data/lib/ably/models/auth_details.rb +45 -0
  7. data/lib/ably/models/channel_details.rb +69 -0
  8. data/lib/ably/models/channel_metrics.rb +107 -0
  9. data/lib/ably/models/channel_occupancy.rb +50 -0
  10. data/lib/ably/models/channel_options.rb +120 -0
  11. data/lib/ably/models/channel_state_change.rb +84 -0
  12. data/lib/ably/models/channel_status.rb +63 -0
  13. data/lib/ably/models/cipher_params.rb +137 -0
  14. data/lib/ably/models/connection_details.rb +149 -0
  15. data/lib/ably/models/connection_state_change.rb +83 -0
  16. data/lib/ably/models/delta_extras.rb +28 -0
  17. data/lib/ably/models/device_details.rb +126 -0
  18. data/lib/ably/models/device_push_details.rb +94 -0
  19. data/lib/ably/models/error_info.rb +104 -0
  20. data/lib/ably/models/http_paginated_response.rb +112 -0
  21. data/lib/ably/models/idiomatic_ruby_wrapper.rb +235 -0
  22. data/lib/ably/models/message.rb +223 -0
  23. data/lib/ably/models/message_encoders/base.rb +135 -0
  24. data/lib/ably/models/message_encoders/base64.rb +40 -0
  25. data/lib/ably/models/message_encoders/cipher.rb +84 -0
  26. data/lib/ably/models/message_encoders/json.rb +34 -0
  27. data/lib/ably/models/message_encoders/utf8.rb +26 -0
  28. data/lib/ably/models/nil_logger.rb +20 -0
  29. data/lib/ably/models/paginated_result.rb +201 -0
  30. data/lib/ably/models/presence_message.rb +244 -0
  31. data/lib/ably/models/protocol_message.rb +288 -0
  32. data/lib/ably/models/push_channel_subscription.rb +114 -0
  33. data/lib/ably/models/stats.rb +242 -0
  34. data/lib/ably/models/stats_types.rb +107 -0
  35. data/lib/ably/models/token_details.rb +148 -0
  36. data/lib/ably/models/token_request.rb +180 -0
  37. data/lib/ably/modules/ably.rb +26 -0
  38. data/lib/ably/modules/async_wrapper.rb +69 -0
  39. data/lib/ably/modules/channels_collection.rb +105 -0
  40. data/lib/ably/modules/conversions.rb +153 -0
  41. data/lib/ably/modules/encodeable.rb +111 -0
  42. data/lib/ably/modules/enum.rb +230 -0
  43. data/lib/ably/modules/event_emitter.rb +200 -0
  44. data/lib/ably/modules/event_machine_helpers.rb +26 -0
  45. data/lib/ably/modules/exception_codes.rb +128 -0
  46. data/lib/ably/modules/http_helpers.rb +41 -0
  47. data/lib/ably/modules/message_emitter.rb +78 -0
  48. data/lib/ably/modules/message_pack.rb +14 -0
  49. data/lib/ably/modules/model_common.rb +84 -0
  50. data/lib/ably/modules/safe_deferrable.rb +83 -0
  51. data/lib/ably/modules/safe_yield.rb +41 -0
  52. data/lib/ably/modules/state_emitter.rb +181 -0
  53. data/lib/ably/modules/state_machine.rb +59 -0
  54. data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  55. data/lib/ably/modules/uses_state_machine.rb +111 -0
  56. data/lib/ably/realtime/auth.rb +283 -0
  57. data/lib/ably/realtime/channel/channel_manager.rb +272 -0
  58. data/lib/ably/realtime/channel/channel_properties.rb +40 -0
  59. data/lib/ably/realtime/channel/channel_state_machine.rb +104 -0
  60. data/lib/ably/realtime/channel/publisher.rb +82 -0
  61. data/lib/ably/realtime/channel/push_channel.rb +69 -0
  62. data/lib/ably/realtime/channel.rb +440 -0
  63. data/lib/ably/realtime/channels.rb +71 -0
  64. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +225 -0
  65. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +87 -0
  66. data/lib/ably/realtime/client.rb +363 -0
  67. data/lib/ably/realtime/connection/connection_manager.rb +594 -0
  68. data/lib/ably/realtime/connection/connection_state_machine.rb +144 -0
  69. data/lib/ably/realtime/connection/websocket_transport.rb +287 -0
  70. data/lib/ably/realtime/connection.rb +738 -0
  71. data/lib/ably/realtime/models/nil_channel.rb +30 -0
  72. data/lib/ably/realtime/presence/members_map.rb +395 -0
  73. data/lib/ably/realtime/presence/presence_manager.rb +78 -0
  74. data/lib/ably/realtime/presence/presence_state_machine.rb +57 -0
  75. data/lib/ably/realtime/presence.rb +502 -0
  76. data/lib/ably/realtime/push/admin.rb +75 -0
  77. data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  78. data/lib/ably/realtime/push/device_registrations.rb +105 -0
  79. data/lib/ably/realtime/push.rb +25 -0
  80. data/lib/ably/realtime/recovery_key_context.rb +36 -0
  81. data/lib/ably/realtime.rb +60 -0
  82. data/lib/ably/rest/channel/push_channel.rb +59 -0
  83. data/lib/ably/rest/channel.rb +194 -0
  84. data/lib/ably/rest/channels.rb +47 -0
  85. data/lib/ably/rest/client.rb +727 -0
  86. data/lib/ably/rest/middleware/encoder.rb +49 -0
  87. data/lib/ably/rest/middleware/exceptions.rb +53 -0
  88. data/lib/ably/rest/middleware/external_exceptions.rb +24 -0
  89. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +22 -0
  90. data/lib/ably/rest/middleware/logger.rb +58 -0
  91. data/lib/ably/rest/middleware/parse_json.rb +27 -0
  92. data/lib/ably/rest/middleware/parse_message_pack.rb +43 -0
  93. data/lib/ably/rest/presence.rb +113 -0
  94. data/lib/ably/rest/push/admin.rb +58 -0
  95. data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  96. data/lib/ably/rest/push/device_registrations.rb +114 -0
  97. data/lib/ably/rest/push.rb +25 -0
  98. data/lib/ably/rest.rb +40 -0
  99. data/lib/ably/util/ably_extensions.rb +29 -0
  100. data/lib/ably/util/crypto.rb +134 -0
  101. data/lib/ably/util/pub_sub.rb +45 -0
  102. data/lib/ably/util/safe_deferrable.rb +44 -0
  103. data/lib/ably/version.rb +7 -0
  104. data/lib/ably.rb +16 -0
  105. metadata +497 -0
@@ -0,0 +1,223 @@
1
+ require 'ably/models/message_encoders/base'
2
+
3
+ module Ably::Models
4
+ # Convert messsage argument to a {Message} object and associate with a protocol message if provided
5
+ #
6
+ # @param message [Message,Hash] A message object or Hash of message properties
7
+ # @param [Hash] options (see Message#initialize)
8
+ #
9
+ # @return [Message]
10
+ #
11
+ def self.Message(message, options = {})
12
+ case message
13
+ when Message
14
+ message.tap do
15
+ message.assign_to_protocol_message options[:protocol_message] if options[:protocol_message]
16
+ end
17
+ else
18
+ Message.new(message, options)
19
+ end
20
+ end
21
+
22
+ # Contains an individual message that is sent to, or received from, Ably.
23
+ #
24
+ class Message
25
+ include Ably::Modules::Conversions
26
+ include Ably::Modules::Encodeable
27
+ include Ably::Modules::ModelCommon
28
+ include Ably::Modules::SafeDeferrable if defined?(Ably::Realtime)
29
+
30
+ # Statically register a default set of encoders for this class
31
+ Ably::Models::MessageEncoders.register_default_encoders self
32
+
33
+ # {Message} initializer
34
+ #
35
+ # @spec TM2, TM3
36
+ #
37
+ # @param attributes [Hash] object with the underlying message detail key value attributes
38
+ # @param [Hash] options an options Hash for this initializer
39
+ # @option options [ProtocolMessage] :protocol_message An optional protocol message to assocate the presence message with
40
+ # @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
41
+ #
42
+ def initialize(attributes, options = {})
43
+ @logger = options[:logger] # Logger expected for SafeDeferrable
44
+ @protocol_message = options[:protocol_message]
45
+ @raw_hash_object = attributes
46
+
47
+ set_attributes_object attributes
48
+
49
+ self.attributes[:name] = ensure_utf_8(:name, name, allow_nil: true) if name
50
+ self.attributes[:client_id] = ensure_utf_8(:client_id, client_id, allow_nil: true) if client_id
51
+ self.attributes[:encoding] = ensure_utf_8(:encoding, encoding, allow_nil: true) if encoding
52
+
53
+ self.attributes.freeze
54
+ end
55
+
56
+ # The client ID of the publisher of this message.
57
+ #
58
+ # @spec RSL1g1, TM2b
59
+ #
60
+ # @return [String]
61
+ #
62
+ def client_id
63
+ attributes[:client_id]
64
+ end
65
+
66
+ # This is typically empty, as all messages received from Ably are automatically decoded client-side using this value.
67
+ # However, if the message encoding cannot be processed, this attribute contains the remaining transformations
68
+ # not applied to the data payload.
69
+ #
70
+ # @spec TM2e
71
+ #
72
+ # @return [String]
73
+ #
74
+ def encoding
75
+ attributes[:encoding]
76
+ end
77
+
78
+ # The event name.
79
+ #
80
+ # @spec TM2g
81
+ #
82
+ # @return [String]
83
+ #
84
+ def name
85
+ attributes[:name]
86
+ end
87
+
88
+ # The message payload, if provided.
89
+ #
90
+ # @spec TM2d
91
+ #
92
+ # @return [Hash, nil]
93
+ #
94
+ def data
95
+ @data ||= attributes[:data].freeze
96
+ end
97
+
98
+ # A Unique ID assigned by Ably to this message.
99
+ #
100
+ # @spec TM2a
101
+ #
102
+ # @return [String]
103
+ #
104
+ def id
105
+ attributes.fetch(:id) { "#{protocol_message.id!}:#{protocol_message_index}" }
106
+ end
107
+
108
+ # The connection ID of the publisher of this message.
109
+ #
110
+ # @spec TM2c
111
+ #
112
+ # @return [String]
113
+ #
114
+ def connection_id
115
+ attributes.fetch(:connection_id) { protocol_message.connection_id if assigned_to_protocol_message? }
116
+ end
117
+
118
+ # Timestamp of when the message was received by Ably, as milliseconds since the Unix epoch.
119
+ #
120
+ # @spec TM2f
121
+ #
122
+ # @return [Integer]
123
+ #
124
+ def timestamp
125
+ if attributes[:timestamp]
126
+ as_time_from_epoch(attributes[:timestamp])
127
+ else
128
+ protocol_message.timestamp
129
+ end
130
+ end
131
+
132
+ def attributes
133
+ @attributes
134
+ end
135
+
136
+ def to_json(*args)
137
+ as_json(*args).tap do |message|
138
+ decode_binary_data_before_to_json message
139
+ end.to_json
140
+ end
141
+
142
+ # The size is the sum over name, data, clientId, and extras in bytes (TO3l8a)
143
+ #
144
+ def size
145
+ %w(name data client_id extras).map do |attr|
146
+ if (value = attributes[attr.to_sym]).is_a?(String)
147
+ value.bytesize
148
+ elsif value.nil?
149
+ 0
150
+ else
151
+ value.to_json.bytesize
152
+ end
153
+ end.sum
154
+ end
155
+
156
+ # Assign this message to a ProtocolMessage before delivery to the Ably system
157
+ # @api private
158
+ def assign_to_protocol_message(protocol_message)
159
+ @protocol_message = protocol_message
160
+ end
161
+
162
+ # True if this message is assigned to a ProtocolMessage for delivery to Ably, or received from Ably
163
+ #
164
+ # @return [Boolean]
165
+ #
166
+ # @api private
167
+ def assigned_to_protocol_message?
168
+ !!@protocol_message
169
+ end
170
+
171
+ # The optional ProtocolMessage this message is assigned to. If ProtocolMessage is nil, an error will be raised.
172
+ #
173
+ # @return [Ably::Models::ProtocolMessage]
174
+ #
175
+ # @api private
176
+ def protocol_message
177
+ raise RuntimeError, 'Message is not yet published with a ProtocolMessage. ProtocolMessage is nil' if @protocol_message.nil?
178
+ @protocol_message
179
+ end
180
+
181
+ # Contains any arbitrary key value pairs which may also contain other primitive JSON types, JSON-encodable objects or JSON-encodable arrays.
182
+ # The extras field is provided to contain message metadata and/or ancillary payloads in support of specific functionality, e.g. push
183
+ # 1.2 adds the delta extension which is of type DeltaExtras, and the headers extension, which contains arbitrary string->string key-value pairs,
184
+ # settable at publish time. Unless otherwise specified, the client library should not attempt to do any filtering or validation of the extras
185
+ # field itself, but should treat it opaquely, encoding it and passing it to realtime unaltered.
186
+ # @api private
187
+ def extras
188
+ attributes[:extras].tap do |val|
189
+ unless val.kind_of?(IdiomaticRubyWrapper) || val.kind_of?(Array) || val.kind_of?(Hash) || val.nil?
190
+ raise ArgumentError, "extras contains an unsupported type #{val.class}"
191
+ end
192
+ end
193
+ end
194
+
195
+ # Delta extras extension (TM2i)
196
+ #
197
+ # @return [DeltaExtras, nil]
198
+ #
199
+ # @api private
200
+ def delta_extras
201
+ return nil if attributes[:extras][:delta].nil?
202
+ @delta_extras ||= DeltaExtras.new(attributes[:extras][:delta]).freeze
203
+ end
204
+
205
+ def protocol_message_index
206
+ protocol_message.messages.map(&:object_id).index(self.object_id)
207
+ end
208
+
209
+ private
210
+ def raw_hash_object
211
+ @raw_hash_object
212
+ end
213
+
214
+ def set_attributes_object(new_attributes)
215
+ @attributes = IdiomaticRubyWrapper(new_attributes.clone, stop_at: [:data, :extras])
216
+ end
217
+
218
+ def logger
219
+ return @logger if @logger
220
+ protocol_message.logger if protocol_message
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,135 @@
1
+ require 'ably/modules/conversions'
2
+
3
+ # MessageEncoders are registered with the Ably client library and are responsible
4
+ # for encoding & decoding messages.
5
+ #
6
+ # For example, if a message body is detected as JSON, it is encoded as a String and the encoding attribute
7
+ # of the message is defined as 'json'.
8
+ # Encrypted messages are encoded & decoded by the Cipher encoder.
9
+ #
10
+ module Ably
11
+ module Models
12
+ module MessageEncoders
13
+ extend Ably::Modules::Conversions
14
+
15
+ # Base interface for an Ably Encoder
16
+ #
17
+ class Base
18
+ attr_reader :client, :options
19
+
20
+ def initialize(client, options = {})
21
+ @client = client
22
+ @options = options
23
+ end
24
+
25
+ # #encode is called once before a message is sent to Ably
26
+ #
27
+ # It is the responsibility of the #encode method to detect the intended encoding and modify the :data & :encoding properties of the message object.
28
+ #
29
+ # @param [Hash] message the message as a Hash object received directly from Ably.
30
+ # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
31
+ # This #encode method should modify the message Hash if any encoding action is to be taken
32
+ # @param [Hash] channel_options the options used to initialize the channel that this message was received on
33
+ #
34
+ # @return [void]
35
+ #
36
+ def encode(message, channel_options)
37
+ raise "Not yet implemented"
38
+ end
39
+
40
+ # #decode is called once for every encoding step
41
+ # i.e. if message encoding arrives with 'utf-8/cipher+aes-128-cbc/base64'
42
+ # the decoder will call #decode once for each encoding part such as 'base64', then 'cipher+aes-128-cbc', and finally 'utf-8'
43
+ #
44
+ # It is the responsibility of the #decode method to detect the current encoding part and modify the :data & :encoding properties of the message object.
45
+ #
46
+ # @param [Hash] message the message as a Hash object received directly from Ably.
47
+ # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
48
+ # This #encode method should modify the message Hash if any decoding action is to be taken
49
+ # @param [Hash] channel_options the options used to initialize the channel that this message was received on
50
+ #
51
+ # @return [void]
52
+ #
53
+ def decode(message, channel_options)
54
+ raise "Not yet implemented"
55
+ end
56
+
57
+ # Add encoding to the message Hash.
58
+ # Ensures that encoding delimeter is used where required i.e utf-8/cipher+aes-128-cbc/base64
59
+ #
60
+ # @param [Hash] message the message as a Hash object received directly from Ably.
61
+ # @param [String] encoding encoding to add to the current encoding
62
+ #
63
+ # @return [void]
64
+ #
65
+ def add_encoding_to_message(encoding, message)
66
+ message[:encoding] = [message[:encoding], encoding].compact.join('/')
67
+ end
68
+
69
+ # Returns the right most encoding form a meessage encoding, and nil if none exists
70
+ # i.e. current_encoding_part('utf-8/cipher+aes-128-cbc/base64') => 'base64'
71
+ #
72
+ # @return [String,nil]
73
+ #
74
+ def current_encoding_part(message)
75
+ if message[:encoding]
76
+ message[:encoding].split('/')[-1]
77
+ end
78
+ end
79
+
80
+ # Strip the current encoding part within the message Hash.
81
+ #
82
+ # For example, calling this method on an :encoding value of 'utf-8/cipher+aes-128-cbc/base64' would update the attribute
83
+ # :encoding to 'utf-8/cipher+aes-128-cbc'
84
+ #
85
+ # @param [Hash] message the message as a Hash object received directly from Ably.
86
+ #
87
+ # @return [void]
88
+ #
89
+ def strip_current_encoding_part(message)
90
+ raise "Cannot strip encoding when there is no encoding for this message" unless message[:encoding]
91
+ message[:encoding] = message[:encoding].split('/')[0...-1].join('/')
92
+ message[:encoding] = nil if message[:encoding].empty?
93
+ end
94
+
95
+ # True of the message data payload is empty
96
+ #
97
+ # @param [Hash] message the message as a Hash object received directly from Ably.
98
+ #
99
+ # @return [Boolean]
100
+ #
101
+ def is_empty?(message)
102
+ message[:data].nil? || message[:data] == ''
103
+ end
104
+ end
105
+
106
+ # @api private
107
+ def self.register_default_encoders(client, options = {})
108
+ binary_protocol = !!options[:binary_protocol]
109
+ client.register_encoder Ably::Models::MessageEncoders::Utf8
110
+ client.register_encoder Ably::Models::MessageEncoders::Json
111
+ client.register_encoder Ably::Models::MessageEncoders::Cipher
112
+ client.register_encoder Ably::Models::MessageEncoders::Base64, binary_protocol: binary_protocol
113
+ end
114
+
115
+ # @api private
116
+ def self.encoder_from(encoder, options)
117
+ encoder_klass = if encoder.kind_of?(String)
118
+ encoder.split('::').inject(Kernel) do |base, klass_name|
119
+ base.public_send(:const_get, klass_name)
120
+ end
121
+ else
122
+ encoder
123
+ end
124
+
125
+ raise "Encoder must inherit from `Ably::Models::MessageEncoders::Base`" unless encoder_klass.ancestors.include?(Ably::Models::MessageEncoders::Base)
126
+ encoder_klass.new(self, options)
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ require 'ably/models/message_encoders/base64'
133
+ require 'ably/models/message_encoders/cipher'
134
+ require 'ably/models/message_encoders/json'
135
+ require 'ably/models/message_encoders/utf8'
@@ -0,0 +1,40 @@
1
+ require 'base64'
2
+ require 'ably/models/message_encoders/base'
3
+
4
+ module Ably::Models::MessageEncoders
5
+ # Base64 binary Encoder and Decoder
6
+ # Uses encoding identifier 'base64'
7
+ #
8
+ class Base64 < Base
9
+ ENCODING_ID = 'base64'
10
+
11
+ def encode(message, channel_options)
12
+ return if is_empty?(message)
13
+
14
+ if is_binary?(message) && transport_protocol_text?
15
+ message[:data] = ::Base64.encode64(message[:data])
16
+ add_encoding_to_message ENCODING_ID, message
17
+ end
18
+ end
19
+
20
+ def decode(message, channel_options)
21
+ if is_base64_encoded?(message)
22
+ message[:data] = ::Base64.decode64(message[:data])
23
+ strip_current_encoding_part message
24
+ end
25
+ end
26
+
27
+ private
28
+ def is_binary?(message)
29
+ message[:data].kind_of?(String) && message[:data].encoding == Encoding::ASCII_8BIT
30
+ end
31
+
32
+ def is_base64_encoded?(message)
33
+ current_encoding_part(message).to_s.match(/^#{ENCODING_ID}$/i)
34
+ end
35
+
36
+ def transport_protocol_text?
37
+ !options[:binary_protocol]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ require 'ably/exceptions'
2
+ require 'ably/models/message_encoders/base'
3
+ require 'ably/util/crypto'
4
+
5
+ module Ably::Models::MessageEncoders
6
+ # Cipher Encoder & Decoder that automatically encrypts & decrypts messages using Ably::Util::Crypto
7
+ # when a channel has the +:cipher+ channel option configured
8
+ #
9
+ class Cipher < Base
10
+ ENCODING_ID = 'cipher'
11
+
12
+ def initialize(*args)
13
+ super
14
+ @cryptos = Hash.new
15
+ end
16
+
17
+ def encode(message, channel_options)
18
+ return if is_empty?(message)
19
+ return if already_encrypted?(message)
20
+
21
+ if channel_configured_for_encryption?(channel_options)
22
+ add_encoding_to_message 'utf-8', message unless is_binary?(message) || is_utf8_encoded?(message)
23
+ crypto = crypto_for(channel_options)
24
+ message[:data] = crypto.encrypt(message[:data])
25
+ add_encoding_to_message "#{ENCODING_ID}+#{crypto.cipher_params.cipher_type.downcase}", message
26
+ end
27
+ rescue ArgumentError => e
28
+ raise Ably::Exceptions::CipherError.new(e.message, nil, 92005)
29
+ rescue RuntimeError => e
30
+ if e.message.match(/unsupported cipher algorithm/i)
31
+ raise Ably::Exceptions::CipherError.new(e.message, nil, 92004)
32
+ else
33
+ raise e
34
+ end
35
+ end
36
+
37
+ def decode(message, channel_options)
38
+ if is_cipher_encoded?(message)
39
+ unless channel_configured_for_encryption?(channel_options)
40
+ raise Ably::Exceptions::CipherError.new('Message cannot be decrypted as the channel is not set up for encryption & decryption', nil, 92001)
41
+ end
42
+
43
+ crypto = crypto_for(channel_options)
44
+ unless crypto.cipher_params.cipher_type == cipher_algorithm(message).upcase
45
+ raise Ably::Exceptions::CipherError.new("Cipher algorithm #{crypto.cipher_params.cipher_type} does not match message cipher algorithm of #{cipher_algorithm(message).upcase}", nil, 92002)
46
+ end
47
+
48
+ message[:data] = crypto.decrypt(message[:data])
49
+ strip_current_encoding_part message
50
+ end
51
+ rescue OpenSSL::Cipher::CipherError => e
52
+ raise Ably::Exceptions::CipherError.new("CipherError decrypting data, the private key may not be correct", nil, 92003)
53
+ end
54
+
55
+ private
56
+ def is_binary?(message)
57
+ message.fetch(:data, '').encoding == Encoding::ASCII_8BIT
58
+ end
59
+
60
+ def is_utf8_encoded?(message)
61
+ current_encoding_part(message).to_s.match(/^utf-8$/i)
62
+ end
63
+
64
+ def crypto_for(channel_options)
65
+ @cryptos[channel_options.to_s] ||= Ably::Util::Crypto.new(channel_options.fetch(:cipher, {}))
66
+ end
67
+
68
+ def channel_configured_for_encryption?(channel_options)
69
+ channel_options[:cipher]
70
+ end
71
+
72
+ def is_cipher_encoded?(message)
73
+ !cipher_algorithm(message).nil?
74
+ end
75
+
76
+ def cipher_algorithm(message)
77
+ current_encoding_part(message).to_s[/^#{ENCODING_ID}\+([\w-]+)$/, 1]
78
+ end
79
+
80
+ def already_encrypted?(message)
81
+ message.fetch(:encoding, '').to_s.match(%r{(^|/)#{ENCODING_ID}\+([\w-]+)($|/)})
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,34 @@
1
+ require 'json'
2
+ require 'ably/models/message_encoders/base'
3
+
4
+ module Ably::Models::MessageEncoders
5
+ # JSON Encoder and Decoder
6
+ # Uses encoding identifier 'json' and encodes all objects that are not strings or byte arrays
7
+ #
8
+ class Json < Base
9
+ ENCODING_ID = 'json'
10
+
11
+ def encode(message, channel_options)
12
+ if needs_json_encoding?(message)
13
+ message[:data] = ::JSON.dump(message[:data])
14
+ add_encoding_to_message ENCODING_ID, message
15
+ end
16
+ end
17
+
18
+ def decode(message, channel_options)
19
+ if is_json_encoded?(message)
20
+ message[:data] = ::JSON.parse(message[:data])
21
+ strip_current_encoding_part message
22
+ end
23
+ end
24
+
25
+ private
26
+ def needs_json_encoding?(message)
27
+ !message[:data].kind_of?(String) && !message[:data].nil?
28
+ end
29
+
30
+ def is_json_encoded?(message)
31
+ current_encoding_part(message).to_s.match(/^#{ENCODING_ID}$/i)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require 'ably/models/message_encoders/base'
2
+
3
+ module Ably::Models::MessageEncoders
4
+ # Utf8 Encoder and Decoder
5
+ # Uses encoding identifier 'utf-8' and encodes all JSON objects as UTF-8, and sets the encoding when decoding
6
+ #
7
+ class Utf8 < Base
8
+ ENCODING_ID = 'utf-8'
9
+
10
+ def encode(message, channel_options)
11
+ # no encoding of UTF-8 required
12
+ end
13
+
14
+ def decode(message, channel_options)
15
+ if is_utf8_encoded?(message)
16
+ message[:data] = message[:data].force_encoding(Encoding::UTF_8)
17
+ strip_current_encoding_part message
18
+ end
19
+ end
20
+
21
+ private
22
+ def is_utf8_encoded?(message)
23
+ current_encoding_part(message).to_s.match(/^#{ENCODING_ID}$/i)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module Ably::Models
2
+ # When Log Level set to none, this NilLogger is used to silence all logging
3
+ # NilLogger provides a Ruby Logger compatible interface
4
+ class NilLogger
5
+ def null_method(*args)
6
+ end
7
+
8
+ def level
9
+ :none
10
+ end
11
+
12
+ def level=(value)
13
+ level
14
+ end
15
+
16
+ [:fatal, :error, :warn, :info, :debug].each do |method|
17
+ alias_method method, :null_method
18
+ end
19
+ end
20
+ end