github.com/goplus/gop@v1.2.6/x/jsonrpc2/messages.go (about)

     1  /*
     2   * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Copyright 2018 The Go Authors. All rights reserved.
    18  // Use of this source code is governed by a BSD-style
    19  // license that can be found in the LICENSE file.
    20  
    21  package jsonrpc2
    22  
    23  import (
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  )
    28  
    29  // ID is a Request identifier.
    30  type ID struct {
    31  	value interface{}
    32  }
    33  
    34  // Message is the interface to all jsonrpc2 message types.
    35  // They share no common functionality, but are a closed set of concrete types
    36  // that are allowed to implement this interface. The message types are *Request
    37  // and *Response.
    38  type Message interface {
    39  	// marshal builds the wire form from the API form.
    40  	// It is private, which makes the set of Message implementations closed.
    41  	marshal(to *wireCombined)
    42  }
    43  
    44  // Request is a Message sent to a peer to request behavior.
    45  // If it has an ID it is a call, otherwise it is a notification.
    46  type Request struct {
    47  	// ID of this request, used to tie the Response back to the request.
    48  	// This will be nil for notifications.
    49  	ID ID
    50  	// Method is a string containing the method name to invoke.
    51  	Method string
    52  	// Params is either a struct or an array with the parameters of the method.
    53  	Params json.RawMessage
    54  }
    55  
    56  // Response is a Message used as a reply to a call Request.
    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  	Error error
    63  	// id of the request this is a response to.
    64  	ID ID
    65  }
    66  
    67  // StringID creates a new string request identifier.
    68  func StringID(s string) ID { return ID{value: s} }
    69  
    70  // Int64ID creates a new integer request identifier.
    71  func Int64ID(i int64) ID { return ID{value: i} }
    72  
    73  // IsValid returns true if the ID is a valid identifier.
    74  // The default value for ID will return false.
    75  func (id ID) IsValid() bool { return id.value != nil }
    76  
    77  // Raw returns the underlying value of the ID.
    78  func (id ID) Raw() interface{} { return id.value }
    79  
    80  // NewNotification constructs a new Notification message for the supplied
    81  // method and parameters.
    82  func NewNotification(method string, params interface{}) (*Request, error) {
    83  	p, merr := marshalToRaw(params)
    84  	return &Request{Method: method, Params: p}, merr
    85  }
    86  
    87  // NewCall constructs a new Call message for the supplied ID, method and
    88  // parameters.
    89  func NewCall(id ID, method string, params interface{}) (*Request, error) {
    90  	p, merr := marshalToRaw(params)
    91  	return &Request{ID: id, Method: method, Params: p}, merr
    92  }
    93  
    94  func (msg *Request) IsCall() bool { return msg.ID.IsValid() }
    95  
    96  func (msg *Request) marshal(to *wireCombined) {
    97  	to.ID = msg.ID.value
    98  	to.Method = msg.Method
    99  	to.Params = msg.Params
   100  }
   101  
   102  // NewResponse constructs a new Response message that is a reply to the
   103  // supplied. If err is set result may be ignored.
   104  func NewResponse(id ID, result interface{}, rerr error) (*Response, error) {
   105  	r, merr := marshalToRaw(result)
   106  	return &Response{ID: id, Result: r, Error: rerr}, merr
   107  }
   108  
   109  func (msg *Response) marshal(to *wireCombined) {
   110  	to.ID = msg.ID.value
   111  	to.Error = toWireError(msg.Error)
   112  	to.Result = msg.Result
   113  }
   114  
   115  func toWireError(err error) *wireError {
   116  	if err == nil {
   117  		// no error, the response is complete
   118  		return nil
   119  	}
   120  	if err, ok := err.(*wireError); ok {
   121  		// already a wire error, just use it
   122  		return err
   123  	}
   124  	result := &wireError{Message: err.Error()}
   125  	var wrapped *wireError
   126  	if errors.As(err, &wrapped) {
   127  		// if we wrapped a wire error, keep the code from the wrapped error
   128  		// but the message from the outer error
   129  		result.Code = wrapped.Code
   130  	}
   131  	return result
   132  }
   133  
   134  func EncodeMessage(msg Message) ([]byte, error) {
   135  	wire := wireCombined{VersionTag: wireVersion}
   136  	msg.marshal(&wire)
   137  	data, err := json.Marshal(&wire)
   138  	if err != nil {
   139  		return data, fmt.Errorf("marshaling jsonrpc message: %w", err)
   140  	}
   141  	return data, nil
   142  }
   143  
   144  func DecodeMessage(data []byte) (Message, error) {
   145  	msg := wireCombined{}
   146  	if err := json.Unmarshal(data, &msg); err != nil {
   147  		return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
   148  	}
   149  	if msg.VersionTag != wireVersion {
   150  		return nil, fmt.Errorf("invalid message version tag %s expected %s", msg.VersionTag, wireVersion)
   151  	}
   152  	id := ID{}
   153  	switch v := msg.ID.(type) {
   154  	case nil:
   155  	case float64:
   156  		// coerce the id type to int64 if it is float64, the spec does not allow fractional parts
   157  		id = Int64ID(int64(v))
   158  	case int64:
   159  		id = Int64ID(v)
   160  	case string:
   161  		id = StringID(v)
   162  	default:
   163  		return nil, fmt.Errorf("invalid message id type <%T>%v", v, v)
   164  	}
   165  	if msg.Method != "" {
   166  		// has a method, must be a call
   167  		return &Request{
   168  			Method: msg.Method,
   169  			ID:     id,
   170  			Params: msg.Params,
   171  		}, nil
   172  	}
   173  	// no method, should be a response
   174  	if !id.IsValid() {
   175  		return nil, ErrInvalidRequest
   176  	}
   177  	resp := &Response{
   178  		ID:     id,
   179  		Result: msg.Result,
   180  	}
   181  	// we have to check if msg.Error is nil to avoid a typed error
   182  	if msg.Error != nil {
   183  		resp.Error = msg.Error
   184  	}
   185  	return resp, nil
   186  }
   187  
   188  func marshalToRaw(obj interface{}) (json.RawMessage, error) {
   189  	if obj == nil {
   190  		return nil, nil
   191  	}
   192  	data, err := json.Marshal(obj)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	return json.RawMessage(data), nil
   197  }