ably 0.1.6 → 0.2.0
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/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/LICENSE.txt +1 -1
- data/README.md +8 -1
- data/Rakefile +10 -0
- data/ably.gemspec +18 -18
- data/lib/ably.rb +6 -5
- data/lib/ably/auth.rb +11 -14
- data/lib/ably/exceptions.rb +18 -15
- data/lib/ably/logger.rb +102 -0
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/message.rb +19 -5
- data/lib/ably/models/message_encoders/base.rb +107 -0
- data/lib/ably/models/message_encoders/base64.rb +39 -0
- data/lib/ably/models/message_encoders/cipher.rb +80 -0
- data/lib/ably/models/message_encoders/json.rb +33 -0
- data/lib/ably/models/message_encoders/utf8.rb +33 -0
- data/lib/ably/models/paginated_resource.rb +23 -6
- data/lib/ably/models/presence_message.rb +19 -7
- data/lib/ably/models/protocol_message.rb +5 -4
- data/lib/ably/models/token.rb +2 -2
- data/lib/ably/modules/channels_collection.rb +0 -3
- data/lib/ably/modules/conversions.rb +3 -3
- data/lib/ably/modules/encodeable.rb +68 -0
- data/lib/ably/modules/event_emitter.rb +10 -4
- data/lib/ably/modules/event_machine_helpers.rb +6 -4
- data/lib/ably/modules/http_helpers.rb +7 -2
- data/lib/ably/modules/model_common.rb +2 -0
- data/lib/ably/modules/state_emitter.rb +10 -1
- data/lib/ably/realtime.rb +19 -12
- data/lib/ably/realtime/channel.rb +26 -13
- data/lib/ably/realtime/client.rb +31 -7
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -3
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +13 -4
- data/lib/ably/realtime/connection.rb +152 -46
- data/lib/ably/realtime/connection/connection_manager.rb +168 -0
- data/lib/ably/realtime/connection/connection_state_machine.rb +56 -33
- data/lib/ably/realtime/connection/websocket_transport.rb +56 -29
- data/lib/ably/{models → realtime/models}/nil_channel.rb +1 -1
- data/lib/ably/realtime/presence.rb +38 -13
- data/lib/ably/rest.rb +7 -5
- data/lib/ably/rest/channel.rb +24 -3
- data/lib/ably/rest/client.rb +56 -17
- data/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/ably/rest/middleware/exceptions.rb +3 -2
- data/lib/ably/rest/middleware/logger.rb +37 -0
- data/lib/ably/rest/presence.rb +10 -2
- data/lib/ably/util/crypto.rb +57 -29
- data/lib/ably/util/pub_sub.rb +11 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_spec.rb +65 -7
- data/spec/acceptance/realtime/connection_spec.rb +123 -27
- data/spec/acceptance/realtime/message_spec.rb +319 -34
- data/spec/acceptance/realtime/presence_history_spec.rb +58 -0
- data/spec/acceptance/realtime/presence_spec.rb +160 -18
- data/spec/acceptance/rest/auth_spec.rb +93 -49
- data/spec/acceptance/rest/base_spec.rb +10 -10
- data/spec/acceptance/rest/channel_spec.rb +35 -19
- data/spec/acceptance/rest/channels_spec.rb +8 -8
- data/spec/acceptance/rest/message_spec.rb +224 -0
- data/spec/acceptance/rest/presence_spec.rb +159 -23
- data/spec/acceptance/rest/stats_spec.rb +5 -5
- data/spec/acceptance/rest/time_spec.rb +4 -4
- data/spec/integration/rest/auth.rb +1 -1
- data/spec/resources/crypto-data-128.json +56 -0
- data/spec/resources/crypto-data-256.json +56 -0
- data/spec/rspec_config.rb +39 -0
- data/spec/spec_helper.rb +4 -42
- data/spec/support/api_helper.rb +1 -1
- data/spec/support/event_machine_helper.rb +0 -5
- data/spec/support/protocol_msgbus_helper.rb +3 -3
- data/spec/support/test_app.rb +3 -3
- data/spec/unit/logger_spec.rb +135 -0
- data/spec/unit/models/message_encoders/base64_spec.rb +181 -0
- data/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
- data/spec/unit/models/message_encoders/json_spec.rb +135 -0
- data/spec/unit/models/message_encoders/utf8_spec.rb +100 -0
- data/spec/unit/models/message_spec.rb +16 -1
- data/spec/unit/models/paginated_resource_spec.rb +46 -0
- data/spec/unit/models/presence_message_spec.rb +18 -5
- data/spec/unit/models/token_spec.rb +1 -1
- data/spec/unit/modules/event_emitter_spec.rb +24 -10
- data/spec/unit/realtime/channel_spec.rb +3 -3
- data/spec/unit/realtime/channels_spec.rb +1 -1
- data/spec/unit/realtime/client_spec.rb +44 -2
- data/spec/unit/realtime/connection_spec.rb +2 -2
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +4 -4
- data/spec/unit/realtime/presence_spec.rb +1 -1
- data/spec/unit/realtime/realtime_spec.rb +3 -3
- data/spec/unit/realtime/websocket_transport_spec.rb +24 -0
- data/spec/unit/rest/channels_spec.rb +1 -1
- data/spec/unit/rest/client_spec.rb +45 -10
- data/spec/unit/util/crypto_spec.rb +82 -0
- data/spec/unit/{modules → util}/pub_sub_spec.rb +13 -1
- metadata +43 -12
- data/spec/acceptance/crypto.rb +0 -63
data/lib/ably/rest/channel.rb
CHANGED
@@ -2,6 +2,13 @@ module Ably
|
|
2
2
|
module Rest
|
3
3
|
# The Ably Realtime service organises the traffic within any application into named channels.
|
4
4
|
# Channels are the "unit" of message distribution; clients attach to channels to subscribe to messages, and every message broadcast by the service is associated with a unique channel.
|
5
|
+
#
|
6
|
+
# @!attribute [r] client
|
7
|
+
# @return {Ably::Realtime::Client} Ably client associated with this channel
|
8
|
+
# @!attribute [r] name
|
9
|
+
# @return {String} channel name
|
10
|
+
# @!attribute [r] options
|
11
|
+
# @return {Hash} channel options configured for this channel, see {#initialize} for channel_options
|
5
12
|
class Channel
|
6
13
|
include Ably::Modules::Conversions
|
7
14
|
|
@@ -11,7 +18,10 @@ module Ably
|
|
11
18
|
#
|
12
19
|
# @param client [Ably::Rest::Client]
|
13
20
|
# @param name [String] The name of the channel
|
14
|
-
# @param channel_options [Hash] Channel options, currently reserved for
|
21
|
+
# @param channel_options [Hash] Channel options, currently reserved for Encryption options
|
22
|
+
# @option channel_options [Boolean] :encrypted setting this to true for this channel will encrypt & decrypt all messages automatically
|
23
|
+
# @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of `cipher_params` options
|
24
|
+
#
|
15
25
|
def initialize(client, name, channel_options = {})
|
16
26
|
@client = client
|
17
27
|
@name = name
|
@@ -29,7 +39,11 @@ module Ably
|
|
29
39
|
data: data
|
30
40
|
}
|
31
41
|
|
32
|
-
|
42
|
+
message = Ably::Models::Message.new(payload, nil).tap do |message|
|
43
|
+
message.encode self
|
44
|
+
end
|
45
|
+
|
46
|
+
response = client.post("#{base_path}/publish", message)
|
33
47
|
|
34
48
|
[201, 204].include?(response.status)
|
35
49
|
end
|
@@ -52,9 +66,16 @@ module Ably
|
|
52
66
|
|
53
67
|
response = client.get(url, options.merge(merge_options))
|
54
68
|
|
55
|
-
Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::Message')
|
69
|
+
Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::Message') do |message|
|
70
|
+
message.tap do |message|
|
71
|
+
message.decode self
|
72
|
+
end
|
73
|
+
end
|
56
74
|
end
|
57
75
|
|
76
|
+
# Return the {Ably::Rest::Presence} object
|
77
|
+
#
|
78
|
+
# @return [Ably::Rest::Presence]
|
58
79
|
def presence
|
59
80
|
@presence ||= Presence.new(client, self)
|
60
81
|
end
|
data/lib/ably/rest/client.rb
CHANGED
@@ -20,16 +20,24 @@ module Ably
|
|
20
20
|
# @return [Logger::Severity] Log level configured for this {Client}
|
21
21
|
# @!attribute [r] channels
|
22
22
|
# @return [Aby::Rest::Channels] The collection of {Ably::Rest::Channel}s that have been created
|
23
|
+
# @!attribute [r] protocol
|
24
|
+
# @return [Symbol] The protocol configured for this client, either binary `:msgpack` or text based `:json`
|
25
|
+
#
|
23
26
|
class Client
|
24
27
|
include Ably::Modules::Conversions
|
25
28
|
include Ably::Modules::HttpHelpers
|
26
29
|
extend Forwardable
|
27
30
|
|
28
|
-
DOMAIN =
|
31
|
+
DOMAIN = 'rest.ably.io'
|
29
32
|
|
30
33
|
attr_reader :environment, :protocol, :auth, :channels, :log_level
|
31
34
|
def_delegators :auth, :client_id, :auth_options
|
32
35
|
|
36
|
+
# @api private
|
37
|
+
# The registered encoders that are used to encode and decode message payloads
|
38
|
+
# @return [Array<Ably::Models::MessageEncoder::Base>]
|
39
|
+
attr_reader :encoders
|
40
|
+
|
33
41
|
# The additional options passed to this Client's #initialize method not available as attributes of this class
|
34
42
|
# @return [Hash]
|
35
43
|
# @api private
|
@@ -45,6 +53,7 @@ module Ably
|
|
45
53
|
# @option options [Symbol] :protocol Protocol used to communicate with Ably, :json and :msgpack currently supported. Defaults to :msgpack
|
46
54
|
# @option options [Boolean] :use_binary_protocol Protocol used to communicate with Ably, defaults to true and uses MessagePack protocol. This option will overide :protocol option
|
47
55
|
# @option options [Logger::Severity,Symbol] :log_level Log level for the standard Logger that outputs to STDOUT. Defaults to Logger::ERROR, can be set to :fatal (Logger::FATAL), :error (Logger::ERROR), :warn (Logger::WARN), :info (Logger::INFO), :debug (Logger::DEBUG)
|
56
|
+
# @option options [Logger] :logger A custom logger can be used however it must adhere to the Ruby Logger interface, see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html
|
48
57
|
#
|
49
58
|
# @yield (see Ably::Auth#authorise)
|
50
59
|
# @yieldparam (see Ably::Auth#authorise)
|
@@ -65,13 +74,14 @@ module Ably
|
|
65
74
|
options = { api_key: options }
|
66
75
|
end
|
67
76
|
|
68
|
-
@tls
|
69
|
-
@environment
|
70
|
-
@protocol
|
71
|
-
@debug_http
|
72
|
-
@log_level
|
77
|
+
@tls = options.delete(:tls) == false ? false : true
|
78
|
+
@environment = options.delete(:environment) # nil is production
|
79
|
+
@protocol = options.delete(:protocol) || :msgpack
|
80
|
+
@debug_http = options.delete(:debug_http)
|
81
|
+
@log_level = options.delete(:log_level) || ::Logger::ERROR
|
82
|
+
@custom_logger = options.delete(:logger)
|
73
83
|
|
74
|
-
@log_level
|
84
|
+
@log_level = ::Logger.const_get(log_level.to_s.upcase) if log_level.kind_of?(Symbol) || log_level.kind_of?(String)
|
75
85
|
|
76
86
|
options.delete(:use_binary_protocol).tap do |use_binary_protocol|
|
77
87
|
if use_binary_protocol == true
|
@@ -85,6 +95,9 @@ module Ably
|
|
85
95
|
@options = options.freeze
|
86
96
|
@auth = Auth.new(self, options, &auth_block)
|
87
97
|
@channels = Ably::Rest::Channels.new(self)
|
98
|
+
@encoders = []
|
99
|
+
|
100
|
+
initialize_default_encoders
|
88
101
|
end
|
89
102
|
|
90
103
|
# Return a REST {Ably::Rest::Channel} for the given name
|
@@ -151,12 +164,10 @@ module Ably
|
|
151
164
|
end
|
152
165
|
|
153
166
|
# @!attribute [r] logger
|
154
|
-
# @return [Logger] The Logger
|
167
|
+
# @return [Logger] The {Ably::Logger} for this client.
|
155
168
|
# Configure the log_level with the `:log_level` option, refer to {Client#initialize}
|
156
169
|
def logger
|
157
|
-
@logger ||= Logger.new(
|
158
|
-
logger.level = log_level
|
159
|
-
end
|
170
|
+
@logger ||= Ably::Logger.new(self, log_level, @custom_logger)
|
160
171
|
end
|
161
172
|
|
162
173
|
# @!attribute [r] mime_type
|
@@ -170,6 +181,32 @@ module Ably
|
|
170
181
|
end
|
171
182
|
end
|
172
183
|
|
184
|
+
# Register a message encoder and decoder that implements Ably::Models::MessageEncoders::Base interface.
|
185
|
+
# Message encoders are used to encode and decode message payloads automatically.
|
186
|
+
# @note Encoders and decoders are processed in the order they are added so the first encoder will be given priority when encoding and decoding
|
187
|
+
#
|
188
|
+
# @param [Ably::Models::MessageEncoders::Base] encoder
|
189
|
+
# @return [void]
|
190
|
+
#
|
191
|
+
# @api private
|
192
|
+
def register_encoder(encoder)
|
193
|
+
encoder_klass = if encoder.kind_of?(String)
|
194
|
+
Object.const_get(encoder)
|
195
|
+
else
|
196
|
+
encoder
|
197
|
+
end
|
198
|
+
|
199
|
+
raise "Encoder must inherit from `Ably::Models::MessageEncoders::Base`" unless encoder_klass.ancestors.include?(Ably::Models::MessageEncoders::Base)
|
200
|
+
|
201
|
+
encoders << encoder_klass.new(self)
|
202
|
+
end
|
203
|
+
|
204
|
+
# @!attribute [r] protocol_binary?
|
205
|
+
# @return [Boolean] True of the transport #protocol communicates with Ably with a binary protocol
|
206
|
+
def protocol_binary?
|
207
|
+
protocol == :msgpack
|
208
|
+
end
|
209
|
+
|
173
210
|
private
|
174
211
|
def request(method, path, params = {}, options = {})
|
175
212
|
reauthorise_on_authorisation_failure do
|
@@ -210,8 +247,9 @@ module Ably
|
|
210
247
|
@connection_options ||= {
|
211
248
|
builder: middleware,
|
212
249
|
headers: {
|
213
|
-
|
214
|
-
|
250
|
+
content_type: mime_type,
|
251
|
+
accept: mime_type,
|
252
|
+
user_agent: user_agent
|
215
253
|
},
|
216
254
|
request: {
|
217
255
|
open_timeout: 5,
|
@@ -230,15 +268,16 @@ module Ably
|
|
230
268
|
# Raise exceptions if response code is invalid
|
231
269
|
builder.use Ably::Rest::Middleware::Exceptions
|
232
270
|
|
233
|
-
setup_incoming_middleware builder, fail_if_unsupported_mime_type: true
|
234
|
-
|
235
|
-
# Log HTTP requests if log level is DEBUG option set
|
236
|
-
builder.response :logger if log_level == Logger::DEBUG
|
271
|
+
setup_incoming_middleware builder, logger, fail_if_unsupported_mime_type: true
|
237
272
|
|
238
273
|
# Set Faraday's HTTP adapter
|
239
274
|
builder.adapter Faraday.default_adapter
|
240
275
|
end
|
241
276
|
end
|
277
|
+
|
278
|
+
def initialize_default_encoders
|
279
|
+
Ably::Models::MessageEncoders.register_default_encoders self
|
280
|
+
end
|
242
281
|
end
|
243
282
|
end
|
244
283
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Ably
|
5
|
+
module Rest
|
6
|
+
module Middleware
|
7
|
+
# Encode the body of the message according to the mime type
|
8
|
+
class Encoder < ::Faraday::Response::Middleware
|
9
|
+
CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
encode env if env.body
|
13
|
+
@app.call env
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def encode(env)
|
18
|
+
env.body = case request_type(env)
|
19
|
+
when 'application/x-msgpack'
|
20
|
+
to_msgpack(env.body)
|
21
|
+
when 'application/json', '', nil
|
22
|
+
env.request_headers[CONTENT_TYPE] = 'application/json'
|
23
|
+
to_json(env.body)
|
24
|
+
else
|
25
|
+
env.body
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_msgpack(body)
|
30
|
+
body.to_msgpack
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_json(body)
|
34
|
+
if body.kind_of?(String)
|
35
|
+
body
|
36
|
+
else
|
37
|
+
body.to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_type(env)
|
42
|
+
type = env.request_headers[CONTENT_TYPE].to_s
|
43
|
+
type = type.split(';', 2).first if type.index(';')
|
44
|
+
type
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module Ably
|
4
5
|
module Rest
|
@@ -25,7 +26,7 @@ module Ably
|
|
25
26
|
message = env.body
|
26
27
|
end
|
27
28
|
|
28
|
-
message =
|
29
|
+
message = 'Unknown server error' if message.to_s.strip == ''
|
29
30
|
|
30
31
|
if env.status >= 500
|
31
32
|
raise Ably::Exceptions::ServerError, message
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Ably
|
4
|
+
module Rest
|
5
|
+
module Middleware
|
6
|
+
class Logger < Faraday::Response::Middleware
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def initialize(app, logger = nil)
|
10
|
+
super(app)
|
11
|
+
@logger = logger || begin
|
12
|
+
require 'logger'
|
13
|
+
::Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
debug "=> URL: #{env.method} #{env.url}, Headers: #{dump_headers env.request_headers}"
|
21
|
+
debug "=> Body: #{env.body}"
|
22
|
+
@app.call env
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_complete(env)
|
26
|
+
debug "<= Status: #{env.status}, Headers: #{dump_headers env.response_headers}"
|
27
|
+
debug "<= Body: #{env.body}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def dump_headers(headers)
|
32
|
+
headers.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/ably/rest/presence.rb
CHANGED
@@ -20,7 +20,11 @@ module Ably
|
|
20
20
|
#
|
21
21
|
def get(options = {})
|
22
22
|
response = client.get(base_path, options)
|
23
|
-
Ably::Models::PaginatedResource.new(response, base_path, client, coerce_into: 'Ably::Models::PresenceMessage')
|
23
|
+
Ably::Models::PaginatedResource.new(response, base_path, client, coerce_into: 'Ably::Models::PresenceMessage') do |presence_message|
|
24
|
+
presence_message.tap do |message|
|
25
|
+
message.decode self.channel
|
26
|
+
end
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
30
|
# Return the presence messages history for the channel
|
@@ -41,7 +45,11 @@ module Ably
|
|
41
45
|
|
42
46
|
response = client.get(url, options.merge(merge_options))
|
43
47
|
|
44
|
-
Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::PresenceMessage')
|
48
|
+
Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::PresenceMessage') do |presence_message|
|
49
|
+
presence_message.tap do |message|
|
50
|
+
message.decode self.channel
|
51
|
+
end
|
52
|
+
end
|
45
53
|
end
|
46
54
|
|
47
55
|
private
|
data/lib/ably/util/crypto.rb
CHANGED
@@ -7,67 +7,95 @@ module Ably::Util
|
|
7
7
|
algorithm: 'AES',
|
8
8
|
mode: 'CBC',
|
9
9
|
key_length: 128,
|
10
|
-
block_length: 16
|
11
10
|
}
|
12
11
|
|
12
|
+
BLOCK_LENGTH = 16
|
13
|
+
|
14
|
+
# Configured options for this Crypto object, see {#initialize} for a list of configured options
|
15
|
+
#
|
16
|
+
# @return [Hash]
|
13
17
|
attr_reader :options
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
# Creates a {Ably::Util::Crypto} object
|
20
|
+
#
|
21
|
+
# @param [Hash] options an options Hash used to configure the Crypto library
|
22
|
+
# @option options [String] :key Required secret key used for encrypting and decrypting
|
23
|
+
# @option options [String] :algorithm optional (default AES), specify the encryption algorithm supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
24
|
+
# @option options [String] :mode optional (default CBC), specify the cipher mode supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
25
|
+
# @option options [Integer] :key_length optional (default 128), specify the key length of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
26
|
+
# @option options [String] :combined optional (default AES-128-CBC), specify in one option the algorithm, key length and cipher of the cipher supported by {http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html OpenSSL::Cipher}
|
27
|
+
#
|
28
|
+
# @return [Ably::Util::Crypto]
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# crypto = Ably::Util::Crypto.new(key: 'mysecret')
|
32
|
+
# encrypted = crypto.encrypt('secret text')
|
33
|
+
# crypto.decrypt(decrypted) # => 'secret text'
|
34
|
+
#
|
35
|
+
def initialize(options)
|
36
|
+
raise ArgumentError, ':key is required' unless options.has_key?(:key)
|
17
37
|
@options = DEFAULTS.merge(options).freeze
|
18
38
|
end
|
19
39
|
|
20
|
-
|
40
|
+
# Encrypt payload using configured Cipher
|
41
|
+
#
|
42
|
+
# @param [String] payload the payload to be encrypted
|
43
|
+
# @param [Hash] encrypt_options an options Hash to configure the encrypt action
|
44
|
+
# @option encrypt_options [String] :iv optionally use the provided Initialization Vector instead of a randomly generated IV
|
45
|
+
#
|
46
|
+
# @return [String] binary string with {Encoding::ASCII_8BIT} encoding
|
47
|
+
#
|
48
|
+
def encrypt(payload, encrypt_options = {})
|
21
49
|
cipher = openssl_cipher
|
22
50
|
cipher.encrypt
|
23
|
-
cipher.key =
|
24
|
-
iv = cipher.random_iv
|
51
|
+
cipher.key = key
|
52
|
+
iv = encrypt_options[:iv] || options[:iv] || cipher.random_iv
|
25
53
|
cipher.iv = iv
|
26
54
|
|
27
|
-
iv << cipher.update(
|
55
|
+
iv << cipher.update(payload) << cipher.final
|
28
56
|
end
|
29
57
|
|
58
|
+
# Decrypt payload using configured Cipher
|
59
|
+
#
|
60
|
+
# @param [String] encrypted_payload_with_iv the encrypted payload to be decrypted
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
#
|
30
64
|
def decrypt(encrypted_payload_with_iv)
|
31
|
-
raise Ably::Exceptions::EncryptionError,
|
65
|
+
raise Ably::Exceptions::EncryptionError, 'iv is missing or not long enough' unless encrypted_payload_with_iv.length >= BLOCK_LENGTH*2
|
32
66
|
|
33
|
-
iv = encrypted_payload_with_iv.slice(0
|
34
|
-
encrypted_payload = encrypted_payload_with_iv.slice(
|
67
|
+
iv = encrypted_payload_with_iv.slice(0...BLOCK_LENGTH)
|
68
|
+
encrypted_payload = encrypted_payload_with_iv.slice(BLOCK_LENGTH..-1)
|
35
69
|
|
36
70
|
decipher = openssl_cipher
|
37
71
|
decipher.decrypt
|
38
|
-
decipher.key =
|
72
|
+
decipher.key = key
|
39
73
|
decipher.iv = iv
|
40
74
|
|
41
|
-
|
75
|
+
decipher.update(encrypted_payload) << decipher.final
|
42
76
|
end
|
43
77
|
|
78
|
+
# Generate a random key
|
79
|
+
# @return [String]
|
44
80
|
def random_key
|
45
81
|
openssl_cipher.random_key
|
46
82
|
end
|
47
83
|
|
84
|
+
# Generate a random IV
|
85
|
+
# @return [String]
|
48
86
|
def random_iv
|
49
87
|
openssl_cipher.random_iv
|
50
88
|
end
|
51
89
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def unpack(msgpack_binary)
|
58
|
-
MessagePack.unpack(msgpack_binary)
|
59
|
-
end
|
60
|
-
|
61
|
-
def secret
|
62
|
-
options[:secret]
|
63
|
-
end
|
64
|
-
|
65
|
-
def block_length
|
66
|
-
options[:block_length]
|
90
|
+
# The Cipher algorithm string such as AES-128-CBC
|
91
|
+
# @return [String]
|
92
|
+
def cipher_type
|
93
|
+
(options[:combined] || "#{options[:algorithm]}-#{options[:key_length]}-#{options[:mode]}").to_s.upcase
|
67
94
|
end
|
68
95
|
|
69
|
-
|
70
|
-
|
96
|
+
private
|
97
|
+
def key
|
98
|
+
options[:key]
|
71
99
|
end
|
72
100
|
|
73
101
|
def openssl_cipher
|