github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/rpc/lib/server/handlers.go (about)

     1  package server
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/hyperledger/burrow/logging"
    15  	"github.com/hyperledger/burrow/rpc/lib/types"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // RegisterRPCFuncs adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
    20  // "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse
    21  func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger *logging.Logger) {
    22  	// HTTP endpoints
    23  	for funcName, rpcFunc := range funcMap {
    24  		mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger))
    25  	}
    26  
    27  	// JSONRPC endpoints
    28  	mux.HandleFunc("/", makeJSONRPCHandler(funcMap, logger))
    29  }
    30  
    31  //-------------------------------------
    32  // function introspection
    33  
    34  // RPCFunc contains the introspected type information for a function
    35  type RPCFunc struct {
    36  	f        reflect.Value  // underlying rpc function
    37  	args     []reflect.Type // type of each function arg
    38  	returns  []reflect.Type // type of each return arg
    39  	argNames []string       // name of each argument
    40  	ws       bool           // websocket only
    41  }
    42  
    43  // NewRPCFunc wraps a function for introspection.
    44  // f is the function, args are comma separated argument names
    45  func NewRPCFunc(f interface{}, args string) *RPCFunc {
    46  	return newRPCFunc(f, args, false)
    47  }
    48  
    49  // NewWSRPCFunc wraps a function for introspection and use in the websockets.
    50  func NewWSRPCFunc(f interface{}, args string) *RPCFunc {
    51  	return newRPCFunc(f, args, true)
    52  }
    53  
    54  func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
    55  	var argNames []string
    56  	if args != "" {
    57  		argNames = strings.Split(args, ",")
    58  	}
    59  	return &RPCFunc{
    60  		f:        reflect.ValueOf(f),
    61  		args:     funcArgTypes(f),
    62  		returns:  funcReturnTypes(f),
    63  		argNames: argNames,
    64  		ws:       ws,
    65  	}
    66  }
    67  
    68  // return a function's argument types
    69  func funcArgTypes(f interface{}) []reflect.Type {
    70  	t := reflect.TypeOf(f)
    71  	n := t.NumIn()
    72  	typez := make([]reflect.Type, n)
    73  	for i := 0; i < n; i++ {
    74  		typez[i] = t.In(i)
    75  	}
    76  	return typez
    77  }
    78  
    79  // return a function's return types
    80  func funcReturnTypes(f interface{}) []reflect.Type {
    81  	t := reflect.TypeOf(f)
    82  	n := t.NumOut()
    83  	typez := make([]reflect.Type, n)
    84  	for i := 0; i < n; i++ {
    85  		typez[i] = t.Out(i)
    86  	}
    87  	return typez
    88  }
    89  
    90  // function introspection
    91  //-----------------------------------------------------------------------------
    92  // rpc.json
    93  
    94  // jsonrpc calls grab the given method's function info and runs reflect.Call
    95  func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger *logging.Logger) http.HandlerFunc {
    96  	return func(w http.ResponseWriter, r *http.Request) {
    97  		b, err := ioutil.ReadAll(r.Body)
    98  		if err != nil {
    99  			WriteRPCResponseHTTP(w, types.RPCInvalidRequestError("", errors.Wrap(err, "Error reading request body")))
   100  			return
   101  		}
   102  		// if its an empty request (like from a browser),
   103  		// just display a list of functions
   104  		if len(b) == 0 {
   105  			writeListOfEndpoints(w, r, funcMap)
   106  			return
   107  		}
   108  
   109  		var request types.RPCRequest
   110  		err = json.Unmarshal(b, &request)
   111  		if err != nil {
   112  			WriteRPCResponseHTTP(w, types.RPCParseError("", errors.Wrap(err, "Error unmarshalling request")))
   113  			return
   114  		}
   115  		// A Notification is a Request object without an "id" member.
   116  		// The Server MUST NOT reply to a Notification, including those that are within a batch request.
   117  		if request.ID == "" {
   118  			logger.TraceMsg("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)")
   119  			return
   120  		}
   121  		if len(r.URL.Path) > 1 {
   122  			WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path)))
   123  			return
   124  		}
   125  		rpcFunc := funcMap[request.Method]
   126  		if rpcFunc == nil || rpcFunc.ws {
   127  			WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID))
   128  			return
   129  		}
   130  		var args []reflect.Value
   131  		if len(request.Params) > 0 {
   132  			args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
   133  			if err != nil {
   134  				WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments")))
   135  				return
   136  			}
   137  		}
   138  		returns := rpcFunc.f.Call(args)
   139  		logger.InfoMsg("HTTP JSONRPC called", "method", request.Method, "args", args, "returns", returns)
   140  		result, err := unreflectResult(returns)
   141  		if err != nil {
   142  			WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err))
   143  			return
   144  		}
   145  		WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(request.ID, result))
   146  	}
   147  }
   148  
   149  func mapParamsToArgs(rpcFunc *RPCFunc, params map[string]json.RawMessage, argsOffset int) ([]reflect.Value, error) {
   150  	values := make([]reflect.Value, len(rpcFunc.argNames))
   151  	for i, argName := range rpcFunc.argNames {
   152  		argType := rpcFunc.args[i+argsOffset]
   153  
   154  		if p, ok := params[argName]; ok && p != nil && len(p) > 0 {
   155  			val := reflect.New(argType)
   156  			err := json.Unmarshal(p, val.Interface())
   157  			if err != nil {
   158  				return nil, err
   159  			}
   160  			values[i] = val.Elem()
   161  		} else { // use default for that type
   162  			values[i] = reflect.Zero(argType)
   163  		}
   164  	}
   165  
   166  	return values, nil
   167  }
   168  
   169  func arrayParamsToArgs(rpcFunc *RPCFunc, params []json.RawMessage, argsOffset int) ([]reflect.Value, error) {
   170  	if len(rpcFunc.argNames) != len(params) {
   171  		return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)",
   172  			len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
   173  	}
   174  
   175  	values := make([]reflect.Value, len(params))
   176  	for i, p := range params {
   177  		argType := rpcFunc.args[i+argsOffset]
   178  		val := reflect.New(argType)
   179  		err := json.Unmarshal(p, val.Interface())
   180  		if err != nil {
   181  			return nil, err
   182  		}
   183  		values[i] = val.Elem()
   184  	}
   185  	return values, nil
   186  }
   187  
   188  // `raw` is unparsed json (from json.RawMessage) encoding either a map or an array.
   189  // `argsOffset` should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames).
   190  //
   191  // Example:
   192  //   rpcFunc.args = [rpctypes.WSRPCContext string]
   193  //   rpcFunc.argNames = ["arg"]
   194  func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) {
   195  
   196  	// TODO: Make more efficient, perhaps by checking the first character for '{' or '['?
   197  	// First, try to get the map.
   198  	var m map[string]json.RawMessage
   199  	err := json.Unmarshal(raw, &m)
   200  	if err == nil {
   201  		return mapParamsToArgs(rpcFunc, m, argsOffset)
   202  	}
   203  
   204  	// Otherwise, try an array.
   205  	var a []json.RawMessage
   206  	err = json.Unmarshal(raw, &a)
   207  	if err == nil {
   208  		return arrayParamsToArgs(rpcFunc, a, argsOffset)
   209  	}
   210  
   211  	// Otherwise, bad format, we cannot parse
   212  	return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err)
   213  }
   214  
   215  // Convert a []interface{} OR a map[string]interface{} to properly typed values
   216  func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params json.RawMessage) ([]reflect.Value, error) {
   217  	return jsonParamsToArgs(rpcFunc, params, 0)
   218  }
   219  
   220  // convert from a function name to the http handler
   221  func makeHTTPHandler(rpcFunc *RPCFunc, logger *logging.Logger) func(http.ResponseWriter, *http.Request) {
   222  	// Exception for websocket endpoints
   223  	if rpcFunc.ws {
   224  		return func(w http.ResponseWriter, r *http.Request) {
   225  			WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(""))
   226  		}
   227  	}
   228  	logger = logger.WithScope("makeHTTPHandler")
   229  	// All other endpoints
   230  	return func(w http.ResponseWriter, r *http.Request) {
   231  		logger.TraceMsg("HTTP REST Handler received request", "request", r)
   232  		args, err := httpParamsToArgs(rpcFunc, r)
   233  		if err != nil {
   234  			WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("", errors.Wrap(err, "Error converting http params to arguments")))
   235  			return
   236  		}
   237  		returns := rpcFunc.f.Call(args)
   238  		logger.InfoMsg("HTTP REST", "method", r.URL.Path, "args", args, "returns", returns)
   239  		result, err := unreflectResult(returns)
   240  		if err != nil {
   241  			WriteRPCResponseHTTP(w, types.RPCInternalError("", err))
   242  			return
   243  		}
   244  		WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result))
   245  	}
   246  }
   247  
   248  // Covert an http query to a list of properly typed values.
   249  // To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
   250  func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) {
   251  	values := make([]reflect.Value, len(rpcFunc.args))
   252  
   253  	for i, name := range rpcFunc.argNames {
   254  		argType := rpcFunc.args[i]
   255  
   256  		values[i] = reflect.Zero(argType) // set default for that type
   257  
   258  		arg := GetParam(r, name)
   259  		// log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
   260  
   261  		if arg == "" {
   262  			continue
   263  		}
   264  
   265  		v, ok, err := nonJSONToArg(argType, arg)
   266  		if err != nil {
   267  			return nil, err
   268  		}
   269  		if ok {
   270  			values[i] = v
   271  			continue
   272  		}
   273  
   274  		values[i], err = jsonStringToArg(argType, arg)
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  	}
   279  
   280  	return values, nil
   281  }
   282  
   283  func jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
   284  	v := reflect.New(ty)
   285  	err := json.Unmarshal([]byte(arg), v.Interface())
   286  	if err != nil {
   287  		return v, err
   288  	}
   289  	v = v.Elem()
   290  	return v, nil
   291  }
   292  
   293  func nonJSONToArg(ty reflect.Type, arg string) (reflect.Value, bool, error) {
   294  	expectingString := ty.Kind() == reflect.String
   295  	expectingBytes := (ty.Kind() == reflect.Slice || ty.Kind() == reflect.Array) && ty.Elem().Kind() == reflect.Uint8
   296  
   297  	isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
   298  
   299  	// Throw quoted strings at JSON parser later... because it always has...
   300  	if expectingString && !isQuotedString {
   301  		return reflect.ValueOf(arg), true, nil
   302  	}
   303  
   304  	if expectingBytes {
   305  		if isQuotedString {
   306  			rv := reflect.New(ty)
   307  			err := json.Unmarshal([]byte(arg), rv.Interface())
   308  			if err != nil {
   309  				return reflect.ValueOf(nil), false, err
   310  			}
   311  			return rv.Elem(), true, nil
   312  		}
   313  		if strings.HasPrefix(strings.ToLower(arg), "0x") {
   314  			arg = arg[2:]
   315  		}
   316  		var value []byte
   317  		value, err := hex.DecodeString(arg)
   318  		if err != nil {
   319  			return reflect.ValueOf(nil), false, err
   320  		}
   321  		if ty.Kind() == reflect.Array {
   322  			// Gives us an empty array of the right type
   323  			rv := reflect.New(ty).Elem()
   324  			reflect.Copy(rv, reflect.ValueOf(value))
   325  			return rv, true, nil
   326  		}
   327  		return reflect.ValueOf(value), true, nil
   328  	}
   329  
   330  	return reflect.ValueOf(nil), false, nil
   331  }
   332  
   333  // NOTE: assume returns is result struct and error. If error is not nil, return it
   334  func unreflectResult(returns []reflect.Value) (interface{}, error) {
   335  	errV := returns[1]
   336  	if errV.Interface() != nil {
   337  		return nil, errors.Errorf("%v", errV.Interface())
   338  	}
   339  	rv := returns[0]
   340  	// the result is a registered interface,
   341  	// we need a pointer to it so we can marshal with type byte
   342  	rvp := reflect.New(rv.Type())
   343  	rvp.Elem().Set(rv)
   344  	return rvp.Interface(), nil
   345  }
   346  
   347  // writes a list of available rpc endpoints as an html page
   348  func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) {
   349  	noArgNames := []string{}
   350  	argNames := []string{}
   351  	for name, funcData := range funcMap {
   352  		if len(funcData.args) == 0 {
   353  			noArgNames = append(noArgNames, name)
   354  		} else {
   355  			argNames = append(argNames, name)
   356  		}
   357  	}
   358  	sort.Strings(noArgNames)
   359  	sort.Strings(argNames)
   360  	buf := new(bytes.Buffer)
   361  	buf.WriteString("<html><body>")
   362  	buf.WriteString("<br>Available endpoints:<br>")
   363  
   364  	for _, name := range noArgNames {
   365  		link := fmt.Sprintf("//%s/%s", r.Host, name)
   366  		buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
   367  	}
   368  
   369  	buf.WriteString("<br>Endpoints that require arguments:<br>")
   370  	for _, name := range argNames {
   371  		link := fmt.Sprintf("//%s/%s?", r.Host, name)
   372  		funcData := funcMap[name]
   373  		for i, argName := range funcData.argNames {
   374  			link += argName + "=_"
   375  			if i < len(funcData.argNames)-1 {
   376  				link += "&"
   377  			}
   378  		}
   379  		buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
   380  	}
   381  	buf.WriteString("</body></html>")
   382  	w.Header().Set("Content-Type", "text/html")
   383  	w.WriteHeader(200)
   384  	w.Write(buf.Bytes()) // nolint: errcheck
   385  }