Class: BlockGiven::Connectors::Http
- Defined in:
- lib/block_given/connectors/http.rb
Overview
Generic JSON-RPC over HTTP(S) transport with retries and exponential backoff.
BlockGiven::Connectors::Http.new(url: "http://127.0.0.1:8545")
BlockGiven::Connectors::Http.new # -> falls back to chain.rpc_urls.first
Direct Known Subclasses
Defined Under Namespace
Classes: Retry
Constant Summary collapse
- RETRIABLE_STATUSES =
[408, 425, 429, 500, 502, 503, 504].freeze
- RETRIABLE_RPC_CODES =
rate limited / internal error
[-32_005, -32_603, 429].freeze
- RETRIABLE_EXCEPTIONS =
[Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, EOFError, SocketError, OpenSSL::SSL::SSLError].freeze
Instance Attribute Summary collapse
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#retries ⇒ Object
readonly
Returns the value of attribute retries.
-
#retry_delay ⇒ Object
readonly
Returns the value of attribute retry_delay.
-
#timeout ⇒ Object
readonly
Returns the value of attribute timeout.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
Instance Method Summary collapse
- #batch(calls, chain: nil) ⇒ Object
- #endpoint(chain) ⇒ Object
-
#initialize(url: nil, headers: {}, timeout: 30, retries: 3, retry_delay: 0.5, logger: nil) ⇒ Http
constructor
A new instance of Http.
- #inspect ⇒ Object
-
#redact(endpoint) ⇒ Object
Endpoint as it may appear in logs and error messages.
- #request(method, params = [], chain: nil) ⇒ Object
Methods inherited from Base
Constructor Details
#initialize(url: nil, headers: {}, timeout: 30, retries: 3, retry_delay: 0.5, logger: nil) ⇒ Http
Returns a new instance of Http.
21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/block_given/connectors/http.rb', line 21 def initialize(url: nil, headers: {}, timeout: 30, retries: 3, retry_delay: 0.5, logger: nil) super() @url = url user_agent = "block_given/#{BlockGiven::VERSION}" @headers = { "Content-Type" => "application/json", "User-Agent" => user_agent }.merge(headers) @timeout = timeout @retries = retries @retry_delay = retry_delay @logger = logger @id = 0 @mutex = Mutex.new end |
Instance Attribute Details
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
19 20 21 |
# File 'lib/block_given/connectors/http.rb', line 19 def headers @headers end |
#retries ⇒ Object (readonly)
Returns the value of attribute retries.
19 20 21 |
# File 'lib/block_given/connectors/http.rb', line 19 def retries @retries end |
#retry_delay ⇒ Object (readonly)
Returns the value of attribute retry_delay.
19 20 21 |
# File 'lib/block_given/connectors/http.rb', line 19 def retry_delay @retry_delay end |
#timeout ⇒ Object (readonly)
Returns the value of attribute timeout.
19 20 21 |
# File 'lib/block_given/connectors/http.rb', line 19 def timeout @timeout end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
19 20 21 |
# File 'lib/block_given/connectors/http.rb', line 19 def url @url end |
Instance Method Details
#batch(calls, chain: nil) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/block_given/connectors/http.rb', line 47 def batch(calls, chain: nil) return [] if calls.empty? payload = calls.map { |(method, params)| { jsonrpc: "2.0", id: next_id, method: method, params: params || [] } } body = with_retries("batch") { post(endpoint(chain), payload) } raise RpcError, "batch response is not an array: #{body.inspect}" unless body.is_a?(Array) by_id = body.to_h { |entry| [entry["id"], entry] } payload.map do |req| entry = by_id[req[:id]] || { "error" => { "message" => "missing response for id #{req[:id]}" } } if entry.key?("error") RpcError.from_payload(entry["error"], rpc_method: req[:method]) else entry["result"] end end end |
#endpoint(chain) ⇒ Object
34 35 36 37 38 39 |
# File 'lib/block_given/connectors/http.rb', line 34 def endpoint(chain) return url if url chain&.rpc_urls&.first || raise(ConfigurationError, "no RPC url: pass url: to the connector or use a chain with rpc_urls") end |
#inspect ⇒ Object
65 |
# File 'lib/block_given/connectors/http.rb', line 65 def inspect = "#<#{self.class.name} url=#{url ? redact(url).inspect : 'chain default'}>" |
#redact(endpoint) ⇒ Object
Endpoint as it may appear in logs and error messages. RPC URLs usually carry the API key in their path (Infura, QuickNode, ...), so only scheme and host are kept.
69 70 71 72 73 74 75 76 |
# File 'lib/block_given/connectors/http.rb', line 69 def redact(endpoint) uri = URI.parse(endpoint.to_s) host = uri.port && uri.port != uri.default_port ? "#{uri.host}:#{uri.port}" : uri.host path = uri.path.to_s.delete_prefix("/").empty? ? "" : "/…" "#{uri.scheme}://#{host}#{path}" rescue URI::InvalidURIError "<invalid url>" end |
#request(method, params = [], chain: nil) ⇒ Object
41 42 43 44 45 |
# File 'lib/block_given/connectors/http.rb', line 41 def request(method, params = [], chain: nil) payload = { jsonrpc: "2.0", id: next_id, method: method, params: params } body = with_retries(method) { post(endpoint(chain), payload) } handle_single(body, method) end |