github.com/aquanetwork/aquachain@v1.7.8/rpc/json.go (about) 1 // Copyright 2015 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The aquachain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 "io" 24 "reflect" 25 "strconv" 26 "strings" 27 "sync" 28 29 "gitlab.com/aquachain/aquachain/common/log" 30 ) 31 32 const ( 33 jsonrpcVersion = "2.0" 34 serviceMethodSeparator = "_" 35 subscribeMethodSuffix = "_subscribe" 36 unsubscribeMethodSuffix = "_unsubscribe" 37 notificationMethodSuffix = "_subscription" 38 ) 39 40 type jsonRequest struct { 41 Method string `json:"method"` 42 Version string `json:"jsonrpc"` 43 Id json.RawMessage `json:"id,omitempty"` 44 Payload json.RawMessage `json:"params,omitempty"` 45 } 46 47 type jsonSuccessResponse struct { 48 Version string `json:"jsonrpc"` 49 Id interface{} `json:"id,omitempty"` 50 Result interface{} `json:"result"` 51 } 52 53 type jsonError struct { 54 Code int `json:"code"` 55 Message string `json:"message"` 56 Data interface{} `json:"data,omitempty"` 57 } 58 59 type jsonErrResponse struct { 60 Version string `json:"jsonrpc"` 61 Id interface{} `json:"id,omitempty"` 62 Error jsonError `json:"error"` 63 } 64 65 type jsonSubscription struct { 66 Subscription string `json:"subscription"` 67 Result interface{} `json:"result,omitempty"` 68 } 69 70 type jsonNotification struct { 71 Version string `json:"jsonrpc"` 72 Method string `json:"method"` 73 Params jsonSubscription `json:"params"` 74 } 75 76 // jsonCodec reads and writes JSON-RPC messages to the underlying connection. It 77 // also has support for parsing arguments and serializing (result) objects. 78 type jsonCodec struct { 79 closer sync.Once // close closed channel once 80 closed chan interface{} // closed on Close 81 decMu sync.Mutex // guards d 82 decode func(v interface{}) error // decodes incoming requests 83 encMu sync.Mutex // guards e 84 encode func(v interface{}) error // encodes responses 85 rw io.ReadWriteCloser // connection 86 } 87 88 func (err *jsonError) Error() string { 89 if err.Message == "" { 90 return fmt.Sprintf("json-rpc error %d", err.Code) 91 } 92 return err.Message 93 } 94 95 func (err *jsonError) ErrorCode() int { 96 return err.Code 97 } 98 99 // NewCodec creates a new RPC server codec with support for JSON-RPC 2.0 based 100 // on explicitly given encoding and decoding methods. 101 func NewCodec(rwc io.ReadWriteCloser, encode, decode func(v interface{}) error) ServerCodec { 102 return &jsonCodec{ 103 closed: make(chan interface{}), 104 encode: encode, 105 decode: decode, 106 rw: rwc, 107 } 108 } 109 110 // NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0 111 func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec { 112 enc := json.NewEncoder(rwc) 113 dec := json.NewDecoder(rwc) 114 dec.UseNumber() 115 116 return &jsonCodec{ 117 closed: make(chan interface{}), 118 encode: enc.Encode, 119 decode: dec.Decode, 120 rw: rwc, 121 } 122 } 123 124 // isBatch returns true when the first non-whitespace characters is '[' 125 func isBatch(msg json.RawMessage) bool { 126 for _, c := range msg { 127 // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) 128 if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { 129 continue 130 } 131 return c == '[' 132 } 133 return false 134 } 135 136 // ReadRequestHeaders will read new requests without parsing the arguments. It will 137 // return a collection of requests, an indication if these requests are in batch 138 // form or an error when the incoming message could not be read/parsed. 139 func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, Error) { 140 c.decMu.Lock() 141 defer c.decMu.Unlock() 142 143 var incomingMsg json.RawMessage 144 if err := c.decode(&incomingMsg); err != nil { 145 return nil, false, &invalidRequestError{err.Error()} 146 } 147 148 if isBatch(incomingMsg) { 149 return parseBatchRequest(incomingMsg) 150 } 151 152 return parseRequest(incomingMsg) 153 } 154 155 // checkReqId returns an error when the given reqId isn't valid for RPC method calls. 156 // valid id's are strings, numbers or null 157 func checkReqId(reqId json.RawMessage) error { 158 if len(reqId) == 0 { 159 return fmt.Errorf("missing request id") 160 } 161 if _, err := strconv.ParseFloat(string(reqId), 64); err == nil { 162 return nil 163 } 164 var str string 165 if err := json.Unmarshal(reqId, &str); err == nil { 166 return nil 167 } 168 return fmt.Errorf("invalid request id") 169 } 170 171 // parseRequest will parse a single request from the given RawMessage. It will return 172 // the parsed request, an indication if the request was a batch or an error when 173 // the request could not be parsed. 174 func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { 175 var in jsonRequest 176 if err := json.Unmarshal(incomingMsg, &in); err != nil { 177 return nil, false, &invalidMessageError{err.Error()} 178 } 179 180 if err := checkReqId(in.Id); err != nil { 181 return nil, false, &invalidMessageError{err.Error()} 182 } 183 184 log.Debug("handling rpc request", "method", in.Method, "params", string(in.Payload)) 185 186 // try keeping eth compatibility 187 if strings.Contains(in.Method, "eth_") { 188 in.Method = strings.Replace(in.Method, "eth_", "aqua_", -1) 189 } 190 191 // subscribe are special, they will always use `subscribeMethod` as first param in the payload 192 if strings.HasSuffix(in.Method, subscribeMethodSuffix) { 193 reqs := []rpcRequest{{id: &in.Id, isPubSub: true}} 194 if len(in.Payload) > 0 { 195 // first param must be subscription name 196 var subscribeMethod [1]string 197 if err := json.Unmarshal(in.Payload, &subscribeMethod); err != nil { 198 log.Debug(fmt.Sprintf("Unable to parse subscription method: %v\n", err)) 199 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 200 } 201 202 reqs[0].service, reqs[0].method = strings.TrimSuffix(in.Method, subscribeMethodSuffix), subscribeMethod[0] 203 reqs[0].params = in.Payload 204 return reqs, false, nil 205 } 206 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 207 } 208 209 if strings.HasSuffix(in.Method, unsubscribeMethodSuffix) { 210 return []rpcRequest{{id: &in.Id, isPubSub: true, 211 method: in.Method, params: in.Payload}}, false, nil 212 } 213 214 elems := strings.Split(in.Method, serviceMethodSeparator) 215 if len(elems) != 2 { 216 return nil, false, &methodNotFoundError{in.Method, ""} 217 } 218 219 // regular RPC call 220 if len(in.Payload) == 0 { 221 return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id}}, false, nil 222 } 223 224 return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id, params: in.Payload}}, false, nil 225 } 226 227 // parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication 228 // if the request was a batch or an error when the request could not be read. 229 func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { 230 var in []jsonRequest 231 if err := json.Unmarshal(incomingMsg, &in); err != nil { 232 return nil, false, &invalidMessageError{err.Error()} 233 } 234 235 requests := make([]rpcRequest, len(in)) 236 for i, r := range in { 237 if err := checkReqId(r.Id); err != nil { 238 return nil, false, &invalidMessageError{err.Error()} 239 } 240 241 id := &in[i].Id 242 243 // subscribe are special, they will always use `subscriptionMethod` as first param in the payload 244 if strings.HasSuffix(r.Method, subscribeMethodSuffix) { 245 requests[i] = rpcRequest{id: id, isPubSub: true} 246 if len(r.Payload) > 0 { 247 // first param must be subscription name 248 var subscribeMethod [1]string 249 if err := json.Unmarshal(r.Payload, &subscribeMethod); err != nil { 250 log.Debug(fmt.Sprintf("Unable to parse subscription method: %v\n", err)) 251 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 252 } 253 254 requests[i].service, requests[i].method = strings.TrimSuffix(r.Method, subscribeMethodSuffix), subscribeMethod[0] 255 requests[i].params = r.Payload 256 continue 257 } 258 259 return nil, true, &invalidRequestError{"Unable to parse (un)subscribe request arguments"} 260 } 261 262 if strings.HasSuffix(r.Method, unsubscribeMethodSuffix) { 263 requests[i] = rpcRequest{id: id, isPubSub: true, method: r.Method, params: r.Payload} 264 continue 265 } 266 267 if len(r.Payload) == 0 { 268 requests[i] = rpcRequest{id: id, params: nil} 269 } else { 270 requests[i] = rpcRequest{id: id, params: r.Payload} 271 } 272 if elem := strings.Split(r.Method, serviceMethodSeparator); len(elem) == 2 { 273 requests[i].service, requests[i].method = elem[0], elem[1] 274 } else { 275 requests[i].err = &methodNotFoundError{r.Method, ""} 276 } 277 } 278 279 return requests, true, nil 280 } 281 282 // ParseRequestArguments tries to parse the given params (json.RawMessage) with the given 283 // types. It returns the parsed values or an error when the parsing failed. 284 func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error) { 285 if args, ok := params.(json.RawMessage); !ok { 286 return nil, &invalidParamsError{"Invalid params supplied"} 287 } else { 288 return parsePositionalArguments(args, argTypes) 289 } 290 } 291 292 // parsePositionalArguments tries to parse the given args to an array of values with the 293 // given types. It returns the parsed values or an error when the args could not be 294 // parsed. Missing optional arguments are returned as reflect.Zero values. 295 func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, Error) { 296 // Read beginning of the args array. 297 dec := json.NewDecoder(bytes.NewReader(rawArgs)) 298 if tok, _ := dec.Token(); tok != json.Delim('[') { 299 return nil, &invalidParamsError{"non-array args"} 300 } 301 // Read args. 302 args := make([]reflect.Value, 0, len(types)) 303 for i := 0; dec.More(); i++ { 304 if i >= len(types) { 305 return nil, &invalidParamsError{fmt.Sprintf("too many arguments, want at most %d", len(types))} 306 } 307 argval := reflect.New(types[i]) 308 if err := dec.Decode(argval.Interface()); err != nil { 309 return nil, &invalidParamsError{fmt.Sprintf("invalid argument %d: %v", i, err)} 310 } 311 if argval.IsNil() && types[i].Kind() != reflect.Ptr { 312 return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} 313 } 314 args = append(args, argval.Elem()) 315 } 316 // Read end of args array. 317 if _, err := dec.Token(); err != nil { 318 return nil, &invalidParamsError{err.Error()} 319 } 320 // Set any missing args to nil. 321 for i := len(args); i < len(types); i++ { 322 if types[i].Kind() != reflect.Ptr { 323 return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} 324 } 325 args = append(args, reflect.Zero(types[i])) 326 } 327 return args, nil 328 } 329 330 // CreateResponse will create a JSON-RPC success response with the given id and reply as result. 331 func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} { 332 if isHexNum(reflect.TypeOf(reply)) { 333 return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} 334 } 335 return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: reply} 336 } 337 338 // CreateErrorResponse will create a JSON-RPC error response with the given id and error. 339 func (c *jsonCodec) CreateErrorResponse(id interface{}, err Error) interface{} { 340 return &jsonErrResponse{Version: jsonrpcVersion, Id: id, Error: jsonError{Code: err.ErrorCode(), Message: err.Error()}} 341 } 342 343 // CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error. 344 // info is optional and contains additional information about the error. When an empty string is passed it is ignored. 345 func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{} { 346 return &jsonErrResponse{Version: jsonrpcVersion, Id: id, 347 Error: jsonError{Code: err.ErrorCode(), Message: err.Error(), Data: info}} 348 } 349 350 // CreateNotification will create a JSON-RPC notification with the given subscription id and event as params. 351 func (c *jsonCodec) CreateNotification(subid, namespace string, event interface{}) interface{} { 352 if isHexNum(reflect.TypeOf(event)) { 353 return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix, 354 Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}} 355 } 356 357 return &jsonNotification{Version: jsonrpcVersion, Method: namespace + notificationMethodSuffix, 358 Params: jsonSubscription{Subscription: subid, Result: event}} 359 } 360 361 // Write message to client 362 func (c *jsonCodec) Write(res interface{}) error { 363 c.encMu.Lock() 364 defer c.encMu.Unlock() 365 366 return c.encode(res) 367 } 368 369 // Close the underlying connection 370 func (c *jsonCodec) Close() { 371 c.closer.Do(func() { 372 close(c.closed) 373 c.rw.Close() 374 }) 375 } 376 377 // Closed returns a channel which will be closed when Close is called 378 func (c *jsonCodec) Closed() <-chan interface{} { 379 return c.closed 380 }