ably-rest 0.7.1 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,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
|