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

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