good morning!!!!

Skip to content
Snippets Groups Projects
json.go 5.86 KiB
Newer Older
a's avatar
a committed
package codec
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
	"strconv"
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
var Null = json.RawMessage("null")
a's avatar
rpc
a committed

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

a's avatar
ok  
a committed
type ExtraFields map[string]json.RawMessage
a's avatar
a committed

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"`
	Result json.RawMessage `json:"result,omitempty"`
	Error  error           `json:"error,omitempty"`
a's avatar
a committed

a's avatar
ok  
a committed
	ExtraFields ExtraFields `json:"-"`
a's avatar
rpc
a committed
}

a's avatar
a committed
func MarshalMessage(m *Message, enc *jx.Encoder) error {
	// 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)
			})
		}
a's avatar
ok  
a committed
		for k, v := range m.ExtraFields {
			e.Field(k, func(e *jx.Encoder) {
				e.Raw(v)
a's avatar
a committed
			})
		}
		if m.Error != nil {
			e.Field("error", func(e *jx.Encoder) {
				EncodeError(e, m.Error)
			})
			return
		}
		if len(m.Params) != 0 {
			e.Field("params", func(e *jx.Encoder) {
				e.Raw(m.Params)
			})
		}
		if len(m.Result) != 0 {
			e.Field("result", func(e *jx.Encoder) {
				e.Raw(m.Result)
			})
		}
	})
	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 {
		default:
			val, err := d.Raw()
			if err != nil {
				return err
			}
a's avatar
a committed
			buf := bytes.NewBuffer(make(json.RawMessage, len(val)))
			buf.Write(val)
a's avatar
a committed
			if m.ExtraFields == nil {
				m.ExtraFields = ExtraFields{}
			}
a's avatar
ok  
a committed
			m.ExtraFields[key] = buf.Bytes()
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":
			val, err := d.Raw()
			if err != nil {
				return err
			}
a's avatar
a committed
			buf := bytes.NewBuffer(m.Params)
			buf.Reset()
a's avatar
a committed
			_, err = buf.Write(val)
a's avatar
a committed
			if err != nil {
				return err
a's avatar
a committed
			}
a's avatar
a committed
			m.Params = buf.Bytes()
a's avatar
a committed
		case "result":
			val, err := d.Raw()
			if err != nil {
				return err
			}
a's avatar
a committed
			buf := bytes.NewBuffer(m.Result)
			buf.Reset()
a's avatar
a committed
			_, err = buf.Write(val)
a's avatar
a committed
			if err != nil {
				return err
a's avatar
a committed
			}
a's avatar
a committed
			m.Result = buf.Bytes()
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
ok  
a committed
func (m ExtraFields) SetExtraField(name string, v any) (err error) {
a's avatar
a committed
	switch name {
	case "id", "jsonrpc", "method", "params", "result", "error":
		return fmt.Errorf("%w: %q", ErrIllegalExtraField, name)
	}
a's avatar
ok  
a committed
	if v == nil {
		delete(m, name)
	}
a's avatar
a committed
	val, err := json.Marshal(v)
	if err != nil {
		return err
	}
a's avatar
ok  
a committed
	m[name] = val
a's avatar
a committed
	return nil
}
a's avatar
ok  
a committed
func (m ExtraFields) Clear() {
	for k := range m {
		delete(m, k)
	}
}

func (m *Message) SetExtraField(name string, v any) error {
	return m.ExtraFields.SetExtraField(name, v)
}
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)
			//err := UnmarshalMessage(msg, d)
			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
	}
}