From 053d463fa6c24f26cc3d301abe7cd272665ca261 Mon Sep 17 00:00:00 2001 From: ReneWerner87 Date: Mon, 10 Aug 2020 10:14:17 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20improve=20routing=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- path.go | 50 +++++++++++++++++++++++++++----------------------- path_test.go | 34 +++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/path.go b/path.go index 7bc26aec..133cea9f 100644 --- a/path.go +++ b/path.go @@ -16,41 +16,44 @@ import ( // routeParser holds the path segments and param names type routeParser struct { - segs []routeSegment - params []string - wildCardCount int - plusCount int + segs []routeSegment // the parsed segments of the route + params []string // that parameter names the parsed route + wildCardCount int // number of wildcard parameters, used internally to give the wildcard parameter its number + plusCount int // number of plus parameters, used internally to give the plus parameter its number } // paramsSeg holds the segment metadata type routeSegment struct { - Const string - - IsParam bool - ParamName string + // const information + Const string // constant part of the route + // parameter information + IsParam bool // Truth value that indicates whether it is a parameter or a constant part + ParamName string // name of the parameter for access to it, for wildcards and plus parameters access iterators starting with 1 are added ComparePart string // search part to find the end of the parameter PartCount int // how often is the search part contained in the non-param segments? -> necessary for greedy search - IsGreedy bool - IsOptional bool - IsLast bool - // TODO: add support for optional groups ? + IsGreedy bool // indicates whether the parameter is greedy or not, is used with wildcard and plus + IsOptional bool // indicates whether the parameter is optional or not + // common information + IsLast bool // shows if the segment is the last one for the route + // future TODO: add support for optional groups "/abc(/def)?" } +// different special routing signs const ( - wildcardParam byte = '*' - plusParam byte = '+' - optionalParam byte = '?' - slashDelimiter byte = '/' - paramStarterChar byte = ':' + wildcardParam byte = '*' // indicates a optional greedy parameter + plusParam byte = '+' // indicates a required greedy parameter + optionalParam byte = '?' // concludes a parameter by name and makes it optional + paramStarterChar byte = ':' // start character for a parameter with name + slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional ) +// list of possible parameter and segment delimiter var ( - // list of possible parameter and segment delimiter // slash has a special role, unlike the other parameters it must not be interpreted as a parameter routeDelimiter = []byte{slashDelimiter, '-', '.'} // list of chars for the parameter recognising parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} - // list of chars at the end of the parameter + // list of chars of delimiters and the starting parameter name char parameterDelimiterChars = append([]byte{paramStarterChar}, routeDelimiter...) // list of chars to find the end of a parameter parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...) @@ -96,6 +99,7 @@ func addParameterMetaInfo(segs []routeSegment) []routeSegment { for i := len(segs) - 1; i >= 0; i-- { // set the compare part for the parameter if segs[i].IsParam { + // important for finding the end of the parameter segs[i].ComparePart = comparePart } else { comparePart = segs[i].Const @@ -109,8 +113,9 @@ func addParameterMetaInfo(segs []routeSegment) []routeSegment { for i := 0; i < len(segs); i++ { // check how often the compare part is in the following const parts if segs[i].IsParam && segs[i].ComparePart != "" { - for j := i + 1; j < len(segs)-1; j++ { + for j := i + 1; j <= len(segs)-1; j++ { if !segs[j].IsParam { + // count is important for the greedy match segs[i].PartCount += strings.Count(segs[j].Const, segs[i].ComparePart) } } @@ -155,6 +160,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro isWildCard := pattern[0] == wildcardParam isPlusParam := pattern[0] == plusParam parameterEndPosition := findNextCharsetPosition(pattern[1:], parameterEndChars) + // handle wildcard end if isWildCard || isPlusParam { parameterEndPosition = 0 @@ -166,7 +172,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro // cut params part processedPart := pattern[0 : parameterEndPosition+1] - paramName := processedPart + paramName := utils.GetTrimmedParam(processedPart) // add access iterator to wildcard and plus if isWildCard { routeParser.wildCardCount++ @@ -174,8 +180,6 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro } else if isPlusParam { routeParser.plusCount++ paramName += strconv.Itoa(routeParser.plusCount) - } else { - paramName = utils.GetTrimmedParam(paramName) } return processedPart, routeSegment{ diff --git a/path_test.go b/path_test.go index 98a68d57..86b9dea4 100644 --- a/path_test.go +++ b/path_test.go @@ -26,9 +26,7 @@ func Test_Path_parseRoute(t *testing.T) { {Const: "/size:"}, {IsParam: true, ParamName: "size", IsLast: true}, }, - params: []string{"filter", "color", "size"}, - wildCardCount: 0, - plusCount: 0, + params: []string{"filter", "color", "size"}, }, rp) rp = parseRoute("/api/v1/:param/abc/*") @@ -41,7 +39,6 @@ func Test_Path_parseRoute(t *testing.T) { }, params: []string{"param", "*1"}, wildCardCount: 1, - plusCount: 0, }, rp) rp = parseRoute("/api/*/:param/:param2") @@ -56,7 +53,6 @@ func Test_Path_parseRoute(t *testing.T) { }, params: []string{"*1", "param", "param2"}, wildCardCount: 1, - plusCount: 0, }, rp) rp = parseRoute("/test:optional?:optional2?") @@ -66,21 +62,18 @@ func Test_Path_parseRoute(t *testing.T) { {IsParam: true, ParamName: "optional", IsOptional: true}, {IsParam: true, ParamName: "optional2", IsOptional: true, IsLast: true}, }, - params: []string{"optional", "optional2"}, - wildCardCount: 0, - plusCount: 0, + params: []string{"optional", "optional2"}, }, rp) rp = parseRoute("/config/+.json") utils.AssertEqual(t, routeParser{ segs: []routeSegment{ {Const: "/config/"}, - {IsParam: true, ParamName: "+1", IsGreedy: true, IsOptional: false, ComparePart: ".json", PartCount: 0}, + {IsParam: true, ParamName: "+1", IsGreedy: true, IsOptional: false, ComparePart: ".json", PartCount: 1}, {Const: ".json", IsLast: true}, }, - params: []string{"+1"}, - wildCardCount: 0, - plusCount: 1, + params: []string{"+1"}, + plusCount: 1, }, rp) rp = parseRoute("/api/:day.:month?.:year?") @@ -93,9 +86,20 @@ func Test_Path_parseRoute(t *testing.T) { {Const: "."}, {IsParam: true, ParamName: "year", IsOptional: true, IsLast: true}, }, - params: []string{"day", "month", "year"}, - wildCardCount: 0, - plusCount: 0, + params: []string{"day", "month", "year"}, + }, rp) + + rp = parseRoute("/*v1*/proxy") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/"}, + {IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, ComparePart: "v1", PartCount: 1}, + {Const: "v1"}, + {IsParam: true, ParamName: "*2", IsGreedy: true, IsOptional: true, ComparePart: "/proxy", PartCount: 1}, + {Const: "/proxy", IsLast: true}, + }, + params: []string{"*1", "*2"}, + wildCardCount: 2, }, rp) }