github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/server/mucp/rpc_codec.go (about)

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