mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-06 13:26:23 +00:00
rpc: refactor lib folder (#4836)
Closes https://github.com/tendermint/tendermint/issues/3857 Moves `lib/` folder to `jsonrpc/`. Renames: **packages** `rpc` package -> `jsonrpc` package `rpcclient` package -> `client` package `rpcserver` package -> `server` package **structs and interfaces** ``` JSONRPCClient to Client JSONRPCRequestBatch to RequestBatch JSONRPCCaller to Caller ``` **functions** ``` StartHTTPServer to Serve StartHTTPAndTLSServer to ServeTLS rpc/jsonrpc/client: rename NewURIClient to NewURI NewJSONRPCClient to New NewJSONRPCClientWithHTTPClient to NewWithHTTPClient NewWSClient to NewWS ``` **misc** - unexpose `ResponseWriterWrapper` - remove unused http_params.go
This commit is contained in:
205
rpc/jsonrpc/server/http_uri_handler.go
Normal file
205
rpc/jsonrpc/server/http_uri_handler.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// HTTP + URI handler
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var reInt = regexp.MustCompile(`^-?[0-9]+$`)
|
||||
|
||||
// convert from a function name to the http handler
|
||||
func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func(http.ResponseWriter, *http.Request) {
|
||||
// Always return -1 as there's no ID here.
|
||||
dummyID := types.JSONRPCIntID(-1) // URIClientRequestID
|
||||
|
||||
// Exception for websocket endpoints
|
||||
if rpcFunc.ws {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(dummyID))
|
||||
}
|
||||
}
|
||||
|
||||
// All other endpoints
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Debug("HTTP HANDLER", "req", r)
|
||||
|
||||
ctx := &types.Context{HTTPReq: r}
|
||||
args := []reflect.Value{reflect.ValueOf(ctx)}
|
||||
|
||||
fnArgs, err := httpParamsToArgs(rpcFunc, cdc, r)
|
||||
if err != nil {
|
||||
WriteRPCResponseHTTP(
|
||||
w,
|
||||
types.RPCInvalidParamsError(
|
||||
dummyID,
|
||||
fmt.Errorf("error converting http params to arguments: %w", err),
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
args = append(args, fnArgs...)
|
||||
|
||||
returns := rpcFunc.f.Call(args)
|
||||
|
||||
logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
|
||||
result, err := unreflectResult(returns)
|
||||
if err != nil {
|
||||
WriteRPCResponseHTTP(w, types.RPCInternalError(dummyID, err))
|
||||
return
|
||||
}
|
||||
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(cdc, dummyID, result))
|
||||
}
|
||||
}
|
||||
|
||||
// Covert an http query to a list of properly typed values.
|
||||
// To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
|
||||
func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]reflect.Value, error) {
|
||||
// skip types.Context
|
||||
const argsOffset = 1
|
||||
|
||||
values := make([]reflect.Value, len(rpcFunc.argNames))
|
||||
|
||||
for i, name := range rpcFunc.argNames {
|
||||
argType := rpcFunc.args[i+argsOffset]
|
||||
|
||||
values[i] = reflect.Zero(argType) // set default for that type
|
||||
|
||||
arg := getParam(r, name)
|
||||
// log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
|
||||
|
||||
if arg == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
v, ok, err := nonJSONStringToArg(cdc, argType, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
values[i] = v
|
||||
continue
|
||||
}
|
||||
|
||||
values[i], err = jsonStringToArg(cdc, argType, arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func jsonStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error) {
|
||||
rv := reflect.New(rt)
|
||||
err := cdc.UnmarshalJSON([]byte(arg), rv.Interface())
|
||||
if err != nil {
|
||||
return rv, err
|
||||
}
|
||||
rv = rv.Elem()
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, bool, error) {
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rv1, ok, err := nonJSONStringToArg(cdc, rt.Elem(), arg)
|
||||
switch {
|
||||
case err != nil:
|
||||
return reflect.Value{}, false, err
|
||||
case ok:
|
||||
rv := reflect.New(rt.Elem())
|
||||
rv.Elem().Set(rv1)
|
||||
return rv, true, nil
|
||||
default:
|
||||
return reflect.Value{}, false, nil
|
||||
}
|
||||
} else {
|
||||
return _nonJSONStringToArg(cdc, rt, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: rt.Kind() isn't a pointer.
|
||||
func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, bool, error) {
|
||||
isIntString := reInt.Match([]byte(arg))
|
||||
isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
|
||||
isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
|
||||
|
||||
var expectingString, expectingByteSlice, expectingInt bool
|
||||
switch rt.Kind() {
|
||||
case reflect.Int,
|
||||
reflect.Uint,
|
||||
reflect.Int8,
|
||||
reflect.Uint8,
|
||||
reflect.Int16,
|
||||
reflect.Uint16,
|
||||
reflect.Int32,
|
||||
reflect.Uint32,
|
||||
reflect.Int64,
|
||||
reflect.Uint64:
|
||||
expectingInt = true
|
||||
case reflect.String:
|
||||
expectingString = true
|
||||
case reflect.Slice:
|
||||
expectingByteSlice = rt.Elem().Kind() == reflect.Uint8
|
||||
}
|
||||
|
||||
if isIntString && expectingInt {
|
||||
qarg := `"` + arg + `"`
|
||||
rv, err := jsonStringToArg(cdc, rt, qarg)
|
||||
if err != nil {
|
||||
return rv, false, err
|
||||
}
|
||||
|
||||
return rv, true, nil
|
||||
}
|
||||
|
||||
if isHexString {
|
||||
if !expectingString && !expectingByteSlice {
|
||||
err := fmt.Errorf("got a hex string arg, but expected '%s'",
|
||||
rt.Kind().String())
|
||||
return reflect.ValueOf(nil), false, err
|
||||
}
|
||||
|
||||
var value []byte
|
||||
value, err := hex.DecodeString(arg[2:])
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), false, err
|
||||
}
|
||||
if rt.Kind() == reflect.String {
|
||||
return reflect.ValueOf(string(value)), true, nil
|
||||
}
|
||||
return reflect.ValueOf(value), true, nil
|
||||
}
|
||||
|
||||
if isQuotedString && expectingByteSlice {
|
||||
v := reflect.New(reflect.TypeOf(""))
|
||||
err := cdc.UnmarshalJSON([]byte(arg), v.Interface())
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), false, err
|
||||
}
|
||||
v = v.Elem()
|
||||
return reflect.ValueOf([]byte(v.String())), true, nil
|
||||
}
|
||||
|
||||
return reflect.ValueOf(nil), false, nil
|
||||
}
|
||||
|
||||
func getParam(r *http.Request, param string) string {
|
||||
s := r.URL.Query().Get(param)
|
||||
if s == "" {
|
||||
s = r.FormValue(param)
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user