micro/proxy/grpc/grpc.go

159 lines
3.5 KiB
Go
Raw Normal View History

2019-06-03 20:44:43 +03:00
// Package grpc transparently forwards the grpc protocol using a go-micro client.
package grpc
import (
"context"
"io"
"strings"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/grpc"
2019-06-03 20:44:43 +03:00
"github.com/micro/go-micro/codec"
2019-08-05 19:44:33 +03:00
"github.com/micro/go-micro/proxy"
2019-06-03 20:44:43 +03:00
"github.com/micro/go-micro/server"
)
// Proxy will transparently proxy requests to the backend.
2019-06-03 20:44:43 +03:00
// If no backend is specified it will call a service using the client.
2019-06-06 19:58:21 +03:00
// If the service matches the Name it will use the server.DefaultRouter.
type Proxy struct {
// The proxy options
2019-12-16 17:55:47 +03:00
options proxy.Options
2019-06-03 20:44:43 +03:00
// Endpoint specified the fixed endpoint to call.
Endpoint string
// The client to use for outbound requests
Client client.Client
}
// read client request and write to server
func readLoop(r server.Request, s client.Stream) error {
// request to backend server
req := s.Request()
for {
// get data from client
// no need to decode it
body, err := r.Read()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
// get the header from client
hdr := r.Header()
msg := &codec.Message{
Type: codec.Request,
Header: hdr,
Body: body,
}
// write the raw request
err = req.Codec().Write(msg, nil)
if err == io.EOF {
return nil
} else if err != nil {
return err
}
}
}
// ProcessMessage acts as a message exchange and forwards messages to ongoing topics
// TODO: should we look at p.Endpoint and only send to the local endpoint? probably
func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error {
// TODO: check that we're not broadcast storming by sending to the same topic
// that we're actually subscribed to
// directly publish to the local client
return p.Client.Publish(ctx, msg)
2019-08-23 16:05:11 +03:00
}
// ServeRequest honours the server.Proxy interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
2019-06-03 20:44:43 +03:00
// set default client
if p.Client == nil {
p.Client = grpc.NewClient()
2019-06-03 20:44:43 +03:00
}
opts := []client.CallOption{}
// service name
service := req.Service()
endpoint := req.Endpoint()
// call a specific backend
if len(p.Endpoint) > 0 {
2019-06-03 20:44:43 +03:00
// address:port
if parts := strings.Split(p.Endpoint, ":"); len(parts) > 1 {
opts = append(opts, client.WithAddress(p.Endpoint))
2019-06-03 20:44:43 +03:00
// use as service name
} else {
service = p.Endpoint
2019-06-03 20:44:43 +03:00
}
}
// create new request with raw bytes body
2019-06-18 20:51:52 +03:00
creq := p.Client.NewRequest(service, endpoint, nil, client.WithContentType(req.ContentType()))
2019-06-03 20:44:43 +03:00
// create new stream
stream, err := p.Client.Stream(ctx, creq, opts...)
if err != nil {
return err
}
defer stream.Close()
// create client request read loop
go readLoop(req, stream)
// get raw response
resp := stream.Response()
// create server response write loop
for {
// read backend response body
body, err := resp.Read()
if err == io.EOF {
return nil
} else if err != nil {
return err
}
// read backend response header
hdr := resp.Header()
// write raw response header to client
rsp.WriteHeader(hdr)
// write raw response body to client
err = rsp.Write(body)
if err == io.EOF {
return nil
} else if err != nil {
return err
}
}
}
// NewProxy returns a new grpc proxy server
2019-12-16 17:55:47 +03:00
func NewProxy(opts ...proxy.Option) proxy.Proxy {
var options proxy.Options
for _, o := range opts {
o(&options)
}
2019-12-16 17:55:47 +03:00
p := new(Proxy)
p.Endpoint = options.Endpoint
p.Client = options.Client
return p
}
// NewSingleHostProxy returns a router which sends requests to a single backend
func NewSingleHostProxy(url string) *Proxy {
return &Proxy{
Endpoint: url,
2019-06-03 20:44:43 +03:00
}
}