Further consolidate the libraries
This commit is contained in:
25
proxy/README.md
Normal file
25
proxy/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Go Proxy [](https://opensource.org/licenses/Apache-2.0) [](https://godoc.org/github.com/micro/go-proxy)
|
||||
|
||||
Go Proxy is a proxy library for Go Micro.
|
||||
|
||||
## Overview
|
||||
|
||||
Go Micro is a distributed systems framework for client/server communication. It handles the details
|
||||
around discovery, fault tolerance, rpc communication, etc. We may want to leverage this in broader ecosystems
|
||||
which make use of standard http or we may also want to offload a number of requirements to a single proxy.
|
||||
|
||||
## Features
|
||||
|
||||
- **Transparent Proxy** - Proxy requests to any micro services through a single location. Go Proxy enables
|
||||
you to write Go Micro proxies which handle and forward requests. This is good for incorporating wrappers.
|
||||
|
||||
- **Single Backend Router** - Enable the single backend router to proxy directly to your local app. The proxy
|
||||
allows you to set a router which serves your backend service whether its http, grpc, etc.
|
||||
|
||||
- **Protocol Aware Handler** - Set a request handler which speaks your app protocol to make outbound requests.
|
||||
Your app may not speak the MUCP protocol so it may be easier to translate internally.
|
||||
|
||||
- **Control Planes** - Additionally we support use of control planes to offload many distributed systems concerns.
|
||||
* [x] [Consul](https://www.consul.io/docs/connect/native.html) - Using Connect-Native to provide secure mTLS.
|
||||
* [x] [NATS](https://nats.io/) - Fully leveraging NATS as the control plane and data plane.
|
||||
|
77
proxy/control/consul/consul.go
Normal file
77
proxy/control/consul/consul.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Package consul provides Consul Connect control plane
|
||||
package consul
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/connect"
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/registry/consul"
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
type proxyService struct {
|
||||
c *connect.Service
|
||||
micro.Service
|
||||
}
|
||||
|
||||
func newService(opts ...micro.Option) micro.Service {
|
||||
// we need to use the consul registry to register connect applications
|
||||
r := consul.NewRegistry(
|
||||
consul.Connect(),
|
||||
)
|
||||
|
||||
// pass in the registry as part of our options
|
||||
newOpts := append([]micro.Option{micro.Registry(r)}, opts...)
|
||||
|
||||
// service := micro.NewService(newOpts...)
|
||||
service := micro.NewService(newOpts...)
|
||||
|
||||
// get the consul address
|
||||
addrs := service.Server().Options().Registry.Options().Addrs
|
||||
|
||||
// set the config
|
||||
config := api.DefaultConfig()
|
||||
if len(addrs) > 0 {
|
||||
config.Address = addrs[0]
|
||||
}
|
||||
|
||||
// create consul client
|
||||
client, err := api.NewClient(api.DefaultConfig())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// create connect service using the service name
|
||||
svc, err := connect.NewService(service.Server().Options().Name, client)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// setup transport tls config
|
||||
service.Options().Transport.Init(
|
||||
transport.TLSConfig(svc.ServerTLSConfig()),
|
||||
)
|
||||
|
||||
// setup broker tls config
|
||||
service.Options().Broker.Init(
|
||||
broker.TLSConfig(svc.ServerTLSConfig()),
|
||||
)
|
||||
|
||||
// return a new proxy enabled service
|
||||
return &proxyService{
|
||||
c: svc,
|
||||
Service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *proxyService) String() string {
|
||||
return "consul"
|
||||
}
|
||||
|
||||
// NewService returns a Consul Connect-Native micro.Service
|
||||
func NewService(opts ...micro.Option) micro.Service {
|
||||
return newService(opts...)
|
||||
}
|
30
proxy/control/nats/nats.go
Normal file
30
proxy/control/nats/nats.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Package nats provides a NATS control plane
|
||||
package nats
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro"
|
||||
broker "github.com/micro/go-plugins/broker/nats"
|
||||
registry "github.com/micro/go-plugins/registry/nats"
|
||||
transport "github.com/micro/go-plugins/transport/nats"
|
||||
)
|
||||
|
||||
// NewService returns a NATS micro.Service
|
||||
func NewService(opts ...micro.Option) micro.Service {
|
||||
// initialise nats components
|
||||
b := broker.NewBroker()
|
||||
r := registry.NewRegistry()
|
||||
t := transport.NewTransport()
|
||||
|
||||
// create new options
|
||||
options := []micro.Option{
|
||||
micro.Broker(b),
|
||||
micro.Registry(r),
|
||||
micro.Transport(t),
|
||||
}
|
||||
|
||||
// append user options
|
||||
options = append(options, opts...)
|
||||
|
||||
// return a nats service
|
||||
return micro.NewService(options...)
|
||||
}
|
BIN
proxy/proto/.proxy.proto.swp
Normal file
BIN
proxy/proto/.proxy.proto.swp
Normal file
Binary file not shown.
207
proxy/proto/proxy.micro.go
Normal file
207
proxy/proto/proxy.micro.go
Normal file
@@ -0,0 +1,207 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: github.com/micro/go-proxy/proto/proxy.proto
|
||||
|
||||
/*
|
||||
Package proxy is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-proxy/proto/proxy.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Request
|
||||
Response
|
||||
Message
|
||||
Empty
|
||||
*/
|
||||
package proxy
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "context"
|
||||
client "github.com/micro/go-micro/client"
|
||||
server "github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Service service
|
||||
|
||||
type Service interface {
|
||||
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
|
||||
Stream(ctx context.Context, opts ...client.CallOption) (Service_StreamService, error)
|
||||
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewService(name string, c client.Client) Service {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = "proxy"
|
||||
}
|
||||
return &service{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *service) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
|
||||
req := c.c.NewRequest(c.name, "Service.Call", in)
|
||||
out := new(Response)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *service) Stream(ctx context.Context, opts ...client.CallOption) (Service_StreamService, error) {
|
||||
req := c.c.NewRequest(c.name, "Service.Stream", &Request{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &serviceStream{stream}, nil
|
||||
}
|
||||
|
||||
type Service_StreamService interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
}
|
||||
|
||||
type serviceStream struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *serviceStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *serviceStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *serviceStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *serviceStream) Send(m *Request) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *serviceStream) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *service) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Empty, error) {
|
||||
req := c.c.NewRequest(c.name, "Service.Publish", in)
|
||||
out := new(Empty)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Service service
|
||||
|
||||
type ServiceHandler interface {
|
||||
Call(context.Context, *Request, *Response) error
|
||||
Stream(context.Context, Service_StreamStream) error
|
||||
Publish(context.Context, *Message, *Empty) error
|
||||
}
|
||||
|
||||
func RegisterServiceHandler(s server.Server, hdlr ServiceHandler, opts ...server.HandlerOption) error {
|
||||
type service interface {
|
||||
Call(ctx context.Context, in *Request, out *Response) error
|
||||
Stream(ctx context.Context, stream server.Stream) error
|
||||
Publish(ctx context.Context, in *Message, out *Empty) error
|
||||
}
|
||||
type Service struct {
|
||||
service
|
||||
}
|
||||
h := &serviceHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Service{h}, opts...))
|
||||
}
|
||||
|
||||
type serviceHandler struct {
|
||||
ServiceHandler
|
||||
}
|
||||
|
||||
func (h *serviceHandler) Call(ctx context.Context, in *Request, out *Response) error {
|
||||
return h.ServiceHandler.Call(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *serviceHandler) Stream(ctx context.Context, stream server.Stream) error {
|
||||
return h.ServiceHandler.Stream(ctx, &serviceStreamStream{stream})
|
||||
}
|
||||
|
||||
type Service_StreamStream interface {
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
}
|
||||
|
||||
type serviceStreamStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *serviceStreamStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *serviceStreamStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *serviceStreamStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *serviceStreamStream) Send(m *Response) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *serviceStreamStream) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.stream.Recv(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (h *serviceHandler) Publish(ctx context.Context, in *Message, out *Empty) error {
|
||||
return h.ServiceHandler.Publish(ctx, in, out)
|
||||
}
|
345
proxy/proto/proxy.pb.go
Normal file
345
proxy/proto/proxy.pb.go
Normal file
@@ -0,0 +1,345 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/micro/go-proxy/proto/proxy.proto
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Request struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_242fe7b9d67fe7d7, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
type Response struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_242fe7b9d67fe7d7, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
type Message struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_242fe7b9d67fe7d7, []int{2}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
type Empty struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_242fe7b9d67fe7d7, []int{3}
|
||||
}
|
||||
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Empty) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Empty.Merge(m, src)
|
||||
}
|
||||
func (m *Empty) XXX_Size() int {
|
||||
return xxx_messageInfo_Empty.Size(m)
|
||||
}
|
||||
func (m *Empty) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Empty.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Empty proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "proxy.Request")
|
||||
proto.RegisterType((*Response)(nil), "proxy.Response")
|
||||
proto.RegisterType((*Message)(nil), "proxy.Message")
|
||||
proto.RegisterType((*Empty)(nil), "proxy.Empty")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/micro/go-proxy/proto/proxy.proto", fileDescriptor_242fe7b9d67fe7d7)
|
||||
}
|
||||
|
||||
var fileDescriptor_242fe7b9d67fe7d7 = []byte{
|
||||
// 186 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0xcf, 0xcd, 0x4c, 0x2e, 0xca, 0xd7, 0x4f, 0xcf, 0xd7,
|
||||
0x2d, 0x28, 0xca, 0xaf, 0xa8, 0xd4, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x07, 0xb3, 0xf5, 0xc0,
|
||||
0x6c, 0x21, 0x56, 0x30, 0x47, 0x89, 0x93, 0x8b, 0x3d, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44,
|
||||
0x89, 0x8b, 0x8b, 0x23, 0x28, 0xb5, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x15, 0x24, 0xec, 0x9b, 0x5a,
|
||||
0x5c, 0x9c, 0x98, 0x9e, 0xaa, 0xc4, 0xce, 0xc5, 0xea, 0x9a, 0x5b, 0x50, 0x52, 0x69, 0x34, 0x81,
|
||||
0x91, 0x8b, 0x3d, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 0x55, 0x48, 0x93, 0x8b, 0xc5, 0x39, 0x31,
|
||||
0x27, 0x47, 0x88, 0x4f, 0x0f, 0x62, 0x26, 0xd4, 0x0c, 0x29, 0x7e, 0x38, 0x1f, 0x6a, 0x10, 0x83,
|
||||
0x90, 0x3e, 0x17, 0x5b, 0x70, 0x49, 0x51, 0x6a, 0x62, 0x2e, 0x11, 0x8a, 0x35, 0x18, 0x0d, 0x18,
|
||||
0x85, 0x34, 0xb9, 0xd8, 0x03, 0x4a, 0x93, 0x72, 0x32, 0x8b, 0x33, 0xe0, 0x3a, 0xa0, 0x6e, 0x91,
|
||||
0xe2, 0x81, 0xf2, 0xc1, 0x0e, 0x52, 0x62, 0x48, 0x62, 0x03, 0xfb, 0xc5, 0x18, 0x10, 0x00, 0x00,
|
||||
0xff, 0xff, 0x51, 0x0d, 0x40, 0x94, 0xfa, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ServiceClient is the client API for Service service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ServiceClient interface {
|
||||
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
Stream(ctx context.Context, opts ...grpc.CallOption) (Service_StreamClient, error)
|
||||
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type serviceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewServiceClient(cc *grpc.ClientConn) ServiceClient {
|
||||
return &serviceClient{cc}
|
||||
}
|
||||
|
||||
func (c *serviceClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/proxy.Service/Call", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *serviceClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Service_StreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Service_serviceDesc.Streams[0], "/proxy.Service/Stream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &serviceStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Service_StreamClient interface {
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type serviceStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *serviceStreamClient) Send(m *Request) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *serviceStreamClient) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *serviceClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/proxy.Service/Publish", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ServiceServer is the server API for Service service.
|
||||
type ServiceServer interface {
|
||||
Call(context.Context, *Request) (*Response, error)
|
||||
Stream(Service_StreamServer) error
|
||||
Publish(context.Context, *Message) (*Empty, error)
|
||||
}
|
||||
|
||||
func RegisterServiceServer(s *grpc.Server, srv ServiceServer) {
|
||||
s.RegisterService(&_Service_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Service_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ServiceServer).Call(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proxy.Service/Call",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ServiceServer).Call(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Service_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(ServiceServer).Stream(&serviceStreamServer{stream})
|
||||
}
|
||||
|
||||
type Service_StreamServer interface {
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type serviceStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *serviceStreamServer) Send(m *Response) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *serviceStreamServer) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _Service_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Message)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ServiceServer).Publish(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proxy.Service/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ServiceServer).Publish(ctx, req.(*Message))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Service_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proxy.Service",
|
||||
HandlerType: (*ServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Call",
|
||||
Handler: _Service_Call_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Service_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Stream",
|
||||
Handler: _Service_Stream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "github.com/micro/go-proxy/proto/proxy.proto",
|
||||
}
|
18
proxy/proto/proxy.proto
Normal file
18
proxy/proto/proxy.proto
Normal file
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package proxy;
|
||||
|
||||
service Service {
|
||||
rpc Call(Request) returns (Response) {};
|
||||
rpc Stream(stream Request) returns (stream Response) {};
|
||||
rpc Publish(Message) returns (Empty) {};
|
||||
rpc Subscribe(Message) returns (stream Message) {};
|
||||
}
|
||||
|
||||
message Request {}
|
||||
|
||||
message Response {}
|
||||
|
||||
message Message {}
|
||||
|
||||
message Empty {}
|
235
proxy/router/grpc/grpc.go
Normal file
235
proxy/router/grpc/grpc.go
Normal file
@@ -0,0 +1,235 @@
|
||||
// Package grpc transparently forwards the grpc protocol using a go-micro client.
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/service/grpc"
|
||||
)
|
||||
|
||||
// Router will transparently proxy requests to the backend.
|
||||
// If no backend is specified it will call a service using the client.
|
||||
// If the service matches the Name it will use the server.DefaultRouter.
|
||||
type Router struct {
|
||||
// Name of the local service. In the event it's to be left alone
|
||||
Name string
|
||||
|
||||
// Backend is a single backend to route to
|
||||
// If backend is of the form address:port it will call the address.
|
||||
// Otherwise it will use it as the service name to call.
|
||||
Backend string
|
||||
|
||||
// Endpoint specified the fixed endpoint to call.
|
||||
// In the event you proxy to a fixed backend this lets you
|
||||
// call a single endpoint
|
||||
Endpoint string
|
||||
|
||||
// The client to use for outbound requests
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
var (
|
||||
// The default name of this local service
|
||||
DefaultName = "go.micro.proxy"
|
||||
// The default router
|
||||
DefaultRouter = &Router{}
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Router interface
|
||||
func (p *Router) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
||||
// set the default name e.g local proxy
|
||||
if p.Name == "" {
|
||||
p.Name = DefaultName
|
||||
}
|
||||
|
||||
// set default client
|
||||
if p.Client == nil {
|
||||
p.Client = client.DefaultClient
|
||||
}
|
||||
|
||||
// check service route
|
||||
if req.Service() == p.Name {
|
||||
// use the default router
|
||||
return server.DefaultRouter.ServeRequest(ctx, req, rsp)
|
||||
}
|
||||
|
||||
opts := []client.CallOption{}
|
||||
|
||||
// service name
|
||||
service := req.Service()
|
||||
endpoint := req.Endpoint()
|
||||
|
||||
// call a specific backend
|
||||
if len(p.Backend) > 0 {
|
||||
// address:port
|
||||
if parts := strings.Split(p.Backend, ":"); len(parts) > 0 {
|
||||
opts = append(opts, client.WithAddress(p.Backend))
|
||||
// use as service name
|
||||
} else {
|
||||
service = p.Backend
|
||||
}
|
||||
}
|
||||
|
||||
// call a specific endpoint
|
||||
if len(p.Endpoint) > 0 {
|
||||
endpoint = p.Endpoint
|
||||
}
|
||||
|
||||
// read initial request
|
||||
body, err := req.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create new request with raw bytes body
|
||||
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSingleHostRouter returns a router which sends requests to a single backend
|
||||
//
|
||||
// It is used by setting it in a new micro service to act as a proxy for a backend.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// Create a new router to the http backend
|
||||
//
|
||||
// r := NewSingleHostRouter("localhost:10001")
|
||||
//
|
||||
// // Create your new service
|
||||
// service := micro.NewService(
|
||||
// micro.Name("greeter"),
|
||||
// // Set the router
|
||||
// http.WithRouter(r),
|
||||
// )
|
||||
//
|
||||
// // Run the service
|
||||
// service.Run()
|
||||
func NewSingleHostRouter(url string) *Router {
|
||||
return &Router{
|
||||
Backend: url,
|
||||
}
|
||||
}
|
||||
|
||||
// NewService returns a new proxy. It acts as a micro service proxy.
|
||||
// Any request on the transport is routed to via the client to a service.
|
||||
// In the event a backend is specified then it routes to that backend.
|
||||
// The name of the backend can be a local address:port or a service name.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// New micro proxy routes via micro client to any service
|
||||
//
|
||||
// proxy := NewService()
|
||||
//
|
||||
// OR with address:port routes to local service
|
||||
//
|
||||
// service := NewService(
|
||||
// // Sets the default http endpoint
|
||||
// proxy.WithBackend("localhost:10001"),
|
||||
// )
|
||||
//
|
||||
// OR with service name routes to a fixed backend service
|
||||
//
|
||||
// service := NewService(
|
||||
// // Sets the backend service
|
||||
// proxy.WithBackend("greeter"),
|
||||
// )
|
||||
//
|
||||
func NewService(opts ...micro.Option) micro.Service {
|
||||
router := DefaultRouter
|
||||
name := DefaultName
|
||||
|
||||
// prepend router to opts
|
||||
opts = append([]micro.Option{
|
||||
micro.Name(name),
|
||||
WithRouter(router),
|
||||
}, opts...)
|
||||
|
||||
// create the new service
|
||||
service := grpc.NewService(opts...)
|
||||
|
||||
// set router name
|
||||
router.Name = service.Server().Options().Name
|
||||
|
||||
return service
|
||||
}
|
32
proxy/router/grpc/options.go
Normal file
32
proxy/router/grpc/options.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// WithBackend provides an option to set the proxy backend url
|
||||
func WithBackend(url string) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
// get the router
|
||||
r := o.Server.Options().Router
|
||||
|
||||
// not set
|
||||
if r == nil {
|
||||
r = DefaultRouter
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
|
||||
// check its a proxy router
|
||||
if proxyRouter, ok := r.(*Router); ok {
|
||||
proxyRouter.Backend = url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter provides an option to set the proxy router
|
||||
func WithRouter(r server.Router) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
}
|
177
proxy/router/http/http.go
Normal file
177
proxy/router/http/http.go
Normal file
@@ -0,0 +1,177 @@
|
||||
// Package http provides a micro rpc to http proxy
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/errors"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Router will proxy rpc requests as http POST requests. It is a server.Router
|
||||
type Router struct {
|
||||
// The http backend to call
|
||||
Backend string
|
||||
|
||||
// first request
|
||||
first bool
|
||||
}
|
||||
|
||||
var (
|
||||
// The default backend
|
||||
DefaultBackend = "http://localhost:9090"
|
||||
// The default router
|
||||
DefaultRouter = &Router{}
|
||||
)
|
||||
|
||||
func getMethod(hdr map[string]string) string {
|
||||
switch hdr["Micro-Method"] {
|
||||
case "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH":
|
||||
return hdr["Micro-Method"]
|
||||
default:
|
||||
return "POST"
|
||||
}
|
||||
}
|
||||
|
||||
func getEndpoint(hdr map[string]string) string {
|
||||
ep := hdr["Micro-Endpoint"]
|
||||
if len(ep) > 0 && ep[0] == '/' {
|
||||
return ep
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Router interface
|
||||
func (p *Router) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
||||
if p.Backend == "" {
|
||||
p.Backend = DefaultBackend
|
||||
}
|
||||
|
||||
for {
|
||||
// get data
|
||||
body, err := req.Read()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the header
|
||||
hdr := req.Header()
|
||||
|
||||
// get method
|
||||
method := getMethod(hdr)
|
||||
|
||||
// get endpoint
|
||||
endpoint := getEndpoint(hdr)
|
||||
|
||||
// set the endpoint
|
||||
if len(endpoint) == 0 {
|
||||
endpoint = p.Backend
|
||||
} else {
|
||||
// add endpoint to backend
|
||||
u, err := url.Parse(p.Backend)
|
||||
if err != nil {
|
||||
return errors.InternalServerError(req.Service(), err.Error())
|
||||
}
|
||||
u.Path = path.Join(u.Path, endpoint)
|
||||
endpoint = u.String()
|
||||
}
|
||||
|
||||
// send to backend
|
||||
hreq, err := http.NewRequest(method, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return errors.InternalServerError(req.Service(), err.Error())
|
||||
}
|
||||
|
||||
// set the headers
|
||||
for k, v := range hdr {
|
||||
hreq.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// make the call
|
||||
hrsp, err := http.DefaultClient.Do(hreq)
|
||||
if err != nil {
|
||||
return errors.InternalServerError(req.Service(), err.Error())
|
||||
}
|
||||
|
||||
// read body
|
||||
b, err := ioutil.ReadAll(hrsp.Body)
|
||||
hrsp.Body.Close()
|
||||
if err != nil {
|
||||
return errors.InternalServerError(req.Service(), err.Error())
|
||||
}
|
||||
|
||||
// set response headers
|
||||
hdr = map[string]string{}
|
||||
for k, _ := range hrsp.Header {
|
||||
hdr[k] = hrsp.Header.Get(k)
|
||||
}
|
||||
// write the header
|
||||
rsp.WriteHeader(hdr)
|
||||
// write the body
|
||||
err = rsp.Write(b)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.InternalServerError(req.Service(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSingleHostRouter returns a router which sends requests to a single http backend
|
||||
//
|
||||
// It is used by setting it in a new micro service to act as a proxy for a http backend.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// Create a new router to the http backend
|
||||
//
|
||||
// r := NewSingleHostRouter("http://localhost:10001")
|
||||
//
|
||||
// // Create your new service
|
||||
// service := micro.NewService(
|
||||
// micro.Name("greeter"),
|
||||
// // Set the router
|
||||
// http.WithRouter(r),
|
||||
// )
|
||||
//
|
||||
// // Run the service
|
||||
// service.Run()
|
||||
func NewSingleHostRouter(url string) *Router {
|
||||
return &Router{
|
||||
Backend: url,
|
||||
}
|
||||
}
|
||||
|
||||
// NewService returns a new http proxy. It acts as a micro service proxy.
|
||||
// Any request on the transport is routed to a fixed http backend.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// service := NewService(
|
||||
// micro.Name("greeter"),
|
||||
// // Sets the default http endpoint
|
||||
// http.WithBackend("http://localhost:10001"),
|
||||
// )
|
||||
//
|
||||
func NewService(opts ...micro.Option) micro.Service {
|
||||
// prepend router to opts
|
||||
opts = append([]micro.Option{
|
||||
WithRouter(DefaultRouter),
|
||||
}, opts...)
|
||||
|
||||
// create the new service
|
||||
return micro.NewService(opts...)
|
||||
}
|
122
proxy/router/http/http_test.go
Normal file
122
proxy/router/http/http_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
type testHandler struct{}
|
||||
|
||||
func (t *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{"hello": "world"}`))
|
||||
}
|
||||
|
||||
func TestHTTPRouter(t *testing.T) {
|
||||
c, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
addr := c.Addr().String()
|
||||
|
||||
url := fmt.Sprintf("http://%s", addr)
|
||||
|
||||
testCases := []struct {
|
||||
// http endpoint to call e.g /foo/bar
|
||||
httpEp string
|
||||
// rpc endpoint called e.g Foo.Bar
|
||||
rpcEp string
|
||||
// should be an error
|
||||
err bool
|
||||
}{
|
||||
{"/", "Foo.Bar", false},
|
||||
{"/", "Foo.Baz", false},
|
||||
{"/helloworld", "Hello.World", true},
|
||||
}
|
||||
|
||||
// handler
|
||||
http.Handle("/", new(testHandler))
|
||||
|
||||
// new proxy
|
||||
p := NewSingleHostRouter(url)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
// new micro service
|
||||
service := micro.NewService(
|
||||
micro.Context(ctx),
|
||||
micro.Name("foobar"),
|
||||
micro.Registry(memory.NewRegistry()),
|
||||
micro.AfterStart(func() error {
|
||||
wg.Done()
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
// set router
|
||||
service.Server().Init(
|
||||
server.WithRouter(p),
|
||||
)
|
||||
|
||||
// run service
|
||||
// server
|
||||
go http.Serve(c, nil)
|
||||
go service.Run()
|
||||
|
||||
// wait till service is started
|
||||
wg.Wait()
|
||||
|
||||
for _, test := range testCases {
|
||||
req := service.Client().NewRequest("foobar", test.rpcEp, map[string]string{"foo": "bar"}, client.WithContentType("application/json"))
|
||||
var rsp map[string]string
|
||||
err := service.Client().Call(ctx, req, &rsp)
|
||||
if err != nil && test.err == false {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v := rsp["hello"]; v != "world" {
|
||||
t.Fatalf("Expected hello world got %s from %s", v, test.rpcEp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPRouterOptions(t *testing.T) {
|
||||
// test endpoint
|
||||
service := NewService(
|
||||
WithBackend("http://foo.bar"),
|
||||
)
|
||||
|
||||
r := service.Server().Options().Router
|
||||
httpRouter, ok := r.(*Router)
|
||||
if !ok {
|
||||
t.Fatal("Expected http router to be installed")
|
||||
}
|
||||
if httpRouter.Backend != "http://foo.bar" {
|
||||
t.Fatalf("Expected endpoint http://foo.bar got %v", httpRouter.Backend)
|
||||
}
|
||||
|
||||
// test router
|
||||
service = NewService(
|
||||
WithRouter(&Router{Backend: "http://foo2.bar"}),
|
||||
)
|
||||
r = service.Server().Options().Router
|
||||
httpRouter, ok = r.(*Router)
|
||||
if !ok {
|
||||
t.Fatal("Expected http router to be installed")
|
||||
}
|
||||
if httpRouter.Backend != "http://foo2.bar" {
|
||||
t.Fatalf("Expected endpoint http://foo2.bar got %v", httpRouter.Backend)
|
||||
}
|
||||
}
|
32
proxy/router/http/options.go
Normal file
32
proxy/router/http/options.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// WithBackend provides an option to set the http backend url
|
||||
func WithBackend(url string) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
// get the router
|
||||
r := o.Server.Options().Router
|
||||
|
||||
// not set
|
||||
if r == nil {
|
||||
r = DefaultRouter
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
|
||||
// check its a http router
|
||||
if httpRouter, ok := r.(*Router); ok {
|
||||
httpRouter.Backend = url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter provides an option to set the http router
|
||||
func WithRouter(r server.Router) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
}
|
234
proxy/router/mucp/mucp.go
Normal file
234
proxy/router/mucp/mucp.go
Normal file
@@ -0,0 +1,234 @@
|
||||
// Package mucp transparently forwards the incoming request using a go-micro client.
|
||||
package mucp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/codec/bytes"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Router will transparently proxy requests to the backend.
|
||||
// If no backend is specified it will call a service using the client.
|
||||
// If the service matches the Name it will use the server.DefaultRouter.
|
||||
type Router struct {
|
||||
// Name of the local service. In the event it's to be left alone
|
||||
Name string
|
||||
|
||||
// Backend is a single backend to route to
|
||||
// If backend is of the form address:port it will call the address.
|
||||
// Otherwise it will use it as the service name to call.
|
||||
Backend string
|
||||
|
||||
// Endpoint specified the fixed endpoint to call.
|
||||
// In the event you proxy to a fixed backend this lets you
|
||||
// call a single endpoint
|
||||
Endpoint string
|
||||
|
||||
// The client to use for outbound requests
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
var (
|
||||
// The default name of this local service
|
||||
DefaultName = "go.micro.proxy"
|
||||
// The default router
|
||||
DefaultRouter = &Router{}
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ServeRequest honours the server.Router interface
|
||||
func (p *Router) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
||||
// set the default name e.g local proxy
|
||||
if p.Name == "" {
|
||||
p.Name = DefaultName
|
||||
}
|
||||
|
||||
// set default client
|
||||
if p.Client == nil {
|
||||
p.Client = client.DefaultClient
|
||||
}
|
||||
|
||||
// check service route
|
||||
if req.Service() == p.Name {
|
||||
// use the default router
|
||||
return server.DefaultRouter.ServeRequest(ctx, req, rsp)
|
||||
}
|
||||
|
||||
opts := []client.CallOption{}
|
||||
|
||||
// service name
|
||||
service := req.Service()
|
||||
endpoint := req.Endpoint()
|
||||
|
||||
// call a specific backend
|
||||
if len(p.Backend) > 0 {
|
||||
// address:port
|
||||
if parts := strings.Split(p.Backend, ":"); len(parts) > 0 {
|
||||
opts = append(opts, client.WithAddress(p.Backend))
|
||||
// use as service name
|
||||
} else {
|
||||
service = p.Backend
|
||||
}
|
||||
}
|
||||
|
||||
// call a specific endpoint
|
||||
if len(p.Endpoint) > 0 {
|
||||
endpoint = p.Endpoint
|
||||
}
|
||||
|
||||
// read initial request
|
||||
body, err := req.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create new request with raw bytes body
|
||||
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSingleHostRouter returns a router which sends requests to a single backend
|
||||
//
|
||||
// It is used by setting it in a new micro service to act as a proxy for a backend.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// Create a new router to the http backend
|
||||
//
|
||||
// r := NewSingleHostRouter("localhost:10001")
|
||||
//
|
||||
// // Create your new service
|
||||
// service := micro.NewService(
|
||||
// micro.Name("greeter"),
|
||||
// // Set the router
|
||||
// http.WithRouter(r),
|
||||
// )
|
||||
//
|
||||
// // Run the service
|
||||
// service.Run()
|
||||
func NewSingleHostRouter(url string) *Router {
|
||||
return &Router{
|
||||
Backend: url,
|
||||
}
|
||||
}
|
||||
|
||||
// NewService returns a new proxy. It acts as a micro service proxy.
|
||||
// Any request on the transport is routed to via the client to a service.
|
||||
// In the event a backend is specified then it routes to that backend.
|
||||
// The name of the backend can be a local address:port or a service name.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// New micro proxy routes via micro client to any service
|
||||
//
|
||||
// proxy := NewService()
|
||||
//
|
||||
// OR with address:port routes to local service
|
||||
//
|
||||
// service := NewService(
|
||||
// // Sets the default http endpoint
|
||||
// proxy.WithBackend("localhost:10001"),
|
||||
// )
|
||||
//
|
||||
// OR with service name routes to a fixed backend service
|
||||
//
|
||||
// service := NewService(
|
||||
// // Sets the backend service
|
||||
// proxy.WithBackend("greeter"),
|
||||
// )
|
||||
//
|
||||
func NewService(opts ...micro.Option) micro.Service {
|
||||
router := DefaultRouter
|
||||
name := DefaultName
|
||||
|
||||
// prepend router to opts
|
||||
opts = append([]micro.Option{
|
||||
micro.Name(name),
|
||||
WithRouter(router),
|
||||
}, opts...)
|
||||
|
||||
// create the new service
|
||||
service := micro.NewService(opts...)
|
||||
|
||||
// set router name
|
||||
router.Name = service.Server().Options().Name
|
||||
|
||||
return service
|
||||
}
|
32
proxy/router/mucp/options.go
Normal file
32
proxy/router/mucp/options.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package mucp
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// WithBackend provides an option to set the proxy backend url
|
||||
func WithBackend(url string) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
// get the router
|
||||
r := o.Server.Options().Router
|
||||
|
||||
// not set
|
||||
if r == nil {
|
||||
r = DefaultRouter
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
|
||||
// check its a proxy router
|
||||
if proxyRouter, ok := r.(*Router); ok {
|
||||
proxyRouter.Backend = url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouter provides an option to set the proxy router
|
||||
func WithRouter(r server.Router) micro.Option {
|
||||
return func(o *micro.Options) {
|
||||
o.Server.Init(server.WithRouter(r))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user