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  }