class Hive::RPC::HttpClient
{HttpClient} is intended for single-threaded applications. For multi-threaded apps, use {ThreadSafeHttpClient}.
Constants
- JSON_RPC_BATCH_SIZE_MAXIMUM
- POST_HEADERS
@private
- TIMEOUT_ERRORS
Timeouts are lower level errors, related in that retrying them is trivial, unlike, for example
TransactionExpiredError
, that requires the client to do something before retrying.These situations are hopefully momentary interruptions or rate limiting but they might indicate a bigger problem with the node, so they are not retried forever, only up to MAX_TIMEOUT_RETRY_COUNT and then we give up.
Note: {JSON::ParserError} is included in this list because under certain timeout conditions, a web server may respond with a generic http status code of 200 and HTML page.
@private
Public Instance Methods
# File lib/hive/rpc/http_client.rb, line 32 def http @http ||= Net::HTTP.new(uri.host, uri.port).tap do |http| http.use_ssl = true if uri.to_s =~ /^https/i http.keep_alive_timeout = 150 # seconds # WARNING This method opens a serious security hole. Never use this # method in production code. # http.set_debug_output(STDOUT) if !!ENV['DEBUG'] end end
# File lib/hive/rpc/http_client.rb, line 43 def http_post @http_post ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS) end
# File lib/hive/rpc/http_client.rb, line 47 def http_request(request) http.request(request) end
# File lib/hive/rpc/http_client.rb, line 157 def rpc_batch_execute(options = {}, &block) api_name = options[:api_name] yield_response rpc_execute(api_name, nil, options), &block end
This is the main method used by API instances to actually fetch data from the remote node. It abstracts the api namespace, method name, and parameters so that the API instance can be decoupled from the protocol.
@param api_name [String] API namespace of the method being called. @param api_method [String] API method name being called. @param options [Hash] options @option options [Object] :request_object Hash or Array to become json in request body.
# File lib/hive/rpc/http_client.rb, line 59 def rpc_execute(api_name = @api_name, api_method = nil, options = {}, &block) reset_timeout response = nil loop do sub_options = options.dup request = http_post(api_name) request_object = if !!api_name && !!api_method put(api_name, api_method, sub_options) elsif !!options && defined?(sub_options.delete) sub_options.delete(:request_object) end if request_object.size > JSON_RPC_BATCH_SIZE_MAXIMUM raise JsonRpcBatchMaximumSizeExceededError, "Maximum json-rpc-batch is #{JSON_RPC_BATCH_SIZE_MAXIMUM} elements." end request.body = if request_object.class == Hash request_object elsif request_object.size == 1 request_object.first else request_object end.to_json response = catch :http_request do; begin; http_request(request) rescue *TIMEOUT_ERRORS => e retry_timeout(:http_request, e) and redo end; end if response.nil? retry_timeout(:tota_cera_pila, 'response was nil') and redo end case response.code when '200' response = catch :parse_json do; begin; JSON[response.body] rescue *TIMEOUT_ERRORS => e throw retry_timeout(:parse_json, e) end; end response = case response when Hash Hashie::Mash.new(response).tap do |r| evaluate_id(request: request_object.first, response: r, api_method: api_method) end when Array Hashie::Array.new(response).tap do |r| request_object.each_with_index do |req, index| evaluate_id(request: req, response: r[index], api_method: api_method) end end else; response end timeout_detected = false timeout_cause = nil [response].flatten.each_with_index do |r, i| if defined?(r.error) && !!r.error if !!r.error.message begin rpc_method_name = "#{api_name}.#{api_method}" rpc_args = [request_object].flatten[i] raise_error_response rpc_method_name, rpc_args, r rescue *TIMEOUT_ERRORS => e timeout_detected = true timeout_cause = JSON[e.message]['error'] + " while posting: #{rpc_args}" rescue e.to_s break # fail fast end else raise Hive::ArgumentError, r.error.inspect end end end if timeout_detected retry_timeout(:tota_cera_pila, timeout_cause) and redo end yield_response response, &block when '504' # Gateway Timeout retry_timeout(:tota_cera_pila, response.body) and redo when '502' # Bad Gateway retry_timeout(:tota_cera_pila, response.body) and redo else raise UnknownError, "#{api_name}.#{api_method}: #{response.body}" end break # success! end response end