clusterviz/rpc.go

93 lines
2.3 KiB
Go
Raw Permalink Normal View History

2018-06-19 13:06:42 +00:00
package main
import (
"bytes"
2018-06-20 14:45:05 +00:00
"context"
2018-06-19 13:06:42 +00:00
"fmt"
2018-06-20 14:45:05 +00:00
"io"
2018-06-19 13:06:42 +00:00
"net/http"
2018-06-20 14:45:05 +00:00
"time"
2018-06-19 15:40:46 +00:00
"github.com/ethereum/go-ethereum/p2p"
2018-06-19 13:06:42 +00:00
)
// RPCClient defines subset of client that
// can call needed methods to geth's RPC server.
type RPCClient interface {
2018-06-20 14:45:05 +00:00
AdminPeers(ctx context.Context, ip string) ([]*Node, error)
NodeInfo(ctx context.Context, ip string) (*p2p.NodeInfo, error)
2018-06-19 13:06:42 +00:00
}
// HTTPRPCClient implements RPCClient for
// HTTP transport.
type HTTPRPCClient struct {
2018-06-20 14:45:05 +00:00
client *http.Client
2018-06-19 13:06:42 +00:00
}
2018-06-20 14:45:05 +00:00
const HTTPRPCTimeout = 3 * time.Second
2018-06-19 13:06:42 +00:00
// NewHTTPRPCClient creates new HTTP RPC client for eth JSON-RPC server.
2018-06-19 15:40:46 +00:00
func NewHTTPRPCClient() *HTTPRPCClient {
2018-06-20 14:45:05 +00:00
return &HTTPRPCClient{
client: &http.Client{
Timeout: ConsulTimeout,
},
}
2018-06-19 13:06:42 +00:00
}
// AdminPeers executes `admin_peers` RPC call and parses the response.
// Satisfies RPCClient interface.
2018-06-20 14:45:05 +00:00
func (h *HTTPRPCClient) AdminPeers(ctx context.Context, ip string) ([]*Node, error) {
r, err := h.postMethod(ctx, ip, "admin_peers")
2018-06-19 13:06:42 +00:00
if err != nil {
2018-06-20 14:45:05 +00:00
return nil, fmt.Errorf("rpc admin_peers: %s", err)
2018-06-19 13:06:42 +00:00
}
2018-06-20 14:45:05 +00:00
defer r.Close()
2018-06-19 13:06:42 +00:00
2018-06-20 14:45:05 +00:00
nodes, err := ParsePeersResponse(r)
2018-06-19 13:06:42 +00:00
if err != nil {
return nil, fmt.Errorf("get admin peers: %s", err)
}
return PeersToNodes(nodes)
}
2018-06-19 15:40:46 +00:00
// NodeInfo executes `admin_nodeInfo` RPC call and parses the response.
// Satisfies RPCClient interface.
2018-06-20 14:45:05 +00:00
func (h *HTTPRPCClient) NodeInfo(ctx context.Context, ip string) (*p2p.NodeInfo, error) {
r, err := h.postMethod(ctx, ip, "admin_nodeInfo")
2018-06-19 15:40:46 +00:00
if err != nil {
2018-06-20 14:45:05 +00:00
return nil, fmt.Errorf("rpc admin_nodeInfo: %s", err)
2018-06-19 15:40:46 +00:00
}
2018-06-20 14:45:05 +00:00
defer r.Close()
2018-06-19 15:40:46 +00:00
2018-06-20 14:45:05 +00:00
nodeInfo, err := ParseNodeInfoResponse(r)
2018-06-19 15:40:46 +00:00
if err != nil {
return nil, fmt.Errorf("get node info: %s", err)
}
return nodeInfo, err
}
2018-06-20 14:45:05 +00:00
// postMethod performs POST RPC request for single method RPC calls without params.
// it reads body and return the whole answer.
func (h *HTTPRPCClient) postMethod(ctx context.Context, ip, method string) (io.ReadCloser, error) {
payload := fmt.Sprintf("{\"jsonrpc\":\"2.0\",\"method\":\"%s\",\"params\":[],\"id\":1}", method)
data := bytes.NewBufferString(payload)
url := "http://" + ip
req, err := http.NewRequest("POST", url, data)
if err != nil {
return nil, fmt.Errorf("request: %s", err)
}
2018-06-20 15:30:14 +00:00
2018-06-20 14:45:05 +00:00
req = req.WithContext(ctx)
2018-06-20 15:30:14 +00:00
req.Header.Set("Content-Type", "application/json")
2018-06-20 14:45:05 +00:00
resp, err := h.client.Do(req)
if err != nil {
return nil, fmt.Errorf("POST RPC request: %s", err)
}
return resp.Body, nil
}