Newer
Older
stdjson "encoding/json"
"github.com/goccy/go-json"
const (
defaultWriteTimeout = 10 * time.Second // used if context has no deadline
)
var null = json.RawMessage("null")
// A value of this type can a JSON-RPC request, notification, successful response or
// error response. Which one it is depends on the fields.
type jsonrpcMessage struct {
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Result json.RawMessage `json:"result,omitempty"`
}
func MakeCall(id int, method string, params []any) *JsonRpcMessage {
return &JsonRpcMessage{
}
}
type JsonRpcMessage = jsonrpcMessage
func (msg *jsonrpcMessage) isNotification() bool {
return msg.hasValidID() && len(msg.Method) == 0 && msg.Params == nil && (msg.Result != nil || msg.Error != nil)
func (msg *jsonrpcMessage) toResponse() *Response {
return &Response{
ID: msg.ID,
Result: msg.Result,
Error: msg.Error,
}
}
func (msg *jsonrpcMessage) isSubscribe() bool {
return strings.HasSuffix(msg.Method, subscribeMethodSuffix)
}
func (msg *jsonrpcMessage) isUnsubscribe() bool {
return strings.HasSuffix(msg.Method, unsubscribeMethodSuffix)
}
func (msg *jsonrpcMessage) namespace() string {
elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2)
return elem[0]
}
b, _ := json.Marshal(msg)
return string(b)
}
func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage {
resp := errorMessage(err)
return resp
}
func (msg *jsonrpcMessage) response(result any) *jsonrpcMessage {
// do a funny marshaling
enc, err := jzon.Marshal(result)
type jsonError struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
type JsonError = jsonError
func (err *jsonError) Error() string {
if err.Message == "" {
}
return err.Message
}
func (err *jsonError) ErrorCode() int {
return err.Code
}
func (err *jsonError) ErrorData() any {
return err.Data
}
// error message produces json rpc message with error message
func errorMessage(err error) *jsonrpcMessage {
msg := &jsonrpcMessage{
ID: NewNullIDPtr(),
Error: &jsonError{
Code: defaultErrorCode,
Message: err.Error(),
}}
ec, ok := err.(Error)
if ok {
msg.Error.Code = ec.ErrorCode()
}
de, ok := err.(DataError)
if ok {
msg.Error.Data = de.ErrorData()
}
return msg
}
// isBatch returns true when the first non-whitespace characters is '['
func isBatch(raw json.RawMessage) bool {
for _, c := range raw {
// skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt)
switch c {
case 0x20, 0x09, 0x0a, 0x0d:
continue
}
// parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error
// checks in this function because the raw message has already been syntax-checked when it
// is called. Any non-JSON-RPC messages in the input return the zero value of
// jsonrpcMessage.
func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) {
if !isBatch(raw) {
msgs := []*jsonrpcMessage{{}}
// TODO:
// for some reason other json decoders are incompatible with our test suite
// pretty sure its how we handle EOFs and stuff
dec := stdjson.NewDecoder(bytes.NewReader(raw))
dec.Token() // skip '['
var msgs []*jsonrpcMessage
for dec.More() {
msgs = append(msgs, new(jsonrpcMessage))
dec.Decode(&msgs[len(msgs)-1])
}
return msgs, true
}
// parsePositionalArguments tries to parse the given args to an array of values with the
// given types. It returns the parsed values or an error when the args could not be
// parsed. Missing optional arguments are returned as reflect.Zero values.
func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) {
var args []reflect.Value
switch {
var err error
if args, err = parseArgumentArray(rawArgs, types); err != nil {
default:
return nil, errors.New("non-array args")
}
// Set any missing args to nil.
for i := len(args); i < len(types); i++ {
if types[i].Kind() != reflect.Ptr {
return nil, fmt.Errorf("missing value for required argument %d", i)
}
args = append(args, reflect.Zero(types[i]))
}
return args, nil
}
func parseArgumentArray(p json.RawMessage, types []reflect.Type) ([]reflect.Value, error) {
dec := jzon.BorrowIterator(p)
defer jzon.ReturnIterator(dec)
if i >= len(types) {
return args, fmt.Errorf("too many arguments, want at most %d", len(types))
}
argval := reflect.New(types[i])
dec.ReadVal(argval.Interface())
if err := dec.Error; err != nil {
return args, fmt.Errorf("invalid argument %d: %v", i, err)
}
if argval.IsNil() && types[i].Kind() != reflect.Ptr {
return args, fmt.Errorf("missing value for required argument %d", i)
}
args = append(args, argval.Elem())
}