Class: Steem::RPC::HttpClient

Inherits:
BaseClient show all
Defined in:
lib/steem/rpc/http_client.rb

Overview

HttpClient is intended for single-threaded applications. For multi-threaded apps, use ThreadSafeHttpClient.

Direct Known Subclasses

ThreadSafeHttpClient

Constant Summary collapse

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.

[Net::OpenTimeout, JSON::ParserError, Net::ReadTimeout,
Errno::EBADF, IOError, Errno::ENETDOWN, Steem::RemoteDatabaseLockError]
POST_HEADERS =
{
  'Content-Type' => 'application/json; charset=utf-8',
  'User-Agent' => Steem::AGENT_ID
}
JSON_RPC_BATCH_SIZE_MAXIMUM =
50

Constants inherited from BaseClient

BaseClient::MAX_TIMEOUT_BACKOFF, BaseClient::MAX_TIMEOUT_RETRY_COUNT

Constants included from ChainConfig

ChainConfig::EXPIRE_IN_SECS, ChainConfig::EXPIRE_IN_SECS_PROPOSAL, ChainConfig::NETWORKS_STEEM_ADDRESS_PREFIX, ChainConfig::NETWORKS_STEEM_CHAIN_ID, ChainConfig::NETWORKS_STEEM_CORE_ASSET, ChainConfig::NETWORKS_STEEM_DEBT_ASSET, ChainConfig::NETWORKS_STEEM_DEFAULT_NODE, ChainConfig::NETWORKS_STEEM_VEST_ASSET, ChainConfig::NETWORKS_TEST_ADDRESS_PREFIX, ChainConfig::NETWORKS_TEST_CHAIN_ID, ChainConfig::NETWORKS_TEST_CORE_ASSET, ChainConfig::NETWORKS_TEST_DEBT_ASSET, ChainConfig::NETWORKS_TEST_DEFAULT_NODE, ChainConfig::NETWORKS_TEST_VEST_ASSET, ChainConfig::NETWORK_CHAIN_IDS

Instance Attribute Summary

Attributes inherited from BaseClient

#chain, #error_pipe, #url

Instance Method Summary collapse

Methods inherited from BaseClient

#evaluate_id, #initialize, #put, #rpc_id, #uri, #yield_response

Constructor Details

This class inherits a constructor from Steem::RPC::BaseClient

Instance Method Details

#httpObject



30
31
32
33
34
35
36
37
38
39
# File 'lib/steem/rpc/http_client.rb', line 30

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

#http_postObject



41
42
43
# File 'lib/steem/rpc/http_client.rb', line 41

def http_post
  @http_post ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
end

#http_request(request) ⇒ Object



45
46
47
# File 'lib/steem/rpc/http_client.rb', line 45

def http_request(request)
  http.request(request)
end

#rpc_batch_execute(options = {}, &block) ⇒ Object



138
139
140
# File 'lib/steem/rpc/http_client.rb', line 138

def rpc_batch_execute(options = {}, &block)
  yield_response rpc_execute(nil, nil, options), &block
end

#rpc_execute(api_name = @api_name, api_method = nil, options = {}, &block) ⇒ Object

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.

Parameters:

  • api_name (String) (defaults to: @api_name)

    API namespace of the method being called.

  • api_method (String) (defaults to: nil)

    API method name being called.

  • options (Hash) (defaults to: {})

    options

Options Hash (options):

  • :request_object (Object)

    Hash or Array to become json in request body.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/steem/rpc/http_client.rb', line 57

def rpc_execute(api_name = @api_name, api_method = nil, options = {}, &block)
  reset_timeout
  
  catch :tota_cera_pila do; begin
    request = http_post
    
    request_object = if !!api_name && !!api_method
      put(api_name, api_method, options)
    elsif !!options && defined?(options.delete)
      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
      throw retry_timeout(:http_request, e)
    end; end
    
    if response.nil?
      throw retry_timeout(:tota_cera_pila, 'response was nil')
    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
      
      [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
              throw retry_timeout(:tota_cera_pila, e)
            end
          else
            raise Steem::ArgumentError, r.error.inspect
          end
        end
      end
      
      yield_response response, &block
    when '504' # Gateway Timeout
      throw retry_timeout(:tota_cera_pila, response.body)
    when '502' # Bad Gateway
      throw retry_timeout(:tota_cera_pila, response.body)
    else
      raise UnknownError, "#{api_name}.#{api_method}: #{response.body}"
    end
  end; end
end