Class: SolanaRpcRuby::WebsocketClient

Inherits:
Object
  • Object
show all
Includes:
RequestBody
Defined in:
lib/solana_rpc_ruby/websocket_client.rb

Overview

WebsocketClient class serves as a websocket client for solana JSON RPC API.

Constant Summary collapse

KEEPALIVE_TIME =
60
SLEEP_TIME =
10
RETRIES_LIMIT =
3

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RequestBody

#base_body, #create_json_body

Constructor Details

#initialize(websocket_client: Faye::WebSocket, cluster: nil) ⇒ WebsocketClient

Initialize object with cluster address where requests will be sent.

Parameters:

  • websocket_client (Object) (defaults to: Faye::WebSocket)
  • cluster (String) (defaults to: nil)

Raises:

  • (ArgumentError)

26
27
28
29
30
31
32
33
# 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

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

Instance Attribute Details

#clientObject

Api client used to connect with API.

Returns:

  • (Object)

20
21
22
# File 'lib/solana_rpc_ruby/websocket_client.rb', line 20

def client
  @client
end

#clusterString

Determines which cluster will be used to send requests.

Returns:

  • (String)

16
17
18
# File 'lib/solana_rpc_ruby/websocket_client.rb', line 16

def cluster
  @cluster
end

Instance Method Details

#connect(body, &block) ⇒ String

Connects with cluster's websocket.

Parameters:

  • body (String)
  • &block (Proc)

Returns:

  • (String)

    # messages from websocket


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
# File 'lib/solana_rpc_ruby/websocket_client.rb', line 41

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
      end
    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

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

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

      @retries += 1
      if @retries <= RETRIES_LIMIT
        puts 'Retry...'
        # It restarts the websocket connection.
        connect(body, &block) 
      else
        ws = nil
        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