github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/pkg/gethrpc/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 "context" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "reflect" 27 "strings" 28 "sync" 29 "time" 30 ) 31 32 const ( 33 vsn = "2.0" 34 serviceMethodSeparator = "_" 35 36 defaultWriteTimeout = 10 * time.Second // used if context has no deadline 37 ) 38 39 var null = json.RawMessage("null") 40 41 type subscriptionResult struct { 42 ID string `json:"subscription"` 43 Result json.RawMessage `json:"result,omitempty"` 44 } 45 46 // A value of this type can a JSON-RPC request, notification, successful response or 47 // error response. Which one it is depends on the fields. 48 type jsonrpcMessage struct { 49 Version string `json:"jsonrpc,omitempty"` 50 ID json.RawMessage `json:"id,omitempty"` 51 Method string `json:"method,omitempty"` 52 Params json.RawMessage `json:"params,omitempty"` 53 Error *jsonError `json:"error,omitempty"` 54 Result json.RawMessage `json:"result,omitempty"` 55 } 56 57 func (msg *jsonrpcMessage) isNotification() bool { 58 return msg.ID == nil && msg.Method != "" 59 } 60 61 func (msg *jsonrpcMessage) isCall() bool { 62 return msg.hasValidID() && msg.Method != "" 63 } 64 65 func (msg *jsonrpcMessage) isResponse() bool { 66 return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) 67 } 68 69 func (msg *jsonrpcMessage) hasValidID() bool { 70 return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' 71 } 72 73 func (msg *jsonrpcMessage) namespace() string { 74 elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2) 75 return elem[0] 76 } 77 78 func (msg *jsonrpcMessage) String() string { 79 b, _ := json.Marshal(msg) 80 return string(b) 81 } 82 83 func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { 84 resp := errorMessage(err) 85 resp.ID = msg.ID 86 return resp 87 } 88 89 func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { 90 enc, err := json.Marshal(result) 91 if err != nil { 92 // TODO: wrap with 'internal server error' 93 return msg.errorResponse(err) 94 } 95 return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} 96 } 97 98 func errorMessage(err error) *jsonrpcMessage { 99 msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ 100 Code: defaultErrorCode, 101 Message: err.Error(), 102 }} 103 ec, ok := err.(Error) 104 if ok { 105 msg.Error.Code = ec.ErrorCode() 106 } 107 return msg 108 } 109 110 type jsonError struct { 111 Code int `json:"code"` 112 Message string `json:"message"` 113 Data interface{} `json:"data,omitempty"` 114 } 115 116 func (err *jsonError) Error() string { 117 if err.Message == "" { 118 return fmt.Sprintf("json-rpc error %d", err.Code) 119 } 120 return err.Message 121 } 122 123 func (err *jsonError) ErrorCode() int { 124 return err.Code 125 } 126 127 // Conn is a subset of the methods of net.Conn which are sufficient for ServerCodec. 128 type Conn interface { 129 io.ReadWriteCloser 130 SetWriteDeadline(time.Time) error 131 } 132 133 type deadlineCloser interface { 134 io.Closer 135 SetWriteDeadline(time.Time) error 136 } 137 138 // ConnRemoteAddr wraps the RemoteAddr operation, which returns a description 139 // of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this 140 // description is used in log messages. 141 type ConnRemoteAddr interface { 142 RemoteAddr() string 143 } 144 145 // connWithRemoteAddr overrides the remote address of a connection. 146 type connWithRemoteAddr struct { 147 Conn 148 addr string 149 } 150 151 func (c connWithRemoteAddr) RemoteAddr() string { return c.addr } 152 153 // jsonCodec reads and writes JSON-RPC messages to the underlying connection. It also has 154 // support for parsing arguments and serializing (result) objects. 155 type jsonCodec struct { 156 remoteAddr string 157 closer sync.Once // close closed channel once 158 closed chan interface{} // closed on Close 159 decode func(v interface{}) error // decoder to allow multiple transports 160 encMu sync.Mutex // guards the encoder 161 encode func(v interface{}) error // encoder to allow multiple transports 162 conn deadlineCloser 163 } 164 165 func newCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec { 166 codec := &jsonCodec{ 167 closed: make(chan interface{}), 168 encode: encode, 169 decode: decode, 170 conn: conn, 171 } 172 if ra, ok := conn.(ConnRemoteAddr); ok { 173 codec.remoteAddr = ra.RemoteAddr() 174 } 175 return codec 176 } 177 178 // NewJSONCodec creates a codec that reads from the given connection. If conn implements 179 // ConnRemoteAddr, log messages will use it to include the remote address of the 180 // connection. 181 func NewJSONCodec(conn Conn) ServerCodec { 182 enc := json.NewEncoder(conn) 183 dec := json.NewDecoder(conn) 184 dec.UseNumber() 185 return newCodec(conn, enc.Encode, dec.Decode) 186 } 187 188 func (c *jsonCodec) RemoteAddr() string { 189 return c.remoteAddr 190 } 191 192 func (c *jsonCodec) Read() (msg []*jsonrpcMessage, batch bool, err error) { 193 // Decode the next JSON object in the input stream. 194 // This verifies basic syntax, etc. 195 var rawmsg json.RawMessage 196 if err := c.decode(&rawmsg); err != nil { 197 return nil, false, err 198 } 199 msg, batch = parseMessage(rawmsg) 200 return msg, batch, nil 201 } 202 203 // Write sends a message to client. 204 func (c *jsonCodec) Write(ctx context.Context, v interface{}) error { 205 c.encMu.Lock() 206 defer c.encMu.Unlock() 207 208 deadline, ok := ctx.Deadline() 209 if !ok { 210 deadline = time.Now().Add(defaultWriteTimeout) 211 } 212 c.conn.SetWriteDeadline(deadline) 213 return c.encode(v) 214 } 215 216 // Close the underlying connection 217 func (c *jsonCodec) Close() { 218 c.closer.Do(func() { 219 close(c.closed) 220 c.conn.Close() 221 }) 222 } 223 224 // Closed returns a channel which will be closed when Close is called 225 func (c *jsonCodec) Closed() <-chan interface{} { 226 return c.closed 227 } 228 229 // parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error 230 // checks in this function because the raw message has already been syntax-checked when it 231 // is called. Any non-JSON-RPC messages in the input return the zero value of 232 // jsonrpcMessage. 233 func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { 234 if !isBatch(raw) { 235 msgs := []*jsonrpcMessage{{}} 236 json.Unmarshal(raw, &msgs[0]) 237 return msgs, false 238 } 239 dec := json.NewDecoder(bytes.NewReader(raw)) 240 dec.Token() // skip '[' 241 var msgs []*jsonrpcMessage 242 for dec.More() { 243 msgs = append(msgs, new(jsonrpcMessage)) 244 dec.Decode(&msgs[len(msgs)-1]) 245 } 246 return msgs, true 247 } 248 249 // isBatch returns true when the first non-whitespace characters is '[' 250 func isBatch(raw json.RawMessage) bool { 251 for _, c := range raw { 252 // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) 253 if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { 254 continue 255 } 256 return c == '[' 257 } 258 return false 259 } 260 261 // parsePositionalArguments tries to parse the given args to an array of values with the 262 // given types. It returns the parsed values or an error when the args could not be 263 // parsed. Missing optional arguments are returned as reflect.Zero values. 264 func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) { 265 dec := json.NewDecoder(bytes.NewReader(rawArgs)) 266 var args []reflect.Value 267 tok, err := dec.Token() 268 switch { 269 case err == io.EOF || tok == nil && err == nil: 270 // "params" is optional and may be empty. Also allow "params":null even though it's 271 // not in the spec because our own client used to send it. 272 case err != nil: 273 return nil, err 274 case tok == json.Delim('['): 275 // Read argument array. 276 if args, err = parseArgumentArray(dec, types); err != nil { 277 return nil, err 278 } 279 default: 280 return nil, errors.New("non-array args") 281 } 282 // Set any missing args to nil. 283 for i := len(args); i < len(types); i++ { 284 if types[i].Kind() != reflect.Ptr { 285 return nil, fmt.Errorf("missing value for required argument %d", i) 286 } 287 args = append(args, reflect.Zero(types[i])) 288 } 289 return args, nil 290 } 291 292 func parseArgumentArray(dec *json.Decoder, types []reflect.Type) ([]reflect.Value, error) { 293 args := make([]reflect.Value, 0, len(types)) 294 for i := 0; dec.More(); i++ { 295 if i >= len(types) { 296 return args, fmt.Errorf("too many arguments, want at most %d", len(types)) 297 } 298 argval := reflect.New(types[i]) 299 if err := dec.Decode(argval.Interface()); err != nil { 300 return args, fmt.Errorf("invalid argument %d: %v", i, err) 301 } 302 if argval.IsNil() && types[i].Kind() != reflect.Ptr { 303 return args, fmt.Errorf("missing value for required argument %d", i) 304 } 305 args = append(args, argval.Elem()) 306 } 307 // Read end of args array. 308 _, err := dec.Token() 309 return args, err 310 } 311 312 // parseSubscriptionName extracts the subscription name from an encoded argument array. 313 func parseSubscriptionName(rawArgs json.RawMessage) (string, error) { 314 dec := json.NewDecoder(bytes.NewReader(rawArgs)) 315 if tok, _ := dec.Token(); tok != json.Delim('[') { 316 return "", errors.New("non-array args") 317 } 318 v, _ := dec.Token() 319 method, ok := v.(string) 320 if !ok { 321 return "", errors.New("expected subscription name as first argument") 322 } 323 return method, nil 324 }