github.com/number571/tendermint@v0.34.11-gost/rpc/jsonrpc/server/http_uri_handler.go (about)

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