github.com/vipernet-xyz/tm@v0.34.24/rpc/jsonrpc/server/http_uri_handler.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"net/http"
     7  	"reflect"
     8  	"regexp"
     9  	"strings"
    10  
    11  	tmjson "github.com/vipernet-xyz/tm/libs/json"
    12  	"github.com/vipernet-xyz/tm/libs/log"
    13  	types "github.com/vipernet-xyz/tm/rpc/jsonrpc/types"
    14  )
    15  
    16  // HTTP + URI handler
    17  
    18  var reInt = regexp.MustCompile(`^-?[0-9]+$`)
    19  
    20  // convert from a function name to the http handler
    21  func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWriter, *http.Request) {
    22  	// Always return -1 as there's no ID here.
    23  	dummyID := types.JSONRPCIntID(-1) // URIClientRequestID
    24  
    25  	// Exception for websocket endpoints
    26  	if rpcFunc.ws {
    27  		return func(w http.ResponseWriter, r *http.Request) {
    28  			res := types.RPCMethodNotFoundError(dummyID)
    29  			if wErr := WriteRPCResponseHTTPError(w, http.StatusNotFound, res); wErr != nil {
    30  				logger.Error("failed to write response", "res", res, "err", wErr)
    31  			}
    32  		}
    33  	}
    34  
    35  	// All other endpoints
    36  	return func(w http.ResponseWriter, r *http.Request) {
    37  		logger.Debug("HTTP HANDLER", "req", r)
    38  
    39  		ctx := &types.Context{HTTPReq: r}
    40  		args := []reflect.Value{reflect.ValueOf(ctx)}
    41  
    42  		fnArgs, err := httpParamsToArgs(rpcFunc, r)
    43  		if err != nil {
    44  			res := types.RPCInvalidParamsError(dummyID,
    45  				fmt.Errorf("error converting http params to arguments: %w", err),
    46  			)
    47  			if wErr := WriteRPCResponseHTTPError(w, http.StatusInternalServerError, res); wErr != nil {
    48  				logger.Error("failed to write response", "res", res, "err", wErr)
    49  			}
    50  			return
    51  		}
    52  		args = append(args, fnArgs...)
    53  
    54  		returns := rpcFunc.f.Call(args)
    55  
    56  		logger.Debug("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
    57  		result, err := unreflectResult(returns)
    58  		if err != nil {
    59  			if err := WriteRPCResponseHTTPError(w, http.StatusInternalServerError,
    60  				types.RPCInternalError(dummyID, err)); err != nil {
    61  				logger.Error("failed to write response", "res", result, "err", err)
    62  				return
    63  			}
    64  			return
    65  		}
    66  
    67  		resp := types.NewRPCSuccessResponse(dummyID, result)
    68  		if rpcFunc.cacheableWithArgs(args) {
    69  			err = WriteCacheableRPCResponseHTTP(w, resp)
    70  		} else {
    71  			err = WriteRPCResponseHTTP(w, resp)
    72  		}
    73  		if err != nil {
    74  			logger.Error("failed to write response", "res", result, "err", err)
    75  			return
    76  		}
    77  	}
    78  }
    79  
    80  // Covert an http query to a list of properly typed values.
    81  // To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
    82  func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) {
    83  	// skip types.Context
    84  	const argsOffset = 1
    85  
    86  	values := make([]reflect.Value, len(rpcFunc.argNames))
    87  
    88  	for i, name := range rpcFunc.argNames {
    89  		argType := rpcFunc.args[i+argsOffset]
    90  
    91  		values[i] = reflect.Zero(argType) // set default for that type
    92  
    93  		arg := getParam(r, name)
    94  		// log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
    95  
    96  		if arg == "" {
    97  			continue
    98  		}
    99  
   100  		v, ok, err := nonJSONStringToArg(argType, arg)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		if ok {
   105  			values[i] = v
   106  			continue
   107  		}
   108  
   109  		values[i], err = jsonStringToArg(argType, arg)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  	}
   114  
   115  	return values, nil
   116  }
   117  
   118  func jsonStringToArg(rt reflect.Type, arg string) (reflect.Value, error) {
   119  	rv := reflect.New(rt)
   120  	err := tmjson.Unmarshal([]byte(arg), rv.Interface())
   121  	if err != nil {
   122  		return rv, err
   123  	}
   124  	rv = rv.Elem()
   125  	return rv, nil
   126  }
   127  
   128  func nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) {
   129  	if rt.Kind() == reflect.Ptr {
   130  		rv1, ok, err := nonJSONStringToArg(rt.Elem(), arg)
   131  		switch {
   132  		case err != nil:
   133  			return reflect.Value{}, false, err
   134  		case ok:
   135  			rv := reflect.New(rt.Elem())
   136  			rv.Elem().Set(rv1)
   137  			return rv, true, nil
   138  		default:
   139  			return reflect.Value{}, false, nil
   140  		}
   141  	} else {
   142  		return _nonJSONStringToArg(rt, arg)
   143  	}
   144  }
   145  
   146  // NOTE: rt.Kind() isn't a pointer.
   147  func _nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) {
   148  	isIntString := reInt.Match([]byte(arg))
   149  	isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
   150  	isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
   151  
   152  	var expectingString, expectingByteSlice, expectingInt bool
   153  	switch rt.Kind() {
   154  	case reflect.Int,
   155  		reflect.Uint,
   156  		reflect.Int8,
   157  		reflect.Uint8,
   158  		reflect.Int16,
   159  		reflect.Uint16,
   160  		reflect.Int32,
   161  		reflect.Uint32,
   162  		reflect.Int64,
   163  		reflect.Uint64:
   164  		expectingInt = true
   165  	case reflect.String:
   166  		expectingString = true
   167  	case reflect.Slice:
   168  		expectingByteSlice = rt.Elem().Kind() == reflect.Uint8
   169  	}
   170  
   171  	if isIntString && expectingInt {
   172  		qarg := `"` + arg + `"`
   173  		rv, err := jsonStringToArg(rt, qarg)
   174  		if err != nil {
   175  			return rv, false, err
   176  		}
   177  
   178  		return rv, true, nil
   179  	}
   180  
   181  	if isHexString {
   182  		if !expectingString && !expectingByteSlice {
   183  			err := fmt.Errorf("got a hex string arg, but expected '%s'",
   184  				rt.Kind().String())
   185  			return reflect.ValueOf(nil), false, err
   186  		}
   187  
   188  		var value []byte
   189  		value, err := hex.DecodeString(arg[2:])
   190  		if err != nil {
   191  			return reflect.ValueOf(nil), false, err
   192  		}
   193  		if rt.Kind() == reflect.String {
   194  			return reflect.ValueOf(string(value)), true, nil
   195  		}
   196  		return reflect.ValueOf(value), true, nil
   197  	}
   198  
   199  	if isQuotedString && expectingByteSlice {
   200  		v := reflect.New(reflect.TypeOf(""))
   201  		err := tmjson.Unmarshal([]byte(arg), v.Interface())
   202  		if err != nil {
   203  			return reflect.ValueOf(nil), false, err
   204  		}
   205  		v = v.Elem()
   206  		return reflect.ValueOf([]byte(v.String())), true, nil
   207  	}
   208  
   209  	return reflect.ValueOf(nil), false, nil
   210  }
   211  
   212  func getParam(r *http.Request, param string) string {
   213  	s := r.URL.Query().Get(param)
   214  	if s == "" {
   215  		s = r.FormValue(param)
   216  	}
   217  	return s
   218  }