github.com/number571/tendermint@v0.34.11-gost/rpc/jsonrpc/server/http_uri_handler.go (about) 1 package server 2 3 import ( 4 "encoding/hex" 5 "errors" 6 "fmt" 7 "net/http" 8 "reflect" 9 "regexp" 10 "strings" 11 12 tmjson "github.com/number571/tendermint/libs/json" 13 "github.com/number571/tendermint/libs/log" 14 ctypes "github.com/number571/tendermint/rpc/core/types" 15 types "github.com/number571/tendermint/rpc/jsonrpc/types" 16 ) 17 18 // HTTP + URI handler 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 res := types.RPCMethodNotFoundError(dummyID) 31 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 32 logger.Error("failed to write response", "res", res, "err", wErr) 33 } 34 } 35 } 36 37 // All other endpoints 38 return func(w http.ResponseWriter, r *http.Request) { 39 logger.Debug("HTTP HANDLER", "req", r) 40 41 ctx := &types.Context{HTTPReq: r} 42 args := []reflect.Value{reflect.ValueOf(ctx)} 43 44 fnArgs, err := httpParamsToArgs(rpcFunc, r) 45 if err != nil { 46 res := types.RPCInvalidParamsError(dummyID, 47 fmt.Errorf("error converting http params to arguments: %w", err), 48 ) 49 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 50 logger.Error("failed to write response", "res", res, "err", wErr) 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 switch e := err.(type) { 61 // if no error then return a success response 62 case nil: 63 res := types.NewRPCSuccessResponse(dummyID, result) 64 if wErr := WriteRPCResponseHTTP(w, rpcFunc.cache, res); wErr != nil { 65 logger.Error("failed to write response", "res", res, "err", wErr) 66 } 67 68 // if this already of type RPC error then forward that error. 69 case *types.RPCError: 70 res := types.NewRPCErrorResponse(dummyID, e.Code, e.Message, e.Data) 71 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 72 logger.Error("failed to write response", "res", res, "err", wErr) 73 } 74 75 default: // we need to unwrap the error and parse it accordingly 76 var res types.RPCResponse 77 78 switch errors.Unwrap(err) { 79 case ctypes.ErrZeroOrNegativeHeight, 80 ctypes.ErrZeroOrNegativePerPage, 81 ctypes.ErrPageOutOfRange, 82 ctypes.ErrInvalidRequest: 83 res = types.RPCInvalidRequestError(dummyID, err) 84 default: // ctypes.ErrHeightNotAvailable, ctypes.ErrHeightExceedsChainHead: 85 res = types.RPCInternalError(dummyID, err) 86 } 87 88 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 89 logger.Error("failed to write response", "res", res, "err", wErr) 90 } 91 } 92 93 } 94 } 95 96 // Covert an http query to a list of properly typed values. 97 // To be properly decoded the arg must be a concrete type from tendermint (if its an interface). 98 func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) { 99 // skip types.Context 100 const argsOffset = 1 101 102 values := make([]reflect.Value, len(rpcFunc.argNames)) 103 104 for i, name := range rpcFunc.argNames { 105 argType := rpcFunc.args[i+argsOffset] 106 107 values[i] = reflect.Zero(argType) // set default for that type 108 109 arg := getParam(r, name) 110 // log.Notice("param to arg", "argType", argType, "name", name, "arg", arg) 111 112 if arg == "" { 113 continue 114 } 115 116 v, ok, err := nonJSONStringToArg(argType, arg) 117 if err != nil { 118 return nil, err 119 } 120 if ok { 121 values[i] = v 122 continue 123 } 124 125 values[i], err = jsonStringToArg(argType, arg) 126 if err != nil { 127 return nil, err 128 } 129 } 130 131 return values, nil 132 } 133 134 func jsonStringToArg(rt reflect.Type, arg string) (reflect.Value, error) { 135 rv := reflect.New(rt) 136 err := tmjson.Unmarshal([]byte(arg), rv.Interface()) 137 if err != nil { 138 return rv, err 139 } 140 rv = rv.Elem() 141 return rv, nil 142 } 143 144 func nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) { 145 if rt.Kind() == reflect.Ptr { 146 rv1, ok, err := nonJSONStringToArg(rt.Elem(), arg) 147 switch { 148 case err != nil: 149 return reflect.Value{}, false, err 150 case ok: 151 rv := reflect.New(rt.Elem()) 152 rv.Elem().Set(rv1) 153 return rv, true, nil 154 default: 155 return reflect.Value{}, false, nil 156 } 157 } else { 158 return _nonJSONStringToArg(rt, arg) 159 } 160 } 161 162 // NOTE: rt.Kind() isn't a pointer. 163 func _nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, bool, error) { 164 isIntString := reInt.Match([]byte(arg)) 165 isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`) 166 isHexString := strings.HasPrefix(strings.ToLower(arg), "0x") 167 168 var expectingString, expectingByteSlice, expectingInt bool 169 switch rt.Kind() { 170 case reflect.Int, 171 reflect.Uint, 172 reflect.Int8, 173 reflect.Uint8, 174 reflect.Int16, 175 reflect.Uint16, 176 reflect.Int32, 177 reflect.Uint32, 178 reflect.Int64, 179 reflect.Uint64: 180 expectingInt = true 181 case reflect.String: 182 expectingString = true 183 case reflect.Slice: 184 expectingByteSlice = rt.Elem().Kind() == reflect.Uint8 185 } 186 187 if isIntString && expectingInt { 188 qarg := `"` + arg + `"` 189 rv, err := jsonStringToArg(rt, qarg) 190 if err != nil { 191 return rv, false, err 192 } 193 194 return rv, true, nil 195 } 196 197 if isHexString { 198 if !expectingString && !expectingByteSlice { 199 err := fmt.Errorf("got a hex string arg, but expected '%s'", 200 rt.Kind().String()) 201 return reflect.ValueOf(nil), false, err 202 } 203 204 var value []byte 205 value, err := hex.DecodeString(arg[2:]) 206 if err != nil { 207 return reflect.ValueOf(nil), false, err 208 } 209 if rt.Kind() == reflect.String { 210 return reflect.ValueOf(string(value)), true, nil 211 } 212 return reflect.ValueOf(value), true, nil 213 } 214 215 if isQuotedString && expectingByteSlice { 216 v := reflect.New(reflect.TypeOf("")) 217 err := tmjson.Unmarshal([]byte(arg), v.Interface()) 218 if err != nil { 219 return reflect.ValueOf(nil), false, err 220 } 221 v = v.Elem() 222 return reflect.ValueOf([]byte(v.String())), true, nil 223 } 224 225 return reflect.ValueOf(nil), false, nil 226 } 227 228 func getParam(r *http.Request, param string) string { 229 s := r.URL.Query().Get(param) 230 if s == "" { 231 s = r.FormValue(param) 232 } 233 return s 234 }