-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclient.go
134 lines (110 loc) · 2.94 KB
/
client.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
package jsonrpc2
//go:generate easyjson
import (
"context"
"encoding/json"
"math/rand"
"net/http"
)
const (
// ErrCodeServer indicates that client could not unmarshal response from server.
ErrCodeParseError = -32700
protocolVersionStr = "2.0"
contentTypeApplicationJSON = "application/json"
)
// ResponseError is a struct which represents a typical jsonrpc2 error according to specification.
//easyjson:json
type ResponseError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// Error returns an error message of ResponseError.
func (e *ResponseError) Error() string {
return e.Message
}
// Request represents a jsonrpc2 request.
//easyjson:json
type Request struct {
Version string `json:"jsonrpc"`
Method string `json:"method"`
Params interface{} `json:"params"`
Id uint64 `json:"id"`
}
// Request represents a jsonrpc2 response.
//easyjson:json
type Response struct {
Version string `json:"jsonrpc"`
Result *json.RawMessage `json:"result"`
Error *json.RawMessage `json:"error"`
}
type (
// Client represents jsonrpc caller interface.
// It have only one method call which satisfies simple case of jsonrpc2 usage.
Client interface {
Call(ctx context.Context, methodName string, params interface{}, result interface{}) error
}
client struct {
url string
httpClient HTTPClient
}
clientOption func(c *client)
)
// WithHttpClient is an option which sets http client implementation for jsonrpc2 client.
func WithHttpClient(httpClient HTTPClient) clientOption {
return func(c *client) {
c.httpClient = httpClient
}
}
// NewClient returns jsonrpc2 client.
func NewClient(rpcEndpointURL string, options ...clientOption) Client {
c := &client{
url: rpcEndpointURL,
httpClient: NewHttpClient(http.DefaultClient),
}
for _, opt := range options {
opt(c)
}
return c
}
// Call makes and does jsonrpc2 request.
func (c *client) Call(ctx context.Context, methodName string, params interface{}, result interface{}) error {
encodedReq, err := encodeRequest(methodName, params)
if err != nil {
return err
}
resp, err := c.httpClient.Post(ctx, c.url, encodedReq)
if err != nil {
return err
}
return decodeResponse(resp, result)
}
func encodeRequest(method string, args interface{}) ([]byte, error) {
return json.Marshal(&Request{
Version: protocolVersionStr,
Method: method,
Params: args,
Id: uint64(rand.Int63()),
})
}
func decodeResponse(r []byte, reply interface{}) error {
var resp Response
err := json.Unmarshal(r, &resp)
if err != nil {
return err
}
if resp.Error != nil {
jsonRpcError := &ResponseError{}
if err := json.Unmarshal(*resp.Error, jsonRpcError); err != nil {
return &ResponseError{
Code: ErrCodeParseError,
Message: string(*resp.Error),
}
}
return jsonRpcError
}
if resp.Result == nil {
return nil
}
return json.Unmarshal(*resp.Result, reply)
}