github.com/v2fly/tools@v0.100.0/internal/jsonrpc2_v2/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  
    10  	errors "golang.org/x/xerrors"
    11  )
    12  
    13  // ID is a Request identifier.
    14  type ID struct {
    15  	value interface{}
    16  }
    17  
    18  // Message is the interface to all jsonrpc2 message types.
    19  // They share no common functionality, but are a closed set of concrete types
    20  // that are allowed to implement this interface. The message types are *Request
    21  // and *Response.
    22  type Message interface {
    23  	// marshal builds the wire form from the API form.
    24  	// It is private, which makes the set of Message implementations closed.
    25  	marshal(to *wireCombined)
    26  }
    27  
    28  // Request is a Message sent to a peer to request behavior.
    29  // If it has an ID it is a call, otherwise it is a notification.
    30  type Request struct {
    31  	// ID of this request, used to tie the Response back to the request.
    32  	// This will be nil for notifications.
    33  	ID ID
    34  	// Method is a string containing the method name to invoke.
    35  	Method string
    36  	// Params is either a struct or an array with the parameters of the method.
    37  	Params json.RawMessage
    38  }
    39  
    40  // Response is a Message used as a reply to a call Request.
    41  // It will have the same ID as the call it is a response to.
    42  type Response struct {
    43  	// result is the content of the response.
    44  	Result json.RawMessage
    45  	// err is set only if the call failed.
    46  	Error error
    47  	// id of the request this is a response to.
    48  	ID ID
    49  }
    50  
    51  // StringID creates a new string request identifier.
    52  func StringID(s string) ID { return ID{value: s} }
    53  
    54  // Int64ID creates a new integer request identifier.
    55  func Int64ID(i int64) ID { return ID{value: i} }
    56  
    57  // IsValid returns true if the ID is a valid identifier.
    58  // The default value for ID will return false.
    59  func (id ID) IsValid() bool { return id.value != nil }
    60  
    61  // Raw returns the underlying value of the ID.
    62  func (id ID) Raw() interface{} { return id.value }
    63  
    64  // NewNotification constructs a new Notification message for the supplied
    65  // method and parameters.
    66  func NewNotification(method string, params interface{}) (*Request, error) {
    67  	p, merr := marshalToRaw(params)
    68  	return &Request{Method: method, Params: p}, merr
    69  }
    70  
    71  // NewCall constructs a new Call message for the supplied ID, method and
    72  // parameters.
    73  func NewCall(id ID, method string, params interface{}) (*Request, error) {
    74  	p, merr := marshalToRaw(params)
    75  	return &Request{ID: id, Method: method, Params: p}, merr
    76  }
    77  
    78  func (msg *Request) IsCall() bool { return msg.ID.IsValid() }
    79  
    80  func (msg *Request) marshal(to *wireCombined) {
    81  	to.ID = msg.ID.value
    82  	to.Method = msg.Method
    83  	to.Params = msg.Params
    84  }
    85  
    86  // NewResponse constructs a new Response message that is a reply to the
    87  // supplied. If err is set result may be ignored.
    88  func NewResponse(id ID, result interface{}, rerr error) (*Response, error) {
    89  	r, merr := marshalToRaw(result)
    90  	return &Response{ID: id, Result: r, Error: rerr}, merr
    91  }
    92  
    93  func (msg *Response) marshal(to *wireCombined) {
    94  	to.ID = msg.ID.value
    95  	to.Error = toWireError(msg.Error)
    96  	to.Result = msg.Result
    97  }
    98  
    99  func toWireError(err error) *wireError {
   100  	if err == nil {
   101  		// no error, the response is complete
   102  		return nil
   103  	}
   104  	if err, ok := err.(*wireError); ok {
   105  		// already a wire error, just use it
   106  		return err
   107  	}
   108  	result := &wireError{Message: err.Error()}
   109  	var wrapped *wireError
   110  	if errors.As(err, &wrapped) {
   111  		// if we wrapped a wire error, keep the code from the wrapped error
   112  		// but the message from the outer error
   113  		result.Code = wrapped.Code
   114  	}
   115  	return result
   116  }
   117  
   118  func EncodeMessage(msg Message) ([]byte, error) {
   119  	wire := wireCombined{VersionTag: wireVersion}
   120  	msg.marshal(&wire)
   121  	data, err := json.Marshal(&wire)
   122  	if err != nil {
   123  		return data, errors.Errorf("marshaling jsonrpc message: %w", err)
   124  	}
   125  	return data, nil
   126  }
   127  
   128  func DecodeMessage(data []byte) (Message, error) {
   129  	msg := wireCombined{}
   130  	if err := json.Unmarshal(data, &msg); err != nil {
   131  		return nil, errors.Errorf("unmarshaling jsonrpc message: %w", err)
   132  	}
   133  	if msg.VersionTag != wireVersion {
   134  		return nil, errors.Errorf("invalid message version tag %s expected %s", msg.VersionTag, wireVersion)
   135  	}
   136  	id := ID{}
   137  	switch v := msg.ID.(type) {
   138  	case nil:
   139  	case float64:
   140  		// coerce the id type to int64 if it is float64, the spec does not allow fractional parts
   141  		id = Int64ID(int64(v))
   142  	case int64:
   143  		id = Int64ID(v)
   144  	case string:
   145  		id = StringID(v)
   146  	default:
   147  		return nil, errors.Errorf("invalid message id type <%T>%v", v, v)
   148  	}
   149  	if msg.Method != "" {
   150  		// has a method, must be a call
   151  		return &Request{
   152  			Method: msg.Method,
   153  			ID:     id,
   154  			Params: msg.Params,
   155  		}, nil
   156  	}
   157  	// no method, should be a response
   158  	if !id.IsValid() {
   159  		return nil, ErrInvalidRequest
   160  	}
   161  	resp := &Response{
   162  		ID:     id,
   163  		Result: msg.Result,
   164  	}
   165  	// we have to check if msg.Error is nil to avoid a typed error
   166  	if msg.Error != nil {
   167  		resp.Error = msg.Error
   168  	}
   169  	return resp, nil
   170  }
   171  
   172  func marshalToRaw(obj interface{}) (json.RawMessage, error) {
   173  	if obj == nil {
   174  		return nil, nil
   175  	}
   176  	data, err := json.Marshal(obj)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	return json.RawMessage(data), nil
   181  }