mirror of
https://github.com/go-micro/go-micro.git
synced 2025-01-05 10:20:53 +02:00
382 lines
6.5 KiB
Go
382 lines
6.5 KiB
Go
// Package msgpackrpc provides a msgpack-rpc codec
|
|
package msgpackrpc
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/tinylib/msgp/msgp"
|
|
)
|
|
|
|
// The msgpack-rpc specification: https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
|
|
const (
|
|
RequestType = 0
|
|
ResponseType = 1
|
|
NotificationType = 2
|
|
|
|
RequestPackSize = 4
|
|
ResponsePackSize = 4
|
|
NotificationPackSize = 3
|
|
)
|
|
|
|
var (
|
|
ErrBadPackSize = errors.New("Bad pack size")
|
|
ErrBadMessageType = errors.New("Bad message type")
|
|
ErrBadErrorType = errors.New("Bad error type")
|
|
ErrUnexpectedParams = errors.New("Unexpected params")
|
|
ErrNotEncodable = errors.New("Not encodable")
|
|
ErrNotDecodable = errors.New("Not decodable")
|
|
)
|
|
|
|
// decodeBody decodes the body of the message.
|
|
func decodeBody(r *msgp.Reader, v interface{}) error {
|
|
b, ok := v.(msgp.Decodable)
|
|
if !ok {
|
|
return ErrNotDecodable
|
|
}
|
|
|
|
return msgp.Decode(r, b)
|
|
}
|
|
|
|
// Request is what the client can construct to be sent to the server.
|
|
// The params represents the body of the request.
|
|
type Request struct {
|
|
ID string
|
|
Method string
|
|
Body interface{}
|
|
|
|
hasBody bool
|
|
}
|
|
|
|
// EncodeMsg encodes the request to writer. The body is expected to
|
|
// be an encodable type.
|
|
func (r *Request) EncodeMsg(w *msgp.Writer) error {
|
|
var bm msgp.Encodable
|
|
|
|
if r.Body != nil {
|
|
var ok bool
|
|
bm, ok = r.Body.(msgp.Encodable)
|
|
if !ok {
|
|
return ErrNotEncodable
|
|
}
|
|
}
|
|
|
|
var err error
|
|
|
|
if err = w.WriteArrayHeader(RequestPackSize); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteInt(RequestType); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteString(r.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteString(r.Method); err != nil {
|
|
return err
|
|
}
|
|
|
|
// No body to encode. Write a zero-length params array.
|
|
if bm == nil {
|
|
return w.WriteArrayHeader(0)
|
|
}
|
|
|
|
// 1-item array containing the body.
|
|
if err = w.WriteArrayHeader(1); err != nil {
|
|
return err
|
|
}
|
|
|
|
return msgp.Encode(w, bm)
|
|
}
|
|
|
|
func (r *Request) DecodeMsg(mr *msgp.Reader) error {
|
|
var bm msgp.Decodable
|
|
|
|
if r.Body != nil {
|
|
var ok bool
|
|
bm, ok = r.Body.(msgp.Decodable)
|
|
if !ok {
|
|
return ErrNotDecodable
|
|
}
|
|
}
|
|
|
|
if size, err := mr.ReadArrayHeader(); err != nil {
|
|
return err
|
|
} else if size != RequestPackSize {
|
|
return ErrBadPackSize
|
|
}
|
|
|
|
if typ, err := mr.ReadInt(); err != nil {
|
|
return err
|
|
} else if typ != RequestType {
|
|
return ErrBadMessageType
|
|
}
|
|
|
|
id, err := mr.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.ID = id
|
|
|
|
method, err := mr.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.Method = method
|
|
|
|
// The request body is packed in an array.
|
|
l, err := mr.ReadArrayHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if l > 1 {
|
|
return ErrUnexpectedParams
|
|
} else if l == 0 {
|
|
return nil
|
|
}
|
|
|
|
r.hasBody = true
|
|
|
|
// Skip decoding the body if no value is present to decode into.
|
|
// The caller is expected to decode the body or skip it.
|
|
if bm != nil {
|
|
return decodeBody(mr, bm)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Response struct {
|
|
ID string
|
|
Error string
|
|
Body interface{}
|
|
|
|
hasBody bool
|
|
}
|
|
|
|
func (r *Response) EncodeMsg(w *msgp.Writer) error {
|
|
var bm msgp.Encodable
|
|
|
|
if r.Body != nil {
|
|
var ok bool
|
|
bm, ok = r.Body.(msgp.Encodable)
|
|
if !ok {
|
|
return ErrNotEncodable
|
|
}
|
|
}
|
|
|
|
var err error
|
|
|
|
if err = w.WriteArrayHeader(ResponsePackSize); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteInt(ResponseType); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteString(r.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
// No error.
|
|
if r.Error == "" {
|
|
if err = w.WriteNil(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if bm != nil {
|
|
return msgp.Encode(w, bm)
|
|
}
|
|
} else {
|
|
if err = w.WriteString(r.Error); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Write nil body.
|
|
return w.WriteNil()
|
|
}
|
|
|
|
func (r *Response) DecodeMsg(mr *msgp.Reader) error {
|
|
var bm msgp.Decodable
|
|
|
|
if r.Body != nil {
|
|
var ok bool
|
|
bm, ok = r.Body.(msgp.Decodable)
|
|
if !ok {
|
|
return ErrNotDecodable
|
|
}
|
|
}
|
|
|
|
if size, err := mr.ReadArrayHeader(); err != nil {
|
|
return err
|
|
} else if size != ResponsePackSize {
|
|
return ErrBadPackSize
|
|
}
|
|
|
|
if typ, err := mr.ReadInt(); err != nil {
|
|
return err
|
|
} else if typ != ResponseType {
|
|
return ErrBadMessageType
|
|
}
|
|
|
|
id, err := mr.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.ID = id
|
|
|
|
// Error can be nil or a string.
|
|
typ, err := mr.NextType()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch typ {
|
|
case msgp.StrType:
|
|
s, err := mr.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Error = s
|
|
|
|
case msgp.NilType:
|
|
if err := mr.ReadNil(); err != nil {
|
|
return err
|
|
}
|
|
r.Error = ""
|
|
|
|
default:
|
|
return ErrBadErrorType
|
|
}
|
|
|
|
// Body can be nil.
|
|
typ, err = mr.NextType()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if typ == msgp.NilType {
|
|
r.hasBody = false
|
|
return mr.ReadNil()
|
|
}
|
|
|
|
r.hasBody = true
|
|
|
|
// Skip decoding the body if no value is present to decode into.
|
|
// The caller is expected to read the body or skip it.
|
|
if bm != nil {
|
|
return decodeBody(mr, bm)
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
type Notification struct {
|
|
Method string
|
|
Body interface{}
|
|
|
|
hasBody bool
|
|
}
|
|
|
|
// EncodeMsg encodes the notification to writer. The body is expected to
|
|
// be an encodable type.
|
|
func (n *Notification) EncodeMsg(w *msgp.Writer) error {
|
|
var bm msgp.Encodable
|
|
|
|
if n.Body != nil {
|
|
var ok bool
|
|
bm, ok = n.Body.(msgp.Encodable)
|
|
if !ok {
|
|
return ErrNotEncodable
|
|
}
|
|
}
|
|
|
|
var err error
|
|
|
|
if err = w.WriteArrayHeader(NotificationPackSize); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteInt(NotificationType); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.WriteString(n.Method); err != nil {
|
|
return err
|
|
}
|
|
|
|
// No body to encode. Write a zero-length params array.
|
|
if bm == nil {
|
|
return w.WriteArrayHeader(0)
|
|
}
|
|
|
|
// 1-item array containing the body.
|
|
if err = w.WriteArrayHeader(1); err != nil {
|
|
return err
|
|
}
|
|
|
|
return msgp.Encode(w, bm)
|
|
}
|
|
|
|
func (n *Notification) DecodeMsg(mr *msgp.Reader) error {
|
|
var bm msgp.Decodable
|
|
|
|
if n.Body != nil {
|
|
var ok bool
|
|
bm, ok = n.Body.(msgp.Decodable)
|
|
if !ok {
|
|
return ErrNotDecodable
|
|
}
|
|
}
|
|
|
|
if size, err := mr.ReadArrayHeader(); err != nil {
|
|
return err
|
|
} else if size != NotificationPackSize {
|
|
return ErrBadPackSize
|
|
}
|
|
|
|
if typ, err := mr.ReadInt(); err != nil {
|
|
return err
|
|
} else if typ != NotificationType {
|
|
return ErrBadMessageType
|
|
}
|
|
|
|
method, err := mr.ReadString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n.Method = method
|
|
|
|
// The notification body is packed in an array.
|
|
l, err := mr.ReadArrayHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if l > 1 {
|
|
return ErrUnexpectedParams
|
|
} else if l == 0 {
|
|
return nil
|
|
}
|
|
|
|
n.hasBody = true
|
|
|
|
// Skip decoding the body if no value is present to decode into.
|
|
// The caller is expected to decode the body or skip it.
|
|
if bm != nil {
|
|
return decodeBody(mr, bm)
|
|
}
|
|
|
|
return nil
|
|
}
|