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