github.com/number571/tendermint@v0.34.11-gost/rpc/jsonrpc/server/http_json_handler.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "reflect" 11 "sort" 12 13 tmjson "github.com/number571/tendermint/libs/json" 14 "github.com/number571/tendermint/libs/log" 15 ctypes "github.com/number571/tendermint/rpc/core/types" 16 "github.com/number571/tendermint/rpc/jsonrpc/types" 17 ) 18 19 // HTTP + JSON handler 20 21 // jsonrpc calls grab the given method's function info and runs reflect.Call 22 func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc { 23 return func(w http.ResponseWriter, r *http.Request) { 24 b, err := ioutil.ReadAll(r.Body) 25 if err != nil { 26 res := types.RPCInvalidRequestError(nil, 27 fmt.Errorf("error reading request body: %w", err), 28 ) 29 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 30 logger.Error("failed to write response", "res", res, "err", wErr) 31 } 32 return 33 } 34 35 // if its an empty request (like from a browser), just display a list of 36 // functions 37 if len(b) == 0 { 38 writeListOfEndpoints(w, r, funcMap) 39 return 40 } 41 42 // first try to unmarshal the incoming request as an array of RPC requests 43 var ( 44 requests []types.RPCRequest 45 responses []types.RPCResponse 46 ) 47 if err := json.Unmarshal(b, &requests); err != nil { 48 // next, try to unmarshal as a single request 49 var request types.RPCRequest 50 if err := json.Unmarshal(b, &request); err != nil { 51 res := types.RPCParseError(fmt.Errorf("error unmarshaling request: %w", err)) 52 if wErr := WriteRPCResponseHTTPError(w, res); wErr != nil { 53 logger.Error("failed to write response", "res", res, "err", wErr) 54 } 55 return 56 } 57 requests = []types.RPCRequest{request} 58 } 59 60 // Set the default response cache to true unless 61 // 1. Any RPC request rrror. 62 // 2. Any RPC request doesn't allow to be cached. 63 // 3. Any RPC request has the height argument and the value is 0 (the default). 64 var c = true 65 for _, request := range requests { 66 request := request 67 68 // A Notification is a Request object without an "id" member. 69 // The Server MUST NOT reply to a Notification, including those that are within a batch request. 70 if request.ID == nil { 71 logger.Debug( 72 "HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)", 73 "req", request, 74 ) 75 continue 76 } 77 if len(r.URL.Path) > 1 { 78 responses = append( 79 responses, 80 types.RPCInvalidRequestError(request.ID, fmt.Errorf("path %s is invalid", r.URL.Path)), 81 ) 82 c = false 83 continue 84 } 85 rpcFunc, ok := funcMap[request.Method] 86 if !ok || rpcFunc.ws { 87 responses = append(responses, types.RPCMethodNotFoundError(request.ID)) 88 c = false 89 continue 90 } 91 ctx := &types.Context{JSONReq: &request, HTTPReq: r} 92 args := []reflect.Value{reflect.ValueOf(ctx)} 93 if len(request.Params) > 0 { 94 fnArgs, err := jsonParamsToArgs(rpcFunc, request.Params) 95 if err != nil { 96 responses = append( 97 responses, 98 types.RPCInvalidParamsError(request.ID, fmt.Errorf("error converting json params to arguments: %w", err)), 99 ) 100 c = false 101 continue 102 } 103 args = append(args, fnArgs...) 104 105 } 106 107 if hasDefaultHeight(request, args) { 108 c = false 109 } 110 111 returns := rpcFunc.f.Call(args) 112 logger.Debug("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) 113 result, err := unreflectResult(returns) 114 switch e := err.(type) { 115 // if no error then return a success response 116 case nil: 117 responses = append(responses, types.NewRPCSuccessResponse(request.ID, result)) 118 119 // if this already of type RPC error then forward that error 120 case *types.RPCError: 121 responses = append(responses, types.NewRPCErrorResponse(request.ID, e.Code, e.Message, e.Data)) 122 c = false 123 default: // we need to unwrap the error and parse it accordingly 124 switch errors.Unwrap(err) { 125 // check if the error was due to an invald request 126 case ctypes.ErrZeroOrNegativeHeight, ctypes.ErrZeroOrNegativePerPage, 127 ctypes.ErrPageOutOfRange, ctypes.ErrInvalidRequest: 128 responses = append(responses, types.RPCInvalidRequestError(request.ID, err)) 129 c = false 130 // lastly default all remaining errors as internal errors 131 default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead 132 responses = append(responses, types.RPCInternalError(request.ID, err)) 133 c = false 134 } 135 } 136 137 if c && !rpcFunc.cache { 138 c = false 139 } 140 } 141 142 if len(responses) > 0 { 143 if wErr := WriteRPCResponseHTTP(w, c, responses...); wErr != nil { 144 logger.Error("failed to write responses", "err", wErr) 145 } 146 } 147 } 148 } 149 150 func handleInvalidJSONRPCPaths(next http.HandlerFunc) http.HandlerFunc { 151 return func(w http.ResponseWriter, r *http.Request) { 152 // Since the pattern "/" matches all paths not matched by other registered patterns, 153 // we check whether the path is indeed "/", otherwise return a 404 error 154 if r.URL.Path != "/" { 155 http.NotFound(w, r) 156 return 157 } 158 159 next(w, r) 160 } 161 } 162 163 func mapParamsToArgs( 164 rpcFunc *RPCFunc, 165 params map[string]json.RawMessage, 166 argsOffset int, 167 ) ([]reflect.Value, error) { 168 169 values := make([]reflect.Value, len(rpcFunc.argNames)) 170 for i, argName := range rpcFunc.argNames { 171 argType := rpcFunc.args[i+argsOffset] 172 173 if p, ok := params[argName]; ok && p != nil && len(p) > 0 { 174 val := reflect.New(argType) 175 err := tmjson.Unmarshal(p, val.Interface()) 176 if err != nil { 177 return nil, err 178 } 179 values[i] = val.Elem() 180 } else { // use default for that type 181 values[i] = reflect.Zero(argType) 182 } 183 } 184 185 return values, nil 186 } 187 188 func arrayParamsToArgs( 189 rpcFunc *RPCFunc, 190 params []json.RawMessage, 191 argsOffset int, 192 ) ([]reflect.Value, error) { 193 194 if len(rpcFunc.argNames) != len(params) { 195 return nil, fmt.Errorf("expected %v parameters (%v), got %v (%v)", 196 len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) 197 } 198 199 values := make([]reflect.Value, len(params)) 200 for i, p := range params { 201 argType := rpcFunc.args[i+argsOffset] 202 val := reflect.New(argType) 203 err := tmjson.Unmarshal(p, val.Interface()) 204 if err != nil { 205 return nil, err 206 } 207 values[i] = val.Elem() 208 } 209 return values, nil 210 } 211 212 // raw is unparsed json (from json.RawMessage) encoding either a map or an 213 // array. 214 // 215 // Example: 216 // rpcFunc.args = [rpctypes.Context string] 217 // rpcFunc.argNames = ["arg"] 218 func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte) ([]reflect.Value, error) { 219 const argsOffset = 1 220 221 // TODO: Make more efficient, perhaps by checking the first character for '{' or '['? 222 // First, try to get the map. 223 var m map[string]json.RawMessage 224 err := json.Unmarshal(raw, &m) 225 if err == nil { 226 return mapParamsToArgs(rpcFunc, m, argsOffset) 227 } 228 229 // Otherwise, try an array. 230 var a []json.RawMessage 231 err = json.Unmarshal(raw, &a) 232 if err == nil { 233 return arrayParamsToArgs(rpcFunc, a, argsOffset) 234 } 235 236 // Otherwise, bad format, we cannot parse 237 return nil, fmt.Errorf("unknown type for JSON params: %v. Expected map or array", err) 238 } 239 240 // writes a list of available rpc endpoints as an html page 241 func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) { 242 noArgNames := []string{} 243 argNames := []string{} 244 for name, funcData := range funcMap { 245 if len(funcData.args) == 0 { 246 noArgNames = append(noArgNames, name) 247 } else { 248 argNames = append(argNames, name) 249 } 250 } 251 sort.Strings(noArgNames) 252 sort.Strings(argNames) 253 buf := new(bytes.Buffer) 254 buf.WriteString("<html><body>") 255 buf.WriteString("<br>Available endpoints:<br>") 256 257 for _, name := range noArgNames { 258 link := fmt.Sprintf("//%s/%s", r.Host, name) 259 buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link)) 260 } 261 262 buf.WriteString("<br>Endpoints that require arguments:<br>") 263 for _, name := range argNames { 264 link := fmt.Sprintf("//%s/%s?", r.Host, name) 265 funcData := funcMap[name] 266 for i, argName := range funcData.argNames { 267 link += argName + "=_" 268 if i < len(funcData.argNames)-1 { 269 link += "&" 270 } 271 } 272 buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link)) 273 } 274 buf.WriteString("</body></html>") 275 w.Header().Set("Content-Type", "text/html") 276 w.WriteHeader(200) 277 w.Write(buf.Bytes()) // nolint: errcheck 278 } 279 280 func hasDefaultHeight(r types.RPCRequest, h []reflect.Value) bool { 281 switch r.Method { 282 case "block", "block_results", "commit", "consensus_params", "validators": 283 return len(h) < 2 || h[1].IsZero() 284 default: 285 return false 286 } 287 }