github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/rpc/lib/server/handlers.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "reflect" 11 "sort" 12 "strings" 13 14 "github.com/hyperledger/burrow/logging" 15 "github.com/hyperledger/burrow/rpc/lib/types" 16 "github.com/pkg/errors" 17 ) 18 19 // RegisterRPCFuncs adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions. 20 // "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse 21 func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger *logging.Logger) { 22 // HTTP endpoints 23 for funcName, rpcFunc := range funcMap { 24 mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger)) 25 } 26 27 // JSONRPC endpoints 28 mux.HandleFunc("/", makeJSONRPCHandler(funcMap, logger)) 29 } 30 31 //------------------------------------- 32 // function introspection 33 34 // RPCFunc contains the introspected type information for a function 35 type RPCFunc struct { 36 f reflect.Value // underlying rpc function 37 args []reflect.Type // type of each function arg 38 returns []reflect.Type // type of each return arg 39 argNames []string // name of each argument 40 ws bool // websocket only 41 } 42 43 // NewRPCFunc wraps a function for introspection. 44 // f is the function, args are comma separated argument names 45 func NewRPCFunc(f interface{}, args string) *RPCFunc { 46 return newRPCFunc(f, args, false) 47 } 48 49 // NewWSRPCFunc wraps a function for introspection and use in the websockets. 50 func NewWSRPCFunc(f interface{}, args string) *RPCFunc { 51 return newRPCFunc(f, args, true) 52 } 53 54 func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc { 55 var argNames []string 56 if args != "" { 57 argNames = strings.Split(args, ",") 58 } 59 return &RPCFunc{ 60 f: reflect.ValueOf(f), 61 args: funcArgTypes(f), 62 returns: funcReturnTypes(f), 63 argNames: argNames, 64 ws: ws, 65 } 66 } 67 68 // return a function's argument types 69 func funcArgTypes(f interface{}) []reflect.Type { 70 t := reflect.TypeOf(f) 71 n := t.NumIn() 72 typez := make([]reflect.Type, n) 73 for i := 0; i < n; i++ { 74 typez[i] = t.In(i) 75 } 76 return typez 77 } 78 79 // return a function's return types 80 func funcReturnTypes(f interface{}) []reflect.Type { 81 t := reflect.TypeOf(f) 82 n := t.NumOut() 83 typez := make([]reflect.Type, n) 84 for i := 0; i < n; i++ { 85 typez[i] = t.Out(i) 86 } 87 return typez 88 } 89 90 // function introspection 91 //----------------------------------------------------------------------------- 92 // rpc.json 93 94 // jsonrpc calls grab the given method's function info and runs reflect.Call 95 func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger *logging.Logger) http.HandlerFunc { 96 return func(w http.ResponseWriter, r *http.Request) { 97 b, err := ioutil.ReadAll(r.Body) 98 if err != nil { 99 WriteRPCResponseHTTP(w, types.RPCInvalidRequestError("", errors.Wrap(err, "Error reading request body"))) 100 return 101 } 102 // if its an empty request (like from a browser), 103 // just display a list of functions 104 if len(b) == 0 { 105 writeListOfEndpoints(w, r, funcMap) 106 return 107 } 108 109 var request types.RPCRequest 110 err = json.Unmarshal(b, &request) 111 if err != nil { 112 WriteRPCResponseHTTP(w, types.RPCParseError("", errors.Wrap(err, "Error unmarshalling request"))) 113 return 114 } 115 // A Notification is a Request object without an "id" member. 116 // The Server MUST NOT reply to a Notification, including those that are within a batch request. 117 if request.ID == "" { 118 logger.TraceMsg("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)") 119 return 120 } 121 if len(r.URL.Path) > 1 { 122 WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path))) 123 return 124 } 125 rpcFunc := funcMap[request.Method] 126 if rpcFunc == nil || rpcFunc.ws { 127 WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID)) 128 return 129 } 130 var args []reflect.Value 131 if len(request.Params) > 0 { 132 args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) 133 if err != nil { 134 WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) 135 return 136 } 137 } 138 returns := rpcFunc.f.Call(args) 139 logger.InfoMsg("HTTP JSONRPC called", "method", request.Method, "args", args, "returns", returns) 140 result, err := unreflectResult(returns) 141 if err != nil { 142 WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err)) 143 return 144 } 145 WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(request.ID, result)) 146 } 147 } 148 149 func mapParamsToArgs(rpcFunc *RPCFunc, params map[string]json.RawMessage, argsOffset int) ([]reflect.Value, error) { 150 values := make([]reflect.Value, len(rpcFunc.argNames)) 151 for i, argName := range rpcFunc.argNames { 152 argType := rpcFunc.args[i+argsOffset] 153 154 if p, ok := params[argName]; ok && p != nil && len(p) > 0 { 155 val := reflect.New(argType) 156 err := json.Unmarshal(p, val.Interface()) 157 if err != nil { 158 return nil, err 159 } 160 values[i] = val.Elem() 161 } else { // use default for that type 162 values[i] = reflect.Zero(argType) 163 } 164 } 165 166 return values, nil 167 } 168 169 func arrayParamsToArgs(rpcFunc *RPCFunc, params []json.RawMessage, argsOffset int) ([]reflect.Value, error) { 170 if len(rpcFunc.argNames) != len(params) { 171 return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)", 172 len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) 173 } 174 175 values := make([]reflect.Value, len(params)) 176 for i, p := range params { 177 argType := rpcFunc.args[i+argsOffset] 178 val := reflect.New(argType) 179 err := json.Unmarshal(p, val.Interface()) 180 if err != nil { 181 return nil, err 182 } 183 values[i] = val.Elem() 184 } 185 return values, nil 186 } 187 188 // `raw` is unparsed json (from json.RawMessage) encoding either a map or an array. 189 // `argsOffset` should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames). 190 // 191 // Example: 192 // rpcFunc.args = [rpctypes.WSRPCContext string] 193 // rpcFunc.argNames = ["arg"] 194 func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) { 195 196 // TODO: Make more efficient, perhaps by checking the first character for '{' or '['? 197 // First, try to get the map. 198 var m map[string]json.RawMessage 199 err := json.Unmarshal(raw, &m) 200 if err == nil { 201 return mapParamsToArgs(rpcFunc, m, argsOffset) 202 } 203 204 // Otherwise, try an array. 205 var a []json.RawMessage 206 err = json.Unmarshal(raw, &a) 207 if err == nil { 208 return arrayParamsToArgs(rpcFunc, a, argsOffset) 209 } 210 211 // Otherwise, bad format, we cannot parse 212 return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err) 213 } 214 215 // Convert a []interface{} OR a map[string]interface{} to properly typed values 216 func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params json.RawMessage) ([]reflect.Value, error) { 217 return jsonParamsToArgs(rpcFunc, params, 0) 218 } 219 220 // convert from a function name to the http handler 221 func makeHTTPHandler(rpcFunc *RPCFunc, logger *logging.Logger) func(http.ResponseWriter, *http.Request) { 222 // Exception for websocket endpoints 223 if rpcFunc.ws { 224 return func(w http.ResponseWriter, r *http.Request) { 225 WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError("")) 226 } 227 } 228 logger = logger.WithScope("makeHTTPHandler") 229 // All other endpoints 230 return func(w http.ResponseWriter, r *http.Request) { 231 logger.TraceMsg("HTTP REST Handler received request", "request", r) 232 args, err := httpParamsToArgs(rpcFunc, r) 233 if err != nil { 234 WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("", errors.Wrap(err, "Error converting http params to arguments"))) 235 return 236 } 237 returns := rpcFunc.f.Call(args) 238 logger.InfoMsg("HTTP REST", "method", r.URL.Path, "args", args, "returns", returns) 239 result, err := unreflectResult(returns) 240 if err != nil { 241 WriteRPCResponseHTTP(w, types.RPCInternalError("", err)) 242 return 243 } 244 WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result)) 245 } 246 } 247 248 // Covert an http query to a list of properly typed values. 249 // To be properly decoded the arg must be a concrete type from tendermint (if its an interface). 250 func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) { 251 values := make([]reflect.Value, len(rpcFunc.args)) 252 253 for i, name := range rpcFunc.argNames { 254 argType := rpcFunc.args[i] 255 256 values[i] = reflect.Zero(argType) // set default for that type 257 258 arg := GetParam(r, name) 259 // log.Notice("param to arg", "argType", argType, "name", name, "arg", arg) 260 261 if arg == "" { 262 continue 263 } 264 265 v, ok, err := nonJSONToArg(argType, arg) 266 if err != nil { 267 return nil, err 268 } 269 if ok { 270 values[i] = v 271 continue 272 } 273 274 values[i], err = jsonStringToArg(argType, arg) 275 if err != nil { 276 return nil, err 277 } 278 } 279 280 return values, nil 281 } 282 283 func jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) { 284 v := reflect.New(ty) 285 err := json.Unmarshal([]byte(arg), v.Interface()) 286 if err != nil { 287 return v, err 288 } 289 v = v.Elem() 290 return v, nil 291 } 292 293 func nonJSONToArg(ty reflect.Type, arg string) (reflect.Value, bool, error) { 294 expectingString := ty.Kind() == reflect.String 295 expectingBytes := (ty.Kind() == reflect.Slice || ty.Kind() == reflect.Array) && ty.Elem().Kind() == reflect.Uint8 296 297 isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`) 298 299 // Throw quoted strings at JSON parser later... because it always has... 300 if expectingString && !isQuotedString { 301 return reflect.ValueOf(arg), true, nil 302 } 303 304 if expectingBytes { 305 if isQuotedString { 306 rv := reflect.New(ty) 307 err := json.Unmarshal([]byte(arg), rv.Interface()) 308 if err != nil { 309 return reflect.ValueOf(nil), false, err 310 } 311 return rv.Elem(), true, nil 312 } 313 if strings.HasPrefix(strings.ToLower(arg), "0x") { 314 arg = arg[2:] 315 } 316 var value []byte 317 value, err := hex.DecodeString(arg) 318 if err != nil { 319 return reflect.ValueOf(nil), false, err 320 } 321 if ty.Kind() == reflect.Array { 322 // Gives us an empty array of the right type 323 rv := reflect.New(ty).Elem() 324 reflect.Copy(rv, reflect.ValueOf(value)) 325 return rv, true, nil 326 } 327 return reflect.ValueOf(value), true, nil 328 } 329 330 return reflect.ValueOf(nil), false, nil 331 } 332 333 // NOTE: assume returns is result struct and error. If error is not nil, return it 334 func unreflectResult(returns []reflect.Value) (interface{}, error) { 335 errV := returns[1] 336 if errV.Interface() != nil { 337 return nil, errors.Errorf("%v", errV.Interface()) 338 } 339 rv := returns[0] 340 // the result is a registered interface, 341 // we need a pointer to it so we can marshal with type byte 342 rvp := reflect.New(rv.Type()) 343 rvp.Elem().Set(rv) 344 return rvp.Interface(), nil 345 } 346 347 // writes a list of available rpc endpoints as an html page 348 func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) { 349 noArgNames := []string{} 350 argNames := []string{} 351 for name, funcData := range funcMap { 352 if len(funcData.args) == 0 { 353 noArgNames = append(noArgNames, name) 354 } else { 355 argNames = append(argNames, name) 356 } 357 } 358 sort.Strings(noArgNames) 359 sort.Strings(argNames) 360 buf := new(bytes.Buffer) 361 buf.WriteString("<html><body>") 362 buf.WriteString("<br>Available endpoints:<br>") 363 364 for _, name := range noArgNames { 365 link := fmt.Sprintf("//%s/%s", r.Host, name) 366 buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link)) 367 } 368 369 buf.WriteString("<br>Endpoints that require arguments:<br>") 370 for _, name := range argNames { 371 link := fmt.Sprintf("//%s/%s?", r.Host, name) 372 funcData := funcMap[name] 373 for i, argName := range funcData.argNames { 374 link += argName + "=_" 375 if i < len(funcData.argNames)-1 { 376 link += "&" 377 } 378 } 379 buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link)) 380 } 381 buf.WriteString("</body></html>") 382 w.Header().Set("Content-Type", "text/html") 383 w.WriteHeader(200) 384 w.Write(buf.Bytes()) // nolint: errcheck 385 }