github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/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  	err := json.Unmarshal(data, &unsafeReq)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	req.JSONRPC = unsafeReq.JSONRPC
    71  	req.Method = unsafeReq.Method
    72  	req.Params = unsafeReq.Params
    73  	if unsafeReq.ID == nil {
    74  		return nil
    75  	}
    76  	id, err := idFromInterface(unsafeReq.ID)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	req.ID = id
    81  	return nil
    82  }
    83  
    84  func NewRPCRequest(id jsonrpcid, method string, params json.RawMessage) RPCRequest {
    85  	return RPCRequest{
    86  		JSONRPC: "2.0",
    87  		ID:      id,
    88  		Method:  method,
    89  		Params:  params,
    90  	}
    91  }
    92  
    93  func (req RPCRequest) String() string {
    94  	return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params)
    95  }
    96  
    97  func MapToRequest(id jsonrpcid, method string, params map[string]interface{}) (RPCRequest, error) {
    98  	var paramsMap = make(map[string]json.RawMessage, len(params))
    99  	for name, value := range params {
   100  		valueJSON, err := tmjson.Marshal(value)
   101  		if err != nil {
   102  			return RPCRequest{}, err
   103  		}
   104  		paramsMap[name] = valueJSON
   105  	}
   106  
   107  	payload, err := json.Marshal(paramsMap)
   108  	if err != nil {
   109  		return RPCRequest{}, err
   110  	}
   111  
   112  	return NewRPCRequest(id, method, payload), nil
   113  }
   114  
   115  func ArrayToRequest(id jsonrpcid, method string, params []interface{}) (RPCRequest, error) {
   116  	var paramsMap = make([]json.RawMessage, len(params))
   117  	for i, value := range params {
   118  		valueJSON, err := tmjson.Marshal(value)
   119  		if err != nil {
   120  			return RPCRequest{}, err
   121  		}
   122  		paramsMap[i] = valueJSON
   123  	}
   124  
   125  	payload, err := json.Marshal(paramsMap)
   126  	if err != nil {
   127  		return RPCRequest{}, err
   128  	}
   129  
   130  	return NewRPCRequest(id, method, payload), nil
   131  }
   132  
   133  //----------------------------------------
   134  // RESPONSE
   135  
   136  type RPCError struct {
   137  	Code    int    `json:"code"`
   138  	Message string `json:"message"`
   139  	Data    string `json:"data,omitempty"`
   140  }
   141  
   142  func (err RPCError) Error() string {
   143  	const baseFormat = "RPC error %v - %s"
   144  	if err.Data != "" {
   145  		return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data)
   146  	}
   147  	return fmt.Sprintf(baseFormat, err.Code, err.Message)
   148  }
   149  
   150  type RPCResponse struct {
   151  	JSONRPC string          `json:"jsonrpc"`
   152  	ID      jsonrpcid       `json:"id,omitempty"`
   153  	Result  json.RawMessage `json:"result,omitempty"`
   154  	Error   *RPCError       `json:"error,omitempty"`
   155  }
   156  
   157  // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int
   158  func (resp *RPCResponse) UnmarshalJSON(data []byte) error {
   159  	unsafeResp := &struct {
   160  		JSONRPC string          `json:"jsonrpc"`
   161  		ID      interface{}     `json:"id,omitempty"`
   162  		Result  json.RawMessage `json:"result,omitempty"`
   163  		Error   *RPCError       `json:"error,omitempty"`
   164  	}{}
   165  	err := json.Unmarshal(data, &unsafeResp)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	resp.JSONRPC = unsafeResp.JSONRPC
   170  	resp.Error = unsafeResp.Error
   171  	resp.Result = unsafeResp.Result
   172  	if unsafeResp.ID == nil {
   173  		return nil
   174  	}
   175  	id, err := idFromInterface(unsafeResp.ID)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	resp.ID = id
   180  	return nil
   181  }
   182  
   183  func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse {
   184  	var rawMsg json.RawMessage
   185  
   186  	if res != nil {
   187  		var js []byte
   188  		js, err := tmjson.Marshal(res)
   189  		if err != nil {
   190  			return RPCInternalError(id, fmt.Errorf("error marshalling response: %w", err))
   191  		}
   192  		rawMsg = json.RawMessage(js)
   193  	}
   194  
   195  	return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg}
   196  }
   197  
   198  func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse {
   199  	return RPCResponse{
   200  		JSONRPC: "2.0",
   201  		ID:      id,
   202  		Error:   &RPCError{Code: code, Message: msg, Data: data},
   203  	}
   204  }
   205  
   206  func (resp RPCResponse) String() string {
   207  	if resp.Error == nil {
   208  		return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Result)
   209  	}
   210  	return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error)
   211  }
   212  
   213  // From the JSON-RPC 2.0 spec:
   214  //	If there was an error in detecting the id in the Request object (e.g. Parse
   215  // 	error/Invalid Request), it MUST be Null.
   216  func RPCParseError(err error) RPCResponse {
   217  	return NewRPCErrorResponse(nil, -32700, "Parse error. Invalid JSON", err.Error())
   218  }
   219  
   220  // From the JSON-RPC 2.0 spec:
   221  //	If there was an error in detecting the id in the Request object (e.g. Parse
   222  // 	error/Invalid Request), it MUST be Null.
   223  func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse {
   224  	return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
   225  }
   226  
   227  func RPCMethodNotFoundError(id jsonrpcid) RPCResponse {
   228  	return NewRPCErrorResponse(id, -32601, "Method not found", "")
   229  }
   230  
   231  func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse {
   232  	return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
   233  }
   234  
   235  func RPCInternalError(id jsonrpcid, err error) RPCResponse {
   236  	return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
   237  }
   238  
   239  func RPCServerError(id jsonrpcid, err error) RPCResponse {
   240  	return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
   241  }
   242  
   243  //----------------------------------------
   244  
   245  // WSRPCConnection represents a websocket connection.
   246  type WSRPCConnection interface {
   247  	// GetRemoteAddr returns a remote address of the connection.
   248  	GetRemoteAddr() string
   249  	// WriteRPCResponse writes the resp onto connection (BLOCKING).
   250  	WriteRPCResponse(resp RPCResponse)
   251  	// TryWriteRPCResponse tries to write the resp onto connection (NON-BLOCKING).
   252  	TryWriteRPCResponse(resp RPCResponse) bool
   253  	// Context returns the connection's context.
   254  	Context() context.Context
   255  }
   256  
   257  // Context is the first parameter for all functions. It carries a json-rpc
   258  // request, http request and websocket connection.
   259  //
   260  // - JSONReq is non-nil when JSONRPC is called over websocket or HTTP.
   261  // - WSConn is non-nil when we're connected via a websocket.
   262  // - HTTPReq is non-nil when URI or JSONRPC is called over HTTP.
   263  type Context struct {
   264  	// json-rpc request
   265  	JSONReq *RPCRequest
   266  	// websocket connection
   267  	WSConn WSRPCConnection
   268  	// http request
   269  	HTTPReq *http.Request
   270  }
   271  
   272  // RemoteAddr returns the remote address (usually a string "IP:port").
   273  // If neither HTTPReq nor WSConn is set, an empty string is returned.
   274  // HTTP:
   275  //		http.Request#RemoteAddr
   276  // WS:
   277  //		result of GetRemoteAddr
   278  func (ctx *Context) RemoteAddr() string {
   279  	if ctx.HTTPReq != nil {
   280  		return ctx.HTTPReq.RemoteAddr
   281  	} else if ctx.WSConn != nil {
   282  		return ctx.WSConn.GetRemoteAddr()
   283  	}
   284  	return ""
   285  }
   286  
   287  // Context returns the request's context.
   288  // The returned context is always non-nil; it defaults to the background context.
   289  // HTTP:
   290  //		The context is canceled when the client's connection closes, the request
   291  //		is canceled (with HTTP/2), or when the ServeHTTP method returns.
   292  // WS:
   293  //		The context is canceled when the client's connections closes.
   294  func (ctx *Context) Context() context.Context {
   295  	if ctx.HTTPReq != nil {
   296  		return ctx.HTTPReq.Context()
   297  	} else if ctx.WSConn != nil {
   298  		return ctx.WSConn.Context()
   299  	}
   300  	return context.Background()
   301  }
   302  
   303  //----------------------------------------
   304  // SOCKETS
   305  
   306  //
   307  // Determine if its a unix or tcp socket.
   308  // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
   309  // TODO: deprecate
   310  func SocketType(listenAddr string) string {
   311  	socketType := "unix"
   312  	if len(strings.Split(listenAddr, ":")) >= 2 {
   313  		socketType = "tcp"
   314  	}
   315  	return socketType
   316  }