forked from toorop/go-bitcoind
-
Notifications
You must be signed in to change notification settings - Fork 2
/
rpcClient.go
156 lines (138 loc) · 3.61 KB
/
rpcClient.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package navcoind
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"go.uber.org/zap"
"io/ioutil"
"net/http"
"time"
)
// A rpcClient represents a JSON RPC client (over HTTP(s)).
type rpcClient struct {
serverAddr string
user string
passwd string
httpClient *http.Client
timeout int
debug bool
}
// rpcRequest represent a RCP request
type rpcRequest struct {
Method string `json:"method"`
Params interface{} `json:"params"`
Id int64 `json:"id"`
JsonRpc string `json:"jsonrpc"`
}
// RPCErrorCode represents an error code to be used as a part of an RPCError
// which is in turn used in a JSON-RPC Response object.
//
// A specific type is used to help ensure the wrong errors aren't used.
type RPCErrorCode int
// RPCError represents an error that is used as a part of a JSON-RPC Response
// object.
type RPCError struct {
Code RPCErrorCode `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
// Guarantee RPCError satisfies the builtin error interface.
var _, _ error = RPCError{}, (*RPCError)(nil)
// Error returns a string describing the RPC error. This satisfies the
// builtin error interface.
func (e RPCError) Error() string {
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
type rpcResponse struct {
Id int64 `json:"id"`
Result json.RawMessage `json:"result"`
Err *RPCError `json:"error"`
}
func newClient(host string, port int, user, passwd string, useSSL bool, timeout int, debug bool) (c *rpcClient, err error) {
if len(host) == 0 {
err = errors.New("Bad call missing argument host")
return
}
var serverAddr string
var httpClient *http.Client
if useSSL {
serverAddr = "https://"
t := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient = &http.Client{Transport: t}
} else {
serverAddr = "http://"
httpClient = &http.Client{}
}
c = &rpcClient{
fmt.Sprintf("%s%s:%d", serverAddr, host, port),
user,
passwd,
httpClient,
timeout,
debug,
}
return
}
// doTimeoutRequest process a HTTP request with timeout
func (c *rpcClient) doTimeoutRequest(timer *time.Timer, req *http.Request) (*http.Response, error) {
type result struct {
resp *http.Response
err error
}
done := make(chan result, 1)
go func() {
resp, err := c.httpClient.Do(req)
done <- result{resp, err}
}()
// Wait for the read or the timeout
select {
case r := <-done:
return r.resp, r.err
case <-timer.C:
return nil, errors.New("Timeout reading data from server")
}
}
// call prepare & exec the request
func (c *rpcClient) call(method string, params interface{}) (rr rpcResponse, err error) {
rpcR := rpcRequest{method, params, time.Now().UnixNano(), "1.0"}
payloadBuffer := &bytes.Buffer{}
jsonEncoder := json.NewEncoder(payloadBuffer)
err = jsonEncoder.Encode(rpcR)
if err != nil {
return
}
if c.debug {
zap.L().With(
zap.String("request", payloadBuffer.String()),
).Debug("Navcoind: RPC Request")
}
req, err := http.NewRequest("POST", c.serverAddr, payloadBuffer)
if err != nil {
return
}
req.Header.Add("Content-Type", "application/json;charset=utf-8")
req.Header.Add("Accept", "application/json")
// Auth ?
if len(c.user) > 0 || len(c.passwd) > 0 {
req.SetBasicAuth(c.user, c.passwd)
}
resp, err := c.doTimeoutRequest(time.NewTimer(time.Duration(c.timeout)*time.Second), req)
if err != nil {
return
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
if c.debug {
zap.L().With(
zap.String("response", string(data)),
).Debug("Navcoind: RPC Response")
}
err = json.Unmarshal(data, &rr)
return
}