From 8ff86ae08b707c5c921c57300c905eacd44469dc Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Wed, 8 Apr 2020 16:21:53 +0100 Subject: [PATCH 1/5] Extract micro resolver --- api/resolver/micro/micro.go | 45 ----------- api/resolver/micro/route.go | 95 ---------------------- api/resolver/micro/route_test.go | 130 ------------------------------- api/router/options.go | 7 -- 4 files changed, 277 deletions(-) delete mode 100644 api/resolver/micro/micro.go delete mode 100644 api/resolver/micro/route.go delete mode 100644 api/resolver/micro/route_test.go diff --git a/api/resolver/micro/micro.go b/api/resolver/micro/micro.go deleted file mode 100644 index 9d940887..00000000 --- a/api/resolver/micro/micro.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package micro provides a micro rpc resolver which prefixes a namespace -package micro - -import ( - "net/http" - - "github.com/micro/go-micro/v2/api/resolver" -) - -// default resolver for legacy purposes -// it uses proxy routing to resolve names -// /foo becomes namespace.foo -// /v1/foo becomes namespace.v1.foo -type Resolver struct { - Options resolver.Options -} - -func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { - var name, method string - - switch r.Options.Handler { - // internal handlers - case "meta", "api", "rpc", "micro": - name, method = apiRoute(req.URL.Path) - default: - method = req.Method - name = proxyRoute(req.URL.Path) - } - - return &resolver.Endpoint{ - Name: name, - Method: method, - }, nil -} - -func (r *Resolver) String() string { - return "micro" -} - -// NewResolver creates a new micro resolver -func NewResolver(opts ...resolver.Option) resolver.Resolver { - return &Resolver{ - Options: resolver.NewOptions(opts...), - } -} diff --git a/api/resolver/micro/route.go b/api/resolver/micro/route.go deleted file mode 100644 index b2f94b01..00000000 --- a/api/resolver/micro/route.go +++ /dev/null @@ -1,95 +0,0 @@ -package micro - -import ( - "path" - "regexp" - "strings" -) - -var ( - proxyRe = regexp.MustCompile("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$") - versionRe = regexp.MustCompilePOSIX("^v[0-9]+$") -) - -// Translates /foo/bar/zool into api service go.micro.api.foo method Bar.Zool -// Translates /foo/bar into api service go.micro.api.foo method Foo.Bar -func apiRoute(p string) (string, string) { - p = path.Clean(p) - p = strings.TrimPrefix(p, "/") - parts := strings.Split(p, "/") - - // if we have 1 part assume name Name.Call - if len(parts) == 1 && len(parts[0]) > 0 { - return parts[0], methodName(append(parts, "Call")) - } - - // If we've got two or less parts - // Use first part as service - // Use all parts as method - if len(parts) <= 2 { - name := parts[0] - return name, methodName(parts) - } - - // Treat /v[0-9]+ as versioning where we have 3 parts - // /v1/foo/bar => service: v1.foo method: Foo.bar - if len(parts) == 3 && versionRe.Match([]byte(parts[0])) { - name := strings.Join(parts[:len(parts)-1], ".") - return name, methodName(parts[len(parts)-2:]) - } - - // Service is everything minus last two parts - // Method is the last two parts - name := strings.Join(parts[:len(parts)-2], ".") - return name, methodName(parts[len(parts)-2:]) -} - -func proxyRoute(p string) string { - parts := strings.Split(p, "/") - if len(parts) < 2 { - return "" - } - - var service string - var alias string - - // /[service]/methods - if len(parts) > 2 { - // /v1/[service] - if versionRe.MatchString(parts[1]) { - service = parts[1] + "." + parts[2] - alias = parts[2] - } else { - service = parts[1] - alias = parts[1] - } - // /[service] - } else { - service = parts[1] - alias = parts[1] - } - - // check service name is valid - if !proxyRe.MatchString(alias) { - return "" - } - - return service -} - -func methodName(parts []string) string { - for i, part := range parts { - parts[i] = toCamel(part) - } - - return strings.Join(parts, ".") -} - -func toCamel(s string) string { - words := strings.Split(s, "-") - var out string - for _, word := range words { - out += strings.Title(word) - } - return out -} diff --git a/api/resolver/micro/route_test.go b/api/resolver/micro/route_test.go deleted file mode 100644 index b2c61bce..00000000 --- a/api/resolver/micro/route_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package micro - -import ( - "testing" -) - -func TestApiRoute(t *testing.T) { - testData := []struct { - path string - service string - method string - }{ - { - "/foo/bar", - "foo", - "Foo.Bar", - }, - { - "/foo/foo/bar", - "foo", - "Foo.Bar", - }, - { - "/foo/bar/baz", - "foo", - "Bar.Baz", - }, - { - "/foo/bar/baz-xyz", - "foo", - "Bar.BazXyz", - }, - { - "/foo/bar/baz/cat", - "foo.bar", - "Baz.Cat", - }, - { - "/foo/bar/baz/cat/car", - "foo.bar.baz", - "Cat.Car", - }, - { - "/foo/fooBar/bazCat", - "foo", - "FooBar.BazCat", - }, - { - "/v1/foo/bar", - "v1.foo", - "Foo.Bar", - }, - { - "/v1/foo/bar/baz", - "v1.foo", - "Bar.Baz", - }, - { - "/v1/foo/bar/baz/cat", - "v1.foo.bar", - "Baz.Cat", - }, - } - - for _, d := range testData { - s, m := apiRoute(d.path) - if d.service != s { - t.Fatalf("Expected service: %s for path: %s got: %s %s", d.service, d.path, s, m) - } - if d.method != m { - t.Fatalf("Expected service: %s for path: %s got: %s", d.method, d.path, m) - } - } -} - -func TestProxyRoute(t *testing.T) { - testData := []struct { - path string - service string - }{ - // no namespace - { - "/f", - "f", - }, - { - "/f", - "f", - }, - { - "/f-b", - "f-b", - }, - { - "/foo/bar", - "foo", - }, - { - "/foo-bar", - "foo-bar", - }, - { - "/foo-bar-baz", - "foo-bar-baz", - }, - { - "/foo/bar/bar", - "foo", - }, - { - "/v1/foo/bar", - "v1.foo", - }, - { - "/v1/foo/bar/baz", - "v1.foo", - }, - { - "/v1/foo/bar/baz/cat", - "v1.foo", - }, - } - - for _, d := range testData { - s := proxyRoute(d.path) - if d.service != s { - t.Fatalf("Expected service: %s for path: %s got: %s", d.service, d.path, s) - } - } -} diff --git a/api/router/options.go b/api/router/options.go index 9f0c9325..8dc54327 100644 --- a/api/router/options.go +++ b/api/router/options.go @@ -2,7 +2,6 @@ package router import ( "github.com/micro/go-micro/v2/api/resolver" - "github.com/micro/go-micro/v2/api/resolver/micro" "github.com/micro/go-micro/v2/registry" ) @@ -24,12 +23,6 @@ func NewOptions(opts ...Option) Options { o(&options) } - if options.Resolver == nil { - options.Resolver = micro.NewResolver( - resolver.WithHandler(options.Handler), - ) - } - return options } From f102aba4c1014b529d59c09b466213e4f0e0e8e5 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Thu, 9 Apr 2020 10:28:38 +0100 Subject: [PATCH 2/5] Fix HTTP tests --- api/handler/http/http_test.go | 10 ++++++++-- api/resolver/host/host.go | 6 ++++-- api/resolver/path/path.go | 9 ++++++--- api/resolver/vpath/vpath.go | 25 ++++++++++++++++++------- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/api/handler/http/http_test.go b/api/handler/http/http_test.go index 6f094874..1a9d496d 100644 --- a/api/handler/http/http_test.go +++ b/api/handler/http/http_test.go @@ -7,6 +7,8 @@ import ( "testing" "github.com/micro/go-micro/v2/api/handler" + "github.com/micro/go-micro/v2/api/resolver" + res "github.com/micro/go-micro/v2/api/resolver/vpath" "github.com/micro/go-micro/v2/api/router" regRouter "github.com/micro/go-micro/v2/api/router/registry" "github.com/micro/go-micro/v2/registry" @@ -54,8 +56,10 @@ func testHttp(t *testing.T, path, service, ns string) { // initialise the handler rt := regRouter.NewRouter( router.WithHandler("http"), - router.WithNamespace(ns), router.WithRegistry(r), + router.WithResolver(res.NewResolver( + resolver.WithNamespace(ns), + )), ) p := NewHandler(handler.WithRouter(rt)) @@ -116,6 +120,8 @@ func TestHttpHandler(t *testing.T) { } for _, d := range testData { - testHttp(t, d.path, d.service, d.namespace) + t.Run(d.service, func(t *testing.T) { + testHttp(t, d.path, d.service, d.namespace) + }) } } diff --git a/api/resolver/host/host.go b/api/resolver/host/host.go index 157c138f..204bdfa8 100644 --- a/api/resolver/host/host.go +++ b/api/resolver/host/host.go @@ -7,7 +7,9 @@ import ( "github.com/micro/go-micro/v2/api/resolver" ) -type Resolver struct{} +type Resolver struct { + opts resolver.Options +} func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { return &resolver.Endpoint{ @@ -23,5 +25,5 @@ func (r *Resolver) String() string { } func NewResolver(opts ...resolver.Option) resolver.Resolver { - return &Resolver{} + return &Resolver{opts: resolver.NewOptions(opts...)} } diff --git a/api/resolver/path/path.go b/api/resolver/path/path.go index c4d41fc6..88bec8a7 100644 --- a/api/resolver/path/path.go +++ b/api/resolver/path/path.go @@ -8,15 +8,18 @@ import ( "github.com/micro/go-micro/v2/api/resolver" ) -type Resolver struct{} +type Resolver struct { + opts resolver.Options +} func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { if req.URL.Path == "/" { return nil, resolver.ErrNotFound } + parts := strings.Split(req.URL.Path[1:], "/") return &resolver.Endpoint{ - Name: parts[0], + Name: r.opts.Namespace + "." + parts[0], Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -28,5 +31,5 @@ func (r *Resolver) String() string { } func NewResolver(opts ...resolver.Option) resolver.Resolver { - return &Resolver{} + return &Resolver{opts: resolver.NewOptions(opts...)} } diff --git a/api/resolver/vpath/vpath.go b/api/resolver/vpath/vpath.go index 45f6faaf..f1e49399 100644 --- a/api/resolver/vpath/vpath.go +++ b/api/resolver/vpath/vpath.go @@ -3,6 +3,7 @@ package vpath import ( "errors" + "fmt" "net/http" "regexp" "strings" @@ -10,7 +11,13 @@ import ( "github.com/micro/go-micro/v2/api/resolver" ) -type Resolver struct{} +func NewResolver(opts ...resolver.Option) resolver.Resolver { + return &Resolver{opts: resolver.NewOptions(opts...)} +} + +type Resolver struct { + opts resolver.Options +} var ( re = regexp.MustCompile("^v[0-9]+$") @@ -21,11 +28,12 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { return nil, errors.New("unknown name") } - parts := strings.Split(req.URL.Path[1:], "/") + fmt.Println(req.URL.Path) + parts := strings.Split(req.URL.Path[1:], "/") if len(parts) == 1 { return &resolver.Endpoint{ - Name: parts[0], + Name: addNamespace(r.opts.Namespace, parts...), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -35,7 +43,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { // /v1/foo if re.MatchString(parts[0]) { return &resolver.Endpoint{ - Name: parts[1], + Name: addNamespace(r.opts.Namespace, parts[0:2]...), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -43,7 +51,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { } return &resolver.Endpoint{ - Name: parts[0], + Name: addNamespace(r.opts.Namespace, parts[0]), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -54,6 +62,9 @@ func (r *Resolver) String() string { return "path" } -func NewResolver(opts ...resolver.Option) resolver.Resolver { - return &Resolver{} +func addNamespace(ns string, parts ...string) string { + if len(ns) == 0 { + return strings.Join(parts, ".") + } + return strings.Join(append([]string{ns}, parts...), ".") } From 3ede494945c974ac72aa88d82ef8f20a2ad2c986 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Thu, 9 Apr 2020 10:32:08 +0100 Subject: [PATCH 3/5] Change import name --- api/handler/http/http_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/handler/http/http_test.go b/api/handler/http/http_test.go index 1a9d496d..6844a45a 100644 --- a/api/handler/http/http_test.go +++ b/api/handler/http/http_test.go @@ -8,7 +8,7 @@ import ( "github.com/micro/go-micro/v2/api/handler" "github.com/micro/go-micro/v2/api/resolver" - res "github.com/micro/go-micro/v2/api/resolver/vpath" + "github.com/micro/go-micro/v2/api/resolver/vpath" "github.com/micro/go-micro/v2/api/router" regRouter "github.com/micro/go-micro/v2/api/router/registry" "github.com/micro/go-micro/v2/registry" @@ -57,7 +57,7 @@ func testHttp(t *testing.T, path, service, ns string) { rt := regRouter.NewRouter( router.WithHandler("http"), router.WithRegistry(r), - router.WithResolver(res.NewResolver( + router.WithResolver(vpath.NewResolver( resolver.WithNamespace(ns), )), ) From 27eb7db1c2a947f706dabd967427ffc137c62efc Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Thu, 9 Apr 2020 10:34:21 +0100 Subject: [PATCH 4/5] Add default resolver to api router --- api/router/options.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/router/options.go b/api/router/options.go index 8dc54327..69b8a504 100644 --- a/api/router/options.go +++ b/api/router/options.go @@ -2,6 +2,7 @@ package router import ( "github.com/micro/go-micro/v2/api/resolver" + "github.com/micro/go-micro/v2/api/resolver/vpath" "github.com/micro/go-micro/v2/registry" ) @@ -23,6 +24,12 @@ func NewOptions(opts ...Option) Options { o(&options) } + if options.Resolver == nil { + options.Resolver = vpath.NewResolver( + resolver.WithHandler(options.Handler), + ) + } + return options } From 4ff959ef50a6e2e2872de9a4fd85aff2d3a4a366 Mon Sep 17 00:00:00 2001 From: Ben Toogood Date: Thu, 9 Apr 2020 11:03:33 +0100 Subject: [PATCH 5/5] Dynamic Namespace --- api/handler/http/http_test.go | 2 +- api/resolver/options.go | 15 +++++++++++++-- api/resolver/path/path.go | 4 +++- api/resolver/resolver.go | 9 ++++++++- api/resolver/vpath/vpath.go | 10 ++++++---- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/api/handler/http/http_test.go b/api/handler/http/http_test.go index 6844a45a..f0ccd1ff 100644 --- a/api/handler/http/http_test.go +++ b/api/handler/http/http_test.go @@ -58,7 +58,7 @@ func testHttp(t *testing.T, path, service, ns string) { router.WithHandler("http"), router.WithRegistry(r), router.WithResolver(vpath.NewResolver( - resolver.WithNamespace(ns), + resolver.WithNamespace(resolver.StaticNamespace(ns)), )), ) diff --git a/api/resolver/options.go b/api/resolver/options.go index ce4b2e49..eb9ce875 100644 --- a/api/resolver/options.go +++ b/api/resolver/options.go @@ -1,11 +1,22 @@ package resolver +import ( + "net/http" + + "github.com/micro/go-micro/v2/auth" +) + // NewOptions returns new initialised options func NewOptions(opts ...Option) Options { var options Options for _, o := range opts { o(&options) } + + if options.Namespace == nil { + options.Namespace = StaticNamespace(auth.DefaultNamespace) + } + return options } @@ -16,8 +27,8 @@ func WithHandler(h string) Option { } } -// WithNamespace sets the namespace being used -func WithNamespace(n string) Option { +// WithNamespace sets the function which determines the namespace for a request +func WithNamespace(n func(*http.Request) string) Option { return func(o *Options) { o.Namespace = n } diff --git a/api/resolver/path/path.go b/api/resolver/path/path.go index 88bec8a7..84a92656 100644 --- a/api/resolver/path/path.go +++ b/api/resolver/path/path.go @@ -18,8 +18,10 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { } parts := strings.Split(req.URL.Path[1:], "/") + ns := r.opts.Namespace(req) + return &resolver.Endpoint{ - Name: r.opts.Namespace + "." + parts[0], + Name: ns + "." + parts[0], Host: req.Host, Method: req.Method, Path: req.URL.Path, diff --git a/api/resolver/resolver.go b/api/resolver/resolver.go index 12854b19..81e8194d 100644 --- a/api/resolver/resolver.go +++ b/api/resolver/resolver.go @@ -31,7 +31,14 @@ type Endpoint struct { type Options struct { Handler string - Namespace string + Namespace func(*http.Request) string } type Option func(o *Options) + +// StaticNamespace returns the same namespace for each request +func StaticNamespace(ns string) func(*http.Request) string { + return func(*http.Request) string { + return ns + } +} diff --git a/api/resolver/vpath/vpath.go b/api/resolver/vpath/vpath.go index f1e49399..cf407ded 100644 --- a/api/resolver/vpath/vpath.go +++ b/api/resolver/vpath/vpath.go @@ -33,7 +33,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { parts := strings.Split(req.URL.Path[1:], "/") if len(parts) == 1 { return &resolver.Endpoint{ - Name: addNamespace(r.opts.Namespace, parts...), + Name: r.withNamespace(req, parts...), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -43,7 +43,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { // /v1/foo if re.MatchString(parts[0]) { return &resolver.Endpoint{ - Name: addNamespace(r.opts.Namespace, parts[0:2]...), + Name: r.withNamespace(req, parts[0:2]...), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -51,7 +51,7 @@ func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) { } return &resolver.Endpoint{ - Name: addNamespace(r.opts.Namespace, parts[0]), + Name: r.withNamespace(req, parts[0]), Host: req.Host, Method: req.Method, Path: req.URL.Path, @@ -62,9 +62,11 @@ func (r *Resolver) String() string { return "path" } -func addNamespace(ns string, parts ...string) string { +func (r *Resolver) withNamespace(req *http.Request, parts ...string) string { + ns := r.opts.Namespace(req) if len(ns) == 0 { return strings.Join(parts, ".") } + return strings.Join(append([]string{ns}, parts...), ".") }