api/router: support pcre and google.api pattern matching (#1549)
* api/router: support pcre and google.api pattern matching Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
ecbc42755c
commit
f00fd7a49e
@ -23,6 +23,7 @@ import (
|
|||||||
type endpoint struct {
|
type endpoint struct {
|
||||||
hostregs []*regexp.Regexp
|
hostregs []*regexp.Regexp
|
||||||
pathregs []util.Pattern
|
pathregs []util.Pattern
|
||||||
|
pcreregs []*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// router is the default router
|
// router is the default router
|
||||||
@ -185,13 +186,23 @@ func (r *registryRouter) store(services []*registry.Service) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range ep.Endpoint.Path {
|
for _, p := range ep.Endpoint.Path {
|
||||||
|
var pcreok bool
|
||||||
|
pcrereg, err := regexp.CompilePOSIX(p)
|
||||||
|
if err == nil {
|
||||||
|
cep.pcreregs = append(cep.pcreregs, pcrereg)
|
||||||
|
pcreok = true
|
||||||
|
}
|
||||||
|
|
||||||
rule, err := util.Parse(p)
|
rule, err := util.Parse(p)
|
||||||
if err != nil {
|
if err != nil && !pcreok {
|
||||||
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
||||||
logger.Tracef("endpoint have invalid path pattern: %v", err)
|
logger.Tracef("endpoint have invalid path pattern: %v", err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
} else if err != nil && pcreok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tpl := rule.Compile()
|
tpl := rule.Compile()
|
||||||
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
|
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -303,11 +314,10 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
ep := e.Endpoint
|
ep := e.Endpoint
|
||||||
var mMatch, hMatch, pMatch bool
|
var mMatch, hMatch, pMatch bool
|
||||||
// 1. try method
|
// 1. try method
|
||||||
methodLoop:
|
|
||||||
for _, m := range ep.Method {
|
for _, m := range ep.Method {
|
||||||
if m == req.Method {
|
if m == req.Method {
|
||||||
mMatch = true
|
mMatch = true
|
||||||
break methodLoop
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !mMatch {
|
if !mMatch {
|
||||||
@ -321,15 +331,14 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
if len(ep.Host) == 0 {
|
if len(ep.Host) == 0 {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
} else {
|
} else {
|
||||||
hostLoop:
|
|
||||||
for idx, h := range ep.Host {
|
for idx, h := range ep.Host {
|
||||||
if h == "" || h == "*" {
|
if h == "" || h == "*" {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
break hostLoop
|
break
|
||||||
} else {
|
} else {
|
||||||
if cep.hostregs[idx].MatchString(req.URL.Host) {
|
if cep.hostregs[idx].MatchString(req.URL.Host) {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
break hostLoop
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,14 +350,12 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
logger.Debugf("api host match %s", req.URL.Host)
|
logger.Debugf("api host match %s", req.URL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. try path
|
// 3. try path via google.api path matching
|
||||||
// 3. try path
|
|
||||||
pathLoop:
|
|
||||||
for _, pathreg := range cep.pathregs {
|
for _, pathreg := range cep.pathregs {
|
||||||
matches, err := pathreg.Match(path, "")
|
matches, err := pathreg.Match(path, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
||||||
logger.Debugf("api path not match %s != %v", path, pathreg)
|
logger.Debugf("api gpath not match %s != %v", path, pathreg)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -363,8 +370,21 @@ func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
|
|||||||
}
|
}
|
||||||
md["x-api-body"] = ep.Body
|
md["x-api-body"] = ep.Body
|
||||||
*req = *req.Clone(metadata.NewContext(ctx, md))
|
*req = *req.Clone(metadata.NewContext(ctx, md))
|
||||||
break pathLoop
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. try path via pcre path matching
|
||||||
|
for _, pathreg := range cep.pcreregs {
|
||||||
|
if !pathreg.MatchString(req.URL.Path) {
|
||||||
|
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
||||||
|
logger.Debugf("api pcre path not match %s != %v", path, pathreg)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pMatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if !pMatch {
|
if !pMatch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,8 @@ func initial(t *testing.T) (server.Server, client.Client) {
|
|||||||
return s, c
|
return s, c
|
||||||
}
|
}
|
||||||
|
|
||||||
func check(addr string, t *testing.T) {
|
func check(t *testing.T, addr string, path string, expected string) {
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/api/v0/test/call/TEST", addr), nil)
|
req, err := http.NewRequest("POST", fmt.Sprintf(path, addr), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to created http.Request: %v", err)
|
t.Fatalf("Failed to created http.Request: %v", err)
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ func check(addr string, t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonMsg := `{"msg":"Hello TEST"}`
|
jsonMsg := expected
|
||||||
if string(buf) != jsonMsg {
|
if string(buf) != jsonMsg {
|
||||||
t.Fatalf("invalid message received, parsing error %s != %s", buf, jsonMsg)
|
t.Fatalf("invalid message received, parsing error %s != %s", buf, jsonMsg)
|
||||||
}
|
}
|
||||||
@ -108,10 +108,51 @@ func TestRouterRegistry(t *testing.T) {
|
|||||||
|
|
||||||
defer hsrv.Close()
|
defer hsrv.Close()
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
check(hsrv.Addr, t)
|
check(t, hsrv.Addr, "http://%s/api/v0/test/call/TEST", `{"msg":"Hello TEST"}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterStatic(t *testing.T) {
|
func TestRouterStaticPcre(t *testing.T) {
|
||||||
|
s, c := initial(t)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
router := rstatic.NewRouter(
|
||||||
|
router.WithHandler(rpc.Handler),
|
||||||
|
router.WithRegistry(s.Options().Registry),
|
||||||
|
)
|
||||||
|
|
||||||
|
err := router.Register(&api.Endpoint{
|
||||||
|
Name: "foo.Test.Call",
|
||||||
|
Method: []string{"POST"},
|
||||||
|
Path: []string{"^/api/v0/test/call/?$"},
|
||||||
|
Handler: "rpc",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hrpc := rpc.NewHandler(
|
||||||
|
handler.WithClient(c),
|
||||||
|
handler.WithRouter(router),
|
||||||
|
)
|
||||||
|
hsrv := &http.Server{
|
||||||
|
Handler: hrpc,
|
||||||
|
Addr: "127.0.0.1:6543",
|
||||||
|
WriteTimeout: 15 * time.Second,
|
||||||
|
ReadTimeout: 15 * time.Second,
|
||||||
|
IdleTimeout: 20 * time.Second,
|
||||||
|
MaxHeaderBytes: 1024 * 1024 * 1, // 1Mb
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Println(hsrv.ListenAndServe())
|
||||||
|
}()
|
||||||
|
defer hsrv.Close()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
check(t, hsrv.Addr, "http://%s/api/v0/test/call", `{"msg":"Hello "}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterStaticG(t *testing.T) {
|
||||||
s, c := initial(t)
|
s, c := initial(t)
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
@ -149,5 +190,5 @@ func TestRouterStatic(t *testing.T) {
|
|||||||
defer hsrv.Close()
|
defer hsrv.Close()
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
check(hsrv.Addr, t)
|
check(t, hsrv.Addr, "http://%s/api/v0/test/call/TEST", `{"msg":"Hello TEST"}`)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ type endpoint struct {
|
|||||||
apiep *api.Endpoint
|
apiep *api.Endpoint
|
||||||
hostregs []*regexp.Regexp
|
hostregs []*regexp.Regexp
|
||||||
pathregs []util.Pattern
|
pathregs []util.Pattern
|
||||||
|
pcreregs []*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// router is the default router
|
// router is the default router
|
||||||
@ -94,6 +95,7 @@ func (r *staticRouter) Register(ep *api.Endpoint) error {
|
|||||||
|
|
||||||
var pathregs []util.Pattern
|
var pathregs []util.Pattern
|
||||||
var hostregs []*regexp.Regexp
|
var hostregs []*regexp.Regexp
|
||||||
|
var pcreregs []*regexp.Regexp
|
||||||
|
|
||||||
for _, h := range ep.Host {
|
for _, h := range ep.Host {
|
||||||
if h == "" || h == "*" {
|
if h == "" || h == "*" {
|
||||||
@ -107,9 +109,18 @@ func (r *staticRouter) Register(ep *api.Endpoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range ep.Path {
|
for _, p := range ep.Path {
|
||||||
|
var pcreok bool
|
||||||
|
pcrereg, err := regexp.CompilePOSIX(p)
|
||||||
|
if err == nil {
|
||||||
|
pcreregs = append(pcreregs, pcrereg)
|
||||||
|
pcreok = true
|
||||||
|
}
|
||||||
|
|
||||||
rule, err := util.Parse(p)
|
rule, err := util.Parse(p)
|
||||||
if err != nil {
|
if err != nil && !pcreok {
|
||||||
return err
|
return err
|
||||||
|
} else if err != nil && pcreok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
tpl := rule.Compile()
|
tpl := rule.Compile()
|
||||||
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
|
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "")
|
||||||
@ -120,7 +131,12 @@ func (r *staticRouter) Register(ep *api.Endpoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.Lock()
|
r.Lock()
|
||||||
r.eps[ep.Name] = &endpoint{apiep: ep, pathregs: pathregs, hostregs: hostregs}
|
r.eps[ep.Name] = &endpoint{
|
||||||
|
apiep: ep,
|
||||||
|
pcreregs: pcreregs,
|
||||||
|
pathregs: pathregs,
|
||||||
|
hostregs: hostregs,
|
||||||
|
}
|
||||||
r.Unlock()
|
r.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -219,11 +235,10 @@ func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
|||||||
var mMatch, hMatch, pMatch bool
|
var mMatch, hMatch, pMatch bool
|
||||||
|
|
||||||
// 1. try method
|
// 1. try method
|
||||||
methodLoop:
|
|
||||||
for _, m := range ep.apiep.Method {
|
for _, m := range ep.apiep.Method {
|
||||||
if m == req.Method {
|
if m == req.Method {
|
||||||
mMatch = true
|
mMatch = true
|
||||||
break methodLoop
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !mMatch {
|
if !mMatch {
|
||||||
@ -237,15 +252,14 @@ func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
|||||||
if len(ep.apiep.Host) == 0 {
|
if len(ep.apiep.Host) == 0 {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
} else {
|
} else {
|
||||||
hostLoop:
|
|
||||||
for idx, h := range ep.apiep.Host {
|
for idx, h := range ep.apiep.Host {
|
||||||
if h == "" || h == "*" {
|
if h == "" || h == "*" {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
break hostLoop
|
break
|
||||||
} else {
|
} else {
|
||||||
if ep.hostregs[idx].MatchString(req.URL.Host) {
|
if ep.hostregs[idx].MatchString(req.URL.Host) {
|
||||||
hMatch = true
|
hMatch = true
|
||||||
break hostLoop
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,13 +271,12 @@ func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
|||||||
logger.Debugf("api host match %s", req.URL.Host)
|
logger.Debugf("api host match %s", req.URL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. try path
|
// 3. try google.api path
|
||||||
pathLoop:
|
|
||||||
for _, pathreg := range ep.pathregs {
|
for _, pathreg := range ep.pathregs {
|
||||||
matches, err := pathreg.Match(path, "")
|
matches, err := pathreg.Match(path, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
||||||
logger.Debugf("api path not match %s != %v", path, pathreg)
|
logger.Debugf("api gpath not match %s != %v", path, pathreg)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -278,8 +291,21 @@ func (r *staticRouter) endpoint(req *http.Request) (*endpoint, error) {
|
|||||||
}
|
}
|
||||||
md["x-api-body"] = ep.apiep.Body
|
md["x-api-body"] = ep.apiep.Body
|
||||||
*req = *req.Clone(metadata.NewContext(ctx, md))
|
*req = *req.Clone(metadata.NewContext(ctx, md))
|
||||||
break pathLoop
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. try path via pcre path matching
|
||||||
|
for _, pathreg := range ep.pcreregs {
|
||||||
|
if !pathreg.MatchString(req.URL.Path) {
|
||||||
|
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
|
||||||
|
logger.Debugf("api pcre path not match %s != %v", req.URL.Path, pathreg)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pMatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if !pMatch {
|
if !pMatch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user