ably-rest 0.8.2 → 0.8.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -43
  3. data/SPEC.md +707 -580
  4. data/lib/submodules/ably-ruby/.travis.yml +1 -0
  5. data/lib/submodules/ably-ruby/CHANGELOG.md +143 -3
  6. data/lib/submodules/ably-ruby/README.md +1 -1
  7. data/lib/submodules/ably-ruby/SPEC.md +842 -520
  8. data/lib/submodules/ably-ruby/ably.gemspec +1 -1
  9. data/lib/submodules/ably-ruby/lib/ably/auth.rb +114 -87
  10. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +40 -14
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +3 -5
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +3 -12
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +8 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -3
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +1 -1
  16. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +1 -1
  17. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +7 -1
  18. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +1 -1
  19. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +6 -3
  20. data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +2 -2
  21. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +1 -1
  22. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -2
  23. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +1 -0
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +191 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +97 -25
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +11 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +22 -6
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +73 -40
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +48 -33
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +17 -3
  31. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +43 -16
  32. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +57 -26
  33. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +3 -1
  34. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -2
  35. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -0
  36. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +242 -0
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +277 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +64 -0
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +26 -5
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +23 -6
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +167 -16
  43. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +9 -8
  44. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +1 -0
  45. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +121 -10
  46. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +13 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +161 -79
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +3 -3
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +142 -15
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +23 -0
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +180 -18
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +8 -8
  53. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +136 -25
  54. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +60 -4
  55. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +54 -3
  56. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +7 -6
  57. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +1 -9
  58. data/lib/submodules/ably-ruby/spec/unit/models/paginated_result_spec.rb +1 -18
  59. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +1 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +21 -1
  61. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +10 -3
  62. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +27 -8
  63. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +0 -8
  64. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +7 -7
  65. metadata +5 -2
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'faraday', '~> 0.9'
25
25
  spec.add_runtime_dependency 'json'
26
26
  spec.add_runtime_dependency 'websocket-driver', '~> 0.3'
27
- spec.add_runtime_dependency 'msgpack', '0.6.0pre1'
27
+ spec.add_runtime_dependency 'msgpack', '>= 0.6.2'
28
28
 
29
29
  spec.add_development_dependency 'bundler', '~> 1.3'
30
30
  spec.add_development_dependency 'rake'
@@ -22,44 +22,53 @@ module Ably
22
22
  # @!attribute [r] key_secret
23
23
  # @return [String] Key secret (private secure part of the API key), if present
24
24
  # @!attribute [r] options
25
- # @return [Hash] {Ably::Auth} options configured for this client
26
-
25
+ # @return [Hash] Default {Ably::Auth} options configured for this client
26
+ # @!attribute [r] token_params
27
+ # @return [Hash] Default token params used for token requests, see {#request_token}
28
+ #
27
29
  class Auth
28
30
  include Ably::Modules::Conversions
29
31
  include Ably::Modules::HttpHelpers
30
32
 
31
33
  # Default capability Hash object and TTL in seconds for issued tokens
32
34
  TOKEN_DEFAULTS = {
33
- capability: { '*' => ['*'] },
34
- ttl: 60 * 60 # 1 hour in seconds
35
- }
35
+ capability: { '*' => ['*'] },
36
+ ttl: 60 * 60, # 1 hour in seconds
37
+ renew_token_buffer: 10 # buffer to allow a token to be reissued before the token is considered expired (Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER)
38
+ }.freeze
36
39
 
37
- attr_reader :options, :current_token_details
40
+ attr_reader :options, :token_params, :current_token_details
38
41
  alias_method :auth_options, :options
39
42
 
40
43
  # Creates an Auth object
41
44
  #
42
45
  # @param [Ably::Rest::Client] client {Ably::Rest::Client} this Auth object uses
43
- # @param [Hash] options (see Ably::Rest::Client#initialize)
44
- # @option (see Ably::Rest::Client#initialize)
46
+ # @param [Hash] auth_options the authentication options used as a default future token requests
47
+ # @param [Hash] token_params the token params used as a default for future token requests
48
+ # @option (see #request_token)
45
49
  #
46
- def initialize(client, options)
47
- ensure_valid_auth_attributes options
50
+ def initialize(client, auth_options, token_params)
51
+ unless auth_options.kind_of?(Hash)
52
+ raise ArgumentError, 'Expected auth_options to be a Hash'
53
+ end
48
54
 
49
- auth_options = options.dup
55
+ unless token_params.kind_of?(Hash)
56
+ raise ArgumentError, 'Expected token_params to be a Hash'
57
+ end
58
+
59
+ ensure_valid_auth_attributes auth_options
50
60
 
51
61
  @client = client
52
- @options = auth_options
62
+ @options = auth_options.dup
63
+ @token_params = token_params.dup
53
64
 
54
- unless auth_options.kind_of?(Hash)
55
- raise ArgumentError, 'Expected auth_options to be a Hash'
56
- end
65
+ @options.delete :force # Forcing token auth for every request is not a valid default
57
66
 
58
- if auth_options[:key] && (auth_options[:key_secret] || auth_options[:key_name])
67
+ if options[:key] && (options[:key_secret] || options[:key_name])
59
68
  raise ArgumentError, 'key and key_name or key_secret are mutually exclusive. Provider either a key or key_name & key_secret'
60
69
  end
61
70
 
62
- split_api_key_into_key_and_secret! auth_options if auth_options[:key]
71
+ split_api_key_into_key_and_secret! options if options[:key]
63
72
 
64
73
  if using_basic_auth? && !api_key_present?
65
74
  raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided'
@@ -71,85 +80,90 @@ module Ably
71
80
  end
72
81
 
73
82
  @options.freeze
83
+ @token_params.freeze
74
84
  end
75
85
 
76
86
  # Ensures valid auth credentials are present for the library instance. This may rely on an already-known and valid token, and will obtain a new token if necessary.
77
87
  #
78
- # In the event that a new token request is made, the specified options are used.
88
+ # In the event that a new token request is made, the provided options are used.
79
89
  #
80
- # @param [Hash] options the options for the token request
81
- # @option options (see #request_token)
82
- # @option options [String] :key API key comprising the key name and key secret in a single string
83
- # @option options [Boolean] :force obtains a new token even if the current token is valid
90
+ # @param [Hash] auth_options the authentication options used for future token requests
91
+ # @param [Hash] token_params the token params used for future token requests
92
+ # @option auth_options [Boolean] :force obtains a new token even if the current token is valid
93
+ # @option (see #request_token)
84
94
  #
85
- # @return (see #request_token)
95
+ # @return (see #create_token_request)
86
96
  #
87
97
  # @example
88
98
  # # will issue a simple token request using basic auth
89
99
  # client = Ably::Rest::Client.new(key: 'key.id:secret')
90
- # token = client.auth.authorise
100
+ # token_details = client.auth.authorise
91
101
  #
92
102
  # # will use token request from block to authorise if not already authorised
93
- # token = client.auth.authorise do |options|
103
+ # token_details = client.auth.authorise auth_callback: Proc.new do
94
104
  # # create token_request object
95
105
  # token_request
96
106
  # end
97
107
  #
98
- def authorise(options = {})
99
- ensure_valid_auth_attributes options
108
+ def authorise(auth_options = {}, token_params = {})
109
+ ensure_valid_auth_attributes auth_options
110
+
111
+ auth_options = auth_options.clone
100
112
 
101
- if current_token_details && !options[:force]
113
+ if current_token_details && !auth_options.delete(:force)
102
114
  return current_token_details unless current_token_details.expired?
103
115
  end
104
116
 
105
- options = options.clone
106
- split_api_key_into_key_and_secret! options if options[:key]
107
- @options = @options.merge(options) # update default options
117
+ split_api_key_into_key_and_secret! auth_options if auth_options[:key]
118
+ @options = @options.merge(auth_options) # update defaults
119
+
120
+ token_params = (auth_options.delete(:token_params) || {}).merge(token_params)
121
+ @token_params = @token_params.merge(token_params) # update defaults
108
122
 
109
- @current_token_details = request_token(options)
123
+ @current_token_details = request_token(auth_options, token_params)
110
124
  end
111
125
 
112
126
  # Request a {Ably::Models::TokenDetails} which can be used to make authenticated token based requests
113
127
  #
114
- # @param [Hash] options the options for the token request
115
- # @option options [String] :key complete API key for the designated application
116
- # @option options [String] :client_id client ID identifying this connection to other clients (defaults to client client_id if configured)
117
- # @option options [String] :auth_url a URL to be used to GET or POST a set of token request params, to obtain a signed token request.
118
- # @option options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the authUrl
119
- # @option options [Hash] :auth_params a set of application-specific query params to be added to any request made to the authUrl
120
- # @option options [Symbol] :auth_method HTTP method to use with auth_url, must be either `:get` or `:post` (defaults to :get)
121
- # @option options [Proc] :auth_callback this Proc / block will be called with the {#auth_options Auth#auth_options Hash} as the first argument whenever a new token is required.
122
- # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
123
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
124
- # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
125
- # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
126
- # @option options [Time] :timestamp the time of the of the request
127
- # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
128
+ # @param [Hash] auth_options (see #create_token_request)
129
+ # @option auth_options [String] :auth_url a URL to be used to GET or POST a set of token request params, to obtain a signed token request
130
+ # @option auth_options [Hash] :auth_headers a set of application-specific headers to be added to any request made to the +auth_url+
131
+ # @option auth_options [Hash] :auth_params a set of application-specific query params to be added to any request made to the +auth_url+
132
+ # @option auth_options [Symbol] :auth_method (:get) HTTP method to use with +auth_url+, must be either +:get+ or +:post+
133
+ # @option auth_options [Proc] :auth_callback when provided, the Proc will be called with the token params hash as the first argument, whenever a new token is required.
134
+ # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
135
+ # @param [Hash] token_params (see #create_token_request)
136
+ # @option (see #create_token_request)
128
137
  #
129
138
  # @return [Ably::Models::TokenDetails]
130
139
  #
131
140
  # @example
132
141
  # # simple token request using basic auth
133
142
  # client = Ably::Rest::Client.new(key: 'key.id:secret')
134
- # token = client.auth.request_token
143
+ # token_details = client.auth.request_token
144
+ #
145
+ # # token request with token params
146
+ # client.auth.request_token token_params: { ttl: 1.hour }
135
147
  #
136
148
  # # token request using auth block
137
- # token = client.auth.request_token do |options|
149
+ # token_details = client.auth.request_token auth_callback: Proc.new do
138
150
  # # create token_request object
139
151
  # token_request
140
152
  # end
141
153
  #
142
- def request_token(options = {})
143
- ensure_valid_auth_attributes options
154
+ def request_token(auth_options = {}, token_params = {})
155
+ ensure_valid_auth_attributes auth_options
144
156
 
145
- token_options = auth_options.merge(options)
157
+ token_params = (auth_options[:token_params] || {}).merge(token_params)
158
+ token_params = self.token_params.merge(token_params)
159
+ auth_options = self.options.merge(auth_options)
146
160
 
147
- token_request = if auth_callback = token_options.delete(:auth_callback)
148
- auth_callback.call(token_options)
149
- elsif auth_url = token_options.delete(:auth_url)
150
- token_request_from_auth_url(auth_url, token_options)
161
+ token_request = if auth_callback = auth_options.delete(:auth_callback)
162
+ auth_callback.call(token_params)
163
+ elsif auth_url = auth_options.delete(:auth_url)
164
+ token_request_from_auth_url(auth_url, auth_options)
151
165
  else
152
- create_token_request(token_options)
166
+ create_token_request(auth_options, token_params)
153
167
  end
154
168
 
155
169
  case token_request
@@ -172,19 +186,23 @@ module Ably
172
186
 
173
187
  # Creates and signs a token request that can then subsequently be used by any client to request a token
174
188
  #
175
- # @param [Hash] options the options for the token request
176
- # @option options [String] :key complete API key for the designated application
177
- # @option options [String] :client_id client ID identifying this connection to other clients
178
- # @option options [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
179
- # @option options [Hash] :capability canonicalised representation of the resource paths and associated operations
180
- # @option options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
181
- # @option options [Time] :timestamp the time of the of the request
182
- # @option options [String] :nonce an unquoted, unescaped random string of at least 16 characters
189
+ # @param [Hash] auth_options the authentication options for the token request
190
+ # @option auth_options [String] :key API key comprising the key name and key secret in a single string
191
+ # @option auth_options [String] :client_id client ID identifying this connection to other clients (will use +client_id+ specified when library was instanced if provided)
192
+ # @option auth_options [Boolean] :query_time when true will query the {https://www.ably.io Ably} system for the current time instead of using the local time
193
+ # @option auth_options [Hash] :token_params convenience to pass in +token_params+ within the +auth_options+ argument, this helps avoid the following +authorise({key: key}, {ttl: 23})+ by allowing +authorise(key:key,token_params:{ttl:23})+
194
+ #
195
+ # @param [Hash] token_params the token params used in the token request
196
+ # @option token_params [String] :client_id A client ID to associate with this token. The generated token may be used to authenticate as this +client_id+
197
+ # @option token_params [Integer] :ttl validity time in seconds for the requested {Ably::Models::TokenDetails}. Limits may apply, see {https://www.ably.io/documentation/other/authentication}
198
+ # @option token_params [Hash] :capability canonicalised representation of the resource paths and associated operations
199
+ # @option token_params [Time] :timestamp the time of the request
200
+ # @option token_params [String] :nonce an unquoted, unescaped random string of at least 16 characters
183
201
  #
184
202
  # @return [Models::TokenRequest]
185
203
  #
186
204
  # @example
187
- # client.auth.create_token_request(id: 'asd.asd', ttl: 3600)
205
+ # client.auth.create_token_request(id: 'asd.asd', token_params: { ttl: 3600 })
188
206
  # #<Ably::Models::TokenRequest:0x007fd5d919df78
189
207
  # # @hash={
190
208
  # # :id=>"asds.adsa",
@@ -196,30 +214,37 @@ module Ably
196
214
  # # :mac=>"881oZHeFo6oMim7....uE56a8gUxHw="
197
215
  # # }
198
216
  # #>>
199
- def create_token_request(options = {})
200
- ensure_valid_auth_attributes options
217
+ def create_token_request(auth_options = {}, token_params = {})
218
+ ensure_valid_auth_attributes auth_options
201
219
 
202
- token_options = options.clone
220
+ auth_options = auth_options.clone
221
+ token_params = (auth_options[:token_params] || {}).merge(token_params)
203
222
 
204
- split_api_key_into_key_and_secret! token_options if token_options[:key]
205
- request_key_name = token_options.delete(:key_name) || key_name
206
- request_key_secret = token_options.delete(:key_secret) || key_secret
207
- raise Ably::Exceptions::TokenRequestError, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret
223
+ split_api_key_into_key_and_secret! auth_options if auth_options[:key]
224
+ request_key_name = auth_options.delete(:key_name) || key_name
225
+ request_key_secret = auth_options.delete(:key_secret) || key_secret
208
226
 
209
- timestamp = if token_options[:query_time]
227
+ raise Ably::Exceptions::TokenRequestFailed, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret
228
+
229
+ timestamp = if auth_options[:query_time]
210
230
  client.time
211
231
  else
212
- token_options.delete(:timestamp) || Time.now
232
+ token_params.delete(:timestamp) || Time.now
213
233
  end
214
234
  timestamp = Time.at(timestamp) if timestamp.kind_of?(Integer)
215
235
 
236
+ ttl = [
237
+ (token_params[:ttl] || TOKEN_DEFAULTS.fetch(:ttl)),
238
+ Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER + TOKEN_DEFAULTS.fetch(:renew_token_buffer) # never issue a token that will be immediately considered expired due to the buffer
239
+ ].max
240
+
216
241
  token_request = {
217
- keyName: token_options[:key_name] || request_key_name,
218
- clientId: token_options[:client_id] || client_id,
219
- ttl: ((token_options[:ttl] || TOKEN_DEFAULTS.fetch(:ttl)) * 1000).to_i,
242
+ keyName: request_key_name,
243
+ clientId: token_params[:client_id] || auth_options[:client_id] || client_id,
244
+ ttl: (ttl * 1000).to_i,
220
245
  timestamp: (timestamp.to_f * 1000).round,
221
- capability: token_options[:capability] || TOKEN_DEFAULTS.fetch(:capability),
222
- nonce: token_options[:nonce] || SecureRandom.hex.force_encoding('UTF-8')
246
+ capability: token_params[:capability] || TOKEN_DEFAULTS.fetch(:capability),
247
+ nonce: token_params[:nonce] || SecureRandom.hex.force_encoding('UTF-8')
223
248
  }
224
249
 
225
250
  token_request[:capability] = JSON.dump(token_request[:capability]) if token_request[:capability].is_a?(Hash)
@@ -227,7 +252,7 @@ module Ably
227
252
  token_request[:mac] = sign_params(token_request, request_key_secret)
228
253
 
229
254
  # Undocumented feature to request a persisted token
230
- token_request[:persisted] = options[:persisted] if options[:persisted]
255
+ token_request[:persisted] = token_params[:persisted] if token_params[:persisted]
231
256
 
232
257
  Models::TokenRequest.new(token_request)
233
258
  end
@@ -252,7 +277,7 @@ module Ably
252
277
  # True when Token Auth is being used to authenticate with Ably
253
278
  def using_token_auth?
254
279
  return options[:use_token_auth] if options.has_key?(:use_token_auth)
255
- token || current_token_details || has_client_id? || token_creatable_externally?
280
+ !!(token || current_token_details || has_client_id? || token_creatable_externally?)
256
281
  end
257
282
 
258
283
  def client_id
@@ -270,6 +295,7 @@ module Ably
270
295
  end
271
296
 
272
297
  # Auth header string used in HTTP requests to Ably
298
+ # Will reauthorise implicitly if required and capable
273
299
  #
274
300
  # @return [String] HTTP authentication value used in HTTP_AUTHORIZATION header
275
301
  def auth_header
@@ -281,6 +307,7 @@ module Ably
281
307
  end
282
308
 
283
309
  # Auth params used in URI endpoint for Realtime connections
310
+ # Will reauthorise implicitly if required and capable
284
311
  #
285
312
  # @return [Hash] Auth params for a new Realtime connection
286
313
  def auth_params
@@ -353,7 +380,7 @@ module Ably
353
380
  end
354
381
 
355
382
  def ensure_api_key_sent_over_secure_connection
356
- raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
383
+ raise Ably::Exceptions::InsecureRequest, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
357
384
  end
358
385
 
359
386
  # Basic Auth HTTP Authorization header value
@@ -424,18 +451,18 @@ module Ably
424
451
  # Retrieve a token request from a specified URL, expects a JSON response
425
452
  #
426
453
  # @return [Hash]
427
- def token_request_from_auth_url(auth_url, options = {})
454
+ def token_request_from_auth_url(auth_url, auth_options)
428
455
  uri = URI.parse(auth_url)
429
456
  connection = Faraday.new("#{uri.scheme}://#{uri.host}", connection_options)
430
- method = options[:auth_method] || :get
457
+ method = auth_options[:auth_method] || :get
431
458
 
432
459
  response = connection.send(method) do |request|
433
460
  request.url uri.path
434
- request.params = options[:auth_params] || {}
435
- request.headers = options[:auth_headers] || {}
461
+ request.params = CGI.parse(uri.query || '').merge(auth_options[:auth_params] || {})
462
+ request.headers = auth_options[:auth_headers] || {}
436
463
  end
437
464
 
438
- if !response.body.kind_of?(Hash) && response.headers['Content-Type'] != 'text/plain'
465
+ if !response.body.kind_of?(Hash) && !response.headers['Content-Type'].to_s.match(%r{text/plain}i)
439
466
  raise Ably::Exceptions::InvalidResponseBody,
440
467
  "Content Type #{response.headers['Content-Type']} is not supported by this client library"
441
468
  end
@@ -16,14 +16,22 @@ module Ably
16
16
  @status = status
17
17
  @code = code
18
18
  end
19
+
20
+ def to_s
21
+ message = [super]
22
+ if status || code
23
+ additional_info = []
24
+ additional_info << "code: #{code}" if code
25
+ additional_info << "http status: #{status}" if status
26
+ message << "(#{additional_info.join(', ')})"
27
+ end
28
+ message.join(' ')
29
+ end
19
30
  end
20
31
 
21
32
  # An invalid request was received by Ably
22
33
  class InvalidRequest < BaseAblyException; end
23
34
 
24
- # The token is invalid
25
- class InvalidToken < BaseAblyException; end
26
-
27
35
  # Ably Protocol message received that is invalid
28
36
  class ProtocolError < BaseAblyException; end
29
37
 
@@ -39,19 +47,28 @@ module Ably
39
47
  super message, status, code
40
48
  @base_error = base_error
41
49
  end
50
+
51
+ def to_s
52
+ message = [super]
53
+ message << "#{@base_error}" if @base_error
54
+ message.join(' < ')
55
+ end
42
56
  end
43
57
 
44
58
  # Connection Timeout accessing Realtime or REST service
45
- class ConnectionTimeoutError < ConnectionError; end
59
+ class ConnectionTimeout < ConnectionError; end
46
60
 
47
61
  # Connection closed unexpectedly
48
- class ConnectionClosedError < ConnectionError; end
62
+ class ConnectionClosed < ConnectionError; end
49
63
 
50
- # Invalid State Change error on a {https://github.com/gocardless/statesman Statesman State Machine}
51
- class StateChangeError < BaseAblyException; end
64
+ # Connection suspended
65
+ class ConnectionSuspended < ConnectionError; end
52
66
 
53
- # The state of the object is not suitable for this operation
54
- class IncompatibleStateForOperation < BaseAblyException; end
67
+ # Connection failed
68
+ class ConnectionFailed < ConnectionError; end
69
+
70
+ # Invalid State Change error on a {https://github.com/gocardless/statesman Statesman State Machine}
71
+ class InvalidStateChange < BaseAblyException; end
55
72
 
56
73
  # A generic Ably exception taht supports a status & code.
57
74
  # See https://github.com/ably/ably-common/blob/master/protocol/errors.json for a list of Ably errors
@@ -61,21 +78,30 @@ module Ably
61
78
  class ServerError < BaseAblyException; end
62
79
 
63
80
  # PaginatedResult cannot retrieve the page
64
- class InvalidPageError < BaseAblyException; end
81
+ class PageMissing < BaseAblyException; end
65
82
 
66
83
  # The expected response from the server was invalid
67
84
  class InvalidResponseBody < BaseAblyException; end
68
85
 
69
86
  # The request cannot be performed because it is insecure
70
- class InsecureRequestError < BaseAblyException; end
87
+ class InsecureRequest < BaseAblyException; end
71
88
 
72
89
  # The token request could not be created
73
- class TokenRequestError < BaseAblyException; end
90
+ class TokenRequestFailed < BaseAblyException; end
91
+
92
+ # The token has expired
93
+ class TokenExpired < BaseAblyException; end
74
94
 
75
95
  # The message could not be delivered to the server
76
- class MessageDeliveryError < BaseAblyException; end
96
+ class MessageDeliveryFailed < BaseAblyException; end
97
+
98
+ # The client has been configured to not queue messages i.e. only publish down an active connection
99
+ class MessageQueueingDisabled < BaseAblyException; end
77
100
 
78
101
  # The data payload type is not supported
79
- class UnsupportedDataTypeError < BaseAblyException; end
102
+ class UnsupportedDataType < BaseAblyException; end
103
+
104
+ # When a channel is detached / failed, certain operations are not permitted such as publishing messages
105
+ class ChannelInactive < BaseAblyException; end
80
106
  end
81
107
  end