lib/ably/rest/client.rb in ably-0.8.13 vs lib/ably/rest/client.rb in ably-0.8.14
- old
+ new
@@ -76,14 +76,18 @@
# The additional options passed to this Client's #initialize method not available as attributes of this class
# @return [Hash]
# @api private
attr_reader :options
+ # The list of fallback hosts to be used by this client
+ # if empty or nil then fallback host functionality is disabled
+ attr_reader :fallback_hosts
+
# Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
#
# @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key or Token ID
- # @option options [Boolean] :tls (true) When fales, TLS is disabled. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
+ # @option options [Boolean] :tls (true) When false, TLS is disabled. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
# @option options [String] :key API key comprising the key name and key secret in a single string
# @option options [String] :token Token string or {Models::TokenDetails} used to authenticate requests
# @option options [String] :token_details {Models::TokenDetails} used to authenticate requests
# @option options [Boolean] :use_token_auth Will force Basic Auth if set to false, and Token auth if set to true
# @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment
@@ -104,10 +108,13 @@
# @option options [Integer] :http_open_timeout (4 seconds) timeout in seconds for opening an HTTP connection for all HTTP requests
# @option options [Integer] :http_request_timeout (15 seconds) timeout in seconds for any single complete HTTP request and response
# @option options [Integer] :http_max_retry_count (3) maximum number of fallback host retries for HTTP requests that fail due to network issues or server problems
# @option options [Integer] :http_max_retry_duration (10 seconds) maximum elapsed time in which fallback host retries for HTTP requests will be attempted i.e. if the first default host attempt takes 5s, and then the subsequent fallback retry attempt takes 7s, no further fallback host attempts will be made as the total elapsed time of 12s exceeds the default 10s limit
#
+ # @option options [Boolean] :fallback_hosts_use_default (false) When true, forces the user of fallback hosts even if a non-default production endpoint is being used
+ # @option options [Array<String>] :fallback_hosts When an array of fallback hosts are provided, these fallback hosts are always used if a request fails to the primary endpoint. If an empty array is provided, the fallback host functionality is disabled
+ #
# @return [Ably::Rest::Client]
#
# @example
# # create a new client authenticating with basic auth
# client = Ably::Rest::Client.new('key.id:secret')
@@ -127,18 +134,33 @@
end
end
@tls = options.delete(:tls) == false ? false : true
@environment = options.delete(:environment) # nil is production
+ @environment = nil if [:production, 'production'].include?(@environment)
@protocol = options.delete(:protocol) || :msgpack
@debug_http = options.delete(:debug_http)
@log_level = options.delete(:log_level) || ::Logger::WARN
@custom_logger = options.delete(:logger)
@custom_host = options.delete(:rest_host)
@custom_port = options.delete(:port)
@custom_tls_port = options.delete(:tls_port)
+ if options[:fallback_hosts_use_default] && options[:fallback_jhosts]
+ raise ArgumentError, "fallback_hosts_use_default cannot be set to trye when fallback_jhosts is also provided"
+ end
+ @fallback_hosts = case
+ when options.delete(:fallback_hosts_use_default)
+ Ably::FALLBACK_HOSTS
+ when options_fallback_hosts = options.delete(:fallback_hosts)
+ options_fallback_hosts
+ when environment || custom_host || options[:realtime_host] || custom_port || custom_tls_port
+ []
+ else
+ Ably::FALLBACK_HOSTS
+ end
+
@http_defaults = HTTP_DEFAULTS.dup
options.each do |key, val|
if http_key = key[/^http_(.+)/, 1]
@http_defaults[http_key.to_sym] = val if val && @http_defaults.has_key?(http_key.to_sym)
end
@@ -293,11 +315,11 @@
end
# Connection used to make HTTP requests
#
# @param [Hash] options
- # @option options [Boolean] :use_fallback when true, one of the fallback connections is used randomly, see {Ably::FALLBACK_HOSTS}
+ # @option options [Boolean] :use_fallback when true, one of the fallback connections is used randomly, see the default {Ably::FALLBACK_HOSTS}
#
# @return [Faraday::Connection]
#
# @api private
def connection(options = {})
@@ -307,26 +329,38 @@
@connection ||= Faraday.new(endpoint.to_s, connection_options)
end
end
# Fallback connection used to make HTTP requests.
- # Note, each request uses a random and then subsequent random {Ably::FALLBACK_HOSTS fallback host}
+ # Note, each request uses a random and then subsequent random {Ably::FALLBACK_HOSTS fallback hosts}
+ # are used (unless custom fallback hosts are provided with fallback_hosts)
#
# @return [Faraday::Connection]
#
# @api private
def fallback_connection
unless defined?(@fallback_connections) && @fallback_connections
- @fallback_connections = Ably::FALLBACK_HOSTS.shuffle.map { |host| Faraday.new(endpoint_for_host(host).to_s, connection_options) }
+ @fallback_connections = fallback_hosts.shuffle.map { |host| Faraday.new(endpoint_for_host(host).to_s, connection_options) }
+ @fallback_connections << Faraday.new(endpoint.to_s, connection_options) # Try the original host last if all fallbacks have been used
end
@fallback_index ||= 0
@fallback_connections[@fallback_index % @fallback_connections.count].tap do
@fallback_index += 1
end
end
+ # Library Ably version user agent
+ # @api private
+ def lib_version_id
+ @lib_version_id ||= [
+ 'ruby',
+ Ably.lib_variant,
+ Ably::VERSION
+ ].compact.join('-')
+ end
+
private
def request(method, path, params = {}, options = {})
options = options.clone
if options.delete(:disable_automatic_reauthorise) == true
send_request(method, path, params, options)
@@ -350,18 +384,17 @@
connection(use_fallback: use_fallback).send(method, path, params) do |request|
unless options[:send_auth_header] == false
request.headers[:authorization] = auth.auth_header
end
- request.headers['X-Ably-Version'] = Ably::PROTOCOL_VERSION
- request.headers['X-Ably-Lib'] = Ably::LIB_VERSION_ID
end
rescue Faraday::TimeoutError, Faraday::ClientError, Ably::Exceptions::ServerError => error
time_passed = Time.now - requested_at
if can_fallback_to_alternate_ably_host? && retry_count < max_retry_count && time_passed <= max_retry_duration
retry_count += 1
+ logger.warn "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}"
retry
end
case error
when Faraday::TimeoutError
@@ -408,13 +441,15 @@
# @return [Hash]
def connection_options
@connection_options ||= {
builder: middleware,
headers: {
- content_type: mime_type,
- accept: mime_type,
- user_agent: user_agent
+ content_type: mime_type,
+ accept: mime_type,
+ user_agent: user_agent,
+ 'X-Ably-Version' => Ably::PROTOCOL_VERSION,
+ 'X-Ably-Lib' => lib_version_id
},
request: {
open_timeout: http_defaults.fetch(:open_timeout),
timeout: http_defaults.fetch(:request_timeout)
}
@@ -437,10 +472,10 @@
builder.adapter Faraday.default_adapter
end
end
def can_fallback_to_alternate_ably_host?
- !custom_host && !environment
+ fallback_hosts && !fallback_hosts.empty?
end
def initialize_default_encoders
Ably::Models::MessageEncoders.register_default_encoders self
end