class SolanaRpcRuby::WebsocketClient

WebsocketClient class serves as a websocket client for solana JSON RPC API. @see docs.solana.com/developing/clients/jsonrpc-api

Constants

KEEPALIVE_TIME
RETRIES_LIMIT
SLEEP_TIME

Attributes

client[RW]

Api client used to connect with API. @return [Object]

cluster[RW]

Determines which cluster will be used to send requests. @return [String]

Public Class Methods

new(websocket_client: Faye::WebSocket, cluster: nil) click to toggle source

Initialize object with cluster address where requests will be sent.

@param websocket_client [Object] @param cluster [String]

# File lib/solana_rpc_ruby/websocket_client.rb, line 26
def initialize(websocket_client: Faye::WebSocket, cluster: nil)
  @client = websocket_client
  @cluster = cluster || SolanaRpcRuby.ws_cluster
  @retries = 0
  @subscription_info = nil

  message = 'Websocket cluster is missing. Please provide default cluster in config or pass it to the client directly.'
  raise ArgumentError, message unless @cluster
end

Public Instance Methods

connect(body, &block) click to toggle source

Connects with cluster's websocket.

@param body [String] @param &block [Proc]

@return [String] # messages from websocket

# File lib/solana_rpc_ruby/websocket_client.rb, line 42
def connect(body, &block)
  EM.run {
    # ping option sends some data to the server periodically,
    # which prevents the connection to go idle.
    ws = Faye::WebSocket::Client.new(@cluster, nil)

    EM::PeriodicTimer.new(KEEPALIVE_TIME) do
      while !ws.ping
        @retries += 1

        unless @retries <= 3
          puts '3 ping retries failed, close connection.'
          ws.close
          break
        end

        puts 'Ping failed, sleep for 10 seconds...'
        sleep SLEEP_TIME
        puts "#{@retries} ping retry..."
      end
    end

    # Uncomment to disconnect websocket.
    # EM::Timer.new(2) do
    #   ws.send(unsubscribe_body(body))
    # end

    ws.on :open do |event|
      p [:open]
      p "Status: #{ws.status}"
      ws.send(body)
    end
  
    ws.on :message do |event|
      # To run websocket_methods_wrapper_spec.rb, uncomment code below
      # to return info about connection estabilished.
      # Also, read the comment from the top of the mentioned file.
      #
      # if ENV['test'] == 'true'
      #   result = block_given? ? block.call(event.data) : event.data
      #   return result
      # end
      @subscription_info = event.data unless @subscription_info

      if block_given?
        block.call(event.data)
      else
        puts event.data
      end
    end

    ws.on :close do |event|
      p [:close, event.code, event.reason]
      ws = nil

      @retries += 1
      if @retries <= RETRIES_LIMIT
        puts 'Retry...'
        # It restarts the websocket connection.
        connect(body, &block) 
      else
        puts 'Retries limit reached, closing. Wrong cluster address or unhealthy node might be a reason, please check.'
        EM.stop
      end
    end
  }
rescue Timeout::Error,
       Net::HTTPError,
       Net::HTTPNotFound,
       Net::HTTPClientException,
       Net::HTTPFatalError,
       Net::ReadTimeout => e
  fail ApiError.new(message: e.message)
rescue StandardError => e
  message = "#{e.class} #{e.message}\n Backtrace: \n #{e.backtrace}"
  fail ApiError.new(message: message)
end
unsubscribe_body(body) click to toggle source
# File lib/solana_rpc_ruby/websocket_client.rb, line 120
def unsubscribe_body(body)
  method = JSON.parse(body)['method']
  info = JSON.parse(@subscription_info)
  
  subscription_id = info['result']
  id = info['id']
  unsubscribe_method = method.gsub('Sub', 'Unsub')

  create_json_body(unsubscribe_method, method_params: [subscription_id], id: id)
end