Further consolidate the libraries

This commit is contained in:
Asim Aslam
2019-06-03 18:44:43 +01:00
parent fe060b2d0b
commit b42b6fa0fc
110 changed files with 11934 additions and 83 deletions

25
proxy/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Go Proxy [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/micro/go-proxy?status.svg)](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.

View 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...)
}

View 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...)
}

Binary file not shown.

207
proxy/proto/proxy.micro.go Normal file
View 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
View 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
View 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
View 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
}

View 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
View 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...)
}

View 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)
}
}

View 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
View 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
}

View 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))
}
}