github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/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 amino "github.com/tendermint/go-amino" 12 13 "github.com/franono/tendermint/libs/log" 14 types "github.com/franono/tendermint/rpc/jsonrpc/types" 15 ) 16 17 /////////////////////////////////////////////////////////////////////////////// 18 // HTTP + URI handler 19 /////////////////////////////////////////////////////////////////////////////// 20 21 var reInt = regexp.MustCompile(`^-?[0-9]+$`) 22 23 // convert from a function name to the http handler 24 func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func(http.ResponseWriter, *http.Request) { 25 // Always return -1 as there's no ID here. 26 dummyID := types.JSONRPCIntID(-1) // URIClientRequestID 27 28 // Exception for websocket endpoints 29 if rpcFunc.ws { 30 return func(w http.ResponseWriter, r *http.Request) { 31 WriteRPCResponseHTTP(w, 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, cdc, r) 43 if err != nil { 44 WriteRPCResponseHTTP( 45 w, 46 types.RPCInvalidParamsError( 47 dummyID, 48 fmt.Errorf("error converting http params to arguments: %w", err), 49 ), 50 ) 51 return 52 } 53 args = append(args, fnArgs...) 54 55 returns := rpcFunc.f.Call(args) 56 57 logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) 58 result, err := unreflectResult(returns) 59 if err != nil { 60 WriteRPCResponseHTTP(w, types.RPCInternalError(dummyID, err)) 61 return 62 } 63 WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(cdc, 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, cdc *amino.Codec, 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(cdc, 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(cdc, argType, arg) 97 if err != nil { 98 return nil, err 99 } 100 } 101 102 return values, nil 103 } 104 105 func jsonStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error) { 106 rv := reflect.New(rt) 107 err := cdc.UnmarshalJSON([]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(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, bool, error) { 116 if rt.Kind() == reflect.Ptr { 117 rv1, ok, err := nonJSONStringToArg(cdc, 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(cdc, rt, arg) 130 } 131 } 132 133 // NOTE: rt.Kind() isn't a pointer. 134 func _nonJSONStringToArg(cdc *amino.Codec, 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(cdc, 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 := cdc.UnmarshalJSON([]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 }