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