github.com/arcology-network/consensus-engine@v1.9.0/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/arcology-network/consensus-engine/libs/json" 12 "github.com/arcology-network/consensus-engine/libs/log" 13 types "github.com/arcology-network/consensus-engine/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 WriteRPCResponseHTTPError(w, http.StatusNotFound, 29 types.RPCMethodNotFoundError(dummyID)) 30 } 31 } 32 33 // All other endpoints 34 return func(w http.ResponseWriter, r *http.Request) { 35 logger.Debug("HTTP HANDLER", "req", r) 36 37 ctx := &types.Context{HTTPReq: r} 38 args := []reflect.Value{reflect.ValueOf(ctx)} 39 40 fnArgs, err := httpParamsToArgs(rpcFunc, r) 41 if err != nil { 42 WriteRPCResponseHTTPError( 43 w, 44 http.StatusInternalServerError, 45 types.RPCInvalidParamsError( 46 dummyID, 47 fmt.Errorf("error converting http params to arguments: %w", err), 48 ), 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 WriteRPCResponseHTTPError(w, http.StatusInternalServerError, 60 types.RPCInternalError(dummyID, err)) 61 return 62 } 63 WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(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, 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(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(argType, arg) 97 if err != nil { 98 return nil, err 99 } 100 } 101 102 return values, nil 103 } 104 105 func jsonStringToArg(rt reflect.Type, arg string) (reflect.Value, error) { 106 rv := reflect.New(rt) 107 err := tmjson.Unmarshal([]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(rt reflect.Type, arg string) (reflect.Value, bool, error) { 116 if rt.Kind() == reflect.Ptr { 117 rv1, ok, err := nonJSONStringToArg(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(rt, arg) 130 } 131 } 132 133 // NOTE: rt.Kind() isn't a pointer. 134 func _nonJSONStringToArg(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(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 := tmjson.Unmarshal([]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 }