// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package multisend import ( "encoding/json" "fmt" "strings" "time" ) const ( vsn = "2.0" serviceMethodSeparator = "_" subscribeMethodSuffix = "_subscribe" unsubscribeMethodSuffix = "_unsubscribe" notificationMethodSuffix = "_subscription" defaultWriteTimeout = 10 * time.Second // used if context has no deadline ) const defaultErrorCode = -32000 // Error wraps RPC errors, which contain an error code in addition to the message. type Error interface { Error() string // returns the message ErrorCode() int // returns the code } // A DataError contains some data in addition to the error message. type DataError interface { Error() string // returns the message ErrorData() interface{} // returns the error data } var null = json.RawMessage("null") type subscriptionResult struct { ID string `json:"subscription"` Result json.RawMessage `json:"result,omitempty"` } // A value of this type can a JSON-RPC request, notification, successful response or // error response. Which one it is depends on the fields. type jsonrpcMessage struct { Version string `json:"jsonrpc,omitempty"` ID json.RawMessage `json:"id,omitempty"` Method string `json:"method,omitempty"` Params json.RawMessage `json:"params,omitempty"` Error *jsonError `json:"error,omitempty"` Result json.RawMessage `json:"result,omitempty"` } func (msg *jsonrpcMessage) isNotification() bool { return msg.ID == nil && msg.Method != "" } func (msg *jsonrpcMessage) isCall() bool { return msg.hasValidID() && msg.Method != "" } func (msg *jsonrpcMessage) isResponse() bool { return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) } func (msg *jsonrpcMessage) hasValidID() bool { return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' } func (msg *jsonrpcMessage) isSubscribe() bool { return strings.HasSuffix(msg.Method, subscribeMethodSuffix) } func (msg *jsonrpcMessage) isUnsubscribe() bool { return strings.HasSuffix(msg.Method, unsubscribeMethodSuffix) } func (msg *jsonrpcMessage) namespace() string { elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2) return elem[0] } func (msg *jsonrpcMessage) String() string { b, _ := json.Marshal(msg) return string(b) } func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { resp := errorMessage(err) resp.ID = msg.ID return resp } func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { enc, err := json.Marshal(result) if err != nil { // TODO: wrap with 'internal server error' return msg.errorResponse(err) } return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} } func errorMessage(err error) *jsonrpcMessage { msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ Code: defaultErrorCode, Message: err.Error(), }} ec, ok := err.(Error) if ok { msg.Error.Code = ec.ErrorCode() } de, ok := err.(DataError) if ok { msg.Error.Data = de.ErrorData() } return msg } type jsonError struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } func (err *jsonError) Error() string { if err.Message == "" { return fmt.Sprintf("json-rpc error %d", err.Code) } return err.Message } func (err *jsonError) ErrorCode() int { return err.Code } func (err *jsonError) ErrorData() interface{} { return err.Data } // // Conn is a subset of the methods of net.Conn which are sufficient for ServerCodec. // type Conn interface { // io.ReadWriteCloser // SetWriteDeadline(time.Time) error // } // type deadlineCloser interface { // io.Closer // SetWriteDeadline(time.Time) error // } // // ConnRemoteAddr wraps the RemoteAddr operation, which returns a description // // of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this // // description is used in log messages. // type ConnRemoteAddr interface { // RemoteAddr() string // } // // jsonCodec reads and writes JSON-RPC messages to the underlying connection. It also has // // support for parsing arguments and serializing (result) objects. // type jsonCodec struct { // remote string // closer sync.Once // close closed channel once // closeCh chan interface{} // closed on Close // decode func(v interface{}) error // decoder to allow multiple transports // encMu sync.Mutex // guards the encoder // encode func(v interface{}) error // encoder to allow multiple transports // conn deadlineCloser // } // // NewFuncCodec creates a codec which uses the given functions to read and write. If conn // // implements ConnRemoteAddr, log messages will use it to include the remote address of // // the connection. // func NewFuncCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec { // codec := &jsonCodec{ // closeCh: make(chan interface{}), // encode: encode, // decode: decode, // conn: conn, // } // if ra, ok := conn.(ConnRemoteAddr); ok { // codec.remote = ra.RemoteAddr() // } // return codec // } // // NewCodec creates a codec on the given connection. If conn implements ConnRemoteAddr, log // // messages will use it to include the remote address of the connection. // func NewCodec(conn Conn) ServerCodec { // enc := json.NewEncoder(conn) // dec := json.NewDecoder(conn) // dec.UseNumber() // return NewFuncCodec(conn, enc.Encode, dec.Decode) // } // func (c *jsonCodec) peerInfo() PeerInfo { // // This returns "ipc" because all other built-in transports have a separate codec type. // return PeerInfo{Transport: "ipc", RemoteAddr: c.remote} // } // func (c *jsonCodec) remoteAddr() string { // return c.remote // } // func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err error) { // // Decode the next JSON object in the input stream. // // This verifies basic syntax, etc. // var rawmsg json.RawMessage // if err := c.decode(&rawmsg); err != nil { // return nil, false, err // } // messages, batch = parseMessage(rawmsg) // for i, msg := range messages { // if msg == nil { // // Message is JSON 'null'. Replace with zero value so it // // will be treated like any other invalid message. // messages[i] = new(jsonrpcMessage) // } // } // return messages, batch, nil // } // func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}) error { // c.encMu.Lock() // defer c.encMu.Unlock() // deadline, ok := ctx.Deadline() // if !ok { // deadline = time.Now().Add(defaultWriteTimeout) // } // c.conn.SetWriteDeadline(deadline) // return c.encode(v) // } // func (c *jsonCodec) close() { // c.closer.Do(func() { // close(c.closeCh) // c.conn.Close() // }) // } // // Closed returns a channel which will be closed when Close is called // func (c *jsonCodec) closed() <-chan interface{} { // return c.closeCh // } // // parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error // // checks in this function because the raw message has already been syntax-checked when it // // is called. Any non-JSON-RPC messages in the input return the zero value of // // jsonrpcMessage. // func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { // if !isBatch(raw) { // msgs := []*jsonrpcMessage{{}} // json.Unmarshal(raw, &msgs[0]) // return msgs, false // } // dec := json.NewDecoder(bytes.NewReader(raw)) // dec.Token() // skip '[' // var msgs []*jsonrpcMessage // for dec.More() { // msgs = append(msgs, new(jsonrpcMessage)) // dec.Decode(&msgs[len(msgs)-1]) // } // return msgs, true // } // // isBatch returns true when the first non-whitespace characters is '[' // func isBatch(raw json.RawMessage) bool { // for _, c := range raw { // // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) // if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { // continue // } // return c == '[' // } // return false // } // // parsePositionalArguments tries to parse the given args to an array of values with the // // given types. It returns the parsed values or an error when the args could not be // // parsed. Missing optional arguments are returned as reflect.Zero values. // func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) { // dec := json.NewDecoder(bytes.NewReader(rawArgs)) // var args []reflect.Value // tok, err := dec.Token() // switch { // case err == io.EOF || tok == nil && err == nil: // // "params" is optional and may be empty. Also allow "params":null even though it's // // not in the spec because our own client used to send it. // case err != nil: // return nil, err // case tok == json.Delim('['): // // Read argument array. // if args, err = parseArgumentArray(dec, types); err != nil { // return nil, err // } // default: // return nil, errors.New("non-array args") // } // // Set any missing args to nil. // for i := len(args); i < len(types); i++ { // if types[i].Kind() != reflect.Ptr { // return nil, fmt.Errorf("missing value for required argument %d", i) // } // args = append(args, reflect.Zero(types[i])) // } // return args, nil // } // func parseArgumentArray(dec *json.Decoder, types []reflect.Type) ([]reflect.Value, error) { // args := make([]reflect.Value, 0, len(types)) // for i := 0; dec.More(); i++ { // if i >= len(types) { // return args, fmt.Errorf("too many arguments, want at most %d", len(types)) // } // argval := reflect.New(types[i]) // if err := dec.Decode(argval.Interface()); err != nil { // return args, fmt.Errorf("invalid argument %d: %v", i, err) // } // if argval.IsNil() && types[i].Kind() != reflect.Ptr { // return args, fmt.Errorf("missing value for required argument %d", i) // } // args = append(args, argval.Elem()) // } // // Read end of args array. // _, err := dec.Token() // return args, err // } // // parseSubscriptionName extracts the subscription name from an encoded argument array. // func parseSubscriptionName(rawArgs json.RawMessage) (string, error) { // dec := json.NewDecoder(bytes.NewReader(rawArgs)) // if tok, _ := dec.Token(); tok != json.Delim('[') { // return "", errors.New("non-array args") // } // v, _ := dec.Token() // method, ok := v.(string) // if !ok { // return "", errors.New("expected subscription name as first argument") // } // return method, nil // }