diff --git a/lib/ethereum/client.rb b/lib/ethereum/client.rb index c94323d..860efef 100644 --- a/lib/ethereum/client.rb +++ b/lib/ethereum/client.rb @@ -1,5 +1,7 @@ module Ethereum class Client + class ResponseFormatError < StandardError; end + # https://github.com/ethereum/wiki/wiki/JSON-RPC RPC_COMMANDS = %w(web3_clientVersion web3_sha3 net_version net_peerCount net_listening eth_protocolVersion eth_syncing eth_coinbase eth_mining eth_hashrate eth_gasPrice eth_accounts eth_blockNumber eth_getBalance eth_getStorageAt eth_getTransactionCount eth_getBlockTransactionCountByHash eth_getBlockTransactionCountByNumber eth_getUncleCountByBlockHash eth_getUncleCountByBlockNumber eth_getCode eth_sign eth_sendTransaction eth_sendRawTransaction eth_call eth_estimateGas eth_getBlockByHash eth_getBlockByNumber eth_getTransactionByHash eth_getTransactionByBlockHashAndIndex eth_getTransactionByBlockNumberAndIndex eth_getTransactionReceipt eth_getUncleByBlockHashAndIndex eth_getUncleByBlockNumberAndIndex eth_getCompilers eth_compileLLL eth_compileSolidity eth_compileSerpent eth_newFilter eth_newBlockFilter eth_newPendingTransactionFilter eth_uninstallFilter eth_getFilterChanges eth_getFilterLogs eth_getLogs eth_getWork eth_submitWork eth_submitHashrate db_putString db_getString db_putHex db_getHex shh_post shh_version shh_newIdentity shh_hasIdentity shh_newGroup shh_addToGroup shh_newFilter shh_uninstallFilter shh_getFilterChanges shh_getMessages) # https://github.com/ethereum/go-ethereum/wiki/Management-APIs @@ -55,9 +57,13 @@ def reset_id return true else read = send_single(payload.to_json) - output = JSON.parse(read) reset_id - return output + begin + output = JSON.parse(read) + return output + rescue JSON::ParserError => ex + raise ResponseFormatError.new("[#{[ex.class]}] #{ex.message}") + end end end end diff --git a/lib/ethereum/http_client.rb b/lib/ethereum/http_client.rb index b8a214d..c5c249a 100644 --- a/lib/ethereum/http_client.rb +++ b/lib/ethereum/http_client.rb @@ -2,6 +2,8 @@ module Ethereum class HttpClient < Client attr_accessor :host, :port, :uri, :ssl + + class ConnectionError < StandardError; end def initialize(host, port, ssl = false, log = false) super(log) @@ -23,12 +25,35 @@ def send_single(payload) header = {'Content-Type' => 'application/json'} request = ::Net::HTTP::Post.new(uri, header) request.body = payload - response = http.request(request) - return response.body + begin + response = http.request(request) + rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, SocketError, Net::OpenTimeout, OpenSSL::SSL::SSLError => ex + raise ConnectionError.new("[#{ex.class}] #{ex.message}") + end + if response.class == Net::HTTPOK + if @use_cookie + set_cookie_headers = response.get_fields('set-cookie') + if set_cookie_headers.present? + @cookie = set_cookie_headers.map{|str| str.split('; ').first}.join('; ') + end + end + return response.body + else + raise ConnectionError.new("[#{response.class}] #{response.body}") + end end def send_batch(batch) - raise NotImplementedError + result = send_single(batch.to_json) + begin + result = JSON.parse(result) + rescue JSON::ParserError => ex + raise Ethereum::Client::ResponseFormatError.new("[#{[ex.class]}] #{ex.message}") + end + + # Make sure the order is the same as it was when batching calls + # See 6 Batch here http://www.jsonrpc.org/specification + return result.sort_by! { |c| c['id'] } end end