github.com/noirx94/tendermintmp@v0.0.1/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/tendermint/tendermint/libs/json"
    12  	"github.com/tendermint/tendermint/libs/log"
    13  	types "github.com/tendermint/tendermint/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  		if err := WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(dummyID, result)); err != nil {
    67  			logger.Error("failed to write response", "res", result, "err", err)
    68  			return
    69  		}
    70  	}
    71  }
    72  
    73  // Covert an http query to a list of properly typed values.
    74  // To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
    75  func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) {
    76  	// skip types.Context
    77  	const argsOffset = 1
    78  
    79  	values := make([]reflect.Value, len(rpcFunc.argNames))
    80  
    81  	for i, name := range rpcFunc.argNames {
    82  		argType := rpcFunc.args[i+argsOffset]
    83  
    84  		values[i] = reflect.Zero(argType) // set default for that type
    85  
    86  		arg := getParam(r, name)
    87  		// log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
    88  
    89  		if arg == "" {
    90  			continue
    91  		}
    92  
    93  		v, ok, err := nonJSONStringToArg(argType, arg)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		if ok {
    98  			values[i] = v
    99  			continue
   100  		}
   101  
   102  		values[i], err = jsonStringToArg(argType, arg)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	return values, nil
   109  }
   110  
   111  func jsonStringToArg(rt reflect.Type, arg string) (reflect.Value, error) {
   112  	rv := reflect.New(rt)
   113  	err := tmjson.Unmarshal([]byte(arg), rv.Interface())
   114  	if err != nil {
   115  		return rv, err
   116  	}
   117  	rv = rv.Elem()
   118  	return rv, nil
   119  }
   120  
   121  func nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) {
   122  	if rt.Kind() == reflect.Ptr {
   123  		rv1, ok, err := nonJSONStringToArg(rt.Elem(), arg)
   124  		switch {
   125  		case err != nil:
   126  			return reflect.Value{}, false, err
   127  		case ok:
   128  			rv := reflect.New(rt.Elem())
   129  			rv.Elem().Set(rv1)
   130  			return rv, true, nil
   131  		default:
   132  			return reflect.Value{}, false, nil
   133  		}
   134  	} else {
   135  		return _nonJSONStringToArg(rt, arg)
   136  	}
   137  }
   138  
   139  // NOTE: rt.Kind() isn't a pointer.
   140  func _nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) {
   141  	isIntString := reInt.Match([]byte(arg))
   142  	isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
   143  	isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
   144  
   145  	var expectingString, expectingByteSlice, expectingInt bool
   146  	switch rt.Kind() {
   147  	case reflect.Int,
   148  		reflect.Uint,
   149  		reflect.Int8,
   150  		reflect.Uint8,
   151  		reflect.Int16,
   152  		reflect.Uint16,
   153  		reflect.Int32,
   154  		reflect.Uint32,
   155  		reflect.Int64,
   156  		reflect.Uint64:
   157  		expectingInt = true
   158  	case reflect.String:
   159  		expectingString = true
   160  	case reflect.Slice:
   161  		expectingByteSlice = rt.Elem().Kind() == reflect.Uint8
   162  	}
   163  
   164  	if isIntString && expectingInt {
   165  		qarg := `"` + arg + `"`
   166  		rv, err := jsonStringToArg(rt, qarg)
   167  		if err != nil {
   168  			return rv, false, err
   169  		}
   170  
   171  		return rv, true, nil
   172  	}
   173  
   174  	if isHexString {
   175  		if !expectingString && !expectingByteSlice {
   176  			err := fmt.Errorf("got a hex string arg, but expected '%s'",
   177  				rt.Kind().String())
   178  			return reflect.ValueOf(nil), false, err
   179  		}
   180  
   181  		var value []byte
   182  		value, err := hex.DecodeString(arg[2:])
   183  		if err != nil {
   184  			return reflect.ValueOf(nil), false, err
   185  		}
   186  		if rt.Kind() == reflect.String {
   187  			return reflect.ValueOf(string(value)), true, nil
   188  		}
   189  		return reflect.ValueOf(value), true, nil
   190  	}
   191  
   192  	if isQuotedString && expectingByteSlice {
   193  		v := reflect.New(reflect.TypeOf(""))
   194  		err := tmjson.Unmarshal([]byte(arg), v.Interface())
   195  		if err != nil {
   196  			return reflect.ValueOf(nil), false, err
   197  		}
   198  		v = v.Elem()
   199  		return reflect.ValueOf([]byte(v.String())), true, nil
   200  	}
   201  
   202  	return reflect.ValueOf(nil), false, nil
   203  }
   204  
   205  func getParam(r *http.Request, param string) string {
   206  	s := r.URL.Query().Get(param)
   207  	if s == "" {
   208  		s = r.FormValue(param)
   209  	}
   210  	return s
   211  }