Further consolidate the libraries
This commit is contained in:
		
							
								
								
									
										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