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.
Files changed (148) hide show
  1. checksums.yaml +13 -5
  2. data/.gitmodules +1 -1
  3. data/.rspec +1 -0
  4. data/.travis.yml +7 -3
  5. data/SPEC.md +495 -419
  6. data/ably-rest.gemspec +19 -5
  7. data/lib/ably-rest.rb +9 -1
  8. data/lib/submodules/ably-ruby/.gitignore +6 -0
  9. data/lib/submodules/ably-ruby/.rspec +1 -0
  10. data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
  11. data/lib/submodules/ably-ruby/.travis.yml +10 -0
  12. data/lib/submodules/ably-ruby/Gemfile +4 -0
  13. data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
  14. data/lib/submodules/ably-ruby/README.md +122 -0
  15. data/lib/submodules/ably-ruby/Rakefile +34 -0
  16. data/lib/submodules/ably-ruby/SPEC.md +1794 -0
  17. data/lib/submodules/ably-ruby/ably.gemspec +36 -0
  18. data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
  19. data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
  20. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
  21. data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
  22. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
  23. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
  24. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
  25. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
  26. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
  27. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
  28. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
  29. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
  30. data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
  31. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
  32. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
  33. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
  34. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
  35. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
  36. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
  37. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
  38. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
  39. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
  40. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
  41. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
  42. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
  43. data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
  44. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
  45. data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
  46. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
  47. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
  48. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
  49. data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  50. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
  51. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
  52. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
  53. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
  54. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
  55. data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
  56. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
  57. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
  58. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
  59. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
  60. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
  61. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
  62. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
  63. data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
  64. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
  65. data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
  66. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
  67. data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
  68. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
  69. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
  70. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
  71. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
  72. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
  73. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
  74. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
  75. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
  76. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
  77. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
  78. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
  79. data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
  80. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
  81. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
  82. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
  83. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
  84. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
  85. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
  86. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
  87. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
  88. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
  89. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
  90. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
  91. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
  92. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
  93. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
  94. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
  95. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
  96. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
  97. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
  98. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
  99. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
  100. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
  101. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
  102. data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
  103. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
  104. data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
  105. data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
  106. data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
  107. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
  108. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
  109. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
  110. data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
  111. data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
  112. data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
  113. data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
  114. data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
  115. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
  116. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
  117. data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
  118. data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
  119. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
  120. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
  121. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
  122. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
  123. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
  124. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
  125. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
  126. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
  127. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
  128. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
  129. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
  130. data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
  131. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
  132. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
  133. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
  134. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
  135. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
  136. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
  137. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
  138. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
  139. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
  140. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
  141. data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
  142. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
  143. data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
  144. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
  145. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
  146. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
  147. data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
  148. metadata +182 -27
@@ -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
@@ -0,0 +1,41 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Ably
5
+ module Rest
6
+ module Middleware
7
+ # HTTP exceptions raised by Ably due to an error status code
8
+ # Ably returns JSON/Msgpack error codes and messages so include this if possible in the exception messages
9
+ class Exceptions < Faraday::Response::Middleware
10
+ def on_complete(env)
11
+ if env.status >= 400
12
+ error_status_code = env.status
13
+ error_code = nil
14
+
15
+ if env.body.kind_of?(Hash)
16
+ error = env.body.fetch('error', {})
17
+ error_status_code = error['statusCode'].to_i if error['statusCode']
18
+ error_code = error['code'].to_i if error['code']
19
+
20
+ if error
21
+ message = "#{error['message']} (status: #{error_status_code}, code: #{error_code})"
22
+ else
23
+ message = env.body
24
+ end
25
+ else
26
+ message = env.body
27
+ end
28
+
29
+ message = 'Unknown server error' if message.to_s.strip == ''
30
+
31
+ if env.status >= 500
32
+ raise Ably::Exceptions::ServerError, message
33
+ else
34
+ raise Ably::Exceptions::InvalidRequest.new(message, error_status_code, error_code)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ require 'faraday'
2
+
3
+ module Ably
4
+ module Rest
5
+ module Middleware
6
+ # HTTP exceptions raised due to a status code error on a 3rd party site
7
+ # Used by auth calls
8
+ class ExternalExceptions < Faraday::Response::Middleware
9
+ def on_complete(env)
10
+ if env.status >= 400
11
+ error_status_code = env.status
12
+ message = "Error #{error_status_code}: #{(env.body || '')[0...200]}"
13
+
14
+ if error_status_code >= 500
15
+ raise Ably::Exceptions::ServerError, message
16
+ else
17
+ raise Ably::Exceptions::InvalidRequest, message
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Ably
5
+ module Rest
6
+ module Middleware
7
+ class FailIfUnsupportedMimeType < Faraday::Response::Middleware
8
+ def on_complete(env)
9
+ unless env.response_headers['Ably-Middleware-Parsed'] == true
10
+ raise Ably::Exceptions::InvalidResponseBody,
11
+ "Content Type #{env.response_headers['Content-Type']} is not supported by this client library"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,58 @@
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: #{body_for(env)}"
22
+ super
23
+ end
24
+
25
+ def on_complete(env)
26
+ debug "<= Status: #{env.status}, Headers: #{dump_headers env.response_headers}"
27
+ debug "<= Body: #{body_for(env)}"
28
+ end
29
+
30
+ private
31
+ def dump_headers(headers)
32
+ headers.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
33
+ end
34
+
35
+ def body_for(env)
36
+ return '' if !env.body || env.body.empty?
37
+
38
+ if env.request_headers['Content-Type'] == 'application/x-msgpack'
39
+ MessagePack.unpack(env.body)
40
+ else
41
+ env.body
42
+ end
43
+
44
+ rescue StandardError
45
+ readable_body(env.body)
46
+ end
47
+
48
+ def readable_body(body)
49
+ if body.respond_to?(:encoding) && body.encoding == Encoding::ASCII_8BIT
50
+ body.unpack('H*')
51
+ else
52
+ body
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,27 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Ably
5
+ module Rest
6
+ module Middleware
7
+ class ParseJson < Faraday::Response::Middleware
8
+ def on_complete(env)
9
+ if env.response_headers['Content-Type'] == 'application/json'
10
+ env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
11
+ env.response_headers['Ably-Middleware-Parsed'] = true
12
+ end
13
+ end
14
+
15
+ def parse(body)
16
+ if body.length > 0
17
+ JSON.parse(body)
18
+ else
19
+ body
20
+ end
21
+ rescue JSON::ParserError => e
22
+ raise Ably::Exceptions::InvalidResponseBody, "Expected JSON response: #{e.message}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'faraday'
2
+ require 'msgpack'
3
+
4
+ module Ably
5
+ module Rest
6
+ module Middleware
7
+ class ParseMessagePack < Faraday::Response::Middleware
8
+ def on_complete(env)
9
+ if env.response_headers['Content-Type'] == 'application/x-msgpack'
10
+ env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
11
+ env.response_headers['Ably-Middleware-Parsed'] = true
12
+ end
13
+ end
14
+
15
+ def parse(body)
16
+ if body.length > 0
17
+ MessagePack.unpack(body)
18
+ else
19
+ body
20
+ end
21
+ rescue MessagePack::MalformedFormatError => e
22
+ raise Ably::Exceptions::InvalidResponseBody, "Expected MessagePack response: #{e.message}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,92 @@
1
+ module Ably
2
+ module Rest
3
+ class Presence
4
+ include Ably::Modules::Conversions
5
+
6
+ # {Ably::Rest::Client} for this Presence object
7
+ # @return {Ably::Rest::Client}
8
+ attr_reader :client
9
+
10
+ # {Ably::Rest::Channel} this Presence object is associated with
11
+ # @return {Ably::Rest::Channel}
12
+ attr_reader :channel
13
+
14
+ # Initialize a new Presence object
15
+ #
16
+ # @param client [Ably::Rest::Client]
17
+ # @param channel [Channel] The channel object
18
+ def initialize(client, channel)
19
+ @client = client
20
+ @channel = channel
21
+ end
22
+
23
+ # Obtain the set of members currently present for a channel
24
+ #
25
+ # @param [Hash] options the options for the set of members present
26
+ # @option options [Integer,Time] :start Time or millisecond since epoch
27
+ # @option options [Integer,Time] :end Time or millisecond since epoch
28
+ # @option options [Symbol] :direction `:forwards` or `:backwards`
29
+ # @option options [Integer] :limit Maximum number of members to retrieve up to 10,000
30
+ #
31
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
32
+ #
33
+ def get(options = {})
34
+ options = options.dup
35
+
36
+ paginated_options = {
37
+ coerce_into: 'Ably::Models::PresenceMessage',
38
+ async_blocking_operations: options.delete(:async_blocking_operations),
39
+ }
40
+
41
+ response = client.get(base_path, options)
42
+
43
+ Ably::Models::PaginatedResource.new(response, base_path, client, paginated_options) do |presence_message|
44
+ presence_message.tap do |presence_message|
45
+ decode_message presence_message
46
+ end
47
+ end
48
+ end
49
+
50
+ # Return the presence messages history for the channel
51
+ #
52
+ # @param [Hash] options the options for the message history request
53
+ # @option options [Integer,Time] :start Time or millisecond since epoch
54
+ # @option options [Integer,Time] :end Time or millisecond since epoch
55
+ # @option options [Symbol] :direction `:forwards` or `:backwards`
56
+ # @option options [Integer] :limit Maximum number of presence messages to retrieve up to 10,000
57
+ #
58
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
59
+ #
60
+ def history(options = {})
61
+ url = "#{base_path}/history"
62
+ options = options.dup
63
+
64
+ [:start, :end].each { |option| options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
65
+
66
+ paginated_options = {
67
+ coerce_into: 'Ably::Models::PresenceMessage',
68
+ async_blocking_operations: options.delete(:async_blocking_operations),
69
+ }
70
+
71
+ response = client.get(url, options)
72
+
73
+ Ably::Models::PaginatedResource.new(response, url, client, paginated_options) do |presence_message|
74
+ presence_message.tap do |presence_message|
75
+ decode_message presence_message
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+ def base_path
82
+ "/channels/#{CGI.escape(channel.name)}/presence"
83
+ end
84
+
85
+ def decode_message(presence_message)
86
+ presence_message.decode self.channel
87
+ rescue Ably::Exceptions::CipherError, Ably::Exceptions::EncoderError => e
88
+ client.logger.error "Decoding Error on presence channel '#{channel.name}', presence message client_id '#{presence_message.client_id}'. #{e.class.name}: #{e.message}"
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,105 @@
1
+ require 'msgpack'
2
+ require 'openssl'
3
+
4
+ module Ably::Util
5
+ class Crypto
6
+ DEFAULTS = {
7
+ algorithm: 'AES',
8
+ mode: 'CBC',
9
+ key_length: 128,
10
+ }
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]
17
+ attr_reader :options
18
+
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)
37
+ @options = DEFAULTS.merge(options).freeze
38
+ end
39
+
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 = {})
49
+ cipher = openssl_cipher
50
+ cipher.encrypt
51
+ cipher.key = key
52
+ iv = encrypt_options[:iv] || options[:iv] || cipher.random_iv
53
+ cipher.iv = iv
54
+
55
+ iv << cipher.update(payload) << cipher.final
56
+ end
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
+ #
64
+ def decrypt(encrypted_payload_with_iv)
65
+ raise Ably::Exceptions::CipherError, 'iv is missing or not long enough' unless encrypted_payload_with_iv.length >= BLOCK_LENGTH*2
66
+
67
+ iv = encrypted_payload_with_iv.slice(0...BLOCK_LENGTH)
68
+ encrypted_payload = encrypted_payload_with_iv.slice(BLOCK_LENGTH..-1)
69
+
70
+ decipher = openssl_cipher
71
+ decipher.decrypt
72
+ decipher.key = key
73
+ decipher.iv = iv
74
+
75
+ decipher.update(encrypted_payload) << decipher.final
76
+ end
77
+
78
+ # Generate a random key
79
+ # @return [String]
80
+ def random_key
81
+ openssl_cipher.random_key
82
+ end
83
+
84
+ # Generate a random IV
85
+ # @return [String]
86
+ def random_iv
87
+ openssl_cipher.random_iv
88
+ end
89
+
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
94
+ end
95
+
96
+ private
97
+ def key
98
+ options[:key]
99
+ end
100
+
101
+ def openssl_cipher
102
+ OpenSSL::Cipher.new(cipher_type)
103
+ end
104
+ end
105
+ end