github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/rpc/jsonrpc/server/http_uri_handler.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"net/http"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/ari-anchor/sei-tendermint/libs/log"
    13  	rpctypes "github.com/ari-anchor/sei-tendermint/rpc/jsonrpc/types"
    14  )
    15  
    16  // uriReqID is a placeholder ID used for GET requests, which do not receive a
    17  // JSON-RPC request ID from the caller.
    18  const uriReqID = -1
    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  	return func(w http.ResponseWriter, req *http.Request) {
    23  		ctx := rpctypes.WithCallInfo(req.Context(), &rpctypes.CallInfo{
    24  			HTTPRequest: req,
    25  		})
    26  		args, err := parseURLParams(rpcFunc.args, req)
    27  		if err != nil {
    28  			w.Header().Set("Content-Type", "text/plain")
    29  			w.WriteHeader(http.StatusBadRequest)
    30  			fmt.Fprintln(w, err.Error())
    31  			return
    32  		}
    33  		jreq := rpctypes.NewRequest(uriReqID)
    34  		result, err := rpcFunc.Call(ctx, args)
    35  		if err == nil {
    36  			writeHTTPResponse(w, logger, jreq.MakeResponse(result))
    37  		} else {
    38  			writeHTTPResponse(w, logger, jreq.MakeError(err))
    39  		}
    40  	}
    41  }
    42  
    43  func parseURLParams(args []argInfo, req *http.Request) ([]byte, error) {
    44  	if err := req.ParseForm(); err != nil {
    45  		return nil, fmt.Errorf("invalid HTTP request: %w", err)
    46  	}
    47  	getArg := func(name string) (string, bool) {
    48  		if req.Form.Has(name) {
    49  			return req.Form.Get(name), true
    50  		}
    51  		return "", false
    52  	}
    53  
    54  	params := make(map[string]interface{})
    55  	for _, arg := range args {
    56  		v, ok := getArg(arg.name)
    57  		if !ok {
    58  			continue
    59  		}
    60  		if z, err := decodeInteger(v); err == nil {
    61  			params[arg.name] = z
    62  		} else if b, err := strconv.ParseBool(v); err == nil {
    63  			params[arg.name] = b
    64  		} else if lc := strings.ToLower(v); strings.HasPrefix(lc, "0x") {
    65  			dec, err := hex.DecodeString(lc[2:])
    66  			if err != nil {
    67  				return nil, fmt.Errorf("invalid hex string: %w", err)
    68  			} else if len(dec) == 0 {
    69  				return nil, errors.New("invalid empty hex string")
    70  			}
    71  			if arg.isBinary {
    72  				params[arg.name] = dec
    73  			} else {
    74  				params[arg.name] = string(dec)
    75  			}
    76  		} else if isQuotedString(v) {
    77  			var dec string
    78  			if err := json.Unmarshal([]byte(v), &dec); err != nil {
    79  				return nil, fmt.Errorf("invalid quoted string: %w", err)
    80  			}
    81  			if arg.isBinary {
    82  				params[arg.name] = []byte(dec)
    83  			} else {
    84  				params[arg.name] = dec
    85  			}
    86  		} else {
    87  			params[arg.name] = v
    88  		}
    89  	}
    90  	return json.Marshal(params)
    91  }
    92  
    93  // isQuotedString reports whether s is enclosed in double quotes.
    94  func isQuotedString(s string) bool {
    95  	return len(s) >= 2 && strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`)
    96  }
    97  
    98  // decodeInteger decodes s into an int64. If s is "double quoted" the quotes
    99  // are removed; otherwise s must be a base-10 digit string.
   100  func decodeInteger(s string) (int64, error) {
   101  	if isQuotedString(s) {
   102  		s = s[1 : len(s)-1]
   103  	}
   104  	return strconv.ParseInt(s, 10, 64)
   105  }