good morning!!!!

Skip to content
Snippets Groups Projects
json.go 5.47 KiB
Newer Older
a's avatar
a committed
package jsonrpc
a's avatar
rpc
a committed

import (
a's avatar
a committed
	"bytes"
a's avatar
a committed
	"encoding/json"
a's avatar
a committed
	"fmt"
a's avatar
a committed
	"io"
a's avatar
a committed
	"strconv"
a's avatar
a committed
	"strings"
a's avatar
ok  
a committed

a's avatar
a committed
	"github.com/go-faster/jx"
a's avatar
rpc
a committed
)

a's avatar
a committed
const NullString = "null"

var Null = json.RawMessage(NullString)
a's avatar
rpc
a committed

a's avatar
a committed
func NewNull() json.RawMessage {
	return json.RawMessage("null")
}

a's avatar
rpc
a committed
// A value of this type can a JSON-RPC request, notification, successful response or
// error response. Which one it is depends on the fields.
a's avatar
a committed
type Message struct {
a's avatar
a committed
	ID         *ID                        `json:"id,omitempty"`
	Method     string                     `json:"method,omitempty"`
	Params     json.RawMessage            `json:"params,omitempty"`
	Error      error                      `json:"error,omitempty"`
	Extensions map[string]json.RawMessage `json:"-"`
a's avatar
a committed

a's avatar
a committed
	Result io.ReadCloser `json:"result,omitempty"`
a's avatar
rpc
a committed
}

a's avatar
a committed
func NewStringReader(x string) io.ReadCloser {
	return io.NopCloser(strings.NewReader(x))
}

a's avatar
a committed
func MarshalMessage(m *Message, enc *jx.Encoder) (err error) {
a's avatar
a committed
	// use encoder
	fail := enc.Obj(func(e *jx.Encoder) {
		e.Field("jsonrpc", func(e *jx.Encoder) {
			e.Str("2.0")
		})
		if m.ID != nil {
			e.Field("id", func(e *jx.Encoder) {
				e.Raw(m.ID.RawMessage())
			})
		}
		if m.Method != "" {
			e.Field("method", func(e *jx.Encoder) {
				e.Str(m.Method)
			})
		}
		if m.Error != nil {
			e.Field("error", func(e *jx.Encoder) {
a's avatar
a committed
				e.Raw(MarshalError(m.Error))
a's avatar
a committed
			})
			return
		}
		if len(m.Params) != 0 {
			e.Field("params", func(e *jx.Encoder) {
				e.Raw(m.Params)
			})
		}
a's avatar
a committed
		if m.Result != nil && err == nil {
a's avatar
a committed
			e.Field("result", func(e *jx.Encoder) {
a's avatar
a committed
				var n int64
				n, err = io.Copy(e, m.Result)
				if n == 0 {
					e.Null()
				}
a's avatar
a committed
			})
		}
a's avatar
a committed
		if m.Extensions != nil {
			for k, v := range m.Extensions {
				e.Field(k, func(e *jx.Encoder) {
					e.Raw(v)
				})
			}
		}
a's avatar
a committed
	})
a's avatar
a committed
	if err != nil {
		return err
	}
a's avatar
a committed
	if fail {
		return fmt.Errorf("jx encoding error")
	}
	// output
	return nil
}

a's avatar
a committed
func UnmarshalMessage(m *Message, dec *jx.Decoder) error {
a's avatar
a committed
	err := dec.Obj(func(d *jx.Decoder, key string) (err error) {
a's avatar
a committed
		switch key {
a's avatar
a committed
		default:
			raw, err := d.Raw()
			if err != nil {
				return err
			}
			if m.Extensions == nil {
				m.Extensions = make(map[string]json.RawMessage)
			}
			m.Extensions[key] = json.RawMessage(raw)
a's avatar
a committed
		case "jsonrpc":
			value, err := d.Str()
			if err != nil {
				return err
			}
			if value != VersionString {
				return NewInvalidRequestError("Invalid Version")
			}
		case "id":
			raw, err := d.Raw()
			if err != nil {
				return err
			}
			id := &ID{}
			err = id.UnmarshalJSON(raw)
			m.ID = id
			if err != nil {
				return err
			}
		case "method":
a's avatar
a committed
			m.Method, err = d.Str()
a's avatar
a committed
		case "params":
a's avatar
a committed
			val, err := d.RawAppend(nil)
a's avatar
a committed
			if err != nil {
				return err
a's avatar
a committed
			}
a's avatar
a committed
			m.Params = json.RawMessage(val)
a's avatar
a committed
		case "result":
a's avatar
a committed
			// allocate full result :)
			val, err := d.RawAppend(nil)
a's avatar
a committed
			if err != nil {
				return err
a's avatar
a committed
			}
a's avatar
a committed
			m.Result = io.NopCloser(bytes.NewBuffer(val))
a's avatar
a committed
		case "error":
			val, err := d.Raw()
			if err != nil {
				return err
			}
			m.Error = &JsonError{}
			err = json.Unmarshal(val, m.Error)
			if err != nil {
				return err
			}
		}
a's avatar
a committed
		return err
a's avatar
a committed
	})
	if err != nil {
		return err
	}
	return nil
}
a's avatar
a committed

func (m *Message) UnmarshalJSON(xs []byte) error {
	dec := jx.GetDecoder()
	defer jx.PutDecoder(dec)
	dec.ResetBytes(xs)
	return UnmarshalMessage(m, dec)
}

a's avatar
a committed
func (m Message) MarshalJSON() ([]byte, error) {
a's avatar
a committed
	buf := &bytes.Buffer{}
	enc := jx.NewStreamingEncoder(buf, 4096)
a's avatar
a committed
	err := MarshalMessage(&m, enc)
a's avatar
a committed
	if err != nil {
		return nil, err
	}
	err = enc.Close()
	if err != nil {
		return nil, err
a's avatar
rpc
a committed
	}
a's avatar
a committed
	return buf.Bytes(), nil
a's avatar
rpc
a committed
}

a's avatar
a committed
func (msg *Message) String() string {
a's avatar
a committed
	b, _ := msg.MarshalJSON()
a's avatar
rpc
a committed
	return string(b)
}

a's avatar
a committed
// encapsulate json rpc error into struct
a's avatar
a committed
type JsonError struct {
a's avatar
rpc
a committed
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data,omitempty"`
}

a's avatar
a committed
func (err *JsonError) Error() string {
a's avatar
rpc
a committed
	if err.Message == "" {
a's avatar
a committed
		return "json-rpc error " + strconv.Itoa(err.Code)
a's avatar
rpc
a committed
	}
	return err.Message
}

a's avatar
a committed
func (err *JsonError) ErrorCode() int {
a's avatar
rpc
a committed
	return err.Code
}

a's avatar
a committed
func (err *JsonError) ErrorData() any {
a's avatar
rpc
a committed
	return err.Data
}

a's avatar
a committed
// isBatch returns true when the first non-whitespace characters is '['
a's avatar
a committed
func IsBatchMessage(raw json.RawMessage) bool {
a's avatar
a committed
	for _, c := range raw {
		// skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt)
		switch c {
		case 0x20, 0x09, 0x0a, 0x0d:
			continue
a's avatar
rpc
a committed
		}
a's avatar
a committed
		return c == '['
a's avatar
rpc
a committed
	}
a's avatar
a committed
	return false
a's avatar
rpc
a committed
}

a's avatar
a committed
// 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
// Message.
func ParseMessage(in json.RawMessage) ([]*Message, bool) {
	return ReadMessage(jx.DecodeBytes(in))

}

// 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
// Message.
func ReadMessage(dec *jx.Decoder) ([]*Message, bool) {
	msgs := []*Message{{}}
	switch dec.Next() {
	case jx.Object:
		_ = UnmarshalMessage(msgs[0], dec)
		return msgs, false
	default:
		return msgs, false
	case jx.Array:
		msgs = []*Message{}
		dec.Arr(func(d *jx.Decoder) error {
			msg := new(Message)
Garet Halliday's avatar
Garet Halliday committed
			// err := UnmarshalMessage(msg, d)
a's avatar
a committed
			raw, err := d.Raw()
a's avatar
a committed
			if err != nil {
				raw = []byte{}
			}
a's avatar
a committed
			err = json.Unmarshal(raw, msg)
			if err != nil {
				msg = nil
			}
			msgs = append(msgs, msg)
			return nil
		})
		return msgs, true
	}
}