github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/jsonrpc/server/http_uri_handler.go (about) 1 package server 2 3 import ( 4 "encoding/hex" 5 "net/http" 6 "reflect" 7 "regexp" 8 "strings" 9 10 "github.com/pkg/errors" 11 12 amino "github.com/tendermint/go-amino" 13 14 "github.com/okex/exchain/libs/tendermint/libs/log" 15 types "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types" 16 ) 17 18 /////////////////////////////////////////////////////////////////////////////// 19 // HTTP + URI handler 20 /////////////////////////////////////////////////////////////////////////////// 21 22 var reInt = regexp.MustCompile(`^-?[0-9]+$`) 23 24 // convert from a function name to the http handler 25 func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func(http.ResponseWriter, *http.Request) { 26 // Always return -1 as there's no ID here. 27 dummyID := types.JSONRPCIntID(-1) // URIClientRequestID 28 29 // Exception for websocket endpoints 30 if rpcFunc.ws { 31 return func(w http.ResponseWriter, r *http.Request) { 32 WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(dummyID)) 33 } 34 } 35 36 // All other endpoints 37 return func(w http.ResponseWriter, r *http.Request) { 38 logger.Debug("HTTP HANDLER", "req", r) 39 40 ctx := &types.Context{HTTPReq: r} 41 args := []reflect.Value{reflect.ValueOf(ctx)} 42 43 fnArgs, err := httpParamsToArgs(rpcFunc, cdc, r) 44 if err != nil { 45 WriteRPCResponseHTTP( 46 w, 47 types.RPCInvalidParamsError( 48 dummyID, 49 errors.Wrap(err, "error converting http params to arguments"), 50 ), 51 ) 52 return 53 } 54 args = append(args, fnArgs...) 55 56 returns := rpcFunc.f.Call(args) 57 58 logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) 59 result, err := unreflectResult(returns) 60 if err != nil { 61 WriteRPCResponseHTTP(w, types.RPCInternalError(dummyID, err)) 62 return 63 } 64 WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(cdc, dummyID, result)) 65 } 66 } 67 68 // Covert an http query to a list of properly typed values. 69 // To be properly decoded the arg must be a concrete type from tendermint (if its an interface). 70 func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]reflect.Value, error) { 71 // skip types.Context 72 const argsOffset = 1 73 74 values := make([]reflect.Value, len(rpcFunc.argNames)) 75 76 for i, name := range rpcFunc.argNames { 77 argType := rpcFunc.args[i+argsOffset] 78 79 values[i] = reflect.Zero(argType) // set default for that type 80 81 arg := getParam(r, name) 82 // log.Notice("param to arg", "argType", argType, "name", name, "arg", arg) 83 84 if arg == "" { 85 continue 86 } 87 88 v, ok, err := nonJSONStringToArg(cdc, argType, arg) 89 if err != nil { 90 return nil, err 91 } 92 if ok { 93 values[i] = v 94 continue 95 } 96 97 values[i], err = jsonStringToArg(cdc, argType, arg) 98 if err != nil { 99 return nil, err 100 } 101 } 102 103 return values, nil 104 } 105 106 func jsonStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error) { 107 rv := reflect.New(rt) 108 err := cdc.UnmarshalJSON([]byte(arg), rv.Interface()) 109 if err != nil { 110 return rv, err 111 } 112 rv = rv.Elem() 113 return rv, nil 114 } 115 116 func nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, bool, error) { 117 if rt.Kind() == reflect.Ptr { 118 rv1, ok, err := nonJSONStringToArg(cdc, rt.Elem(), arg) 119 switch { 120 case err != nil: 121 return reflect.Value{}, false, err 122 case ok: 123 rv := reflect.New(rt.Elem()) 124 rv.Elem().Set(rv1) 125 return rv, true, nil 126 default: 127 return reflect.Value{}, false, nil 128 } 129 } else { 130 return _nonJSONStringToArg(cdc, rt, arg) 131 } 132 } 133 134 // NOTE: rt.Kind() isn't a pointer. 135 func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, bool, error) { 136 isIntString := reInt.Match([]byte(arg)) 137 isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`) 138 isHexString := strings.HasPrefix(strings.ToLower(arg), "0x") 139 140 var expectingString, expectingByteSlice, expectingInt bool 141 switch rt.Kind() { 142 case reflect.Int, 143 reflect.Uint, 144 reflect.Int8, 145 reflect.Uint8, 146 reflect.Int16, 147 reflect.Uint16, 148 reflect.Int32, 149 reflect.Uint32, 150 reflect.Int64, 151 reflect.Uint64: 152 expectingInt = true 153 case reflect.String: 154 expectingString = true 155 case reflect.Slice: 156 expectingByteSlice = rt.Elem().Kind() == reflect.Uint8 157 } 158 159 if isIntString && expectingInt { 160 qarg := `"` + arg + `"` 161 rv, err := jsonStringToArg(cdc, rt, qarg) 162 if err != nil { 163 return rv, false, err 164 } 165 166 return rv, true, nil 167 } 168 169 if isHexString { 170 if !expectingString && !expectingByteSlice { 171 err := errors.Errorf("got a hex string arg, but expected '%s'", 172 rt.Kind().String()) 173 return reflect.ValueOf(nil), false, err 174 } 175 176 var value []byte 177 value, err := hex.DecodeString(arg[2:]) 178 if err != nil { 179 return reflect.ValueOf(nil), false, err 180 } 181 if rt.Kind() == reflect.String { 182 return reflect.ValueOf(string(value)), true, nil 183 } 184 return reflect.ValueOf(value), true, nil 185 } 186 187 if isQuotedString && expectingByteSlice { 188 v := reflect.New(reflect.TypeOf("")) 189 err := cdc.UnmarshalJSON([]byte(arg), v.Interface()) 190 if err != nil { 191 return reflect.ValueOf(nil), false, err 192 } 193 v = v.Elem() 194 return reflect.ValueOf([]byte(v.String())), true, nil 195 } 196 197 return reflect.ValueOf(nil), false, nil 198 } 199 200 func getParam(r *http.Request, param string) string { 201 s := r.URL.Query().Get(param) 202 if s == "" { 203 s = r.FormValue(param) 204 } 205 return s 206 }