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