forked from alibaba-archive/jsonrpc-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsonrpc.go
237 lines (218 loc) · 6.02 KB
/
jsonrpc.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
package jsonrpc
import "encoding/json"
import "bytes"
// MsgType ...
type MsgType string
const (
// InvalidType Invalid message type
InvalidType MsgType = "invalid"
// NotificationType Notification message type
NotificationType MsgType = "notification"
// RequestType Request message type
RequestType MsgType = "request"
// ErrorType Error message type
ErrorType MsgType = "error"
// SuccessType Success message type
SuccessType MsgType = "success"
jsonRPCVersion = "2.0"
)
// RPC represents a JSON-RPC data object.
type RPC struct {
// Type that should be <'request'|'notification'|'success'|'error'|'invalid'>
Type MsgType `json:"-"`
Version string `json:"jsonrpc"`
// A String containing the name of the method to be invoked.
Method string `json:"method,omitempty"`
// Object to pass as request parameter to the method.
Params interface{} `json:"params,omitempty"`
Result interface{} `json:"result,omitempty"`
Error *ErrorObj `json:"error,omitempty"`
// The request id. This can be of any type. It is used to match the
// response with the request that it is replying to.
ID interface{} `json:"id,omitempty"`
}
// ErrorObj ...
type ErrorObj struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// Request return a JSON-RPC 2.0 request message structures.
// the id must be {String|Integer|nil} type
func Request(id interface{}, method string, args ...interface{}) (result []byte, err *ErrorObj) {
if err = validateID(id); err != nil {
return
}
p := &RPC{
Version: jsonRPCVersion,
Method: method,
ID: id,
}
if len(args) > 0 {
p.Params = args[0]
}
return marshal(p)
}
// Notification return a JSON-RPC 2.0 notification message structures without id.
func Notification(method string, args ...interface{}) ([]byte, *ErrorObj) {
return Request(nil, method, args...)
}
//Batch return a JSON-RPC 2.0 batch message structures.
func Batch(batch ...[]byte) (arrstr []byte) {
if len(batch) == 0 {
return []byte("[]")
}
arrstr = []byte{'['}
for _, item := range batch {
arrstr = append(arrstr, item...)
arrstr = append(arrstr, ',')
}
arrstr = arrstr[0 : len(arrstr)-1]
arrstr = append(arrstr, ']')
return
}
// Success return a JSON-RPC 2.0 success message structures.
// The result parameter is required
func Success(id interface{}, msg interface{}) (result []byte, err *ErrorObj) {
if msg == nil {
return result, InternalError()
}
if err = validateID(id); err != nil {
return
}
p := &RPC{
Version: jsonRPCVersion,
Result: msg,
ID: id,
}
return marshal(p)
}
//Error return a JSON-RPC 2.0 error message structures.
func Error(id interface{}, rpcerr *ErrorObj) (result []byte, err *ErrorObj) {
if err = validateID(id); err != nil {
return
}
p := &RPC{
Version: jsonRPCVersion,
Error: rpcerr,
ID: id,
}
return marshal(p)
}
// ErrorFrom return an ErrorObj by native error
func ErrorFrom(err error) (obj *ErrorObj) {
return &ErrorObj{Code: -32701, Message: err.Error()}
}
// ErrorWith return an ErrorObj by arguments
func ErrorWith(code int, msg string, data ...interface{}) (obj *ErrorObj) {
obj = &ErrorObj{Code: code, Message: msg}
if len(data) > 0 {
obj.Data = data[0]
}
return obj
}
// ParseError invalid JSON was received by the server.An error occurred on the server while parsing the JSON text.
func ParseError(data ...interface{}) *ErrorObj {
return ErrorWith(-32700, "Parse error", data...)
}
// InvalidRequest the request is not a valid Request object.
func InvalidRequest(data ...interface{}) *ErrorObj {
return ErrorWith(-32600, "Invalid Request", data...)
}
// MethodNotFound the method does not exist or is not available.
func MethodNotFound(data ...interface{}) *ErrorObj {
return ErrorWith(-32601, "Method not found", data...)
}
// InvalidParams Invalid method parameter(s).
func InvalidParams(data ...interface{}) *ErrorObj {
return ErrorWith(-32602, "Invalid params", data...)
}
// InternalError Internal JSON-RPC error.
func InternalError(data ...interface{}) *ErrorObj {
return ErrorWith(-32603, "Internal error", data...)
}
// ParseString ...
func ParseString(msg string) (req *RPC, batch []*RPC) {
return Parse([]byte(msg))
}
// Parse return jsonrpc 2.0 message object, ignore the first return value if the msg is batch rpc.
func Parse(msg []byte) (req *RPC, batch []*RPC) {
if bytes.HasPrefix(msg, []byte{'['}) && bytes.HasSuffix(msg, []byte{']'}) {
batch = make([]*RPC, 1)
if err := validateMsg(msg, &batch); err == nil {
for _, val := range batch {
parse(val)
}
} else {
req := &RPC{}
req.Error = err
req.Type = InvalidType
batch[0] = req
}
return nil, batch
}
req = &RPC{}
if err := validateMsg(msg, req); err != nil {
req.Error = err
req.Type = InvalidType
} else {
parse(req)
}
return req, nil
}
func parse(r *RPC) {
if r.Version != jsonRPCVersion {
r.Type = InvalidType
r.Error = InvalidRequest()
return
}
if err := validateID(r.ID); err != nil {
r.Error = err
return
}
if r.Method != "" { //Request
if r.ID == nil {
r.Type = NotificationType
} else {
r.Type = RequestType
}
} else {
if r.Error != nil { // Response
r.Type = ErrorType
} else if r.Result != nil {
r.Type = SuccessType
} else {
r.Type = InvalidType
r.Error = InvalidRequest()
}
}
return
}
func validateID(id interface{}) (err *ErrorObj) {
if id != nil {
switch id.(type) {
case string:
case int:
default:
err = InternalError()
}
}
return
}
func validateMsg(msg []byte, p interface{}) *ErrorObj {
if len(msg) < 1 {
return InvalidRequest()
}
err := json.Unmarshal(msg, p)
if err != nil {
return ParseError(err.Error())
}
return nil
}
func marshal(v interface{}) ([]byte, *ErrorObj) {
data, errs := json.Marshal(v)
if errs != nil {
return nil, InternalError(errs.Error())
}
return data, nil
}