github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/proxy/grpc/grpc.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/proxy/grpc/grpc.go 16 17 // Package grpc is a grpc proxy built for the go-micro/server 18 package grpc 19 20 import ( 21 "context" 22 "io" 23 "strings" 24 25 "github.com/tickoalcantara12/micro/v3/service/client" 26 grpcc "github.com/tickoalcantara12/micro/v3/service/client/grpc" 27 "github.com/tickoalcantara12/micro/v3/service/errors" 28 "github.com/tickoalcantara12/micro/v3/service/logger" 29 "github.com/tickoalcantara12/micro/v3/service/proxy" 30 "github.com/tickoalcantara12/micro/v3/service/server" 31 "github.com/tickoalcantara12/micro/v3/util/codec" 32 "github.com/tickoalcantara12/micro/v3/util/codec/bytes" 33 "google.golang.org/grpc" 34 ) 35 36 // Proxy will transparently proxy requests to an endpoint. 37 // If no endpoint is specified it will call a service using the client. 38 type Proxy struct { 39 // embed options 40 options proxy.Options 41 42 // The client to use for outbound requests in the local network 43 Client client.Client 44 45 // Endpoint to route all calls to 46 Endpoint string 47 } 48 49 // read client request and write to server 50 func readLoop(r server.Request, s client.Stream) error { 51 // request to backend server 52 req := s.Request() 53 54 for { 55 // get data from client 56 // no need to decode it 57 select { 58 case <-s.Context().Done(): 59 return nil 60 default: 61 } 62 body, err := r.Read() 63 if err == io.EOF { 64 return s.Close() 65 } else if err != nil { 66 return err 67 } 68 69 // get the header from client 70 hdr := r.Header() 71 msg := &codec.Message{ 72 Type: codec.Request, 73 Header: hdr, 74 Body: body, 75 } 76 77 // send the message to the stream 78 if err := req.Codec().Write(msg, nil); err != nil { 79 return err 80 } 81 } 82 } 83 84 // ProcessMessage acts as a message exchange and forwards messages to ongoing topics 85 // TODO: should we look at p.Endpoint and only send to the local endpoint? probably 86 func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { 87 // TODO: check that we're not broadcast storming by sending to the same topic 88 // that we're actually subscribed to 89 90 if logger.V(logger.TraceLevel, logger.DefaultLogger) { 91 logger.Tracef("Proxy received message for %s", msg.Topic()) 92 } 93 94 // directly publish to the local client 95 return p.Client.Publish(ctx, msg) 96 } 97 98 // ServeRequest honours the server.Router interface 99 func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { 100 // service name to call 101 service := req.Service() 102 // endpoint to call 103 endpoint := req.Endpoint() 104 105 if len(service) == 0 { 106 return errors.BadRequest("go.micro.proxy", "service name is blank") 107 } 108 109 if logger.V(logger.TraceLevel, logger.DefaultLogger) { 110 logger.Tracef("Proxy received request for %s %s", service, endpoint) 111 } 112 113 var opts []client.CallOption 114 115 // call a specific backend 116 if len(p.Endpoint) > 0 { 117 // address:port 118 if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 { 119 opts = append(opts, client.WithAddress(p.Endpoint)) 120 // use as service name 121 } else { 122 service = p.Endpoint 123 } 124 } 125 126 // serve the normal way 127 return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...) 128 } 129 130 func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error { 131 // read initial request 132 body, err := req.Read() 133 if err != nil { 134 return err 135 } 136 137 // create new request with raw bytes body 138 creq := link.NewRequest(service, endpoint, &bytes.Frame{Data: body}, client.WithContentType(req.ContentType())) 139 140 // not a stream so make a client.Call request 141 if !req.Stream() { 142 crsp := new(bytes.Frame) 143 144 // make a call to the backend 145 if err := link.Call(ctx, creq, crsp, opts...); err != nil { 146 return err 147 } 148 149 // write the response 150 if err := rsp.Write(crsp.Data); err != nil { 151 return err 152 } 153 154 return nil 155 } 156 157 // new context with cancel 158 ctx, cancel := context.WithCancel(ctx) 159 160 // create new stream 161 stream, err := link.Stream(ctx, creq, opts...) 162 if err != nil { 163 return err 164 } 165 defer stream.Close() 166 167 // with a grpc stream we have to refire the initial request 168 // client request to start the server side 169 170 // get the header from client 171 msg := &codec.Message{ 172 Type: codec.Request, 173 Header: req.Header(), 174 Body: body, 175 } 176 177 // write the raw request 178 err = stream.Request().Codec().Write(msg, nil) 179 if err == io.EOF { 180 return nil 181 } else if err != nil { 182 return err 183 } 184 185 // create client request read loop if streaming 186 go func() { 187 if err := readLoop(req, stream); err != nil { 188 // cancel the context 189 cancel() 190 } 191 }() 192 193 // get raw response 194 resp := stream.Response() 195 196 // create server response write loop 197 for { 198 // read backend response body 199 body, err := resp.Read() 200 if err != nil { 201 // when we're done if its a grpc stream we have to set the trailer 202 if cc, ok := stream.(grpc.ClientStream); ok { 203 if ss, ok := resp.Codec().(grpc.ServerStream); ok { 204 ss.SetTrailer(cc.Trailer()) 205 } 206 } 207 } 208 209 if err == io.EOF { 210 return nil 211 } else if err != nil { 212 return err 213 } 214 215 // read backend response header 216 hdr := resp.Header() 217 218 // write raw response header to client 219 rsp.WriteHeader(hdr) 220 221 // write raw response body to client 222 err = rsp.Write(body) 223 if err == io.EOF { 224 return nil 225 } else if err != nil { 226 return err 227 } 228 } 229 } 230 231 func (p *Proxy) String() string { 232 return "grpc" 233 } 234 235 // NewProxy returns a new proxy which will route based on mucp headers 236 func NewProxy(opts ...proxy.Option) proxy.Proxy { 237 var options proxy.Options 238 239 for _, o := range opts { 240 o(&options) 241 } 242 243 // create a new grpc proxy 244 p := new(Proxy) 245 p.options = options 246 247 // set the client 248 p.Client = options.Client 249 // set the endpoint 250 p.Endpoint = options.Endpoint 251 252 // set the default client 253 if p.Client == nil { 254 p.Client = grpcc.NewClient() 255 } 256 257 return p 258 }