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 }