268 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mucp
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	errs "errors"
 | 
						|
 | 
						|
	"github.com/micro/go-micro/v3/codec"
 | 
						|
	raw "github.com/micro/go-micro/v3/codec/bytes"
 | 
						|
	"github.com/micro/go-micro/v3/codec/grpc"
 | 
						|
	"github.com/micro/go-micro/v3/codec/json"
 | 
						|
	"github.com/micro/go-micro/v3/codec/jsonrpc"
 | 
						|
	"github.com/micro/go-micro/v3/codec/proto"
 | 
						|
	"github.com/micro/go-micro/v3/codec/protorpc"
 | 
						|
	"github.com/micro/go-micro/v3/errors"
 | 
						|
	"github.com/micro/go-micro/v3/registry"
 | 
						|
	"github.com/micro/go-micro/v3/transport"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	lastStreamResponseError = "EOS"
 | 
						|
)
 | 
						|
 | 
						|
// serverError represents an error that has been returned from
 | 
						|
// the remote side of the RPC connection.
 | 
						|
type serverError string
 | 
						|
 | 
						|
func (e serverError) Error() string {
 | 
						|
	return string(e)
 | 
						|
}
 | 
						|
 | 
						|
// errShutdown holds the specific error for closing/closed connections
 | 
						|
var (
 | 
						|
	errShutdown = errs.New("connection is shut down")
 | 
						|
)
 | 
						|
 | 
						|
type rpcCodec struct {
 | 
						|
	client transport.Client
 | 
						|
	codec  codec.Codec
 | 
						|
 | 
						|
	req *transport.Message
 | 
						|
	buf *readWriteCloser
 | 
						|
 | 
						|
	// signify if its a stream
 | 
						|
	stream string
 | 
						|
}
 | 
						|
 | 
						|
type readWriteCloser struct {
 | 
						|
	wbuf *bytes.Buffer
 | 
						|
	rbuf *bytes.Buffer
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	DefaultContentType = "application/protobuf"
 | 
						|
 | 
						|
	DefaultCodecs = map[string]codec.NewCodec{
 | 
						|
		"application/grpc":         grpc.NewCodec,
 | 
						|
		"application/grpc+json":    grpc.NewCodec,
 | 
						|
		"application/grpc+proto":   grpc.NewCodec,
 | 
						|
		"application/protobuf":     proto.NewCodec,
 | 
						|
		"application/json":         json.NewCodec,
 | 
						|
		"application/json-rpc":     jsonrpc.NewCodec,
 | 
						|
		"application/proto-rpc":    protorpc.NewCodec,
 | 
						|
		"application/octet-stream": raw.NewCodec,
 | 
						|
	}
 | 
						|
 | 
						|
	// TODO: remove legacy codec list
 | 
						|
	defaultCodecs = map[string]codec.NewCodec{
 | 
						|
		"application/json":         jsonrpc.NewCodec,
 | 
						|
		"application/json-rpc":     jsonrpc.NewCodec,
 | 
						|
		"application/protobuf":     protorpc.NewCodec,
 | 
						|
		"application/proto-rpc":    protorpc.NewCodec,
 | 
						|
		"application/octet-stream": protorpc.NewCodec,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func (rwc *readWriteCloser) Read(p []byte) (n int, err error) {
 | 
						|
	return rwc.rbuf.Read(p)
 | 
						|
}
 | 
						|
 | 
						|
func (rwc *readWriteCloser) Write(p []byte) (n int, err error) {
 | 
						|
	return rwc.wbuf.Write(p)
 | 
						|
}
 | 
						|
 | 
						|
func (rwc *readWriteCloser) Close() error {
 | 
						|
	rwc.rbuf.Reset()
 | 
						|
	rwc.wbuf.Reset()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func getHeaders(m *codec.Message) {
 | 
						|
	set := func(v, hdr string) string {
 | 
						|
		if len(v) > 0 {
 | 
						|
			return v
 | 
						|
		}
 | 
						|
		return m.Header[hdr]
 | 
						|
	}
 | 
						|
 | 
						|
	// check error in header
 | 
						|
	m.Error = set(m.Error, "Micro-Error")
 | 
						|
 | 
						|
	// check endpoint in header
 | 
						|
	m.Endpoint = set(m.Endpoint, "Micro-Endpoint")
 | 
						|
 | 
						|
	// check method in header
 | 
						|
	m.Method = set(m.Method, "Micro-Method")
 | 
						|
 | 
						|
	// set the request id
 | 
						|
	m.Id = set(m.Id, "Micro-Id")
 | 
						|
}
 | 
						|
 | 
						|
func setHeaders(m *codec.Message, stream string) {
 | 
						|
	set := func(hdr, v string) {
 | 
						|
		if len(v) == 0 {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		m.Header[hdr] = v
 | 
						|
	}
 | 
						|
 | 
						|
	set("Micro-Id", m.Id)
 | 
						|
	set("Micro-Service", m.Target)
 | 
						|
	set("Micro-Method", m.Method)
 | 
						|
	set("Micro-Endpoint", m.Endpoint)
 | 
						|
	set("Micro-Error", m.Error)
 | 
						|
 | 
						|
	if len(stream) > 0 {
 | 
						|
		set("Micro-Stream", stream)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// setupProtocol sets up the old protocol
 | 
						|
func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
 | 
						|
	// get the protocol from node metadata
 | 
						|
	if protocol := node.Metadata["protocol"]; len(protocol) > 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// processing topic publishing
 | 
						|
	if len(msg.Header["Micro-Topic"]) > 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// no protocol use old codecs
 | 
						|
	switch msg.Header["Content-Type"] {
 | 
						|
	case "application/json":
 | 
						|
		msg.Header["Content-Type"] = "application/json-rpc"
 | 
						|
	case "application/protobuf":
 | 
						|
		msg.Header["Content-Type"] = "application/proto-rpc"
 | 
						|
	}
 | 
						|
 | 
						|
	// now return codec
 | 
						|
	return defaultCodecs[msg.Header["Content-Type"]]
 | 
						|
}
 | 
						|
 | 
						|
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec {
 | 
						|
	rwc := &readWriteCloser{
 | 
						|
		wbuf: bytes.NewBuffer(nil),
 | 
						|
		rbuf: bytes.NewBuffer(nil),
 | 
						|
	}
 | 
						|
	r := &rpcCodec{
 | 
						|
		buf:    rwc,
 | 
						|
		client: client,
 | 
						|
		codec:  c(rwc),
 | 
						|
		req:    req,
 | 
						|
		stream: stream,
 | 
						|
	}
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
 | 
						|
	c.buf.wbuf.Reset()
 | 
						|
 | 
						|
	// create header
 | 
						|
	if m.Header == nil {
 | 
						|
		m.Header = map[string]string{}
 | 
						|
	}
 | 
						|
 | 
						|
	// copy original header
 | 
						|
	for k, v := range c.req.Header {
 | 
						|
		m.Header[k] = v
 | 
						|
	}
 | 
						|
 | 
						|
	// set the mucp headers
 | 
						|
	setHeaders(m, c.stream)
 | 
						|
 | 
						|
	// if body is bytes Frame don't encode
 | 
						|
	if body != nil {
 | 
						|
		if b, ok := body.(*raw.Frame); ok {
 | 
						|
			// set body
 | 
						|
			m.Body = b.Data
 | 
						|
		} else {
 | 
						|
			// write to codec
 | 
						|
			if err := c.codec.Write(m, body); err != nil {
 | 
						|
				return errors.InternalServerError("go.micro.client.codec", err.Error())
 | 
						|
			}
 | 
						|
			// set body
 | 
						|
			m.Body = c.buf.wbuf.Bytes()
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// create new transport message
 | 
						|
	msg := transport.Message{
 | 
						|
		Header: m.Header,
 | 
						|
		Body:   m.Body,
 | 
						|
	}
 | 
						|
 | 
						|
	// send the request
 | 
						|
	if err := c.client.Send(&msg); err != nil {
 | 
						|
		return errors.InternalServerError("go.micro.client.transport", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error {
 | 
						|
	var tm transport.Message
 | 
						|
 | 
						|
	// read message from transport
 | 
						|
	if err := c.client.Recv(&tm); err != nil {
 | 
						|
		return errors.InternalServerError("go.micro.client.transport", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	c.buf.rbuf.Reset()
 | 
						|
	c.buf.rbuf.Write(tm.Body)
 | 
						|
 | 
						|
	// set headers from transport
 | 
						|
	m.Header = tm.Header
 | 
						|
 | 
						|
	// read header
 | 
						|
	err := c.codec.ReadHeader(m, r)
 | 
						|
 | 
						|
	// get headers
 | 
						|
	getHeaders(m)
 | 
						|
 | 
						|
	// return header error
 | 
						|
	if err != nil {
 | 
						|
		return errors.InternalServerError("go.micro.client.codec", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *rpcCodec) ReadBody(b interface{}) error {
 | 
						|
	// read body
 | 
						|
	// read raw data
 | 
						|
	if v, ok := b.(*raw.Frame); ok {
 | 
						|
		v.Data = c.buf.rbuf.Bytes()
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if err := c.codec.ReadBody(b); err != nil {
 | 
						|
		return errors.InternalServerError("go.micro.client.codec", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *rpcCodec) Close() error {
 | 
						|
	c.buf.Close()
 | 
						|
	c.codec.Close()
 | 
						|
	if err := c.client.Close(); err != nil {
 | 
						|
		return errors.InternalServerError("go.micro.client.transport", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *rpcCodec) String() string {
 | 
						|
	return "rpc"
 | 
						|
}
 |