github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/jsonrpc/server/http_uri_handler.go (about)

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