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 }