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 }