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