- package server
-
- import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "reflect"
-
- "github.com/tendermint/tendermint/libs/log"
- )
-
- // RegisterRPCFuncs adds a route for each function in the funcMap, as well as
- // general jsonrpc and websocket handlers for all functions. "result" is the
- // interface on which the result objects are registered, and is popualted with
- // every RPCResponse
- func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) {
- // HTTP endpoints
- for funcName, rpcFunc := range funcMap {
- mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger))
- }
-
- // JSONRPC endpoints
- mux.HandleFunc("/", handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, logger)))
- }
-
- // Function introspection
-
- // RPCFunc contains the introspected type information for a function.
- type RPCFunc struct {
- f reflect.Value // underlying rpc function
- args []reflect.Type // type of each function arg
- returns []reflect.Type // type of each return arg
- argNames []string // name of each argument
- ws bool // websocket only
- }
-
- // NewRPCFunc constructs an RPCFunc for f, which must be a function whose type
- // signature matches one of these schemes:
- //
- // func(context.Context, T1, T2, ...) error
- // func(context.Context, T1, T2, ...) (R, error)
- //
- // for arbitrary types T_i and R. The number of argNames must exactly match the
- // number of non-context arguments to f. Otherwise, NewRPCFunc panics.
- //
- // The parameter names given are used to map JSON object keys to the
- // corresonding parameter of the function. The names do not need to match the
- // declared names, but must match what the client sends in a request.
- func NewRPCFunc(f interface{}, argNames ...string) *RPCFunc {
- rf, err := newRPCFunc(f, argNames)
- if err != nil {
- panic("invalid RPC function: " + err.Error())
- }
- return rf
- }
-
- // NewWSRPCFunc behaves as NewRPCFunc, but marks the resulting function for use
- // via websocket.
- func NewWSRPCFunc(f interface{}, argNames ...string) *RPCFunc {
- rf := NewRPCFunc(f, argNames...)
- rf.ws = true
- return rf
- }
-
- var (
- ctxType = reflect.TypeOf((*context.Context)(nil)).Elem()
- errType = reflect.TypeOf((*error)(nil)).Elem()
- )
-
- // newRPCFunc constructs an RPCFunc for f. See the comment at NewRPCFunc.
- func newRPCFunc(f interface{}, argNames []string) (*RPCFunc, error) {
- if f == nil {
- return nil, errors.New("nil function")
- }
-
- // Check the type and signature of f.
- fv := reflect.ValueOf(f)
- if fv.Kind() != reflect.Func {
- return nil, errors.New("not a function")
- }
-
- ft := fv.Type()
- if np := ft.NumIn(); np == 0 {
- return nil, errors.New("wrong number of parameters")
- } else if ft.In(0) != ctxType {
- return nil, errors.New("first parameter is not context.Context")
- } else if np-1 != len(argNames) {
- return nil, fmt.Errorf("have %d names for %d parameters", len(argNames), np-1)
- }
-
- if no := ft.NumOut(); no < 1 || no > 2 {
- return nil, errors.New("wrong number of results")
- } else if ft.Out(no-1) != errType {
- return nil, errors.New("last result is not error")
- }
-
- args := make([]reflect.Type, ft.NumIn())
- for i := 0; i < ft.NumIn(); i++ {
- args[i] = ft.In(i)
- }
- outs := make([]reflect.Type, ft.NumOut())
- for i := 0; i < ft.NumOut(); i++ {
- outs[i] = ft.Out(i)
- }
- return &RPCFunc{
- f: fv,
- args: args,
- returns: outs,
- argNames: argNames,
- }, nil
- }
-
- //-------------------------------------------------------------
-
- // NOTE: assume returns is result struct and error. If error is not nil, return it
- func unreflectResult(returns []reflect.Value) (interface{}, error) {
- errV := returns[1]
- if err, ok := errV.Interface().(error); ok && err != nil {
- return nil, err
- }
- rv := returns[0]
- // the result is a registered interface,
- // we need a pointer to it so we can marshal with type byte
- rvp := reflect.New(rv.Type())
- rvp.Elem().Set(rv)
- return rvp.Interface(), nil
- }
|