mirror of
https://github.com/go-micro/go-micro.git
synced 2024-11-24 08:02:32 +02:00
parent
28298a30e4
commit
b6b866c0b2
16
api/api.go
16
api/api.go
@ -1,6 +1,8 @@
|
|||||||
|
// Package api is for building api gateways
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -9,6 +11,8 @@ import (
|
|||||||
"go-micro.dev/v4/server"
|
"go-micro.dev/v4/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The Api interface provides a way to
|
||||||
|
// create composable API gateways
|
||||||
type Api interface {
|
type Api interface {
|
||||||
// Initialise options
|
// Initialise options
|
||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
@ -18,11 +22,16 @@ type Api interface {
|
|||||||
Register(*Endpoint) error
|
Register(*Endpoint) error
|
||||||
// Register a route
|
// Register a route
|
||||||
Deregister(*Endpoint) error
|
Deregister(*Endpoint) error
|
||||||
|
// Run the api
|
||||||
|
Run(context.Context) error
|
||||||
// Implemenation of api
|
// Implemenation of api
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct {
|
||||||
|
// Address of the server
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
type Option func(*Options) error
|
type Option func(*Options) error
|
||||||
|
|
||||||
@ -185,3 +194,8 @@ func NewGateway() Gateway {
|
|||||||
func WithEndpoint(e *Endpoint) server.HandlerOption {
|
func WithEndpoint(e *Endpoint) server.HandlerOption {
|
||||||
return server.EndpointMetadata(e.Name, Encode(e))
|
return server.EndpointMetadata(e.Name, Encode(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewApi returns a new api gateway
|
||||||
|
func NewApi(opts ...Option) Api {
|
||||||
|
return newApi(opts...)
|
||||||
|
}
|
||||||
|
84
api/default.go
Normal file
84
api/default.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go-micro.dev/v4/api/handler"
|
||||||
|
"go-micro.dev/v4/api/handler/rpc"
|
||||||
|
"go-micro.dev/v4/api/router/registry"
|
||||||
|
"go-micro.dev/v4/api/server"
|
||||||
|
"go-micro.dev/v4/api/server/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type api struct {
|
||||||
|
options Options
|
||||||
|
|
||||||
|
server server.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApi(opts ...Option) Api {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
|
||||||
|
// TODO: make configurable
|
||||||
|
rtr := registry.NewRouter()
|
||||||
|
|
||||||
|
// TODO: make configurable
|
||||||
|
hdlr := rpc.NewHandler(
|
||||||
|
handler.WithRouter(rtr),
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: make configurable
|
||||||
|
// create a new server
|
||||||
|
srv := http.NewServer(options.Address)
|
||||||
|
|
||||||
|
// TODO: allow multiple handlers
|
||||||
|
// define the handler
|
||||||
|
srv.Handle("/", hdlr)
|
||||||
|
|
||||||
|
return &api{
|
||||||
|
options: options,
|
||||||
|
server: srv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise options
|
||||||
|
func (a *api) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&a.options)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the options
|
||||||
|
func (a *api) Options() Options {
|
||||||
|
return a.options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a http handler
|
||||||
|
func (a *api) Register(*Endpoint) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a route
|
||||||
|
func (a *api) Deregister(*Endpoint) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) Run(ctx context.Context) error {
|
||||||
|
if err := a.server.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait to finish
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
if err := a.server.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *api) String() string {
|
||||||
|
return "http"
|
||||||
|
}
|
@ -4,9 +4,9 @@ package api
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
goapi "go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/handler"
|
"go-micro.dev/v4/api/handler"
|
||||||
api "go-micro.dev/v4/api/proto"
|
api "go-micro.dev/v4/api/proto"
|
||||||
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/client"
|
"go-micro.dev/v4/client"
|
||||||
"go-micro.dev/v4/errors"
|
"go-micro.dev/v4/errors"
|
||||||
"go-micro.dev/v4/selector"
|
"go-micro.dev/v4/selector"
|
||||||
@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
type apiHandler struct {
|
type apiHandler struct {
|
||||||
opts handler.Options
|
opts handler.Options
|
||||||
s *goapi.Service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,12 +38,9 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var service *goapi.Service
|
var service *router.Route
|
||||||
|
|
||||||
if a.s != nil {
|
if a.opts.Router != nil {
|
||||||
// we were given the service
|
|
||||||
service = a.s
|
|
||||||
} else if a.opts.Router != nil {
|
|
||||||
// try get service from router
|
// try get service from router
|
||||||
s, err := a.opts.Router.Route(r)
|
s, err := a.opts.Router.Route(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -66,13 +62,13 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// create request and response
|
// create request and response
|
||||||
c := a.opts.Client
|
c := a.opts.Client
|
||||||
req := c.NewRequest(service.Name, service.Endpoint.Name, request)
|
req := c.NewRequest(service.Service, service.Endpoint.Name, request)
|
||||||
rsp := &api.Response{}
|
rsp := &api.Response{}
|
||||||
|
|
||||||
// create the context from headers
|
// create the context from headers
|
||||||
cx := ctx.FromRequest(r)
|
cx := ctx.FromRequest(r)
|
||||||
// create strategy
|
// create strategy:
|
||||||
so := selector.WithStrategy(strategy(service.Services))
|
so := selector.WithStrategy(strategy(service.Versions))
|
||||||
|
|
||||||
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
|
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
@ -113,11 +109,3 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
opts: options,
|
opts: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
return &apiHandler{
|
|
||||||
opts: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/handler"
|
"go-micro.dev/v4/api/handler"
|
||||||
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/selector"
|
"go-micro.dev/v4/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,9 +19,6 @@ const (
|
|||||||
|
|
||||||
type httpHandler struct {
|
type httpHandler struct {
|
||||||
options handler.Options
|
options handler.Options
|
||||||
|
|
||||||
// set with different initialiser
|
|
||||||
s *api.Service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -47,12 +44,9 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// getService returns the service for this request from the selector
|
// getService returns the service for this request from the selector
|
||||||
func (h *httpHandler) getService(r *http.Request) (string, error) {
|
func (h *httpHandler) getService(r *http.Request) (string, error) {
|
||||||
var service *api.Service
|
var service *router.Route
|
||||||
|
|
||||||
if h.s != nil {
|
if h.options.Router != nil {
|
||||||
// we were given the service
|
|
||||||
service = h.s
|
|
||||||
} else if h.options.Router != nil {
|
|
||||||
// try get service from router
|
// try get service from router
|
||||||
s, err := h.options.Router.Route(r)
|
s, err := h.options.Router.Route(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -65,7 +59,7 @@ func (h *httpHandler) getService(r *http.Request) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a random selector
|
// create a random selector
|
||||||
next := selector.Random(service.Services)
|
next := selector.Random(service.Versions)
|
||||||
|
|
||||||
// get the next node
|
// get the next node
|
||||||
s, err := next()
|
s, err := next()
|
||||||
@ -88,13 +82,3 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
options: options,
|
options: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithService creates a handler with a service
|
|
||||||
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
|
|
||||||
return &httpHandler{
|
|
||||||
options: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -26,12 +26,7 @@ func NewOptions(opts ...Option) Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if options.Client == nil {
|
if options.Client == nil {
|
||||||
WithClient(client.NewClient())(&options)
|
WithClient(client.DefaultClient)(&options)
|
||||||
}
|
|
||||||
|
|
||||||
// set namespace if blank
|
|
||||||
if len(options.Namespace) == 0 {
|
|
||||||
WithNamespace("go.micro.api")(&options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.MaxRecvSize == 0 {
|
if options.MaxRecvSize == 0 {
|
||||||
|
@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||||
"github.com/oxtoacart/bpool"
|
"github.com/oxtoacart/bpool"
|
||||||
"go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/handler"
|
"go-micro.dev/v4/api/handler"
|
||||||
"go-micro.dev/v4/api/internal/proto"
|
"go-micro.dev/v4/api/internal/proto"
|
||||||
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/client"
|
"go-micro.dev/v4/client"
|
||||||
"go-micro.dev/v4/codec"
|
"go-micro.dev/v4/codec"
|
||||||
"go-micro.dev/v4/codec/jsonrpc"
|
"go-micro.dev/v4/codec/jsonrpc"
|
||||||
@ -54,7 +54,6 @@ var (
|
|||||||
|
|
||||||
type rpcHandler struct {
|
type rpcHandler struct {
|
||||||
opts handler.Options
|
opts handler.Options
|
||||||
s *api.Service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type buffer struct {
|
type buffer struct {
|
||||||
@ -82,12 +81,9 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
r.Body = http.MaxBytesReader(w, r.Body, bsize)
|
||||||
|
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
var service *api.Service
|
var service *router.Route
|
||||||
|
|
||||||
if h.s != nil {
|
if h.opts.Router != nil {
|
||||||
// we were given the service
|
|
||||||
service = h.s
|
|
||||||
} else if h.opts.Router != nil {
|
|
||||||
// try get service from router
|
// try get service from router
|
||||||
s, err := h.opts.Router.Route(r)
|
s, err := h.opts.Router.Route(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -142,7 +138,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create strategy
|
// create strategy
|
||||||
so := selector.WithStrategy(strategy(service.Services))
|
so := selector.WithStrategy(strategy(service.Versions))
|
||||||
|
|
||||||
// walk the standard call path
|
// walk the standard call path
|
||||||
// get payload
|
// get payload
|
||||||
@ -167,7 +163,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
response := &proto.Message{}
|
response := &proto.Message{}
|
||||||
|
|
||||||
req := c.NewRequest(
|
req := c.NewRequest(
|
||||||
service.Name,
|
service.Service,
|
||||||
service.Endpoint.Name,
|
service.Endpoint.Name,
|
||||||
request,
|
request,
|
||||||
client.WithContentType(ct),
|
client.WithContentType(ct),
|
||||||
@ -203,7 +199,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
var response json.RawMessage
|
var response json.RawMessage
|
||||||
|
|
||||||
req := c.NewRequest(
|
req := c.NewRequest(
|
||||||
service.Name,
|
service.Service,
|
||||||
service.Endpoint.Name,
|
service.Endpoint.Name,
|
||||||
&request,
|
&request,
|
||||||
client.WithContentType(ct),
|
client.WithContentType(ct),
|
||||||
@ -512,11 +508,3 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
opts: options,
|
opts: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
return &rpcHandler{
|
|
||||||
opts: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/gobwas/httphead"
|
"github.com/gobwas/httphead"
|
||||||
"github.com/gobwas/ws"
|
"github.com/gobwas/ws"
|
||||||
"github.com/gobwas/ws/wsutil"
|
"github.com/gobwas/ws/wsutil"
|
||||||
"go-micro.dev/v4/api"
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/client"
|
"go-micro.dev/v4/client"
|
||||||
raw "go-micro.dev/v4/codec/bytes"
|
raw "go-micro.dev/v4/codec/bytes"
|
||||||
"go-micro.dev/v4/logger"
|
"go-micro.dev/v4/logger"
|
||||||
@ -20,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// serveWebsocket will stream rpc back over websockets assuming json
|
// serveWebsocket will stream rpc back over websockets assuming json
|
||||||
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *api.Service, c client.Client) {
|
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *router.Route, c client.Client) {
|
||||||
var op ws.OpCode
|
var op ws.OpCode
|
||||||
|
|
||||||
ct := r.Header.Get("Content-Type")
|
ct := r.Header.Get("Content-Type")
|
||||||
@ -103,14 +103,14 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
ct = "application/json"
|
ct = "application/json"
|
||||||
}
|
}
|
||||||
req := c.NewRequest(
|
req := c.NewRequest(
|
||||||
service.Name,
|
service.Service,
|
||||||
service.Endpoint.Name,
|
service.Endpoint.Name,
|
||||||
request,
|
request,
|
||||||
client.WithContentType(ct),
|
client.WithContentType(ct),
|
||||||
client.StreamingRequest(),
|
client.StreamingRequest(),
|
||||||
)
|
)
|
||||||
|
|
||||||
so := selector.WithStrategy(strategy(service.Services))
|
so := selector.WithStrategy(strategy(service.Versions))
|
||||||
// create a new stream
|
// create a new stream
|
||||||
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
|
stream, err := c.Stream(ctx, req, client.WithSelectOption(so))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -219,13 +219,13 @@ func writeLoop(rw io.ReadWriter, stream client.Stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStream(r *http.Request, srv *api.Service) bool {
|
func isStream(r *http.Request, srv *router.Route) bool {
|
||||||
// check if it's a web socket
|
// check if it's a web socket
|
||||||
if !isWebSocket(r) {
|
if !isWebSocket(r) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// check if the endpoint supports streaming
|
// check if the endpoint supports streaming
|
||||||
for _, service := range srv.Services {
|
for _, service := range srv.Versions {
|
||||||
for _, ep := range service.Endpoints {
|
for _, ep := range service.Endpoints {
|
||||||
// skip if it doesn't match the name
|
// skip if it doesn't match the name
|
||||||
if ep.Name != srv.Endpoint.Name {
|
if ep.Name != srv.Endpoint.Name {
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/handler"
|
"go-micro.dev/v4/api/handler"
|
||||||
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/selector"
|
"go-micro.dev/v4/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,7 +22,6 @@ const (
|
|||||||
|
|
||||||
type webHandler struct {
|
type webHandler struct {
|
||||||
opts handler.Options
|
opts handler.Options
|
||||||
s *api.Service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -53,12 +52,9 @@ func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// getService returns the service for this request from the selector
|
// getService returns the service for this request from the selector
|
||||||
func (wh *webHandler) getService(r *http.Request) (string, error) {
|
func (wh *webHandler) getService(r *http.Request) (string, error) {
|
||||||
var service *api.Service
|
var service *router.Route
|
||||||
|
|
||||||
if wh.s != nil {
|
if wh.opts.Router != nil {
|
||||||
// we were given the service
|
|
||||||
service = wh.s
|
|
||||||
} else if wh.opts.Router != nil {
|
|
||||||
// try get service from router
|
// try get service from router
|
||||||
s, err := wh.opts.Router.Route(r)
|
s, err := wh.opts.Router.Route(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -71,7 +67,7 @@ func (wh *webHandler) getService(r *http.Request) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a random selector
|
// create a random selector
|
||||||
next := selector.Random(service.Services)
|
next := selector.Random(service.Versions)
|
||||||
|
|
||||||
// get the next node
|
// get the next node
|
||||||
s, err := next()
|
s, err := next()
|
||||||
@ -166,12 +162,3 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
opts: handler.NewOptions(opts...),
|
opts: handler.NewOptions(opts...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
|
|
||||||
options := handler.NewOptions(opts...)
|
|
||||||
|
|
||||||
return &webHandler{
|
|
||||||
opts: options,
|
|
||||||
s: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
13
api/options.go
Normal file
13
api/options.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Address: ":8080",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
99
api/router/endpoint.go
Normal file
99
api/router/endpoint.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func strip(s string) string {
|
||||||
|
return strings.TrimSpace(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func slice(s string) []string {
|
||||||
|
var sl []string
|
||||||
|
|
||||||
|
for _, p := range strings.Split(s, ",") {
|
||||||
|
if str := strip(p); len(str) > 0 {
|
||||||
|
sl = append(sl, strip(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes an endpoint to endpoint metadata
|
||||||
|
func Encode(e *Endpoint) map[string]string {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint map
|
||||||
|
ep := make(map[string]string)
|
||||||
|
|
||||||
|
// set vals only if they exist
|
||||||
|
set := func(k, v string) {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ep[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
set("endpoint", e.Name)
|
||||||
|
set("description", e.Description)
|
||||||
|
set("handler", e.Handler)
|
||||||
|
set("method", strings.Join(e.Method, ","))
|
||||||
|
set("path", strings.Join(e.Path, ","))
|
||||||
|
set("host", strings.Join(e.Host, ","))
|
||||||
|
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes endpoint metadata into an endpoint
|
||||||
|
func Decode(e map[string]string) *Endpoint {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Endpoint{
|
||||||
|
Name: e["endpoint"],
|
||||||
|
Description: e["description"],
|
||||||
|
Method: slice(e["method"]),
|
||||||
|
Path: slice(e["path"]),
|
||||||
|
Host: slice(e["host"]),
|
||||||
|
Handler: e["handler"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates an endpoint to guarantee it won't blow up when being served
|
||||||
|
func Validate(e *Endpoint) error {
|
||||||
|
if e == nil {
|
||||||
|
return errors.New("endpoint is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Name) == 0 {
|
||||||
|
return errors.New("name required")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range e.Path {
|
||||||
|
ps := p[0]
|
||||||
|
pe := p[len(p)-1]
|
||||||
|
|
||||||
|
if ps == '^' && pe == '$' {
|
||||||
|
_, err := regexp.CompilePOSIX(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if ps == '^' && pe != '$' {
|
||||||
|
return errors.New("invalid path")
|
||||||
|
} else if ps != '^' && pe == '$' {
|
||||||
|
return errors.New("invalid path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Handler) == 0 {
|
||||||
|
return errors.New("invalid handler")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -10,7 +10,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/router"
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/api/router/util"
|
"go-micro.dev/v4/api/router/util"
|
||||||
"go-micro.dev/v4/logger"
|
"go-micro.dev/v4/logger"
|
||||||
@ -35,12 +34,12 @@ type registryRouter struct {
|
|||||||
rc cache.Cache
|
rc cache.Cache
|
||||||
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
eps map[string]*api.Service
|
eps map[string]*router.Route
|
||||||
// compiled regexp for host and path
|
// compiled regexp for host and path
|
||||||
ceps map[string]*endpoint
|
ceps map[string]*endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) isClosed() bool {
|
func (r *registryRouter) isStopd() bool {
|
||||||
select {
|
select {
|
||||||
case <-r.exit:
|
case <-r.exit:
|
||||||
return true
|
return true
|
||||||
@ -111,7 +110,7 @@ func (r *registryRouter) process(res *registry.Result) {
|
|||||||
// store local endpoint cache
|
// store local endpoint cache
|
||||||
func (r *registryRouter) store(services []*registry.Service) {
|
func (r *registryRouter) store(services []*registry.Service) {
|
||||||
// endpoints
|
// endpoints
|
||||||
eps := map[string]*api.Service{}
|
eps := map[string]*router.Route{}
|
||||||
|
|
||||||
// services
|
// services
|
||||||
names := map[string]bool{}
|
names := map[string]bool{}
|
||||||
@ -126,10 +125,10 @@ func (r *registryRouter) store(services []*registry.Service) {
|
|||||||
// create a key service:endpoint_name
|
// create a key service:endpoint_name
|
||||||
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
|
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
|
||||||
// decode endpoint
|
// decode endpoint
|
||||||
end := api.Decode(sep.Metadata)
|
end := router.Decode(sep.Metadata)
|
||||||
|
|
||||||
// if we got nothing skip
|
// if we got nothing skip
|
||||||
if err := api.Validate(end); err != nil {
|
if err := router.Validate(end); err != nil {
|
||||||
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
||||||
logger.Tracef("endpoint validation failed: %v", err)
|
logger.Tracef("endpoint validation failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -139,13 +138,13 @@ func (r *registryRouter) store(services []*registry.Service) {
|
|||||||
// try get endpoint
|
// try get endpoint
|
||||||
ep, ok := eps[key]
|
ep, ok := eps[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
ep = &api.Service{Name: service.Name}
|
ep = &router.Route{Service: service.Name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// overwrite the endpoint
|
// overwrite the endpoint
|
||||||
ep.Endpoint = end
|
ep.Endpoint = end
|
||||||
// append services
|
// append services
|
||||||
ep.Services = append(ep.Services, service)
|
ep.Versions = append(ep.Versions, service)
|
||||||
// store it
|
// store it
|
||||||
eps[key] = ep
|
eps[key] = ep
|
||||||
}
|
}
|
||||||
@ -155,9 +154,9 @@ func (r *registryRouter) store(services []*registry.Service) {
|
|||||||
defer r.Unlock()
|
defer r.Unlock()
|
||||||
|
|
||||||
// delete any existing eps for services we know
|
// delete any existing eps for services we know
|
||||||
for key, service := range r.eps {
|
for key, route := range r.eps {
|
||||||
// skip what we don't care about
|
// skip what we don't care about
|
||||||
if !names[service.Name] {
|
if !names[route.Service] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +225,7 @@ func (r *registryRouter) watch() {
|
|||||||
var attempts int
|
var attempts int
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +273,7 @@ func (r *registryRouter) Options() router.Options {
|
|||||||
return r.opts
|
return r.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) Close() error {
|
func (r *registryRouter) Stop() error {
|
||||||
select {
|
select {
|
||||||
case <-r.exit:
|
case <-r.exit:
|
||||||
return nil
|
return nil
|
||||||
@ -285,16 +284,16 @@ func (r *registryRouter) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) Register(ep *api.Endpoint) error {
|
func (r *registryRouter) Register(ep *router.Route) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) Deregister(ep *api.Endpoint) error {
|
func (r *registryRouter) Deregister(ep *router.Route) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return nil, errors.New("router closed")
|
return nil, errors.New("router closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,8 +408,8 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
return nil, errors.New("not found")
|
return nil, errors.New("not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
|
func (r *registryRouter) Route(req *http.Request) (*router.Route, error) {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return nil, errors.New("router closed")
|
return nil, errors.New("router closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,27 +450,27 @@ func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// construct api service
|
// construct api service
|
||||||
return &api.Service{
|
return &router.Route{
|
||||||
Name: name,
|
Service: name,
|
||||||
Endpoint: &api.Endpoint{
|
Endpoint: &router.Endpoint{
|
||||||
Name: rp.Method,
|
Name: rp.Method,
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
},
|
},
|
||||||
Services: services,
|
Versions: services,
|
||||||
}, nil
|
}, nil
|
||||||
// http handler
|
// http handler
|
||||||
case "http", "proxy", "web":
|
case "http", "proxy", "web":
|
||||||
// construct api service
|
// construct api service
|
||||||
return &api.Service{
|
return &router.Route{
|
||||||
Name: name,
|
Service: name,
|
||||||
Endpoint: &api.Endpoint{
|
Endpoint: &router.Endpoint{
|
||||||
Name: req.URL.String(),
|
Name: req.URL.String(),
|
||||||
Handler: r.opts.Handler,
|
Handler: r.opts.Handler,
|
||||||
Host: []string{req.Host},
|
Host: []string{req.Host},
|
||||||
Method: []string{req.Method},
|
Method: []string{req.Method},
|
||||||
Path: []string{req.URL.Path},
|
Path: []string{req.URL.Path},
|
||||||
},
|
},
|
||||||
Services: services,
|
Versions: services,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +483,7 @@ func newRouter(opts ...router.Option) *registryRouter {
|
|||||||
exit: make(chan bool),
|
exit: make(chan bool),
|
||||||
opts: options,
|
opts: options,
|
||||||
rc: cache.New(options.Registry),
|
rc: cache.New(options.Registry),
|
||||||
eps: make(map[string]*api.Service),
|
eps: make(map[string]*router.Route),
|
||||||
ceps: make(map[string]*endpoint),
|
ceps: make(map[string]*endpoint),
|
||||||
}
|
}
|
||||||
go r.watch()
|
go r.watch()
|
||||||
|
@ -4,21 +4,50 @@ package router
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"go-micro.dev/v4/api"
|
"go-micro.dev/v4/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Router is used to determine an endpoint for a request
|
// Router is used to determine an endpoint for a request
|
||||||
type Router interface {
|
type Router interface {
|
||||||
// Returns options
|
// Returns options
|
||||||
Options() Options
|
Options() Options
|
||||||
// Stop the router
|
|
||||||
Close() error
|
|
||||||
// Endpoint returns an api.Service endpoint or an error if it does not exist
|
|
||||||
Endpoint(r *http.Request) (*api.Service, error)
|
|
||||||
// Register endpoint in router
|
// Register endpoint in router
|
||||||
Register(ep *api.Endpoint) error
|
Register(r *Route) error
|
||||||
// Deregister endpoint from router
|
// Deregister endpoint from router
|
||||||
Deregister(ep *api.Endpoint) error
|
Deregister(r *Route) error
|
||||||
// Route returns an api.Service route
|
// Route returns an api.Service route
|
||||||
Route(r *http.Request) (*api.Service, error)
|
Route(r *http.Request) (*Route, error)
|
||||||
|
// Stop the router
|
||||||
|
Stop() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
// Name of service
|
||||||
|
Service string
|
||||||
|
// The endpoint for this service
|
||||||
|
Endpoint *Endpoint
|
||||||
|
// Versions of this service
|
||||||
|
Versions []*registry.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||||
|
type Endpoint struct {
|
||||||
|
// RPC Method e.g. Greeter.Hello
|
||||||
|
Name string
|
||||||
|
// Description e.g what's this endpoint for
|
||||||
|
Description string
|
||||||
|
// API Handler e.g rpc, proxy
|
||||||
|
Handler string
|
||||||
|
// HTTP Host e.g example.com
|
||||||
|
Host []string
|
||||||
|
// HTTP Methods e.g GET, POST
|
||||||
|
Method []string
|
||||||
|
// HTTP Path e.g /greeter. Expect POSIX regex
|
||||||
|
Path []string
|
||||||
|
// Body destination
|
||||||
|
// "*" or "" - top level message value
|
||||||
|
// "string" - inner message value
|
||||||
|
Body string
|
||||||
|
// Stream flag
|
||||||
|
Stream bool
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go-micro.dev/v4/api"
|
|
||||||
"go-micro.dev/v4/api/router"
|
"go-micro.dev/v4/api/router"
|
||||||
"go-micro.dev/v4/api/router/util"
|
"go-micro.dev/v4/api/router/util"
|
||||||
"go-micro.dev/v4/logger"
|
"go-micro.dev/v4/logger"
|
||||||
@ -18,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type endpoint struct {
|
type endpoint struct {
|
||||||
apiep *api.Endpoint
|
apiep *router.Endpoint
|
||||||
hostregs []*regexp.Regexp
|
hostregs []*regexp.Regexp
|
||||||
pathregs []util.Pattern
|
pathregs []util.Pattern
|
||||||
pcreregs []*regexp.Regexp
|
pcreregs []*regexp.Regexp
|
||||||
@ -32,7 +31,7 @@ type staticRouter struct {
|
|||||||
eps map[string]*endpoint
|
eps map[string]*endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) isClosed() bool {
|
func (r *staticRouter) isStopd() bool {
|
||||||
select {
|
select {
|
||||||
case <-r.exit:
|
case <-r.exit:
|
||||||
return true
|
return true
|
||||||
@ -47,7 +46,7 @@ func (r *staticRouter) watch() {
|
|||||||
var attempts int
|
var attempts int
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +87,10 @@ func (r *staticRouter) watch() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (r *staticRouter) Register(ep *api.Endpoint) error {
|
func (r *staticRouter) Register(route *router.Route) error {
|
||||||
if err := api.Validate(ep); err != nil {
|
ep := route.Endpoint
|
||||||
|
|
||||||
|
if err := router.Validate(ep); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,8 +147,9 @@ func (r *staticRouter) Register(ep *api.Endpoint) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) Deregister(ep *api.Endpoint) error {
|
func (r *staticRouter) Deregister(route *router.Route) error {
|
||||||
if err := api.Validate(ep); err != nil {
|
ep := route.Endpoint
|
||||||
|
if err := router.Validate(ep); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.Lock()
|
r.Lock()
|
||||||
@ -160,7 +162,7 @@ func (r *staticRouter) Options() router.Options {
|
|||||||
return r.opts
|
return r.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) Close() error {
|
func (r *staticRouter) Stop() error {
|
||||||
select {
|
select {
|
||||||
case <-r.exit:
|
case <-r.exit:
|
||||||
return nil
|
return nil
|
||||||
@ -170,7 +172,7 @@ func (r *staticRouter) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
func (r *staticRouter) Endpoint(req *http.Request) (*router.Route, error) {
|
||||||
ep, err := r.endpoint(req)
|
ep, err := r.endpoint(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -203,9 +205,9 @@ func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
services = svcs
|
services = svcs
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := &api.Service{
|
svc := &router.Route{
|
||||||
Name: epf[0],
|
Service: epf[0],
|
||||||
Endpoint: &api.Endpoint{
|
Endpoint: &router.Endpoint{
|
||||||
Name: strings.Join(epf[1:], "."),
|
Name: strings.Join(epf[1:], "."),
|
||||||
Handler: "rpc",
|
Handler: "rpc",
|
||||||
Host: ep.apiep.Host,
|
Host: ep.apiep.Host,
|
||||||
@ -214,14 +216,14 @@ func (r *staticRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
Body: ep.apiep.Body,
|
Body: ep.apiep.Body,
|
||||||
Stream: ep.apiep.Stream,
|
Stream: ep.apiep.Stream,
|
||||||
},
|
},
|
||||||
Services: services,
|
Versions: services,
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc, nil
|
return svc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return nil, errors.New("router closed")
|
return nil, errors.New("router closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,8 +331,8 @@ func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
|||||||
return nil, fmt.Errorf("endpoint not found for %v", req.URL)
|
return nil, fmt.Errorf("endpoint not found for %v", req.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *staticRouter) Route(req *http.Request) (*api.Service, error) {
|
func (r *staticRouter) Route(req *http.Request) (*router.Route, error) {
|
||||||
if r.isClosed() {
|
if r.isStopd() {
|
||||||
return nil, errors.New("router closed")
|
return nil, errors.New("router closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user