go-micro.dev/v5@v5.12.0/codec/grpc/grpc.go (about)

     1  // Package grpc provides a grpc codec
     2  package grpc
     3  
     4  import (
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  
    11  	"github.com/golang/protobuf/proto"
    12  	"go-micro.dev/v5/codec"
    13  	"go-micro.dev/v5/transport/headers"
    14  )
    15  
    16  type Codec struct {
    17  	Conn        io.ReadWriteCloser
    18  	ContentType string
    19  }
    20  
    21  func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
    22  	if ct := m.Header["Content-Type"]; len(ct) > 0 {
    23  		c.ContentType = ct
    24  	}
    25  
    26  	if ct := m.Header["content-type"]; len(ct) > 0 {
    27  		c.ContentType = ct
    28  	}
    29  
    30  	// service method
    31  	path := m.Header[":path"]
    32  	if len(path) == 0 || path[0] != '/' {
    33  		m.Target = m.Header[headers.Request]
    34  		m.Endpoint = m.Header[headers.Endpoint]
    35  	} else {
    36  		// [ , a.package.Foo, Bar]
    37  		parts := strings.Split(path, "/")
    38  		if len(parts) != 3 {
    39  			return errors.New("Unknown request path")
    40  		}
    41  		service := strings.Split(parts[1], ".")
    42  		m.Endpoint = strings.Join([]string{service[len(service)-1], parts[2]}, ".")
    43  		m.Target = strings.Join(service[:len(service)-1], ".")
    44  	}
    45  
    46  	return nil
    47  }
    48  
    49  func (c *Codec) ReadBody(b interface{}) error {
    50  	// no body
    51  	if b == nil {
    52  		return nil
    53  	}
    54  
    55  	_, buf, err := decode(c.Conn)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	switch c.ContentType {
    61  	case "application/grpc+json":
    62  		return json.Unmarshal(buf, b)
    63  	case "application/grpc+proto", "application/grpc":
    64  		return proto.Unmarshal(buf, b.(proto.Message))
    65  	}
    66  
    67  	return errors.New("Unsupported Content-Type")
    68  }
    69  
    70  func (c *Codec) Write(m *codec.Message, b interface{}) error {
    71  	var buf []byte
    72  	var err error
    73  
    74  	if ct := m.Header["Content-Type"]; len(ct) > 0 {
    75  		c.ContentType = ct
    76  	}
    77  
    78  	if ct := m.Header["content-type"]; len(ct) > 0 {
    79  		c.ContentType = ct
    80  	}
    81  
    82  	switch m.Type {
    83  	case codec.Request:
    84  		parts := strings.Split(m.Endpoint, ".")
    85  		m.Header[":method"] = "POST"
    86  		m.Header[":path"] = fmt.Sprintf("/%s.%s/%s", m.Target, parts[0], parts[1])
    87  		m.Header[":proto"] = "HTTP/2.0"
    88  		m.Header["te"] = "trailers"
    89  		m.Header["user-agent"] = "grpc-go/1.0.0"
    90  		m.Header[":authority"] = m.Target
    91  		m.Header["content-type"] = c.ContentType
    92  	case codec.Response:
    93  		m.Header["Trailer"] = "grpc-status" // , grpc-message"
    94  		m.Header["content-type"] = c.ContentType
    95  		m.Header[":status"] = "200"
    96  		m.Header["grpc-status"] = "0"
    97  		//		m.Header["grpc-message"] = ""
    98  	case codec.Error:
    99  		m.Header["Trailer"] = "grpc-status, grpc-message"
   100  		// micro end of stream
   101  		if m.Error == "EOS" {
   102  			m.Header["grpc-status"] = "0"
   103  		} else {
   104  			m.Header["grpc-message"] = m.Error
   105  			m.Header["grpc-status"] = "13"
   106  		}
   107  
   108  		return nil
   109  	}
   110  
   111  	// marshal content
   112  	switch c.ContentType {
   113  	case "application/grpc+json":
   114  		buf, err = json.Marshal(b)
   115  	case "application/grpc+proto", "application/grpc":
   116  		pb, ok := b.(proto.Message)
   117  		if ok {
   118  			buf, err = proto.Marshal(pb)
   119  		}
   120  	default:
   121  		err = errors.New("Unsupported Content-Type")
   122  	}
   123  	// check error
   124  	if err != nil {
   125  		m.Header["grpc-status"] = "8"
   126  		m.Header["grpc-message"] = err.Error()
   127  		return err
   128  	}
   129  
   130  	if len(buf) == 0 {
   131  		return nil
   132  	}
   133  
   134  	return encode(0, buf, c.Conn)
   135  }
   136  
   137  func (c *Codec) Close() error {
   138  	return c.Conn.Close()
   139  }
   140  
   141  func (c *Codec) String() string {
   142  	return "grpc"
   143  }
   144  
   145  func NewCodec(c io.ReadWriteCloser) codec.Codec {
   146  	return &Codec{
   147  		Conn:        c,
   148  		ContentType: "application/grpc",
   149  	}
   150  }