github.com/jd-ly/tools@v0.5.7/internal/jsonrpc2/messages.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package jsonrpc2
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	errors "golang.org/x/xerrors"
    12  )
    13  
    14  // Message is the interface to all jsonrpc2 message types.
    15  // They share no common functionality, but are a closed set of concrete types
    16  // that are allowed to implement this interface. The message types are *Call,
    17  // *Notification and *Response.
    18  type Message interface {
    19  	// isJSONRPC2Message is used to make the set of message implementations a
    20  	// closed set.
    21  	isJSONRPC2Message()
    22  }
    23  
    24  // Request is the shared interface to jsonrpc2 messages that request
    25  // a method be invoked.
    26  // The request types are a closed set of *Call and *Notification.
    27  type Request interface {
    28  	Message
    29  	// Method is a string containing the method name to invoke.
    30  	Method() string
    31  	// Params is either a struct or an array with the parameters of the method.
    32  	Params() json.RawMessage
    33  	// isJSONRPC2Request is used to make the set of request implementations closed.
    34  	isJSONRPC2Request()
    35  }
    36  
    37  // Notification is a request for which a response cannot occur, and as such
    38  // it has not ID.
    39  type Notification struct {
    40  	// Method is a string containing the method name to invoke.
    41  	method string
    42  	params json.RawMessage
    43  }
    44  
    45  // Call is a request that expects a response.
    46  // The response will have a matching ID.
    47  type Call struct {
    48  	// Method is a string containing the method name to invoke.
    49  	method string
    50  	// Params is either a struct or an array with the parameters of the method.
    51  	params json.RawMessage
    52  	// id of this request, used to tie the Response back to the request.
    53  	id ID
    54  }
    55  
    56  // Response is a reply to a Call.
    57  // It will have the same ID as the call it is a response to.
    58  type Response struct {
    59  	// result is the content of the response.
    60  	result json.RawMessage
    61  	// err is set only if the call failed.
    62  	err error
    63  	// ID of the request this is a response to.
    64  	id ID
    65  }
    66  
    67  // NewNotification constructs a new Notification message for the supplied
    68  // method and parameters.
    69  func NewNotification(method string, params interface{}) (*Notification, error) {
    70  	p, merr := marshalToRaw(params)
    71  	return &Notification{method: method, params: p}, merr
    72  }
    73  
    74  func (msg *Notification) Method() string          { return msg.method }
    75  func (msg *Notification) Params() json.RawMessage { return msg.params }
    76  func (msg *Notification) isJSONRPC2Message()      {}
    77  func (msg *Notification) isJSONRPC2Request()      {}
    78  
    79  func (n *Notification) MarshalJSON() ([]byte, error) {
    80  	msg := wireRequest{Method: n.method, Params: &n.params}
    81  	data, err := json.Marshal(msg)
    82  	if err != nil {
    83  		return data, fmt.Errorf("marshaling notification: %w", err)
    84  	}
    85  	return data, nil
    86  }
    87  
    88  func (n *Notification) UnmarshalJSON(data []byte) error {
    89  	msg := wireRequest{}
    90  	if err := json.Unmarshal(data, &msg); err != nil {
    91  		return fmt.Errorf("unmarshaling notification: %w", err)
    92  	}
    93  	n.method = msg.Method
    94  	if msg.Params != nil {
    95  		n.params = *msg.Params
    96  	}
    97  	return nil
    98  }
    99  
   100  // NewCall constructs a new Call message for the supplied ID, method and
   101  // parameters.
   102  func NewCall(id ID, method string, params interface{}) (*Call, error) {
   103  	p, merr := marshalToRaw(params)
   104  	return &Call{id: id, method: method, params: p}, merr
   105  }
   106  
   107  func (msg *Call) Method() string          { return msg.method }
   108  func (msg *Call) Params() json.RawMessage { return msg.params }
   109  func (msg *Call) ID() ID                  { return msg.id }
   110  func (msg *Call) isJSONRPC2Message()      {}
   111  func (msg *Call) isJSONRPC2Request()      {}
   112  
   113  func (c *Call) MarshalJSON() ([]byte, error) {
   114  	msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id}
   115  	data, err := json.Marshal(msg)
   116  	if err != nil {
   117  		return data, fmt.Errorf("marshaling call: %w", err)
   118  	}
   119  	return data, nil
   120  }
   121  
   122  func (c *Call) UnmarshalJSON(data []byte) error {
   123  	msg := wireRequest{}
   124  	if err := json.Unmarshal(data, &msg); err != nil {
   125  		return fmt.Errorf("unmarshaling call: %w", err)
   126  	}
   127  	c.method = msg.Method
   128  	if msg.Params != nil {
   129  		c.params = *msg.Params
   130  	}
   131  	if msg.ID != nil {
   132  		c.id = *msg.ID
   133  	}
   134  	return nil
   135  }
   136  
   137  // NewResponse constructs a new Response message that is a reply to the
   138  // supplied. If err is set result may be ignored.
   139  func NewResponse(id ID, result interface{}, err error) (*Response, error) {
   140  	r, merr := marshalToRaw(result)
   141  	return &Response{id: id, result: r, err: err}, merr
   142  }
   143  
   144  func (msg *Response) ID() ID                  { return msg.id }
   145  func (msg *Response) Result() json.RawMessage { return msg.result }
   146  func (msg *Response) Err() error              { return msg.err }
   147  func (msg *Response) isJSONRPC2Message()      {}
   148  
   149  func (r *Response) MarshalJSON() ([]byte, error) {
   150  	msg := &wireResponse{Error: toWireError(r.err), ID: &r.id}
   151  	if msg.Error == nil {
   152  		msg.Result = &r.result
   153  	}
   154  	data, err := json.Marshal(msg)
   155  	if err != nil {
   156  		return data, fmt.Errorf("marshaling notification: %w", err)
   157  	}
   158  	return data, nil
   159  }
   160  
   161  func toWireError(err error) *wireError {
   162  	if err == nil {
   163  		// no error, the response is complete
   164  		return nil
   165  	}
   166  	if err, ok := err.(*wireError); ok {
   167  		// already a wire error, just use it
   168  		return err
   169  	}
   170  	result := &wireError{Message: err.Error()}
   171  	var wrapped *wireError
   172  	if errors.As(err, &wrapped) {
   173  		// if we wrapped a wire error, keep the code from the wrapped error
   174  		// but the message from the outer error
   175  		result.Code = wrapped.Code
   176  	}
   177  	return result
   178  }
   179  
   180  func (r *Response) UnmarshalJSON(data []byte) error {
   181  	msg := wireResponse{}
   182  	if err := json.Unmarshal(data, &msg); err != nil {
   183  		return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
   184  	}
   185  	if msg.Result != nil {
   186  		r.result = *msg.Result
   187  	}
   188  	if msg.Error != nil {
   189  		r.err = msg.Error
   190  	}
   191  	if msg.ID != nil {
   192  		r.id = *msg.ID
   193  	}
   194  	return nil
   195  }
   196  
   197  func DecodeMessage(data []byte) (Message, error) {
   198  	msg := wireCombined{}
   199  	if err := json.Unmarshal(data, &msg); err != nil {
   200  		return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
   201  	}
   202  	if msg.Method == "" {
   203  		// no method, should be a response
   204  		if msg.ID == nil {
   205  			return nil, ErrInvalidRequest
   206  		}
   207  		response := &Response{id: *msg.ID}
   208  		if msg.Error != nil {
   209  			response.err = msg.Error
   210  		}
   211  		if msg.Result != nil {
   212  			response.result = *msg.Result
   213  		}
   214  		return response, nil
   215  	}
   216  	// has a method, must be a request
   217  	if msg.ID == nil {
   218  		// request with no ID is a notify
   219  		notify := &Notification{method: msg.Method}
   220  		if msg.Params != nil {
   221  			notify.params = *msg.Params
   222  		}
   223  		return notify, nil
   224  	}
   225  	// request with an ID, must be a call
   226  	call := &Call{method: msg.Method, id: *msg.ID}
   227  	if msg.Params != nil {
   228  		call.params = *msg.Params
   229  	}
   230  	return call, nil
   231  }
   232  
   233  func marshalToRaw(obj interface{}) (json.RawMessage, error) {
   234  	data, err := json.Marshal(obj)
   235  	if err != nil {
   236  		return json.RawMessage{}, err
   237  	}
   238  	return json.RawMessage(data), nil
   239  }