github.com/noirx94/tendermintmp@v0.0.1/rpc/jsonrpc/types/types.go (about)

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"reflect"
     9  	"strings"
    10  
    11  	tmjson "github.com/tendermint/tendermint/libs/json"
    12  )
    13  
    14  // a wrapper to emulate a sum type: jsonrpcid = string | int
    15  // TODO: refactor when Go 2.0 arrives https://github.com/golang/go/issues/19412
    16  type jsonrpcid interface {
    17  	isJSONRPCID()
    18  }
    19  
    20  // JSONRPCStringID a wrapper for JSON-RPC string IDs
    21  type JSONRPCStringID string
    22  
    23  func (JSONRPCStringID) isJSONRPCID()      {}
    24  func (id JSONRPCStringID) String() string { return string(id) }
    25  
    26  // JSONRPCIntID a wrapper for JSON-RPC integer IDs
    27  type JSONRPCIntID int
    28  
    29  func (JSONRPCIntID) isJSONRPCID()      {}
    30  func (id JSONRPCIntID) String() string { return fmt.Sprintf("%d", id) }
    31  
    32  func idFromInterface(idInterface interface{}) (jsonrpcid, error) {
    33  	switch id := idInterface.(type) {
    34  	case string:
    35  		return JSONRPCStringID(id), nil
    36  	case float64:
    37  		// json.Unmarshal uses float64 for all numbers
    38  		// (https://golang.org/pkg/encoding/json/#Unmarshal),
    39  		// but the JSONRPC2.0 spec says the id SHOULD NOT contain
    40  		// decimals - so we truncate the decimals here.
    41  		return JSONRPCIntID(int(id)), nil
    42  	default:
    43  		typ := reflect.TypeOf(id)
    44  		return nil, fmt.Errorf("json-rpc ID (%v) is of unknown type (%v)", id, typ)
    45  	}
    46  }
    47  
    48  //----------------------------------------
    49  // REQUEST
    50  
    51  type RPCRequest struct {
    52  	JSONRPC string          `json:"jsonrpc"`
    53  	ID      jsonrpcid       `json:"id,omitempty"`
    54  	Method  string          `json:"method"`
    55  	Params  json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
    56  }
    57  
    58  // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
    59  func (req *RPCRequest) UnmarshalJSON(data []byte) error {
    60  	unsafeReq := struct {
    61  		JSONRPC string          `json:"jsonrpc"`
    62  		ID      interface{}     `json:"id,omitempty"`
    63  		Method  string          `json:"method"`
    64  		Params  json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
    65  	}{}
    66  
    67  	err := json.Unmarshal(data, &unsafeReq)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	if unsafeReq.ID == nil { // notification
    73  		return nil
    74  	}
    75  
    76  	req.JSONRPC = unsafeReq.JSONRPC
    77  	req.Method = unsafeReq.Method
    78  	req.Params = unsafeReq.Params
    79  	id, err := idFromInterface(unsafeReq.ID)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	req.ID = id
    84  
    85  	return nil
    86  }
    87  
    88  func NewRPCRequest(id jsonrpcid, method string, params json.RawMessage) RPCRequest {
    89  	return RPCRequest{
    90  		JSONRPC: "2.0",
    91  		ID:      id,
    92  		Method:  method,
    93  		Params:  params,
    94  	}
    95  }
    96  
    97  func (req RPCRequest) String() string {
    98  	return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params)
    99  }
   100  
   101  func MapToRequest(id jsonrpcid, method string, params map[string]interface{}) (RPCRequest, error) {
   102  	var paramsMap = make(map[string]json.RawMessage, len(params))
   103  	for name, value := range params {
   104  		valueJSON, err := tmjson.Marshal(value)
   105  		if err != nil {
   106  			return RPCRequest{}, err
   107  		}
   108  		paramsMap[name] = valueJSON
   109  	}
   110  
   111  	payload, err := json.Marshal(paramsMap)
   112  	if err != nil {
   113  		return RPCRequest{}, err
   114  	}
   115  
   116  	return NewRPCRequest(id, method, payload), nil
   117  }
   118  
   119  func ArrayToRequest(id jsonrpcid, method string, params []interface{}) (RPCRequest, error) {
   120  	var paramsMap = make([]json.RawMessage, len(params))
   121  	for i, value := range params {
   122  		valueJSON, err := tmjson.Marshal(value)
   123  		if err != nil {
   124  			return RPCRequest{}, err
   125  		}
   126  		paramsMap[i] = valueJSON
   127  	}
   128  
   129  	payload, err := json.Marshal(paramsMap)
   130  	if err != nil {
   131  		return RPCRequest{}, err
   132  	}
   133  
   134  	return NewRPCRequest(id, method, payload), nil
   135  }
   136  
   137  //----------------------------------------
   138  // RESPONSE
   139  
   140  type RPCError struct {
   141  	Code    int    `json:"code"`
   142  	Message string `json:"message"`
   143  	Data    string `json:"data,omitempty"`
   144  }
   145  
   146  func (err RPCError) Error() string {
   147  	const baseFormat = "RPC error %v - %s"
   148  	if err.Data != "" {
   149  		return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data)
   150  	}
   151  	return fmt.Sprintf(baseFormat, err.Code, err.Message)
   152  }
   153  
   154  type RPCResponse struct {
   155  	JSONRPC string          `json:"jsonrpc"`
   156  	ID      jsonrpcid       `json:"id,omitempty"`
   157  	Result  json.RawMessage `json:"result,omitempty"`
   158  	Error   *RPCError       `json:"error,omitempty"`
   159  }
   160  
   161  // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
   162  func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
   163  	unsafeResp := &struct {
   164  		JSONRPC string          `json:"jsonrpc"`
   165  		ID      interface{}     `json:"id,omitempty"`
   166  		Result  json.RawMessage `json:"result,omitempty"`
   167  		Error   *RPCError       `json:"error,omitempty"`
   168  	}{}
   169  	err := json.Unmarshal(data, &unsafeResp)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	resp.JSONRPC = unsafeResp.JSONRPC
   174  	resp.Error = unsafeResp.Error
   175  	resp.Result = unsafeResp.Result
   176  	if unsafeResp.ID == nil {
   177  		return nil
   178  	}
   179  	id, err := idFromInterface(unsafeResp.ID)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	resp.ID = id
   184  	return nil
   185  }
   186  
   187  func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse {
   188  	var rawMsg json.RawMessage
   189  
   190  	if res != nil {
   191  		var js []byte
   192  		js, err := tmjson.Marshal(res)
   193  		if err != nil {
   194  			return RPCInternalError(id, fmt.Errorf("error marshalling response: %w", err))
   195  		}
   196  		rawMsg = json.RawMessage(js)
   197  	}
   198  
   199  	return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg}
   200  }
   201  
   202  func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {
   203  	return RPCResponse{
   204  		JSONRPC: "2.0",
   205  		ID:      id,
   206  		Error:   &RPCError{Code: code, Message: msg, Data: data},
   207  	}
   208  }
   209  
   210  func (resp RPCResponse) String() string {
   211  	if resp.Error == nil {
   212  		return fmt.Sprintf("RPCResponse{%s %X}", resp.ID, resp.Result)
   213  	}
   214  	return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error)
   215  }
   216  
   217  // From the JSON-RPC 2.0 spec:
   218  //	If there was an error in detecting the id in the Request object (e.g. Parse
   219  // 	error/Invalid Request), it MUST be Null.
   220  func RPCParseError(err error) RPCResponse {
   221  	return NewRPCErrorResponse(nil, -32700, "Parse error. Invalid JSON", err.Error())
   222  }
   223  
   224  // From the JSON-RPC 2.0 spec:
   225  //	If there was an error in detecting the id in the Request object (e.g. Parse
   226  // 	error/Invalid Request), it MUST be Null.
   227  func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse {
   228  	return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
   229  }
   230  
   231  func RPCMethodNotFoundError(id jsonrpcid) RPCResponse {
   232  	return NewRPCErrorResponse(id, -32601, "Method not found", "")
   233  }
   234  
   235  func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse {
   236  	return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
   237  }
   238  
   239  func RPCInternalError(id jsonrpcid, err error) RPCResponse {
   240  	return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
   241  }
   242  
   243  func RPCServerError(id jsonrpcid, err error) RPCResponse {
   244  	return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
   245  }
   246  
   247  //----------------------------------------
   248  
   249  // WSRPCConnection represents a websocket connection.
   250  type WSRPCConnection interface {
   251  	// GetRemoteAddr returns a remote address of the connection.
   252  	GetRemoteAddr() string
   253  	// WriteRPCResponse writes the response onto connection (BLOCKING).
   254  	WriteRPCResponse(context.Context, RPCResponse) error
   255  	// TryWriteRPCResponse tries to write the response onto connection (NON-BLOCKING).
   256  	TryWriteRPCResponse(RPCResponse) bool
   257  	// Context returns the connection's context.
   258  	Context() context.Context
   259  }
   260  
   261  // Context is the first parameter for all functions. It carries a json-rpc
   262  // request, http request and websocket connection.
   263  //
   264  // - JSONReq is non-nil when JSONRPC is called over websocket or HTTP.
   265  // - WSConn is non-nil when we're connected via a websocket.
   266  // - HTTPReq is non-nil when URI or JSONRPC is called over HTTP.
   267  type Context struct {
   268  	// json-rpc request
   269  	JSONReq *RPCRequest
   270  	// websocket connection
   271  	WSConn WSRPCConnection
   272  	// http request
   273  	HTTPReq *http.Request
   274  }
   275  
   276  // RemoteAddr returns the remote address (usually a string "IP:port").
   277  // If neither HTTPReq nor WSConn is set, an empty string is returned.
   278  // HTTP:
   279  //		http.Request#RemoteAddr
   280  // WS:
   281  //		result of GetRemoteAddr
   282  func (ctx *Context) RemoteAddr() string {
   283  	if ctx.HTTPReq != nil {
   284  		return ctx.HTTPReq.RemoteAddr
   285  	} else if ctx.WSConn != nil {
   286  		return ctx.WSConn.GetRemoteAddr()
   287  	}
   288  	return ""
   289  }
   290  
   291  // Context returns the request's context.
   292  // The returned context is always non-nil; it defaults to the background context.
   293  // HTTP:
   294  //		The context is canceled when the client's connection closes, the request
   295  //		is canceled (with HTTP/2), or when the ServeHTTP method returns.
   296  // WS:
   297  //		The context is canceled when the client's connections closes.
   298  func (ctx *Context) Context() context.Context {
   299  	if ctx.HTTPReq != nil {
   300  		return ctx.HTTPReq.Context()
   301  	} else if ctx.WSConn != nil {
   302  		return ctx.WSConn.Context()
   303  	}
   304  	return context.Background()
   305  }
   306  
   307  //----------------------------------------
   308  // SOCKETS
   309  
   310  //
   311  // Determine if its a unix or tcp socket.
   312  // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
   313  // TODO: deprecate
   314  func SocketType(listenAddr string) string {
   315  	socketType := "unix"
   316  	if len(strings.Split(listenAddr, ":")) >= 2 {
   317  		socketType = "tcp"
   318  	}
   319  	return socketType
   320  }