go-micro.dev/v5@v5.12.0/server/rpc_codec.go (about)

     1  package server
     2  
     3  import (
     4  	"bytes"
     5  	"sync"
     6  
     7  	"github.com/oxtoacart/bpool"
     8  	"github.com/pkg/errors"
     9  
    10  	"go-micro.dev/v5/codec"
    11  	raw "go-micro.dev/v5/codec/bytes"
    12  	"go-micro.dev/v5/codec/grpc"
    13  	"go-micro.dev/v5/codec/json"
    14  	"go-micro.dev/v5/codec/jsonrpc"
    15  	"go-micro.dev/v5/codec/proto"
    16  	"go-micro.dev/v5/codec/protorpc"
    17  	"go-micro.dev/v5/transport"
    18  	"go-micro.dev/v5/transport/headers"
    19  )
    20  
    21  type rpcCodec struct {
    22  	socket transport.Socket
    23  	codec  codec.Codec
    24  
    25  	req *transport.Message
    26  	buf *readWriteCloser
    27  
    28  	first    chan bool
    29  	protocol string
    30  
    31  	// check if we're the first
    32  	sync.RWMutex
    33  }
    34  
    35  type readWriteCloser struct {
    36  	wbuf *bytes.Buffer
    37  	rbuf *bytes.Buffer
    38  	sync.RWMutex
    39  }
    40  
    41  var (
    42  	// DefaultContentType is the default codec content type.
    43  	DefaultContentType = "application/protobuf"
    44  
    45  	DefaultCodecs = map[string]codec.NewCodec{
    46  		"application/grpc":         grpc.NewCodec,
    47  		"application/grpc+json":    grpc.NewCodec,
    48  		"application/grpc+proto":   grpc.NewCodec,
    49  		"application/json":         json.NewCodec,
    50  		"application/json-rpc":     jsonrpc.NewCodec,
    51  		"application/protobuf":     proto.NewCodec,
    52  		"application/proto-rpc":    protorpc.NewCodec,
    53  		"application/octet-stream": raw.NewCodec,
    54  	}
    55  
    56  	// TODO: remove legacy codec list.
    57  	defaultCodecs = map[string]codec.NewCodec{
    58  		"application/json":         jsonrpc.NewCodec,
    59  		"application/json-rpc":     jsonrpc.NewCodec,
    60  		"application/protobuf":     protorpc.NewCodec,
    61  		"application/proto-rpc":    protorpc.NewCodec,
    62  		"application/octet-stream": protorpc.NewCodec,
    63  	}
    64  
    65  	// the local buffer pool.
    66  	bufferPool = bpool.NewSizedBufferPool(32, 1)
    67  )
    68  
    69  func (rwc *readWriteCloser) Read(p []byte) (n int, err error) {
    70  	rwc.RLock()
    71  	defer rwc.RUnlock()
    72  
    73  	return rwc.rbuf.Read(p)
    74  }
    75  
    76  func (rwc *readWriteCloser) Write(p []byte) (n int, err error) {
    77  	rwc.Lock()
    78  	defer rwc.Unlock()
    79  
    80  	return rwc.wbuf.Write(p)
    81  }
    82  
    83  func (rwc *readWriteCloser) Close() error {
    84  	return nil
    85  }
    86  
    87  func getHeader(hdr string, md map[string]string) string {
    88  	if hd := md[hdr]; len(hd) > 0 {
    89  		return hd
    90  	}
    91  
    92  	return md["X-"+hdr]
    93  }
    94  
    95  func getHeaders(m *codec.Message) {
    96  	set := func(v, hdr string) string {
    97  		if len(v) > 0 {
    98  			return v
    99  		}
   100  
   101  		return m.Header[hdr]
   102  	}
   103  
   104  	m.Id = set(m.Id, headers.ID)
   105  	m.Error = set(m.Error, headers.Error)
   106  	m.Endpoint = set(m.Endpoint, headers.Endpoint)
   107  	m.Method = set(m.Method, headers.Method)
   108  	m.Target = set(m.Target, headers.Request)
   109  
   110  	// TODO: remove this cruft
   111  	if len(m.Endpoint) == 0 {
   112  		m.Endpoint = m.Method
   113  	}
   114  }
   115  
   116  func setHeaders(m, r *codec.Message) {
   117  	set := func(hdr, v string) {
   118  		if len(v) == 0 {
   119  			return
   120  		}
   121  
   122  		m.Header[hdr] = v
   123  		m.Header["X-"+hdr] = v
   124  	}
   125  
   126  	// set headers
   127  	set(headers.ID, r.Id)
   128  	set(headers.Request, r.Target)
   129  	set(headers.Method, r.Method)
   130  	set(headers.Endpoint, r.Endpoint)
   131  	set(headers.Error, r.Error)
   132  }
   133  
   134  // setupProtocol sets up the old protocol.
   135  func setupProtocol(msg *transport.Message) codec.NewCodec {
   136  	service := getHeader(headers.Request, msg.Header)
   137  	method := getHeader(headers.Method, msg.Header)
   138  	endpoint := getHeader(headers.Endpoint, msg.Header)
   139  	protocol := getHeader(headers.Protocol, msg.Header)
   140  	target := getHeader(headers.Target, msg.Header)
   141  	topic := getHeader(headers.Message, msg.Header)
   142  
   143  	// if the protocol exists (mucp) do nothing
   144  	if len(protocol) > 0 {
   145  		return nil
   146  	}
   147  
   148  	// newer method of processing messages over transport
   149  	if len(topic) > 0 {
   150  		return nil
   151  	}
   152  
   153  	// if no service/method/endpoint then it's the old protocol
   154  	if len(service) == 0 && len(method) == 0 && len(endpoint) == 0 {
   155  		return defaultCodecs[msg.Header["Content-Type"]]
   156  	}
   157  
   158  	// old target method specified
   159  	if len(target) > 0 {
   160  		return defaultCodecs[msg.Header["Content-Type"]]
   161  	}
   162  
   163  	// no method then set to endpoint
   164  	if len(method) == 0 {
   165  		msg.Header[headers.Method] = endpoint
   166  	}
   167  
   168  	// no endpoint then set to method
   169  	if len(endpoint) == 0 {
   170  		msg.Header[headers.Endpoint] = method
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func newRPCCodec(req *transport.Message, socket transport.Socket, c codec.NewCodec) codec.Codec {
   177  	rwc := &readWriteCloser{
   178  		rbuf: bufferPool.Get(),
   179  		wbuf: bufferPool.Get(),
   180  	}
   181  
   182  	r := &rpcCodec{
   183  		buf:      rwc,
   184  		codec:    c(rwc),
   185  		req:      req,
   186  		socket:   socket,
   187  		protocol: "mucp",
   188  		first:    make(chan bool),
   189  	}
   190  
   191  	// if grpc pre-load the buffer
   192  	// TODO: remove this terrible hack
   193  	switch r.codec.String() {
   194  	case "grpc":
   195  		// write the body
   196  		rwc.rbuf.Write(req.Body)
   197  		r.protocol = "grpc"
   198  	default:
   199  		// first is not preloaded
   200  		close(r.first)
   201  	}
   202  
   203  	return r
   204  }
   205  
   206  func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error {
   207  	// the initial message
   208  	mmsg := codec.Message{
   209  		Header: c.req.Header,
   210  		Body:   c.req.Body,
   211  	}
   212  
   213  	// first message could be pre-loaded
   214  	select {
   215  	case <-c.first:
   216  		// not the first
   217  		var tm transport.Message
   218  
   219  		// read off the socket
   220  		if err := c.socket.Recv(&tm); err != nil {
   221  			return err
   222  		}
   223  		// reset the read buffer
   224  		c.buf.rbuf.Reset()
   225  
   226  		// write the body to the buffer
   227  		if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
   228  			return err
   229  		}
   230  
   231  		// set the message header
   232  		mmsg.Header = tm.Header
   233  		// set the message body
   234  		mmsg.Body = tm.Body
   235  
   236  		// set req
   237  		c.req = &tm
   238  	default:
   239  		// we need to lock here to prevent race conditions
   240  		// and we make use of a channel otherwise because
   241  		// this does not result in a context switch
   242  		// locking to check c.first on every call to ReadHeader
   243  		// would otherwise drastically slow the code execution
   244  		c.Lock()
   245  		// recheck before closing because the select statement
   246  		// above is not thread safe, so thread safety here is
   247  		// mandatory
   248  		select {
   249  		case <-c.first:
   250  		default:
   251  			// disable first
   252  			close(c.first)
   253  		}
   254  		// now unlock and we never need this again
   255  		c.Unlock()
   256  	}
   257  
   258  	// set some internal things
   259  	getHeaders(&mmsg)
   260  
   261  	// read header via codec
   262  	if err := c.codec.ReadHeader(&mmsg, codec.Request); err != nil {
   263  		return err
   264  	}
   265  
   266  	// fallback for 0.14 and older
   267  	if len(mmsg.Endpoint) == 0 {
   268  		mmsg.Endpoint = mmsg.Method
   269  	}
   270  
   271  	// set message
   272  	*r = mmsg
   273  
   274  	return nil
   275  }
   276  
   277  func (c *rpcCodec) ReadBody(b interface{}) error {
   278  	// don't read empty body
   279  	if len(c.req.Body) == 0 {
   280  		return nil
   281  	}
   282  	// read raw data
   283  	if v, ok := b.(*raw.Frame); ok {
   284  		v.Data = c.req.Body
   285  		return nil
   286  	}
   287  	// decode the usual way
   288  	return c.codec.ReadBody(b)
   289  }
   290  
   291  func (c *rpcCodec) Write(r *codec.Message, b interface{}) error {
   292  	c.buf.wbuf.Reset()
   293  
   294  	// create a new message
   295  	m := &codec.Message{
   296  		Target:   r.Target,
   297  		Method:   r.Method,
   298  		Endpoint: r.Endpoint,
   299  		Id:       r.Id,
   300  		Error:    r.Error,
   301  		Type:     r.Type,
   302  		Header:   r.Header,
   303  	}
   304  
   305  	if m.Header == nil {
   306  		m.Header = map[string]string{}
   307  	}
   308  
   309  	setHeaders(m, r)
   310  
   311  	// the body being sent
   312  	var body []byte
   313  
   314  	// is it a raw frame?
   315  	if v, ok := b.(*raw.Frame); ok {
   316  		body = v.Data
   317  		// if we have encoded data just send it
   318  	} else if len(r.Body) > 0 {
   319  		body = r.Body
   320  		// write the body to codec
   321  	} else if err := c.codec.Write(m, b); err != nil {
   322  		c.buf.wbuf.Reset()
   323  
   324  		// write an error if it failed
   325  		m.Error = errors.Wrapf(err, "Unable to encode body").Error()
   326  		m.Header[headers.Error] = m.Error
   327  		// no body to write
   328  		if err := c.codec.Write(m, nil); err != nil {
   329  			return err
   330  		}
   331  	} else {
   332  		// set the body
   333  		body = c.buf.wbuf.Bytes()
   334  	}
   335  
   336  	// Set content type if theres content
   337  	if len(body) > 0 {
   338  		m.Header["Content-Type"] = c.req.Header["Content-Type"]
   339  	}
   340  
   341  	// send on the socket
   342  	return c.socket.Send(&transport.Message{
   343  		Header: m.Header,
   344  		Body:   body,
   345  	})
   346  }
   347  
   348  func (c *rpcCodec) Close() error {
   349  	// close the codec
   350  	c.codec.Close()
   351  	// close the socket
   352  	err := c.socket.Close()
   353  	// put back the buffers
   354  	bufferPool.Put(c.buf.rbuf)
   355  	bufferPool.Put(c.buf.wbuf)
   356  	// return the error
   357  	return err
   358  }
   359  
   360  func (c *rpcCodec) String() string {
   361  	return c.protocol
   362  }