-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrpcclient.go
More file actions
156 lines (134 loc) · 3.81 KB
/
rpcclient.go
File metadata and controls
156 lines (134 loc) · 3.81 KB
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
package bitcoincli
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"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
}
// 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)
func (c *RpcClient) Call(method string, params interface{}) (rpcResponse, error) {
return c.callResource("", method, params)
}
func (c *RpcClient) CallWallet(wallet string, method string, params interface{}) (rr rpcResponse, err error) {
return c.callResource("/wallet/" +wallet, method, params)
}
func (c *RpcClient) callResource(href string, method string, params interface{}) (rr rpcResponse, err error) {
connectTimer := time.NewTimer(time.Duration(c.timeout) * time.Second)
rpcR := rpcRequest{method, params, time.Now().UnixNano(), "1.0"}
payloadBuffer := &bytes.Buffer{}
jsonEncoder := json.NewEncoder(payloadBuffer)
err = jsonEncoder.Encode(rpcR)
if err != nil {
return
}
req, err := http.NewRequest("POST", c.serverAddr + href, 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(connectTimer, req)
if err != nil {
return
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
err = json.Unmarshal(data, &rr)
return
}
// 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)
}
// handleError handle error returned by client.Call
func handleError(err error, r *rpcResponse) error {
if err != nil {
return err
}
if r.Err != nil {
return r.Err
}
return nil
}
type rpcResponse struct {
Id int64 `json:"id"`
Result json.RawMessage `json:"result"`
Err *RPCError `json:"error"`
}
func newRpcClient(host string, port int, user, passwd string, useSSL bool, timeout int) (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{serverAddr: fmt.Sprintf("%s%s:%d", serverAddr, host, port), user: user, passwd: passwd, httpClient: httpClient, timeout: timeout}
return
}
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")
}
}