github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/rpc/json.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "reflect" 24 "strconv" 25 "strings" 26 "sync" 27 28 "github.com/ethereumproject/go-ethereum/logger" 29 "github.com/ethereumproject/go-ethereum/logger/glog" 30 ) 31 32 const ( 33 JSONRPCVersion = "2.0" 34 serviceMethodSeparator = "_" 35 subscribeMethod = "eth_subscribe" 36 unsubscribeMethod = "eth_unsubscribe" 37 notificationMethod = "eth_subscription" 38 ) 39 40 // JSON-RPC request 41 type JSONRequest struct { 42 Method string `json:"method"` 43 Version string `json:"jsonrpc"` 44 Id json.RawMessage `json:"id,omitempty"` 45 Payload json.RawMessage `json:"params,omitempty"` 46 } 47 48 // JSON-RPC response 49 type JSONResponse struct { 50 Version string `json:"jsonrpc"` 51 Id interface{} `json:"id,omitempty"` 52 Result interface{} `json:"result,omitempty"` 53 Error *JSONError `json:"error,omitempty"` 54 } 55 56 // JSON-RPC error object 57 type JSONError struct { 58 Code int `json:"code"` 59 Message string `json:"message"` 60 Data interface{} `json:"data,omitempty"` 61 } 62 63 // JSON-RPC notification payload 64 type jsonSubscription struct { 65 Subscription string `json:"subscription"` 66 Result interface{} `json:"result,omitempty"` 67 } 68 69 // JSON-RPC notification 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 d *json.Decoder // decodes incoming requests 83 encMu sync.Mutex // guards e 84 e *json.Encoder // encodes responses 85 rw io.ReadWriteCloser // connection 86 } 87 88 // NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0 89 func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec { 90 d := json.NewDecoder(rwc) 91 d.UseNumber() 92 return &jsonCodec{closed: make(chan interface{}), d: d, e: json.NewEncoder(rwc), rw: rwc} 93 } 94 95 // isBatch returns true when the first non-whitespace characters is '[' 96 func isBatch(msg json.RawMessage) bool { 97 for _, c := range msg { 98 // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) 99 if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { 100 continue 101 } 102 return c == '[' 103 } 104 return false 105 } 106 107 // ReadRequestHeaders will read new requests without parsing the arguments. It will 108 // return a collection of requests, an indication if these requests are in batch 109 // form or an error when the incoming message could not be read/parsed. 110 func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, RPCError) { 111 c.decMu.Lock() 112 defer c.decMu.Unlock() 113 114 var incomingMsg json.RawMessage 115 if err := c.d.Decode(&incomingMsg); err != nil { 116 return nil, false, &invalidRequestError{err.Error()} 117 } 118 119 if isBatch(incomingMsg) { 120 return parseBatchRequest(incomingMsg) 121 } 122 123 return parseRequest(incomingMsg) 124 } 125 126 // checkReqId returns an error when the given reqId isn't valid for RPC method calls. 127 // valid id's are strings, numbers or null 128 func checkReqId(reqId json.RawMessage) error { 129 if len(reqId) == 0 { 130 return fmt.Errorf("missing request id") 131 } 132 if _, err := strconv.ParseFloat(string(reqId), 64); err == nil { 133 return nil 134 } 135 var str string 136 if err := json.Unmarshal(reqId, &str); err == nil { 137 return nil 138 } 139 return fmt.Errorf("invalid request id") 140 } 141 142 // parseRequest will parse a single request from the given RawMessage. It will return 143 // the parsed request, an indication if the request was a batch or an error when 144 // the request could not be parsed. 145 func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) { 146 var in JSONRequest 147 if err := json.Unmarshal(incomingMsg, &in); err != nil { 148 return nil, false, &invalidMessageError{err.Error()} 149 } 150 151 if err := checkReqId(in.Id); err != nil { 152 return nil, false, &invalidMessageError{err.Error()} 153 } 154 155 // subscribe are special, they will always use `subscribeMethod` as first param in the payload 156 if in.Method == subscribeMethod { 157 reqs := []rpcRequest{{id: &in.Id, isPubSub: true}} 158 if len(in.Payload) > 0 { 159 // first param must be subscription name 160 var subscribeMethod [1]string 161 if err := json.Unmarshal(in.Payload, &subscribeMethod); err != nil { 162 glog.V(logger.Debug).Infof("Unable to parse subscription method: %v\n", err) 163 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 164 } 165 166 // all subscriptions are made on the eth service 167 reqs[0].service, reqs[0].method = "eth", subscribeMethod[0] 168 reqs[0].params = in.Payload 169 return reqs, false, nil 170 } 171 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 172 } 173 174 if in.Method == unsubscribeMethod { 175 return []rpcRequest{{id: &in.Id, isPubSub: true, 176 method: unsubscribeMethod, params: in.Payload}}, false, nil 177 } 178 179 // regular RPC call 180 elems := strings.Split(in.Method, serviceMethodSeparator) 181 if len(elems) != 2 { 182 return nil, false, &methodNotFoundError{in.Method, ""} 183 } 184 185 if len(in.Payload) == 0 { 186 return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id}}, false, nil 187 } 188 189 return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id, params: in.Payload}}, false, nil 190 } 191 192 // parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication 193 // if the request was a batch or an error when the request could not be read. 194 func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, RPCError) { 195 var in []JSONRequest 196 if err := json.Unmarshal(incomingMsg, &in); err != nil { 197 return nil, false, &invalidMessageError{err.Error()} 198 } 199 200 requests := make([]rpcRequest, len(in)) 201 for i, r := range in { 202 if err := checkReqId(r.Id); err != nil { 203 return nil, false, &invalidMessageError{err.Error()} 204 } 205 206 id := &in[i].Id 207 208 // subscribe are special, they will always use `subscribeMethod` as first param in the payload 209 if r.Method == subscribeMethod { 210 requests[i] = rpcRequest{id: id, isPubSub: true} 211 if len(r.Payload) > 0 { 212 // first param must be subscription name 213 var subscribeMethod [1]string 214 if err := json.Unmarshal(r.Payload, &subscribeMethod); err != nil { 215 glog.V(logger.Debug).Infof("Unable to parse subscription method: %v\n", err) 216 return nil, false, &invalidRequestError{"Unable to parse subscription request"} 217 } 218 219 // all subscriptions are made on the eth service 220 requests[i].service, requests[i].method = "eth", subscribeMethod[0] 221 requests[i].params = r.Payload 222 continue 223 } 224 225 return nil, true, &invalidRequestError{"Unable to parse (un)subscribe request arguments"} 226 } 227 228 if r.Method == unsubscribeMethod { 229 requests[i] = rpcRequest{id: id, isPubSub: true, method: unsubscribeMethod, params: r.Payload} 230 continue 231 } 232 233 elems := strings.Split(r.Method, serviceMethodSeparator) 234 if len(elems) != 2 { 235 return nil, true, &methodNotFoundError{r.Method, ""} 236 } 237 238 if len(r.Payload) == 0 { 239 requests[i] = rpcRequest{service: elems[0], method: elems[1], id: id, params: nil} 240 } else { 241 requests[i] = rpcRequest{service: elems[0], method: elems[1], id: id, params: r.Payload} 242 } 243 } 244 245 return requests, true, nil 246 } 247 248 // ParseRequestArguments tries to parse the given params (json.RawMessage) with the given types. It returns the parsed 249 // values or an error when the parsing failed. 250 func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, RPCError) { 251 if args, ok := params.(json.RawMessage); !ok { 252 return nil, &invalidParamsError{"Invalid params supplied"} 253 } else { 254 return parsePositionalArguments(args, argTypes) 255 } 256 } 257 258 // parsePositionalArguments tries to parse the given args to an array of values with the given types. 259 // It returns the parsed values or an error when the args could not be parsed. Missing optional arguments 260 // are returned as reflect.Zero values. 261 func parsePositionalArguments(args json.RawMessage, callbackArgs []reflect.Type) ([]reflect.Value, RPCError) { 262 params := make([]interface{}, 0, len(callbackArgs)) 263 for _, t := range callbackArgs { 264 params = append(params, reflect.New(t).Interface()) 265 } 266 267 if err := json.Unmarshal(args, ¶ms); err != nil { 268 return nil, &invalidParamsError{err.Error()} 269 } 270 271 if len(params) > len(callbackArgs) { 272 return nil, &invalidParamsError{fmt.Sprintf("too many params, want %d got %d", len(callbackArgs), len(params))} 273 } 274 275 // assume missing params are null values 276 for i := len(params); i < len(callbackArgs); i++ { 277 params = append(params, nil) 278 } 279 280 argValues := make([]reflect.Value, len(params)) 281 for i, p := range params { 282 // verify that JSON null values are only supplied for optional arguments (ptr types) 283 if p == nil && callbackArgs[i].Kind() != reflect.Ptr { 284 return nil, &invalidParamsError{fmt.Sprintf("invalid or missing value for params[%d]", i)} 285 } 286 if p == nil { 287 argValues[i] = reflect.Zero(callbackArgs[i]) 288 } else { // deref pointers values creates previously with reflect.New 289 argValues[i] = reflect.ValueOf(p).Elem() 290 } 291 } 292 293 return argValues, nil 294 } 295 296 // CreateResponse will create a JSON-RPC success response with the given id and reply as result. 297 func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} { 298 if isHexNum(reflect.TypeOf(reply)) { 299 return &JSONResponse{Version: JSONRPCVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} 300 } 301 return &JSONResponse{Version: JSONRPCVersion, Id: id, Result: reply} 302 } 303 304 // CreateErrorResponse will create a JSON-RPC error response with the given id and error. 305 func (c *jsonCodec) CreateErrorResponse(id interface{}, err RPCError) interface{} { 306 return &JSONResponse{Version: JSONRPCVersion, Id: id, Error: &JSONError{Code: err.Code(), Message: err.Error()}} 307 } 308 309 // CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error. 310 // info is optional and contains additional information about the error. When an empty string is passed it is ignored. 311 func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err RPCError, info interface{}) interface{} { 312 return &JSONResponse{Version: JSONRPCVersion, Id: id, 313 Error: &JSONError{Code: err.Code(), Message: err.Error(), Data: info}} 314 } 315 316 // CreateNotification will create a JSON-RPC notification with the given subscription id and event as params. 317 func (c *jsonCodec) CreateNotification(subid string, event interface{}) interface{} { 318 if isHexNum(reflect.TypeOf(event)) { 319 return &jsonNotification{Version: JSONRPCVersion, Method: notificationMethod, 320 Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}} 321 } 322 323 return &jsonNotification{Version: JSONRPCVersion, Method: notificationMethod, 324 Params: jsonSubscription{Subscription: subid, Result: event}} 325 } 326 327 // Write message to client 328 func (c *jsonCodec) Write(res interface{}) error { 329 c.encMu.Lock() 330 defer c.encMu.Unlock() 331 332 return c.e.Encode(res) 333 } 334 335 // Close the underlying connection 336 func (c *jsonCodec) Close() { 337 c.closer.Do(func() { 338 close(c.closed) 339 c.rw.Close() 340 }) 341 } 342 343 // Closed returns a channel which will be closed when Close is called 344 func (c *jsonCodec) Closed() <-chan interface{} { 345 return c.closed 346 }