From dc47bef90637125599c1d789e3fafc672496c06c Mon Sep 17 00:00:00 2001 From: Shawnh2 Date: Sun, 4 Jun 2023 19:01:09 +0800 Subject: [PATCH 1/5] add header route in router --- pixiu/pkg/common/router/router.go | 52 +++- pixiu/pkg/common/router/router_test.go | 305 +++++++++++++++++++++- pixiu/pkg/common/router/trie/trie.go | 72 +++-- pixiu/pkg/common/router/trie/trie_test.go | 82 +++--- pixiu/pkg/model/router.go | 75 +++++- pixiu/pkg/router/route.go | 4 +- pixiu/pkg/server/http.go | 2 +- 7 files changed, 513 insertions(+), 79 deletions(-) diff --git a/pixiu/pkg/common/router/router.go b/pixiu/pkg/common/router/router.go index 7148d5a5c..aed674c73 100644 --- a/pixiu/pkg/common/router/router.go +++ b/pixiu/pkg/common/router/router.go @@ -18,15 +18,21 @@ package router import ( + stdHttp "net/http" "strings" "sync" ) +import ( + "github.com/pkg/errors" +) + import ( "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/constant" "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/router/trie" "github.com/apache/dubbo-go-pixiu/pixiu/pkg/common/util/stringutil" "github.com/apache/dubbo-go-pixiu/pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pixiu/pkg/logger" "github.com/apache/dubbo-go-pixiu/pixiu/pkg/model" "github.com/apache/dubbo-go-pixiu/pixiu/pkg/server" ) @@ -46,6 +52,7 @@ func CreateRouterCoordinator(routeConfig *model.RouteConfiguration) *RouterCoord server.GetRouterManager().AddRouterListener(rc) } rc.initTrie() + rc.initRegex() return rc } @@ -54,14 +61,38 @@ func (rm *RouterCoordinator) Route(hc *http.HttpContext) (*model.RouteAction, er rm.rw.RLock() defer rm.rw.RUnlock() - return rm.activeConfig.Route(hc.Request) + return rm.route(hc.Request) } func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.RouteAction, error) { rm.rw.RLock() defer rm.rw.RUnlock() - return rm.activeConfig.RouteByPathAndMethod(path, method) + return rm.activeConfig.RouteByPathAndMethod(path, method, nil) +} + +func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) { + // match those route that only contains headers first + var matched []*model.Router + for _, route := range rm.activeConfig.Routes { + if len(route.Match.Prefix) > 0 { + continue + } + if route.Match.MatchHeader(req) { + matched = append(matched, route) + } + } + + // always return the first match of header if got any + if len(matched) > 0 { + if len(matched[0].Route.Cluster) == 0 { + return nil, errors.New("action is nil. please check your configuration.") + } + return &matched[0].Route, nil + } + + // match those route that only contains prefix or both prefix and header + return rm.activeConfig.Route(req) } func getTrieKey(method string, path string, isPrefix bool) string { @@ -83,6 +114,21 @@ func (rm *RouterCoordinator) initTrie() { } } +func (rm *RouterCoordinator) initRegex() { + for _, router := range rm.activeConfig.Routes { + headers := router.Match.Headers + for i := range headers { + if headers[i].Regex && len(headers[i].Values) > 0 { + // regexp always use first value of header + err := headers[i].SetValueRegex(headers[i].Values[0]) + if err != nil { + logger.Errorf("invalid regexp in headers[%d]: %v", i, err) + } + } + } + } +} + // OnAddRouter add router func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { //TODO: lock move to trie node @@ -99,7 +145,7 @@ func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { } else { key = getTrieKey(method, r.Match.Path, isPrefix) } - _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route) + _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route, r.Match.HeaderMap()) } } diff --git a/pixiu/pkg/common/router/router_test.go b/pixiu/pkg/common/router/router_test.go index 27c35853f..0fe4787ae 100644 --- a/pixiu/pkg/common/router/router_test.go +++ b/pixiu/pkg/common/router/router_test.go @@ -39,7 +39,7 @@ func TestCreateRouterCoordinator(t *testing.T) { RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ Cluster: "test_dubbo", ClusterNotFoundResponseCode: 505, - }), + }, nil), Dynamic: false, }, HTTPFilters: []*model.HTTPFilter{ @@ -74,3 +74,306 @@ func TestCreateRouterCoordinator(t *testing.T) { r.OnAddRouter(router) r.OnDeleteRouter(router) } + +func TestRoute(t *testing.T) { + const ( + Cluster1 = "test-cluster-1" + Cluster2 = "test-cluster-2" + Cluster3 = "test-cluster-3" + ) + + hcmc := model.HttpConnectionManagerConfig{ + RouteConfig: model.RouteConfiguration{ + Routes: []*model.Router{ + { + ID: "1", + Match: model.RouterMatch{ + Prefix: "/user", + Headers: []model.HeaderMatcher{ + { + Name: "A", + Values: []string{"1", "2", "3"}, + }, + { + Name: "A", + Values: []string{"3", "4", "5"}, + }, + { + Name: "B", + Values: []string{"1"}, + }, + { + Name: "normal-regex", + Values: []string{"(k){2}"}, + Regex: true, + }, + { + Name: "broken-regex", + Values: []string{"(t){2]]"}, + Regex: true, + }, + }, + Methods: []string{"GET", "POST"}, + }, + Route: model.RouteAction{ + Cluster: Cluster1, + ClusterNotFoundResponseCode: 505, + }, + }, + { + ID: "2", + Match: model.RouterMatch{ + Headers: []model.HeaderMatcher{ + { + Name: "B", + Values: []string{"1", "9", "8"}, + }, + { + Name: "C", + Values: []string{"7"}, + }, + { + Name: "normal-regex", + Values: []string{"(t){2}"}, + Regex: true, + }, + { + Name: "forget-regex", + Values: []string{"(f){4}"}, + }, + }, + Methods: []string{"GET", "PUT"}, + }, + Route: model.RouteAction{ + Cluster: Cluster2, + ClusterNotFoundResponseCode: 505, + }, + }, + { + ID: "3", + Match: model.RouterMatch{ + Headers: []model.HeaderMatcher{ + { + Name: "D", + Values: []string{"7"}, + }, + { + Name: "normal-regex", + Values: []string{"(n){2}"}, + Regex: true, + }, + }, + Methods: []string{"GET"}, + }, + }, + { + ID: "4", + Match: model.RouterMatch{ + Prefix: "/test", + }, + Route: model.RouteAction{ + Cluster: Cluster3, + ClusterNotFoundResponseCode: 505, + }, + }, + }, + Dynamic: false, + }, + HTTPFilters: []*model.HTTPFilter{ + { + Name: "test", + Config: nil, + }, + }, + ServerName: "test_http_dubbo", + GenerateRequestID: false, + IdleTimeoutStr: "100", + } + + testCases := []struct { + Name string + URL string + Method string + Header map[string]string + Expect string + }{ + { + Name: "aim@ID3, no header but prefix match", + URL: "/test", + Expect: Cluster3, + }, + { + Name: "aim@ID1, one override header", + URL: "/user", + Header: map[string]string{ + "A": "1", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID1, one header matched", + URL: "/user", + Header: map[string]string{ + "A": "3", + }, + Expect: Cluster1, + }, + { + Name: "aim@ID1, more header with one regex matched", + URL: "/user", + Header: map[string]string{ + "A": "5", + "normal-regex": "kkkk", + }, + Expect: Cluster1, + }, + { + Name: "aim@ID1, one header but wrong method", + URL: "/user", + Method: "PUT", + Header: map[string]string{ + "A": "3", + }, + Expect: "route failed for PUT/user, no rules matched.", + }, + { + Name: "aim@ID1, one header but no prefix match", + URL: "/error", + Header: map[string]string{ + "A": "4", + }, + Expect: "route failed for GET/error, no rules matched.", + }, + { + Name: "aim@ID1, one regex header mismatch", + URL: "/user", + Header: map[string]string{ + "normal-regex": "tt", + }, + Expect: Cluster2, + }, + { + Name: "aim@ID1, one broken regex header", + URL: "/user", + Header: map[string]string{ + "broken-regex": "tt", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID2, one matched header 1", + Header: map[string]string{ + "B": "9", + }, + Expect: Cluster2, + }, + { + Name: "aim@ID2, one matched header 2", + Header: map[string]string{ + "B": "1", + }, + Expect: Cluster2, + }, + { + Name: "aim@ID2, only header but wrong method", + Method: "DELETE", + Header: map[string]string{ + "B": "1", + }, + Expect: "route failed for DELETE, no rules matched.", + }, + { + Name: "aim@ID2, one header but mismatch", + Header: map[string]string{ + "C": "4", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID2, more matched header", + Header: map[string]string{ + "C": "7", + "B": "8", + }, + Expect: Cluster2, + }, + { + Name: "aim@ID2, more header but mismatch", + Header: map[string]string{ + "C": "4", + "B": "5", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID2, one matched regex header", + Header: map[string]string{ + "normal-regex": "tt", + }, + Expect: Cluster2, + }, + { + Name: "aim@ID2, but with wrong regex value", + Header: map[string]string{ + "normal-regex": "gg", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID2, intend regex match but forget to enable", + Header: map[string]string{ + "forget-regex": "ffff", + }, + Expect: "prefix matched, but no headers matched.", + }, + { + Name: "aim@ID3, but got no route action", + Header: map[string]string{ + "D": "7", + }, + Expect: "action is nil. please check your configuration.", + }, + { + Name: "aim@ID3, regex match but got no route action", + Header: map[string]string{ + "normal-regex": "nn", + }, + Expect: "action is nil. please check your configuration.", + }, + { + Name: "aim@ID3, but wrong method", + Method: "PUT", + Header: map[string]string{ + "normal-regex": "nn", + }, + Expect: "prefix matched, but no headers matched.", + }, + } + + r := CreateRouterCoordinator(&hcmc.RouteConfig) + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + method := "GET" + if len(tc.Method) > 0 { + method = tc.Method + } + request, err := http.NewRequest(method, tc.URL, nil) + assert.NoError(t, err) + + if tc.Header != nil { + for k, v := range tc.Header { + request.Header.Set(k, v) + } + } + c := mock.GetMockHTTPContext(request) + + a, err := r.Route(c) + if err != nil { + assert.Equal(t, tc.Expect, err.Error()) + } else { + assert.Equal(t, tc.Expect, a.Cluster) + } + }) + } +} diff --git a/pixiu/pkg/common/router/trie/trie.go b/pixiu/pkg/common/router/trie/trie.go index b422e9b04..53437c953 100644 --- a/pixiu/pkg/common/router/trie/trie.go +++ b/pixiu/pkg/common/router/trie/trie.go @@ -18,6 +18,7 @@ package trie import ( + "net/http" "strings" ) @@ -41,21 +42,22 @@ func NewTrie() Trie { } // NewTrieWithDefault -func NewTrieWithDefault(path string, defVal interface{}) Trie { +func NewTrieWithDefault(path string, defVal interface{}, header map[string][]string) Trie { ret := Trie{root: Node{endOfPath: false, matchStr: ""}} - _, _ = ret.Put(path, defVal) + _, _ = ret.Put(path, defVal, header) return ret } // Node type Node struct { - matchStr string //abc match abc, :a match all words as a variable names a , * match all words ,** match all words and children. - children map[string]*Node // in path /a/b/c , b is child of a , c is child of b - PathVariablesSet map[string]*Node // in path /:a/b/c/:d , :a is a path variable node of level1 , :d is path variable node of level4 - PathVariableNode *Node // in path /:a/b/c/:d , /b/c/:d is a child tree of pathVariable node :a ,and some special logic for match pathVariable it better not store in children. - MatchAllNode *Node // /a/b/** /** is a match all Node. - endOfPath bool // if true means a real path exists , /a/b/c/d only node of d is true, a,b,c is false. - bizInfo interface{} // route info and any other info store here. + matchStr string // abc match abc, :a match all words as a variable names a , * match all words ,** match all words and children. + children map[string]*Node // in path /a/b/c , b is child of a , c is child of b + PathVariablesSet map[string]*Node // in path /:a/b/c/:d , :a is a path variable node of level1 , :d is path variable node of level4 + PathVariableNode *Node // in path /:a/b/c/:d , /b/c/:d is a child tree of pathVariable node :a ,and some special logic for match pathVariable it better not store in children. + MatchAllNode *Node // /a/b/** /** is a match all Node. + endOfPath bool // if true means a real path exists , /a/b/c/d only node of d is true, a,b,c is false. + bizInfo interface{} // route info and any other info store here. + header map[string][]string // enabled when prefix along with header } func (trie *Trie) Clear() bool { @@ -68,16 +70,16 @@ func (trie *Trie) IsEmpty() bool { } // Put put key and values into trie as map. -func (trie *Trie) Put(withOutHost string, bizInfo interface{}) (bool, error) { +func (trie *Trie) Put(withOutHost string, bizInfo interface{}, header map[string][]string) (bool, error) { if bizInfo == nil { return false, errors.Errorf("data to put should not be nil.") } parts := stringutil.Split(withOutHost) - return trie.root.internalPut(parts, bizInfo) + return trie.root.internalPut(parts, bizInfo, header) } // Put put key and values into trie as map. -func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}) (bool, error) { +func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}, header map[string][]string) (bool, error) { if bizInfo == nil { return false, errors.Errorf("data to put should not be nil.") } @@ -88,7 +90,7 @@ func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}) (bool, er //if n != nil { // //TODO: log n.bizInfo for trouble shooting //} - return trie.root.internalPut(parts, bizInfo) + return trie.root.internalPut(parts, bizInfo, header) } // Get get values according key.pathVariable not supported. @@ -146,7 +148,7 @@ func (trie Trie) Contains(withOutHost string) (bool, error) { } // Put node put -func (node *Node) internalPut(keys []string, bizInfo interface{}) (bool, error) { +func (node *Node) internalPut(keys []string, bizInfo interface{}, header map[string][]string) (bool, error) { // empty node initialization if node.children == nil { node.children = map[string]*Node{} @@ -158,7 +160,7 @@ func (node *Node) internalPut(keys []string, bizInfo interface{}) (bool, error) key := keys[0] // isReal is the end of url path, means node is a place of url end,so the path with parentNode has a real url exists. isReal := len(keys) == 1 - isSuccess := node.put(key, isReal, bizInfo) + isSuccess := node.put(key, isReal, bizInfo, header) if !isSuccess { return false, nil @@ -166,11 +168,11 @@ func (node *Node) internalPut(keys []string, bizInfo interface{}) (bool, error) childKeys := keys[1:] if stringutil.IsPathVariableOrWildcard(key) { - return node.PathVariableNode.internalPut(childKeys, bizInfo) + return node.PathVariableNode.internalPut(childKeys, bizInfo, header) } else if stringutil.IsMatchAll(key) { return isSuccess, nil } else { - return node.children[key].internalPut(childKeys, bizInfo) + return node.children[key].internalPut(childKeys, bizInfo, header) } } @@ -193,6 +195,25 @@ func (node *Node) GetBizInfo() interface{} { return node.bizInfo } +// HasHeader whether node has header +func (node *Node) HasHeader() bool { + return len(node.header) != 0 +} + +// MatchHeader match the headers, excluding regex type +func (node *Node) MatchHeader(header http.Header) bool { + for h, src := range node.header { + if dst := header.Get(h); len(dst) > 0 { + for _, v := range src { + if v == dst { + return true + } + } + } + } + return false +} + //Match node match func (node *Node) Match(parts []string) (*Node, []string, bool) { @@ -283,20 +304,20 @@ func (node *Node) Get(keys []string) (*Node, []string, bool, error) { } -func (node *Node) put(key string, isReal bool, bizInfo interface{}) bool { +func (node *Node) put(key string, isReal bool, bizInfo interface{}, header map[string][]string) bool { if !stringutil.IsPathVariableOrWildcard(key) { if stringutil.IsMatchAll(key) { - return node.putMatchAllNode(key, isReal, bizInfo) + return node.putMatchAllNode(key, isReal, bizInfo, header) } else { - return node.putNode(key, isReal, bizInfo) + return node.putNode(key, isReal, bizInfo, header) } } pathVariable := stringutil.VariableName(key) - return node.putPathVariable(pathVariable, isReal, bizInfo) + return node.putPathVariable(pathVariable, isReal, bizInfo, header) } -func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo interface{}) bool { +func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo interface{}, header map[string][]string) bool { //path variable put if node.PathVariableNode == nil { node.PathVariableNode = &Node{endOfPath: false} @@ -308,6 +329,7 @@ func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo inte if isReal { node.PathVariableNode.bizInfo = bizInfo node.PathVariableNode.matchStr = pathVariable + node.header = header } node.PathVariableNode.endOfPath = node.PathVariableNode.endOfPath || isReal if node.PathVariablesSet == nil { @@ -317,7 +339,7 @@ func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo inte return true } -func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}) bool { +func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}, header map[string][]string) bool { selfNode := &Node{endOfPath: isReal, matchStr: matchStr} old := node.children[matchStr] @@ -333,13 +355,14 @@ func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}) boo if isReal { selfNode.bizInfo = bizInfo + selfNode.header = header } selfNode.endOfPath = isReal || old.endOfPath node.children[matchStr] = selfNode return true } -func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interface{}) bool { +func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interface{}, header map[string][]string) bool { selfNode := &Node{endOfPath: isReal, matchStr: matchStr} old := node.MatchAllNode @@ -355,6 +378,7 @@ func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interfac if isReal { selfNode.bizInfo = bizInfo + selfNode.header = header } selfNode.endOfPath = selfNode.endOfPath || old.endOfPath node.MatchAllNode = selfNode diff --git a/pixiu/pkg/common/router/trie/trie_test.go b/pixiu/pkg/common/router/trie/trie_test.go index 8b88af750..4753243b5 100644 --- a/pixiu/pkg/common/router/trie/trie_test.go +++ b/pixiu/pkg/common/router/trie/trie_test.go @@ -31,88 +31,88 @@ import ( func TestTrie_Put(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "") + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "") + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "") + ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) assert.False(t, ret) } func TestTrie_MatchAndGet(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "test1") + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "test1", nil) assert.True(t, ret) - _, _ = trie.Put("/a/b", "ab") + _, _ = trie.Put("/a/b", "ab", nil) result, _, _ := trie.Match("/a/b") assert.Equal(t, result.GetBizInfo(), "ab") result, _, _ = trie.Match("/a/b?a=b&c=d") assert.Equal(t, result.GetBizInfo(), "ab") - _, _ = trie.Put("POST/api/v1/**", "ab") + _, _ = trie.Put("POST/api/v1/**", "ab", nil) result, _, _ = trie.Match("POST/api/v1") assert.Equal(t, "ab", result.GetBizInfo()) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "") + ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/a/:432/b/:34/**", "test**") + ret, _ = trie.Put("/a/:432/b/:34/**", "test**", nil) assert.True(t, ret) - ret, _ = trie.Put("/a/:432/b/:34/**", "") + ret, _ = trie.Put("/a/:432/b/:34/**", "", nil) assert.False(t, ret) node, param, ok := trie.Match("/a/v1/b/v2/sadf/asdf") @@ -152,37 +152,37 @@ func TestTrie_Clear(t *testing.T) { assert.Equal(t, "PUT/aa/bb", v) trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "") + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "") + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "") + ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) assert.False(t, ret) assert.False(t, trie.IsEmpty()) @@ -192,7 +192,7 @@ func TestTrie_Clear(t *testing.T) { func TestTrie_ParamMatch(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("PUT/path1/:pathvarible1/path2/:pathvarible2", "") + ret, _ := trie.Put("PUT/path1/:pathvarible1/path2/:pathvarible2", "", nil) assert.True(t, ret) str := "https://www.baidu.com/path1/param1/path2/param2?aaaaa=aaaaa" @@ -200,7 +200,7 @@ func TestTrie_ParamMatch(t *testing.T) { assert.True(t, ok) assert.Equal(t, "", node.GetBizInfo()) - ret, _ = trie.Put("PUT/path1/:pathvarible1/path2", "") + ret, _ = trie.Put("PUT/path1/:pathvarible1/path2", "", nil) node, _, ok = trie.Match(stringutil.GetTrieKey("PUT", str)) assert.True(t, ok) assert.Equal(t, "", node.GetBizInfo()) diff --git a/pixiu/pkg/model/router.go b/pixiu/pkg/model/router.go index 7730a23b0..93b9dc00a 100644 --- a/pixiu/pkg/model/router.go +++ b/pixiu/pkg/model/router.go @@ -44,8 +44,8 @@ type ( Prefix string `yaml:"prefix" json:"prefix" mapstructure:"prefix"` Path string `yaml:"path" json:"path" mapstructure:"path"` // Regex string `yaml:"regex" json:"regex" mapstructure:"regex"` TODO: next version - Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` - // Headers []HeaderMatcher `yaml:"headers" json:"headers" mapstructure:"headers"` + Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` + Headers []HeaderMatcher `yaml:"headers" json:"headers" mapstructure:"headers"` // pathRE *regexp.Regexp } @@ -62,7 +62,7 @@ type ( Dynamic bool `yaml:"dynamic" json:"dynamic" mapstructure:"dynamic"` } - // Name header key, Value header value, Regex header value is regex + // HeaderMatcher include Name header key, Values header value HeaderMatcher struct { Name string `yaml:"name" json:"name" mapstructure:"name"` Values []string `yaml:"values" json:"values" mapstructure:"values"` @@ -75,22 +75,83 @@ func NewRouterMatchPrefix(name string) RouterMatch { return RouterMatch{Prefix: "/" + name + "/"} } -func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string) (*RouteAction, error) { +func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, header stdHttp.Header) (*RouteAction, error) { if rc.RouteTrie.IsEmpty() { return nil, errors.Errorf("router configuration is empty") } node, _, _ := rc.RouteTrie.Match(stringutil.GetTrieKey(method, path)) if node == nil { - return nil, errors.Errorf("route failed for %s,no rules matched.", stringutil.GetTrieKey(method, path)) + return nil, errors.Errorf("route failed for %s, no rules matched.", stringutil.GetTrieKey(method, path)) + } + if node.HasHeader() && !node.MatchHeader(header) { + return nil, errors.New("prefix matched, but no headers matched.") } if node.GetBizInfo() == nil { - return nil, errors.Errorf("info is nil.please check your configuration.") + return nil, errors.Errorf("action is nil. please check your configuration.") } ret := (node.GetBizInfo()).(RouteAction) return &ret, nil } func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { - return rc.RouteByPathAndMethod(req.URL.Path, req.Method) + return rc.RouteByPathAndMethod(req.URL.Path, req.Method, req.Header) +} + +// MatchHeader used when there's only headers to match +func (rm *RouterMatch) MatchHeader(req *stdHttp.Request) bool { + if len(rm.Methods) > 0 { + for _, method := range rm.Methods { + if method == req.Method { + goto HEADER + } + } + return false + } +HEADER: + for _, header := range rm.Headers { + if val := req.Header.Get(header.Name); len(val) > 0 { + if header.MatchValues(val) { + return true + } + } + } + return false +} + +// HeaderMap return the map of headers, excluding regex type +func (rm *RouterMatch) HeaderMap() map[string][]string { + header := map[string][]string{} + for _, h := range rm.Headers { + if h.Regex { + continue + } + header[h.Name] = h.Values + } + return header +} + +// MatchValues match values in header, including regex type +func (hm *HeaderMatcher) MatchValues(dst string) bool { + if hm.Regex && hm.valueRE != nil { + return hm.valueRE.MatchString(dst) + } + + for _, src := range hm.Values { + if src == dst { + return true + } + } + return false +} + +// SetValueRegex compile the regex, disable regex if it failed +func (hm *HeaderMatcher) SetValueRegex(regex string) error { + r, err := regexp.Compile(regex) + if err == nil { + hm.valueRE = r + return nil + } + hm.Regex = false + return err } diff --git a/pixiu/pkg/router/route.go b/pixiu/pkg/router/route.go index 5079d0272..246836c91 100644 --- a/pixiu/pkg/router/route.go +++ b/pixiu/pkg/router/route.go @@ -131,7 +131,7 @@ func (rt *Route) PutAPI(api router.API) error { } rt.lock.Lock() defer rt.lock.Unlock() - _, _ = rt.tree.Put(key, rn) + _, _ = rt.tree.Put(key, rn, nil) return nil } return errors.Errorf("Method %s with address %s already exists in path %s", @@ -169,7 +169,7 @@ func (rt *Route) PutOrUpdateAPI(api router.API) error { } } } else { - if ok, err := rt.tree.PutOrUpdate(key, rn); !ok { + if ok, err := rt.tree.PutOrUpdate(key, rn, nil); !ok { return err } } diff --git a/pixiu/pkg/server/http.go b/pixiu/pkg/server/http.go index 606f861a9..5e588d241 100644 --- a/pixiu/pkg/server/http.go +++ b/pixiu/pkg/server/http.go @@ -29,7 +29,7 @@ func DefaultHttpConnectionManager() *model.HttpConnectionManagerConfig { RouteConfig: model.RouteConfiguration{ RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.RouteAction{ Cluster: constant.HeaderValueAll, - }), + }, nil), }, HTTPFilters: []*model.HTTPFilter{ { From 6bd489e6011647bedcce06fc38a14074844a1c18 Mon Sep 17 00:00:00 2001 From: Shawnh2 Date: Wed, 7 Jun 2023 18:19:20 +0800 Subject: [PATCH 2/5] fix ci --- pixiu/pkg/common/http/manager_test.go | 2 +- pixiu/pkg/model/router.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pixiu/pkg/common/http/manager_test.go b/pixiu/pkg/common/http/manager_test.go index 46424e388..721af9f57 100644 --- a/pixiu/pkg/common/http/manager_test.go +++ b/pixiu/pkg/common/http/manager_test.go @@ -112,7 +112,7 @@ func TestCreateHttpConnectionManager(t *testing.T) { RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ Cluster: "test_dubbo", ClusterNotFoundResponseCode: 505, - }), + }, nil), Dynamic: false, }, HTTPFilters: []*model.HTTPFilter{ diff --git a/pixiu/pkg/model/router.go b/pixiu/pkg/model/router.go index 93b9dc00a..be525b009 100644 --- a/pixiu/pkg/model/router.go +++ b/pixiu/pkg/model/router.go @@ -45,7 +45,7 @@ type ( Path string `yaml:"path" json:"path" mapstructure:"path"` // Regex string `yaml:"regex" json:"regex" mapstructure:"regex"` TODO: next version Methods []string `yaml:"methods" json:"methods" mapstructure:"methods"` - Headers []HeaderMatcher `yaml:"headers" json:"headers" mapstructure:"headers"` + Headers []HeaderMatcher `yaml:"headers,omitempty" json:"headers,omitempty" mapstructure:"headers"` // pathRE *regexp.Regexp } @@ -62,7 +62,7 @@ type ( Dynamic bool `yaml:"dynamic" json:"dynamic" mapstructure:"dynamic"` } - // HeaderMatcher include Name header key, Values header value + // HeaderMatcher include Name header key, Values header value, Regex regex value HeaderMatcher struct { Name string `yaml:"name" json:"name" mapstructure:"name"` Values []string `yaml:"values" json:"values" mapstructure:"values"` From d009c5f9d9e2b19ee74faa0caeed33699b892690 Mon Sep 17 00:00:00 2001 From: Shawnh2 Date: Fri, 16 Jun 2023 20:55:52 +0800 Subject: [PATCH 3/5] fix review --- pixiu/pkg/common/router/router.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pixiu/pkg/common/router/router.go b/pixiu/pkg/common/router/router.go index aed674c73..8cda12c30 100644 --- a/pixiu/pkg/common/router/router.go +++ b/pixiu/pkg/common/router/router.go @@ -123,6 +123,7 @@ func (rm *RouterCoordinator) initRegex() { err := headers[i].SetValueRegex(headers[i].Values[0]) if err != nil { logger.Errorf("invalid regexp in headers[%d]: %v", i, err) + panic(err) } } } From bfdaed71a85b84be1af5441a1de3b236c81d3a93 Mon Sep 17 00:00:00 2001 From: Shawnh2 Date: Fri, 23 Jun 2023 17:40:50 +0800 Subject: [PATCH 4/5] mixup route action and route match into trie --- pixiu/pkg/common/http/manager_test.go | 10 +-- pixiu/pkg/common/router/router.go | 5 +- pixiu/pkg/common/router/router_test.go | 11 +-- pixiu/pkg/common/router/trie/trie.go | 72 +++++++------------- pixiu/pkg/common/router/trie/trie_test.go | 82 +++++++++++------------ pixiu/pkg/model/router.go | 33 ++++----- pixiu/pkg/router/route.go | 4 +- pixiu/pkg/server/http.go | 8 ++- 8 files changed, 102 insertions(+), 123 deletions(-) diff --git a/pixiu/pkg/common/http/manager_test.go b/pixiu/pkg/common/http/manager_test.go index 721af9f57..454f4ee0a 100644 --- a/pixiu/pkg/common/http/manager_test.go +++ b/pixiu/pkg/common/http/manager_test.go @@ -109,10 +109,12 @@ func TestCreateHttpConnectionManager(t *testing.T) { hcmc := model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ - Cluster: "test_dubbo", - ClusterNotFoundResponseCode: 505, - }, nil), + RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.TrieRouteAction{ + RouteAction: model.RouteAction{ + Cluster: "test_dubbo", + ClusterNotFoundResponseCode: 505, + }, + }), Dynamic: false, }, HTTPFilters: []*model.HTTPFilter{ diff --git a/pixiu/pkg/common/router/router.go b/pixiu/pkg/common/router/router.go index 8cda12c30..18513e460 100644 --- a/pixiu/pkg/common/router/router.go +++ b/pixiu/pkg/common/router/router.go @@ -146,7 +146,10 @@ func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { } else { key = getTrieKey(method, r.Match.Path, isPrefix) } - _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route, r.Match.HeaderMap()) + _, _ = rm.activeConfig.RouteTrie.Put(key, model.TrieRouteAction{ + RouteAction: r.Route, + RouterMatch: r.Match, + }) } } diff --git a/pixiu/pkg/common/router/router_test.go b/pixiu/pkg/common/router/router_test.go index 0fe4787ae..1e8035bb1 100644 --- a/pixiu/pkg/common/router/router_test.go +++ b/pixiu/pkg/common/router/router_test.go @@ -36,10 +36,11 @@ import ( func TestCreateRouterCoordinator(t *testing.T) { hcmc := model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ - Cluster: "test_dubbo", - ClusterNotFoundResponseCode: 505, - }, nil), + RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.TrieRouteAction{ + RouteAction: model.RouteAction{ + Cluster: "test_dubbo", + ClusterNotFoundResponseCode: 505}, + }), Dynamic: false, }, HTTPFilters: []*model.HTTPFilter{ @@ -208,7 +209,7 @@ func TestRoute(t *testing.T) { Header: map[string]string{ "A": "1", }, - Expect: "prefix matched, but no headers matched.", + Expect: "test-cluster-1", }, { Name: "aim@ID1, one header matched", diff --git a/pixiu/pkg/common/router/trie/trie.go b/pixiu/pkg/common/router/trie/trie.go index 53437c953..bb533800f 100644 --- a/pixiu/pkg/common/router/trie/trie.go +++ b/pixiu/pkg/common/router/trie/trie.go @@ -18,7 +18,6 @@ package trie import ( - "net/http" "strings" ) @@ -42,22 +41,21 @@ func NewTrie() Trie { } // NewTrieWithDefault -func NewTrieWithDefault(path string, defVal interface{}, header map[string][]string) Trie { +func NewTrieWithDefault(path string, defVal interface{}) Trie { ret := Trie{root: Node{endOfPath: false, matchStr: ""}} - _, _ = ret.Put(path, defVal, header) + _, _ = ret.Put(path, defVal) return ret } // Node type Node struct { - matchStr string // abc match abc, :a match all words as a variable names a , * match all words ,** match all words and children. - children map[string]*Node // in path /a/b/c , b is child of a , c is child of b - PathVariablesSet map[string]*Node // in path /:a/b/c/:d , :a is a path variable node of level1 , :d is path variable node of level4 - PathVariableNode *Node // in path /:a/b/c/:d , /b/c/:d is a child tree of pathVariable node :a ,and some special logic for match pathVariable it better not store in children. - MatchAllNode *Node // /a/b/** /** is a match all Node. - endOfPath bool // if true means a real path exists , /a/b/c/d only node of d is true, a,b,c is false. - bizInfo interface{} // route info and any other info store here. - header map[string][]string // enabled when prefix along with header + matchStr string // abc match abc, :a match all words as a variable names a , * match all words ,** match all words and children. + children map[string]*Node // in path /a/b/c , b is child of a , c is child of b + PathVariablesSet map[string]*Node // in path /:a/b/c/:d , :a is a path variable node of level1 , :d is path variable node of level4 + PathVariableNode *Node // in path /:a/b/c/:d , /b/c/:d is a child tree of pathVariable node :a ,and some special logic for match pathVariable it better not store in children. + MatchAllNode *Node // /a/b/** /** is a match all Node. + endOfPath bool // if true means a real path exists , /a/b/c/d only node of d is true, a,b,c is false. + bizInfo interface{} // route info and any other info store here. } func (trie *Trie) Clear() bool { @@ -70,16 +68,16 @@ func (trie *Trie) IsEmpty() bool { } // Put put key and values into trie as map. -func (trie *Trie) Put(withOutHost string, bizInfo interface{}, header map[string][]string) (bool, error) { +func (trie *Trie) Put(withOutHost string, bizInfo interface{}) (bool, error) { if bizInfo == nil { return false, errors.Errorf("data to put should not be nil.") } parts := stringutil.Split(withOutHost) - return trie.root.internalPut(parts, bizInfo, header) + return trie.root.internalPut(parts, bizInfo) } // Put put key and values into trie as map. -func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}, header map[string][]string) (bool, error) { +func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}) (bool, error) { if bizInfo == nil { return false, errors.Errorf("data to put should not be nil.") } @@ -90,7 +88,7 @@ func (trie *Trie) PutOrUpdate(withOutHost string, bizInfo interface{}, header ma //if n != nil { // //TODO: log n.bizInfo for trouble shooting //} - return trie.root.internalPut(parts, bizInfo, header) + return trie.root.internalPut(parts, bizInfo) } // Get get values according key.pathVariable not supported. @@ -148,7 +146,7 @@ func (trie Trie) Contains(withOutHost string) (bool, error) { } // Put node put -func (node *Node) internalPut(keys []string, bizInfo interface{}, header map[string][]string) (bool, error) { +func (node *Node) internalPut(keys []string, bizInfo interface{}) (bool, error) { // empty node initialization if node.children == nil { node.children = map[string]*Node{} @@ -160,7 +158,7 @@ func (node *Node) internalPut(keys []string, bizInfo interface{}, header map[str key := keys[0] // isReal is the end of url path, means node is a place of url end,so the path with parentNode has a real url exists. isReal := len(keys) == 1 - isSuccess := node.put(key, isReal, bizInfo, header) + isSuccess := node.put(key, isReal, bizInfo) if !isSuccess { return false, nil @@ -168,11 +166,11 @@ func (node *Node) internalPut(keys []string, bizInfo interface{}, header map[str childKeys := keys[1:] if stringutil.IsPathVariableOrWildcard(key) { - return node.PathVariableNode.internalPut(childKeys, bizInfo, header) + return node.PathVariableNode.internalPut(childKeys, bizInfo) } else if stringutil.IsMatchAll(key) { return isSuccess, nil } else { - return node.children[key].internalPut(childKeys, bizInfo, header) + return node.children[key].internalPut(childKeys, bizInfo) } } @@ -195,25 +193,6 @@ func (node *Node) GetBizInfo() interface{} { return node.bizInfo } -// HasHeader whether node has header -func (node *Node) HasHeader() bool { - return len(node.header) != 0 -} - -// MatchHeader match the headers, excluding regex type -func (node *Node) MatchHeader(header http.Header) bool { - for h, src := range node.header { - if dst := header.Get(h); len(dst) > 0 { - for _, v := range src { - if v == dst { - return true - } - } - } - } - return false -} - //Match node match func (node *Node) Match(parts []string) (*Node, []string, bool) { @@ -304,20 +283,20 @@ func (node *Node) Get(keys []string) (*Node, []string, bool, error) { } -func (node *Node) put(key string, isReal bool, bizInfo interface{}, header map[string][]string) bool { +func (node *Node) put(key string, isReal bool, bizInfo interface{}) bool { if !stringutil.IsPathVariableOrWildcard(key) { if stringutil.IsMatchAll(key) { - return node.putMatchAllNode(key, isReal, bizInfo, header) + return node.putMatchAllNode(key, isReal, bizInfo) } else { - return node.putNode(key, isReal, bizInfo, header) + return node.putNode(key, isReal, bizInfo) } } pathVariable := stringutil.VariableName(key) - return node.putPathVariable(pathVariable, isReal, bizInfo, header) + return node.putPathVariable(pathVariable, isReal, bizInfo) } -func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo interface{}, header map[string][]string) bool { +func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo interface{}) bool { //path variable put if node.PathVariableNode == nil { node.PathVariableNode = &Node{endOfPath: false} @@ -329,7 +308,6 @@ func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo inte if isReal { node.PathVariableNode.bizInfo = bizInfo node.PathVariableNode.matchStr = pathVariable - node.header = header } node.PathVariableNode.endOfPath = node.PathVariableNode.endOfPath || isReal if node.PathVariablesSet == nil { @@ -339,7 +317,7 @@ func (node *Node) putPathVariable(pathVariable string, isReal bool, bizInfo inte return true } -func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}, header map[string][]string) bool { +func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}) bool { selfNode := &Node{endOfPath: isReal, matchStr: matchStr} old := node.children[matchStr] @@ -355,14 +333,13 @@ func (node *Node) putNode(matchStr string, isReal bool, bizInfo interface{}, hea if isReal { selfNode.bizInfo = bizInfo - selfNode.header = header } selfNode.endOfPath = isReal || old.endOfPath node.children[matchStr] = selfNode return true } -func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interface{}, header map[string][]string) bool { +func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interface{}) bool { selfNode := &Node{endOfPath: isReal, matchStr: matchStr} old := node.MatchAllNode @@ -378,7 +355,6 @@ func (node *Node) putMatchAllNode(matchStr string, isReal bool, bizInfo interfac if isReal { selfNode.bizInfo = bizInfo - selfNode.header = header } selfNode.endOfPath = selfNode.endOfPath || old.endOfPath node.MatchAllNode = selfNode diff --git a/pixiu/pkg/common/router/trie/trie_test.go b/pixiu/pkg/common/router/trie/trie_test.go index 4753243b5..8b88af750 100644 --- a/pixiu/pkg/common/router/trie/trie_test.go +++ b/pixiu/pkg/common/router/trie/trie_test.go @@ -31,88 +31,88 @@ import ( func TestTrie_Put(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "", nil) + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) + ret, _ = trie.Put("/path1/:432/path2/:34", "") assert.False(t, ret) } func TestTrie_MatchAndGet(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "test1", nil) + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "test1") assert.True(t, ret) - _, _ = trie.Put("/a/b", "ab", nil) + _, _ = trie.Put("/a/b", "ab") result, _, _ := trie.Match("/a/b") assert.Equal(t, result.GetBizInfo(), "ab") result, _, _ = trie.Match("/a/b?a=b&c=d") assert.Equal(t, result.GetBizInfo(), "ab") - _, _ = trie.Put("POST/api/v1/**", "ab", nil) + _, _ = trie.Put("POST/api/v1/**", "ab") result, _, _ = trie.Match("POST/api/v1") assert.Equal(t, "ab", result.GetBizInfo()) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) + ret, _ = trie.Put("/path1/:432/path2/:34", "") assert.False(t, ret) - ret, _ = trie.Put("/a/:432/b/:34/**", "test**", nil) + ret, _ = trie.Put("/a/:432/b/:34/**", "test**") assert.True(t, ret) - ret, _ = trie.Put("/a/:432/b/:34/**", "", nil) + ret, _ = trie.Put("/a/:432/b/:34/**", "") assert.False(t, ret) node, param, ok := trie.Match("/a/v1/b/v2/sadf/asdf") @@ -152,37 +152,37 @@ func TestTrie_Clear(t *testing.T) { assert.Equal(t, "PUT/aa/bb", v) trie := NewTrie() - ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ := trie.Put("/path1/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "", nil) + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/**", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:pathvarible2/3", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3", "") assert.False(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/d/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "", nil) + ret, _ = trie.Put("/path2/3/path2/:432423/3/a/b/c/c/:fdsafdsafsdafsda", "") assert.False(t, ret) - ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "", nil) + ret, _ = trie.Put("/path1/:pathvarible1/path2/:pathvarible2/:fdsa", "") assert.True(t, ret) - ret, _ = trie.Put("/path1/:432/path2/:34", "", nil) + ret, _ = trie.Put("/path1/:432/path2/:34", "") assert.False(t, ret) assert.False(t, trie.IsEmpty()) @@ -192,7 +192,7 @@ func TestTrie_Clear(t *testing.T) { func TestTrie_ParamMatch(t *testing.T) { trie := NewTrie() - ret, _ := trie.Put("PUT/path1/:pathvarible1/path2/:pathvarible2", "", nil) + ret, _ := trie.Put("PUT/path1/:pathvarible1/path2/:pathvarible2", "") assert.True(t, ret) str := "https://www.baidu.com/path1/param1/path2/param2?aaaaa=aaaaa" @@ -200,7 +200,7 @@ func TestTrie_ParamMatch(t *testing.T) { assert.True(t, ok) assert.Equal(t, "", node.GetBizInfo()) - ret, _ = trie.Put("PUT/path1/:pathvarible1/path2", "", nil) + ret, _ = trie.Put("PUT/path1/:pathvarible1/path2", "") node, _, ok = trie.Match(stringutil.GetTrieKey("PUT", str)) assert.True(t, ok) assert.Equal(t, "", node.GetBizInfo()) diff --git a/pixiu/pkg/model/router.go b/pixiu/pkg/model/router.go index be525b009..d71dc874c 100644 --- a/pixiu/pkg/model/router.go +++ b/pixiu/pkg/model/router.go @@ -69,13 +69,19 @@ type ( Regex bool `yaml:"regex" json:"regex" mapstructure:"regex"` valueRE *regexp.Regexp } + + // TrieRouteAction indicates the action when matched the trie + TrieRouteAction struct { + RouteAction + RouterMatch + } ) func NewRouterMatchPrefix(name string) RouterMatch { return RouterMatch{Prefix: "/" + name + "/"} } -func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, header stdHttp.Header) (*RouteAction, error) { +func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, req *stdHttp.Request) (*RouteAction, error) { if rc.RouteTrie.IsEmpty() { return nil, errors.Errorf("router configuration is empty") } @@ -84,18 +90,19 @@ func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, header s if node == nil { return nil, errors.Errorf("route failed for %s, no rules matched.", stringutil.GetTrieKey(method, path)) } - if node.HasHeader() && !node.MatchHeader(header) { - return nil, errors.New("prefix matched, but no headers matched.") - } if node.GetBizInfo() == nil { return nil, errors.Errorf("action is nil. please check your configuration.") } - ret := (node.GetBizInfo()).(RouteAction) - return &ret, nil + ret := (node.GetBizInfo()).(TrieRouteAction) + if len(ret.Headers) > 0 && !ret.MatchHeader(req) { + return nil, errors.New("prefix matched, but no headers matched.") + } + + return &ret.RouteAction, nil } func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { - return rc.RouteByPathAndMethod(req.URL.Path, req.Method, req.Header) + return rc.RouteByPathAndMethod(req.URL.Path, req.Method, req) } // MatchHeader used when there's only headers to match @@ -119,18 +126,6 @@ HEADER: return false } -// HeaderMap return the map of headers, excluding regex type -func (rm *RouterMatch) HeaderMap() map[string][]string { - header := map[string][]string{} - for _, h := range rm.Headers { - if h.Regex { - continue - } - header[h.Name] = h.Values - } - return header -} - // MatchValues match values in header, including regex type func (hm *HeaderMatcher) MatchValues(dst string) bool { if hm.Regex && hm.valueRE != nil { diff --git a/pixiu/pkg/router/route.go b/pixiu/pkg/router/route.go index 246836c91..5079d0272 100644 --- a/pixiu/pkg/router/route.go +++ b/pixiu/pkg/router/route.go @@ -131,7 +131,7 @@ func (rt *Route) PutAPI(api router.API) error { } rt.lock.Lock() defer rt.lock.Unlock() - _, _ = rt.tree.Put(key, rn, nil) + _, _ = rt.tree.Put(key, rn) return nil } return errors.Errorf("Method %s with address %s already exists in path %s", @@ -169,7 +169,7 @@ func (rt *Route) PutOrUpdateAPI(api router.API) error { } } } else { - if ok, err := rt.tree.PutOrUpdate(key, rn, nil); !ok { + if ok, err := rt.tree.PutOrUpdate(key, rn); !ok { return err } } diff --git a/pixiu/pkg/server/http.go b/pixiu/pkg/server/http.go index 5e588d241..78dd09184 100644 --- a/pixiu/pkg/server/http.go +++ b/pixiu/pkg/server/http.go @@ -27,9 +27,11 @@ import ( func DefaultHttpConnectionManager() *model.HttpConnectionManagerConfig { return &model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.RouteAction{ - Cluster: constant.HeaderValueAll, - }, nil), + RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.TrieRouteAction{ + RouteAction: model.RouteAction{ + Cluster: constant.HeaderValueAll, + }, + }), }, HTTPFilters: []*model.HTTPFilter{ { From 97105fd4772998d03d38d3bfcd478150a9ba4812 Mon Sep 17 00:00:00 2001 From: Shawnh2 Date: Thu, 29 Jun 2023 15:30:12 +0800 Subject: [PATCH 5/5] only implement header based route for now --- pixiu/pkg/common/http/manager_test.go | 8 +- pixiu/pkg/common/router/router.go | 10 +- pixiu/pkg/common/router/router_test.go | 178 ++----------------------- pixiu/pkg/model/router.go | 17 +-- pixiu/pkg/server/http.go | 6 +- 5 files changed, 25 insertions(+), 194 deletions(-) diff --git a/pixiu/pkg/common/http/manager_test.go b/pixiu/pkg/common/http/manager_test.go index 454f4ee0a..46424e388 100644 --- a/pixiu/pkg/common/http/manager_test.go +++ b/pixiu/pkg/common/http/manager_test.go @@ -109,11 +109,9 @@ func TestCreateHttpConnectionManager(t *testing.T) { hcmc := model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.TrieRouteAction{ - RouteAction: model.RouteAction{ - Cluster: "test_dubbo", - ClusterNotFoundResponseCode: 505, - }, + RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ + Cluster: "test_dubbo", + ClusterNotFoundResponseCode: 505, }), Dynamic: false, }, diff --git a/pixiu/pkg/common/router/router.go b/pixiu/pkg/common/router/router.go index 18513e460..c807d3bd7 100644 --- a/pixiu/pkg/common/router/router.go +++ b/pixiu/pkg/common/router/router.go @@ -68,7 +68,7 @@ func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.Rou rm.rw.RLock() defer rm.rw.RUnlock() - return rm.activeConfig.RouteByPathAndMethod(path, method, nil) + return rm.activeConfig.RouteByPathAndMethod(path, method) } func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) { @@ -91,7 +91,8 @@ func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, er return &matched[0].Route, nil } - // match those route that only contains prefix or both prefix and header + // match those route that only contains prefix + // TODO: may consider implementing both prefix and header in the future return rm.activeConfig.Route(req) } @@ -146,10 +147,7 @@ func (rm *RouterCoordinator) OnAddRouter(r *model.Router) { } else { key = getTrieKey(method, r.Match.Path, isPrefix) } - _, _ = rm.activeConfig.RouteTrie.Put(key, model.TrieRouteAction{ - RouteAction: r.Route, - RouterMatch: r.Match, - }) + _, _ = rm.activeConfig.RouteTrie.Put(key, r.Route) } } diff --git a/pixiu/pkg/common/router/router_test.go b/pixiu/pkg/common/router/router_test.go index 1e8035bb1..b41a0b5b4 100644 --- a/pixiu/pkg/common/router/router_test.go +++ b/pixiu/pkg/common/router/router_test.go @@ -36,10 +36,9 @@ import ( func TestCreateRouterCoordinator(t *testing.T) { hcmc := model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.TrieRouteAction{ - RouteAction: model.RouteAction{ - Cluster: "test_dubbo", - ClusterNotFoundResponseCode: 505}, + RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{ + Cluster: "test_dubbo", + ClusterNotFoundResponseCode: 505, }), Dynamic: false, }, @@ -79,8 +78,6 @@ func TestCreateRouterCoordinator(t *testing.T) { func TestRoute(t *testing.T) { const ( Cluster1 = "test-cluster-1" - Cluster2 = "test-cluster-2" - Cluster3 = "test-cluster-3" ) hcmc := model.HttpConnectionManagerConfig{ @@ -89,7 +86,6 @@ func TestRoute(t *testing.T) { { ID: "1", Match: model.RouterMatch{ - Prefix: "/user", Headers: []model.HeaderMatcher{ { Name: "A", @@ -121,62 +117,6 @@ func TestRoute(t *testing.T) { ClusterNotFoundResponseCode: 505, }, }, - { - ID: "2", - Match: model.RouterMatch{ - Headers: []model.HeaderMatcher{ - { - Name: "B", - Values: []string{"1", "9", "8"}, - }, - { - Name: "C", - Values: []string{"7"}, - }, - { - Name: "normal-regex", - Values: []string{"(t){2}"}, - Regex: true, - }, - { - Name: "forget-regex", - Values: []string{"(f){4}"}, - }, - }, - Methods: []string{"GET", "PUT"}, - }, - Route: model.RouteAction{ - Cluster: Cluster2, - ClusterNotFoundResponseCode: 505, - }, - }, - { - ID: "3", - Match: model.RouterMatch{ - Headers: []model.HeaderMatcher{ - { - Name: "D", - Values: []string{"7"}, - }, - { - Name: "normal-regex", - Values: []string{"(n){2}"}, - Regex: true, - }, - }, - Methods: []string{"GET"}, - }, - }, - { - ID: "4", - Match: model.RouterMatch{ - Prefix: "/test", - }, - Route: model.RouteAction{ - Cluster: Cluster3, - ClusterNotFoundResponseCode: 505, - }, - }, }, Dynamic: false, }, @@ -199,12 +139,7 @@ func TestRoute(t *testing.T) { Expect string }{ { - Name: "aim@ID3, no header but prefix match", - URL: "/test", - Expect: Cluster3, - }, - { - Name: "aim@ID1, one override header", + Name: "one override header", URL: "/user", Header: map[string]string{ "A": "1", @@ -212,7 +147,7 @@ func TestRoute(t *testing.T) { Expect: "test-cluster-1", }, { - Name: "aim@ID1, one header matched", + Name: "one header matched", URL: "/user", Header: map[string]string{ "A": "3", @@ -220,7 +155,7 @@ func TestRoute(t *testing.T) { Expect: Cluster1, }, { - Name: "aim@ID1, more header with one regex matched", + Name: "more header with one regex matched", URL: "/user", Header: map[string]string{ "A": "5", @@ -229,7 +164,7 @@ func TestRoute(t *testing.T) { Expect: Cluster1, }, { - Name: "aim@ID1, one header but wrong method", + Name: "one header but wrong method", URL: "/user", Method: "PUT", Header: map[string]string{ @@ -238,117 +173,28 @@ func TestRoute(t *testing.T) { Expect: "route failed for PUT/user, no rules matched.", }, { - Name: "aim@ID1, one header but no prefix match", - URL: "/error", - Header: map[string]string{ - "A": "4", - }, - Expect: "route failed for GET/error, no rules matched.", - }, - { - Name: "aim@ID1, one regex header mismatch", - URL: "/user", - Header: map[string]string{ - "normal-regex": "tt", - }, - Expect: Cluster2, - }, - { - Name: "aim@ID1, one broken regex header", + Name: "one broken regex header", URL: "/user", Header: map[string]string{ "broken-regex": "tt", }, - Expect: "prefix matched, but no headers matched.", - }, - { - Name: "aim@ID2, one matched header 1", - Header: map[string]string{ - "B": "9", - }, - Expect: Cluster2, + Expect: "route failed for GET/user, no rules matched.", }, { - Name: "aim@ID2, one matched header 2", + Name: "one matched header 2", Header: map[string]string{ "B": "1", }, - Expect: Cluster2, + Expect: Cluster1, }, { - Name: "aim@ID2, only header but wrong method", + Name: "only header but wrong method", Method: "DELETE", Header: map[string]string{ "B": "1", }, Expect: "route failed for DELETE, no rules matched.", }, - { - Name: "aim@ID2, one header but mismatch", - Header: map[string]string{ - "C": "4", - }, - Expect: "prefix matched, but no headers matched.", - }, - { - Name: "aim@ID2, more matched header", - Header: map[string]string{ - "C": "7", - "B": "8", - }, - Expect: Cluster2, - }, - { - Name: "aim@ID2, more header but mismatch", - Header: map[string]string{ - "C": "4", - "B": "5", - }, - Expect: "prefix matched, but no headers matched.", - }, - { - Name: "aim@ID2, one matched regex header", - Header: map[string]string{ - "normal-regex": "tt", - }, - Expect: Cluster2, - }, - { - Name: "aim@ID2, but with wrong regex value", - Header: map[string]string{ - "normal-regex": "gg", - }, - Expect: "prefix matched, but no headers matched.", - }, - { - Name: "aim@ID2, intend regex match but forget to enable", - Header: map[string]string{ - "forget-regex": "ffff", - }, - Expect: "prefix matched, but no headers matched.", - }, - { - Name: "aim@ID3, but got no route action", - Header: map[string]string{ - "D": "7", - }, - Expect: "action is nil. please check your configuration.", - }, - { - Name: "aim@ID3, regex match but got no route action", - Header: map[string]string{ - "normal-regex": "nn", - }, - Expect: "action is nil. please check your configuration.", - }, - { - Name: "aim@ID3, but wrong method", - Method: "PUT", - Header: map[string]string{ - "normal-regex": "nn", - }, - Expect: "prefix matched, but no headers matched.", - }, } r := CreateRouterCoordinator(&hcmc.RouteConfig) diff --git a/pixiu/pkg/model/router.go b/pixiu/pkg/model/router.go index d71dc874c..ce41669de 100644 --- a/pixiu/pkg/model/router.go +++ b/pixiu/pkg/model/router.go @@ -69,19 +69,13 @@ type ( Regex bool `yaml:"regex" json:"regex" mapstructure:"regex"` valueRE *regexp.Regexp } - - // TrieRouteAction indicates the action when matched the trie - TrieRouteAction struct { - RouteAction - RouterMatch - } ) func NewRouterMatchPrefix(name string) RouterMatch { return RouterMatch{Prefix: "/" + name + "/"} } -func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, req *stdHttp.Request) (*RouteAction, error) { +func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string) (*RouteAction, error) { if rc.RouteTrie.IsEmpty() { return nil, errors.Errorf("router configuration is empty") } @@ -93,16 +87,13 @@ func (rc *RouteConfiguration) RouteByPathAndMethod(path, method string, req *std if node.GetBizInfo() == nil { return nil, errors.Errorf("action is nil. please check your configuration.") } - ret := (node.GetBizInfo()).(TrieRouteAction) - if len(ret.Headers) > 0 && !ret.MatchHeader(req) { - return nil, errors.New("prefix matched, but no headers matched.") - } + ret := (node.GetBizInfo()).(RouteAction) - return &ret.RouteAction, nil + return &ret, nil } func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error) { - return rc.RouteByPathAndMethod(req.URL.Path, req.Method, req) + return rc.RouteByPathAndMethod(req.URL.Path, req.Method) } // MatchHeader used when there's only headers to match diff --git a/pixiu/pkg/server/http.go b/pixiu/pkg/server/http.go index 78dd09184..606f861a9 100644 --- a/pixiu/pkg/server/http.go +++ b/pixiu/pkg/server/http.go @@ -27,10 +27,8 @@ import ( func DefaultHttpConnectionManager() *model.HttpConnectionManagerConfig { return &model.HttpConnectionManagerConfig{ RouteConfig: model.RouteConfiguration{ - RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.TrieRouteAction{ - RouteAction: model.RouteAction{ - Cluster: constant.HeaderValueAll, - }, + RouteTrie: trie.NewTrieWithDefault("/api/v1/**", model.RouteAction{ + Cluster: constant.HeaderValueAll, }), }, HTTPFilters: []*model.HTTPFilter{