ably-rest 0.7.1 → 0.7.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 +13 -5
- data/.gitmodules +1 -1
- data/.rspec +1 -0
- data/.travis.yml +7 -3
- data/SPEC.md +495 -419
- data/ably-rest.gemspec +19 -5
- data/lib/ably-rest.rb +9 -1
- data/lib/submodules/ably-ruby/.gitignore +6 -0
- data/lib/submodules/ably-ruby/.rspec +1 -0
- data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -0
- data/lib/submodules/ably-ruby/Gemfile +4 -0
- data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
- data/lib/submodules/ably-ruby/README.md +122 -0
- data/lib/submodules/ably-ruby/Rakefile +34 -0
- data/lib/submodules/ably-ruby/SPEC.md +1794 -0
- data/lib/submodules/ably-ruby/ably.gemspec +36 -0
- data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
- data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
- data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
- data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
- data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
- data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
- data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
- metadata +182 -27
|
@@ -0,0 +1,108 @@
|
|
|
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::Models::MessageEncoders
|
|
11
|
+
extend Ably::Modules::Conversions
|
|
12
|
+
|
|
13
|
+
# Base interface for an Ably Encoder
|
|
14
|
+
#
|
|
15
|
+
class Base
|
|
16
|
+
attr_reader :client
|
|
17
|
+
|
|
18
|
+
def initialize(client)
|
|
19
|
+
@client = client
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# #encode is called once before a message is sent to Ably
|
|
23
|
+
#
|
|
24
|
+
# It is the responsibility of the #encode method to detect the intended encoding and modify the :data & :encoding properties of the message object.
|
|
25
|
+
#
|
|
26
|
+
# @param [Hash] message the message as a Hash object received directly from Ably.
|
|
27
|
+
# The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
|
|
28
|
+
# This #encode method should modify the message Hash if any encoding action is to be taken
|
|
29
|
+
# @param [Hash] channel_options the options used to initialize the channel that this message was received on
|
|
30
|
+
#
|
|
31
|
+
# @return [void]
|
|
32
|
+
def encode(message, channel_options)
|
|
33
|
+
raise "Not yet implemented"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# #decode is called once for every encoding step
|
|
37
|
+
# i.e. if message encoding arrives with 'utf-8/cipher+aes-128-cbc/base64'
|
|
38
|
+
# the decoder will call #decode once for each encoding part such as 'base64', then 'cipher+aes-128-cbc', and finally 'utf-8'
|
|
39
|
+
#
|
|
40
|
+
# It is the responsibility of the #decode method to detect the current encoding part and modify the :data & :encoding properties of the message object.
|
|
41
|
+
#
|
|
42
|
+
# @param [Hash] message the message as a Hash object received directly from Ably.
|
|
43
|
+
# The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
|
|
44
|
+
# This #encode method should modify the message Hash if any decoding action is to be taken
|
|
45
|
+
# @param [Hash] channel_options the options used to initialize the channel that this message was received on
|
|
46
|
+
#
|
|
47
|
+
# @return [void]
|
|
48
|
+
def decode(message, channel_options)
|
|
49
|
+
raise "Not yet implemented"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Add encoding to the message Hash.
|
|
53
|
+
# Ensures that encoding delimeter is used where required i.e utf-8/cipher+aes-128-cbc/base64
|
|
54
|
+
#
|
|
55
|
+
# @param [Hash] message the message as a Hash object received directly from Ably.
|
|
56
|
+
# @param [String] encoding encoding to add to the current encoding
|
|
57
|
+
#
|
|
58
|
+
# @return [void]
|
|
59
|
+
def add_encoding_to_message(encoding, message)
|
|
60
|
+
message[:encoding] = [message[:encoding], encoding].compact.join('/')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the right most encoding form a meessage encoding, and nil if none exists
|
|
64
|
+
# i.e. current_encoding_part('utf-8/cipher+aes-128-cbc/base64') => 'base64'
|
|
65
|
+
#
|
|
66
|
+
# @return [String,nil]
|
|
67
|
+
def current_encoding_part(message)
|
|
68
|
+
if message[:encoding]
|
|
69
|
+
message[:encoding].split('/')[-1]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Strip the current encoding part within the message Hash.
|
|
74
|
+
#
|
|
75
|
+
# For example, calling this method on an :encoding value of 'utf-8/cipher+aes-128-cbc/base64' would update the attribute
|
|
76
|
+
# :encoding to 'utf-8/cipher+aes-128-cbc'
|
|
77
|
+
#
|
|
78
|
+
# @param [Hash] message the message as a Hash object received directly from Ably.
|
|
79
|
+
#
|
|
80
|
+
# @return [void]
|
|
81
|
+
def strip_current_encoding_part(message)
|
|
82
|
+
raise "Cannot strip encoding when there is no encoding for this message" unless message[:encoding]
|
|
83
|
+
message[:encoding] = message[:encoding].split('/')[0...-1].join('/')
|
|
84
|
+
message[:encoding] = nil if message[:encoding].empty?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# True of the message data payload is empty
|
|
88
|
+
#
|
|
89
|
+
# @param [Hash] message the message as a Hash object received directly from Ably.
|
|
90
|
+
#
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
def is_empty?(message)
|
|
93
|
+
message[:data].nil? || message[:data] == ''
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def self.register_default_encoders(client)
|
|
98
|
+
client.register_encoder Ably::Models::MessageEncoders::Utf8
|
|
99
|
+
client.register_encoder Ably::Models::MessageEncoders::Json
|
|
100
|
+
client.register_encoder Ably::Models::MessageEncoders::Cipher
|
|
101
|
+
client.register_encoder Ably::Models::MessageEncoders::Base64
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
require 'ably/models/message_encoders/base64'
|
|
106
|
+
require 'ably/models/message_encoders/cipher'
|
|
107
|
+
require 'ably/models/message_encoders/json'
|
|
108
|
+
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
|
+
!client.protocol_binary?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
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 option encrypted: true.
|
|
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
|
+
|
|
24
|
+
crypto = crypto_for(channel_options)
|
|
25
|
+
message[:data] = crypto.encrypt(message[:data])
|
|
26
|
+
add_encoding_to_message "#{ENCODING_ID}+#{crypto.cipher_type.downcase}", message
|
|
27
|
+
end
|
|
28
|
+
rescue ArgumentError => e
|
|
29
|
+
raise Ably::Exceptions::CipherError.new(e.message, nil, 92005)
|
|
30
|
+
rescue RuntimeError => e
|
|
31
|
+
if e.message.match(/unsupported cipher algorithm/i)
|
|
32
|
+
raise Ably::Exceptions::CipherError.new(e.message, nil, 92004)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def decode(message, channel_options)
|
|
37
|
+
if is_cipher_encoded?(message)
|
|
38
|
+
unless channel_configured_for_encryption?(channel_options)
|
|
39
|
+
raise Ably::Exceptions::CipherError.new('Message cannot be decrypted as the channel is not set up for encryption & decryption', nil, 92001)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
crypto = crypto_for(channel_options)
|
|
43
|
+
unless crypto.cipher_type == cipher_algorithm(message).upcase
|
|
44
|
+
raise Ably::Exceptions::CipherError.new("Cipher algorithm #{crypto.cipher_type} does not match message cipher algorithm of #{cipher_algorithm(message).upcase}", nil, 92002)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
message[:data] = crypto.decrypt(message[:data])
|
|
48
|
+
strip_current_encoding_part message
|
|
49
|
+
end
|
|
50
|
+
rescue OpenSSL::Cipher::CipherError => e
|
|
51
|
+
raise Ably::Exceptions::CipherError.new("CipherError decrypting data, the private key may not be correct", nil, 92003)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
def is_binary?(message)
|
|
56
|
+
message.fetch(:data, '').encoding == Encoding::ASCII_8BIT
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def is_utf8_encoded?(message)
|
|
60
|
+
current_encoding_part(message).to_s.match(/^utf-8$/i)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def crypto_for(channel_options)
|
|
64
|
+
@cryptos[channel_options.fetch(:cipher_params, :default)] ||= Ably::Util::Crypto.new(channel_options.fetch(:cipher_params, {}))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def channel_configured_for_encryption?(channel_options)
|
|
68
|
+
channel_options.fetch(:encrypted, false)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def is_cipher_encoded?(message)
|
|
72
|
+
!cipher_algorithm(message).nil?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def cipher_algorithm(message)
|
|
76
|
+
current_encoding_part(message).to_s[/^#{ENCODING_ID}\+([\w_-]+)$/, 1]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def already_encrypted?(message)
|
|
80
|
+
message.fetch(:encoding, '').to_s.match(%r{(^|/)#{ENCODING_ID}\+([\w_-]+)($|/)})
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
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
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
module Ably::Models
|
|
2
|
+
# Wraps any Ably HTTP response that supports paging and automatically provides methods to iterate through
|
|
3
|
+
# the array of resources using {#first_page}, {#next_page}, {#first_page?} and {#last_page?}
|
|
4
|
+
#
|
|
5
|
+
# Paging information is provided by Ably in the LINK HTTP headers
|
|
6
|
+
class PaginatedResource
|
|
7
|
+
include Enumerable
|
|
8
|
+
include Ably::Modules::AsyncWrapper if defined?(Ably::Realtime)
|
|
9
|
+
|
|
10
|
+
# @param [Faraday::Response] http_response Initial HTTP response from an Ably request to a paged resource
|
|
11
|
+
# @param [String] base_url Base URL for request that generated the http_response so that subsequent paged requests can be made
|
|
12
|
+
# @param [Client] client {Ably::Client} used to make the request to Ably
|
|
13
|
+
# @param [Hash] options Options for this paged resource
|
|
14
|
+
# @option options [Symbol,String] :coerce_into symbol or string representing class that should be used to create each item in the PaginatedResource
|
|
15
|
+
#
|
|
16
|
+
# @yield [Object] block will be called for each resource object for the current page. This is a useful way to apply a transformation to any page resources after they are retrieved
|
|
17
|
+
#
|
|
18
|
+
# @return [PaginatedResource]
|
|
19
|
+
def initialize(http_response, base_url, client, options = {}, &each_block)
|
|
20
|
+
@http_response = http_response
|
|
21
|
+
@client = client
|
|
22
|
+
@base_url = "#{base_url.gsub(%r{/[^/]*$}, '')}/"
|
|
23
|
+
@coerce_into = options[:coerce_into]
|
|
24
|
+
@raw_body = http_response.body
|
|
25
|
+
@each_block = each_block
|
|
26
|
+
@make_async = options.fetch(:async_blocking_operations, false)
|
|
27
|
+
|
|
28
|
+
@body = http_response.body
|
|
29
|
+
@body = coerce_items_into(body, @coerce_into) if @coerce_into
|
|
30
|
+
@body = body.map { |item| yield item } if block_given?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Retrieve the first page of results.
|
|
34
|
+
# When used as part of the {Ably::Realtime} library, it will return a {EventMachine::Deferrable} object,
|
|
35
|
+
# and allows an optional success callback block to be provided.
|
|
36
|
+
#
|
|
37
|
+
# @return [PaginatedResource,EventMachine::Deferrable]
|
|
38
|
+
def first_page(&success_callback)
|
|
39
|
+
async_wrap_if(make_async, success_callback) do
|
|
40
|
+
PaginatedResource.new(client.get(pagination_url('first')), base_url, client, pagination_options, &each_block)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Retrieve the next page of results.
|
|
45
|
+
# When used as part of the {Ably::Realtime} library, it will return a {EventMachine::Deferrable} object,
|
|
46
|
+
# and allows an optional success callback block to be provided.
|
|
47
|
+
#
|
|
48
|
+
# @return [PaginatedResource,EventMachine::Deferrable]
|
|
49
|
+
def next_page(&success_callback)
|
|
50
|
+
async_wrap_if(make_async, success_callback) do
|
|
51
|
+
raise Ably::Exceptions::InvalidPageError, 'There are no more pages' if supports_pagination? && last_page?
|
|
52
|
+
PaginatedResource.new(client.get(pagination_url('next')), base_url, client, pagination_options, &each_block)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# True if this is the last page in the paged resource set
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean]
|
|
59
|
+
def last_page?
|
|
60
|
+
!supports_pagination? ||
|
|
61
|
+
pagination_header('next').nil?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# True if this is the first page in the paged resource set
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def first_page?
|
|
68
|
+
!supports_pagination? ||
|
|
69
|
+
pagination_header('first') == pagination_header('current')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# True if the HTTP response supports paging with the expected LINK HTTP headers
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
def supports_pagination?
|
|
76
|
+
!pagination_headers.empty?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Standard Array accessor method
|
|
80
|
+
def [](index)
|
|
81
|
+
body[index]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns number of items within this page, not the total number of items in the entire paged resource set
|
|
85
|
+
def length
|
|
86
|
+
body.length
|
|
87
|
+
end
|
|
88
|
+
alias_method :count, :length
|
|
89
|
+
alias_method :size, :length
|
|
90
|
+
|
|
91
|
+
# Method to allow {PaginatedResource} to be {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
|
|
92
|
+
def each(&block)
|
|
93
|
+
return to_enum(:each) unless block_given?
|
|
94
|
+
body.each(&block)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# First item in this page
|
|
98
|
+
def first
|
|
99
|
+
body.first
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Last item in this page
|
|
103
|
+
def last
|
|
104
|
+
body.last
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def inspect
|
|
108
|
+
<<-EOF.gsub(/^ /, '')
|
|
109
|
+
#<#{self.class.name}:#{self.object_id}
|
|
110
|
+
@base_url="#{base_url}",
|
|
111
|
+
@first_page?=#{!!first_page?},
|
|
112
|
+
@last_page?=#{!!first_page?},
|
|
113
|
+
@body=
|
|
114
|
+
#{body.map { |item| item.inspect }.join(",\n ") }
|
|
115
|
+
>
|
|
116
|
+
EOF
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
attr_reader :body, :http_response, :base_url, :client, :coerce_into, :raw_body, :each_block, :make_async
|
|
121
|
+
|
|
122
|
+
def coerce_items_into(items, type_string)
|
|
123
|
+
items.map do |item|
|
|
124
|
+
@coerce_into.split('::').inject(Kernel) do |base, klass_name|
|
|
125
|
+
base.public_send(:const_get, klass_name)
|
|
126
|
+
end.new(item)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def pagination_headers
|
|
131
|
+
link_regex = %r{<(?<url>[^>]+)>; rel="(?<rel>[^"]+)"}
|
|
132
|
+
@pagination_headers ||= begin
|
|
133
|
+
# All `Link:` headers are concatenated by Faraday into a comma separated list
|
|
134
|
+
# Finding matching `<url>; rel="rel"` pairs
|
|
135
|
+
link_headers = http_response.headers['link'] || ''
|
|
136
|
+
link_headers.scan(link_regex).each_with_object({}) do |val_array, hash|
|
|
137
|
+
url, rel = val_array
|
|
138
|
+
hash[rel] = url
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def pagination_header(id)
|
|
144
|
+
pagination_headers[id]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def pagination_url(id)
|
|
148
|
+
raise Ably::Exceptions::InvalidPageError, "Paging header link #{id} does not exist" unless pagination_header(id)
|
|
149
|
+
|
|
150
|
+
if pagination_header(id).match(%r{^\./})
|
|
151
|
+
"#{base_url}#{pagination_header(id)[2..-1]}"
|
|
152
|
+
else
|
|
153
|
+
pagination_header[id]
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def pagination_options
|
|
158
|
+
{
|
|
159
|
+
coerce_into: coerce_into,
|
|
160
|
+
async_blocking_operations: make_async
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def async_wrap_if(is_realtime, success_callback, &operation)
|
|
165
|
+
if is_realtime
|
|
166
|
+
raise 'EventMachine is required for asynchronous operations' unless defined?(EventMachine)
|
|
167
|
+
async_wrap success_callback, &operation
|
|
168
|
+
else
|
|
169
|
+
yield
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|