github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/rpc/jsonrpc/server/rpc_func.go (about)

     1  package server
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"reflect"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/ari-anchor/sei-tendermint/libs/log"
    15  	rpctypes "github.com/ari-anchor/sei-tendermint/rpc/jsonrpc/types"
    16  )
    17  
    18  // DefaultRPCTimeout is the default context timeout for calls to any RPC method
    19  // that does not override it with a more specific timeout.
    20  const DefaultRPCTimeout = 60 * time.Second
    21  
    22  // RegisterRPCFuncs adds a route to mux for each non-websocket function in the
    23  // funcMap, and also a root JSON-RPC POST handler.
    24  func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) {
    25  	for name, fn := range funcMap {
    26  		if fn.ws {
    27  			continue // skip websocket endpoints, not usable via GET calls
    28  		}
    29  		mux.HandleFunc("/"+name, ensureBodyClose(makeHTTPHandler(fn, logger)))
    30  	}
    31  
    32  	// Endpoints for POST.
    33  	mux.HandleFunc("/", ensureBodyClose(handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, logger))))
    34  }
    35  
    36  // Function introspection
    37  
    38  // RPCFunc contains the introspected type information for a function.
    39  type RPCFunc struct {
    40  	f       reflect.Value // underlying rpc function
    41  	param   reflect.Type  // the parameter struct, or nil
    42  	result  reflect.Type  // the non-error result type, or nil
    43  	args    []argInfo     // names and type information (for URL decoding)
    44  	timeout time.Duration // default request timeout, 0 means none
    45  	ws      bool          // websocket only
    46  }
    47  
    48  // argInfo records the name of a field, along with a bit to tell whether the
    49  // value of the field requires binary data, having underlying type []byte.  The
    50  // flag is needed when decoding URL parameters, where we permit quoted strings
    51  // to be passed for either argument type.
    52  type argInfo struct {
    53  	name     string
    54  	isBinary bool // value wants binary data
    55  }
    56  
    57  // Call parses the given JSON parameters and calls the function wrapped by rf
    58  // with the resulting argument value. It reports an error if parameter parsing
    59  // fails, otherwise it returns the result from the wrapped function.
    60  func (rf *RPCFunc) Call(ctx context.Context, params json.RawMessage) (interface{}, error) {
    61  	// If ctx has its own deadline we will respect it; otherwise use rf.timeout.
    62  	if _, ok := ctx.Deadline(); !ok && rf.timeout > 0 {
    63  		var cancel context.CancelFunc
    64  		ctx, cancel = context.WithTimeout(ctx, rf.timeout)
    65  		defer cancel()
    66  	}
    67  	args, err := rf.parseParams(ctx, params)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	returns := rf.f.Call(args)
    72  
    73  	// Case 1: There is no non-error result type.
    74  	if rf.result == nil {
    75  		if oerr := returns[0].Interface(); oerr != nil {
    76  			return nil, oerr.(error)
    77  		}
    78  		return nil, nil
    79  	}
    80  
    81  	// Case 2: There is a non-error result.
    82  	if oerr := returns[1].Interface(); oerr != nil {
    83  		// In case of error, report the error and ignore the result.
    84  		return nil, oerr.(error)
    85  	}
    86  	return returns[0].Interface(), nil
    87  }
    88  
    89  // Timeout updates rf to include a default timeout for calls to rf. This
    90  // timeout is used if one is not already provided on the request context.
    91  // Setting d == 0 means there will be no timeout. Returns rf to allow chaining.
    92  func (rf *RPCFunc) Timeout(d time.Duration) *RPCFunc { rf.timeout = d; return rf }
    93  
    94  // parseParams parses the parameters of a JSON-RPC request and returns the
    95  // corresponding argument values. On success, the first argument value will be
    96  // the value of ctx.
    97  func (rf *RPCFunc) parseParams(ctx context.Context, params json.RawMessage) ([]reflect.Value, error) {
    98  	// If rf does not accept parameters, there is no decoding to do, but verify
    99  	// that no parameters were passed.
   100  	if rf.param == nil {
   101  		if !isNullOrEmpty(params) {
   102  			return nil, invalidParamsError("no parameters accepted for this method")
   103  		}
   104  		return []reflect.Value{reflect.ValueOf(ctx)}, nil
   105  	}
   106  	bits, err := rf.adjustParams(params)
   107  	if err != nil {
   108  		return nil, invalidParamsError(err.Error())
   109  	}
   110  	arg := reflect.New(rf.param)
   111  	if err := json.Unmarshal(bits, arg.Interface()); err != nil {
   112  		return nil, invalidParamsError(err.Error())
   113  	}
   114  	return []reflect.Value{reflect.ValueOf(ctx), arg}, nil
   115  }
   116  
   117  // adjustParams checks whether data is encoded as a JSON array, and if so
   118  // adjusts the values to match the corresponding parameter names.
   119  func (rf *RPCFunc) adjustParams(data []byte) (json.RawMessage, error) {
   120  	base := bytes.TrimSpace(data)
   121  	if bytes.HasPrefix(base, []byte("[")) {
   122  		var args []json.RawMessage
   123  		if err := json.Unmarshal(base, &args); err != nil {
   124  			return nil, err
   125  		} else if len(args) != len(rf.args) {
   126  			return nil, fmt.Errorf("got %d arguments, want %d", len(args), len(rf.args))
   127  		}
   128  		m := make(map[string]json.RawMessage)
   129  		for i, arg := range args {
   130  			m[rf.args[i].name] = arg
   131  		}
   132  		return json.Marshal(m)
   133  	} else if bytes.HasPrefix(base, []byte("{")) || bytes.Equal(base, []byte("null")) {
   134  		return base, nil
   135  	}
   136  	return nil, errors.New("parameters must be an object or an array")
   137  
   138  }
   139  
   140  // NewRPCFunc constructs an RPCFunc for f, which must be a function whose type
   141  // signature matches one of these schemes:
   142  //
   143  //	func(context.Context) error
   144  //	func(context.Context) (R, error)
   145  //	func(context.Context, *T) error
   146  //	func(context.Context, *T) (R, error)
   147  //
   148  // for an arbitrary struct type T and type R. NewRPCFunc will panic if f does
   149  // not have one of these forms.  A newly-constructed RPCFunc has a default
   150  // timeout of DefaultRPCTimeout; use the Timeout method to adjust this as
   151  // needed.
   152  func NewRPCFunc(f interface{}) *RPCFunc {
   153  	rf, err := newRPCFunc(f)
   154  	if err != nil {
   155  		panic("invalid RPC function: " + err.Error())
   156  	}
   157  	return rf
   158  }
   159  
   160  // NewWSRPCFunc behaves as NewRPCFunc, but marks the resulting function for use
   161  // via websocket.
   162  func NewWSRPCFunc(f interface{}) *RPCFunc {
   163  	rf := NewRPCFunc(f)
   164  	rf.ws = true
   165  	return rf
   166  }
   167  
   168  var (
   169  	ctxType = reflect.TypeOf((*context.Context)(nil)).Elem()
   170  	errType = reflect.TypeOf((*error)(nil)).Elem()
   171  )
   172  
   173  // newRPCFunc constructs an RPCFunc for f. See the comment at NewRPCFunc.
   174  func newRPCFunc(f interface{}) (*RPCFunc, error) {
   175  	if f == nil {
   176  		return nil, errors.New("nil function")
   177  	}
   178  
   179  	// Check the type and signature of f.
   180  	fv := reflect.ValueOf(f)
   181  	if fv.Kind() != reflect.Func {
   182  		return nil, errors.New("not a function")
   183  	}
   184  
   185  	var ptype reflect.Type
   186  	ft := fv.Type()
   187  	if np := ft.NumIn(); np == 0 || np > 2 {
   188  		return nil, errors.New("wrong number of parameters")
   189  	} else if ft.In(0) != ctxType {
   190  		return nil, errors.New("first parameter is not context.Context")
   191  	} else if np == 2 {
   192  		ptype = ft.In(1)
   193  		if ptype.Kind() != reflect.Ptr {
   194  			return nil, errors.New("parameter type is not a pointer")
   195  		}
   196  		ptype = ptype.Elem()
   197  		if ptype.Kind() != reflect.Struct {
   198  			return nil, errors.New("parameter type is not a struct")
   199  		}
   200  	}
   201  
   202  	var rtype reflect.Type
   203  	if no := ft.NumOut(); no < 1 || no > 2 {
   204  		return nil, errors.New("wrong number of results")
   205  	} else if ft.Out(no-1) != errType {
   206  		return nil, errors.New("last result is not error")
   207  	} else if no == 2 {
   208  		rtype = ft.Out(0)
   209  	}
   210  
   211  	var args []argInfo
   212  	if ptype != nil {
   213  		for i := 0; i < ptype.NumField(); i++ {
   214  			field := ptype.Field(i)
   215  			if tag := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]; tag != "" && tag != "-" {
   216  				args = append(args, argInfo{
   217  					name:     tag,
   218  					isBinary: isByteArray(field.Type),
   219  				})
   220  			} else if tag == "-" {
   221  				// If the tag is "-" the field should explicitly be ignored, even
   222  				// if it is otherwise eligible.
   223  			} else if field.IsExported() && !field.Anonymous {
   224  				// Examples: Name → name, MaxEffort → maxEffort.
   225  				// Note that this is an aesthetic choice; the standard decoder will
   226  				// match without regard to case anyway.
   227  				name := strings.ToLower(field.Name[:1]) + field.Name[1:]
   228  				args = append(args, argInfo{
   229  					name:     name,
   230  					isBinary: isByteArray(field.Type),
   231  				})
   232  			}
   233  		}
   234  	}
   235  
   236  	return &RPCFunc{
   237  		f:       fv,
   238  		param:   ptype,
   239  		result:  rtype,
   240  		args:    args,
   241  		timeout: DefaultRPCTimeout, // until overridden
   242  	}, nil
   243  }
   244  
   245  // invalidParamsError returns an RPC invalid parameters error with the given
   246  // detail message.
   247  func invalidParamsError(msg string, args ...interface{}) error {
   248  	return &rpctypes.RPCError{
   249  		Code:    int(rpctypes.CodeInvalidParams),
   250  		Message: rpctypes.CodeInvalidParams.String(),
   251  		Data:    fmt.Sprintf(msg, args...),
   252  	}
   253  }
   254  
   255  // isNullOrEmpty reports whether params is either itself empty or represents an
   256  // empty parameter (null, empty object, or empty array).
   257  func isNullOrEmpty(params json.RawMessage) bool {
   258  	return len(params) == 0 ||
   259  		bytes.Equal(params, []byte("null")) ||
   260  		bytes.Equal(params, []byte("{}")) ||
   261  		bytes.Equal(params, []byte("[]"))
   262  }
   263  
   264  // isByteArray reports whether t is (equivalent to) []byte.
   265  func isByteArray(t reflect.Type) bool {
   266  	return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8
   267  }