github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/client/mucp/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/client/mucp/mucp_codec.go 16 17 package mucp 18 19 import ( 20 "bytes" 21 errs "errors" 22 23 "github.com/tickoalcantara12/micro/v3/service/errors" 24 "github.com/tickoalcantara12/micro/v3/service/network/transport" 25 "github.com/tickoalcantara12/micro/v3/service/registry" 26 "github.com/tickoalcantara12/micro/v3/util/codec" 27 raw "github.com/tickoalcantara12/micro/v3/util/codec/bytes" 28 "github.com/tickoalcantara12/micro/v3/util/codec/grpc" 29 "github.com/tickoalcantara12/micro/v3/util/codec/json" 30 "github.com/tickoalcantara12/micro/v3/util/codec/jsonrpc" 31 "github.com/tickoalcantara12/micro/v3/util/codec/proto" 32 "github.com/tickoalcantara12/micro/v3/util/codec/protorpc" 33 ) 34 35 const ( 36 lastStreamResponseError = "EOS" 37 ) 38 39 // serverError represents an error that has been returned from 40 // the remote side of the RPC connection. 41 type serverError string 42 43 func (e serverError) Error() string { 44 return string(e) 45 } 46 47 // errShutdown holds the specific error for closing/closed connections 48 var ( 49 errShutdown = errs.New("connection is shut down") 50 ) 51 52 type rpcCodec struct { 53 client transport.Client 54 codec codec.Codec 55 56 req *transport.Message 57 buf *readWriteCloser 58 59 // signify if its a stream 60 stream string 61 } 62 63 type readWriteCloser struct { 64 wbuf *bytes.Buffer 65 rbuf *bytes.Buffer 66 } 67 68 var ( 69 DefaultContentType = "application/protobuf" 70 71 DefaultCodecs = map[string]codec.NewCodec{ 72 "application/grpc": grpc.NewCodec, 73 "application/grpc+json": grpc.NewCodec, 74 "application/grpc+proto": grpc.NewCodec, 75 "application/protobuf": proto.NewCodec, 76 "application/json": json.NewCodec, 77 "application/json-rpc": jsonrpc.NewCodec, 78 "application/proto-rpc": protorpc.NewCodec, 79 "application/octet-stream": raw.NewCodec, 80 } 81 82 // TODO: remove legacy codec list 83 defaultCodecs = map[string]codec.NewCodec{ 84 "application/json": jsonrpc.NewCodec, 85 "application/json-rpc": jsonrpc.NewCodec, 86 "application/protobuf": protorpc.NewCodec, 87 "application/proto-rpc": protorpc.NewCodec, 88 "application/octet-stream": protorpc.NewCodec, 89 } 90 ) 91 92 func (rwc *readWriteCloser) Read(p []byte) (n int, err error) { 93 return rwc.rbuf.Read(p) 94 } 95 96 func (rwc *readWriteCloser) Write(p []byte) (n int, err error) { 97 return rwc.wbuf.Write(p) 98 } 99 100 func (rwc *readWriteCloser) Close() error { 101 rwc.rbuf.Reset() 102 rwc.wbuf.Reset() 103 return nil 104 } 105 106 func getHeaders(m *codec.Message) { 107 set := func(v, hdr string) string { 108 if len(v) > 0 { 109 return v 110 } 111 return m.Header[hdr] 112 } 113 114 // check error in header 115 m.Error = set(m.Error, "Micro-Error") 116 117 // check endpoint in header 118 m.Endpoint = set(m.Endpoint, "Micro-Endpoint") 119 120 // check method in header 121 m.Method = set(m.Method, "Micro-Method") 122 123 // set the request id 124 m.Id = set(m.Id, "Micro-Id") 125 } 126 127 func setHeaders(m *codec.Message, stream string) { 128 set := func(hdr, v string) { 129 if len(v) == 0 { 130 return 131 } 132 m.Header[hdr] = v 133 } 134 135 set("Micro-Id", m.Id) 136 set("Micro-Service", m.Target) 137 set("Micro-Method", m.Method) 138 set("Micro-Endpoint", m.Endpoint) 139 set("Micro-Error", m.Error) 140 141 if len(stream) > 0 { 142 set("Micro-Stream", stream) 143 } 144 } 145 146 // setupProtocol sets up the old protocol 147 func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec { 148 // get the protocol from node metadata 149 if protocol := node.Metadata["protocol"]; len(protocol) > 0 { 150 return nil 151 } 152 153 // processing topic publishing 154 if len(msg.Header["Micro-Topic"]) > 0 { 155 return nil 156 } 157 158 // no protocol use old codecs 159 switch msg.Header["Content-Type"] { 160 case "application/json": 161 msg.Header["Content-Type"] = "application/json-rpc" 162 case "application/protobuf": 163 msg.Header["Content-Type"] = "application/proto-rpc" 164 } 165 166 // now return codec 167 return defaultCodecs[msg.Header["Content-Type"]] 168 } 169 170 func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec { 171 rwc := &readWriteCloser{ 172 wbuf: bytes.NewBuffer(nil), 173 rbuf: bytes.NewBuffer(nil), 174 } 175 r := &rpcCodec{ 176 buf: rwc, 177 client: client, 178 codec: c(rwc), 179 req: req, 180 stream: stream, 181 } 182 return r 183 } 184 185 func (c *rpcCodec) Write(m *codec.Message, body interface{}) error { 186 c.buf.wbuf.Reset() 187 188 // create header 189 if m.Header == nil { 190 m.Header = map[string]string{} 191 } 192 193 // copy original header 194 for k, v := range c.req.Header { 195 m.Header[k] = v 196 } 197 198 // set the mucp headers 199 setHeaders(m, c.stream) 200 201 // if body is bytes Frame don't encode 202 if body != nil { 203 if b, ok := body.(*raw.Frame); ok { 204 // set body 205 m.Body = b.Data 206 } else { 207 // write to codec 208 if err := c.codec.Write(m, body); err != nil { 209 return errors.InternalServerError("go.micro.client.codec", err.Error()) 210 } 211 // set body 212 m.Body = c.buf.wbuf.Bytes() 213 } 214 } 215 216 // create new transport message 217 msg := transport.Message{ 218 Header: m.Header, 219 Body: m.Body, 220 } 221 222 // send the request 223 if err := c.client.Send(&msg); err != nil { 224 return errors.InternalServerError("go.micro.client.transport", err.Error()) 225 } 226 227 return nil 228 } 229 230 func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error { 231 var tm transport.Message 232 233 // read message from transport 234 if err := c.client.Recv(&tm); err != nil { 235 return errors.InternalServerError("go.micro.client.transport", err.Error()) 236 } 237 238 c.buf.rbuf.Reset() 239 c.buf.rbuf.Write(tm.Body) 240 241 // set headers from transport 242 m.Header = tm.Header 243 244 // read header 245 err := c.codec.ReadHeader(m, r) 246 247 // get headers 248 getHeaders(m) 249 250 // return header error 251 if err != nil { 252 return errors.InternalServerError("go.micro.client.codec", err.Error()) 253 } 254 255 return nil 256 } 257 258 func (c *rpcCodec) ReadBody(b interface{}) error { 259 // read body 260 // read raw data 261 if v, ok := b.(*raw.Frame); ok { 262 v.Data = c.buf.rbuf.Bytes() 263 return nil 264 } 265 266 if err := c.codec.ReadBody(b); err != nil { 267 return errors.InternalServerError("go.micro.client.codec", err.Error()) 268 } 269 return nil 270 } 271 272 func (c *rpcCodec) Close() error { 273 c.buf.Close() 274 c.codec.Close() 275 if err := c.client.Close(); err != nil { 276 return errors.InternalServerError("go.micro.client.transport", err.Error()) 277 } 278 return nil 279 } 280 281 func (c *rpcCodec) String() string { 282 return "rpc" 283 }