From 67caca823bae8dc2bb182336ebfa9ab7904d954d Mon Sep 17 00:00:00 2001 From: ReneWerner87 Date: Mon, 10 Aug 2020 09:50:53 +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 --- ctx_test.go | 1 + path.go | 31 ++++++------------- path_test.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/ctx_test.go b/ctx_test.go index 5c091152..3715303d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -916,6 +916,7 @@ func Test_Ctx_Params(t *testing.T) { app.Get("/test3/*/blafasel/*", func(c *Ctx) { utils.AssertEqual(t, "1111", c.Params("*1")) utils.AssertEqual(t, "2222", c.Params("*2")) + utils.AssertEqual(t, "1111", c.Params("*")) }) app.Get("/test4/:optional?", func(c *Ctx) { utils.AssertEqual(t, "", c.Params("optional")) diff --git a/path.go b/path.go index 2b8e974f..7bc26aec 100644 --- a/path.go +++ b/path.go @@ -30,7 +30,6 @@ type routeSegment struct { ParamName string 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 - IsWildcard bool IsGreedy bool IsOptional bool IsLast bool @@ -48,7 +47,6 @@ const ( 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 - // TODO '(' ')' delimiters for regex patterns routeDelimiter = []byte{slashDelimiter, '-', '.'} // list of chars for the parameter recognising parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} @@ -217,9 +215,8 @@ func (routeParser *routeParser) getMatch(s string, partialCheck bool) ([][2]int, var i, paramsIterator, partLen, paramStart int for index, segment := range routeParser.segs { partLen = len(s) - // check parameter + // check const segment if !segment.IsParam { - // check const segment optionalPart := false i = len(segment.Const) // check if the end of the segment is a optional slash and then if the segement is optional or the last one @@ -284,29 +281,21 @@ func findParamLen(s string, segments []routeSegment, currIndex int) int { } compareSeg := segments[currIndex+1] - // check if parameter segments are directly after each other - if compareSeg.IsParam { - // and if one of them is greedy - if !segments[currIndex].IsGreedy && !compareSeg.IsGreedy && len(s) > 0 { - // in case the next parameter or the current parameter is not a wildcard its not greedy, we only want one character - return 1 - } + // check if parameter segments are directly after each other and if one of them is greedy + if compareSeg.IsParam && !segments[currIndex].IsGreedy && !compareSeg.IsGreedy && len(s) > 0 { + // in case the next parameter or the current parameter is not a wildcard its not greedy, we only want one character + return 1 } - - return findParamLenUntilNextConstSeg(s, segments[currIndex]) -} - -// findParamLenUntilNextConstSeg Search the parameters until the next constant part -func findParamLenUntilNextConstSeg(s string, segment routeSegment) int { + // Search the parameters until the next constant part // special logic for greedy params - if segment.IsGreedy { - searchCount := strings.Count(s, segment.ComparePart) + if segments[currIndex].IsGreedy { + searchCount := strings.Count(s, segments[currIndex].ComparePart) if searchCount > 1 { - return findGreedyParamLen(s, searchCount, segment) + return findGreedyParamLen(s, searchCount, segments[currIndex]) } } - if constPosition := strings.Index(s, segment.ComparePart); constPosition != -1 { + if constPosition := strings.Index(s, segments[currIndex].ComparePart); constPosition != -1 { return constPosition } diff --git a/path_test.go b/path_test.go index 55b75803..98a68d57 100644 --- a/path_test.go +++ b/path_test.go @@ -12,6 +12,93 @@ import ( utils "github.com/gofiber/utils" ) +// go test -race -run Test_Path_parseRoute +func Test_Path_parseRoute(t *testing.T) { + var rp routeParser + + rp = parseRoute("/shop/product/::filter/color::color/size::size") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/shop/product/:"}, + {IsParam: true, ParamName: "filter", ComparePart: "/color:", PartCount: 1}, + {Const: "/color:"}, + {IsParam: true, ParamName: "color", ComparePart: "/size:", PartCount: 1}, + {Const: "/size:"}, + {IsParam: true, ParamName: "size", IsLast: true}, + }, + params: []string{"filter", "color", "size"}, + wildCardCount: 0, + plusCount: 0, + }, rp) + + rp = parseRoute("/api/v1/:param/abc/*") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/api/v1/"}, + {IsParam: true, ParamName: "param", ComparePart: "/abc", PartCount: 1}, + {Const: "/abc/"}, + {IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, IsLast: true}, + }, + params: []string{"param", "*1"}, + wildCardCount: 1, + plusCount: 0, + }, rp) + + rp = parseRoute("/api/*/:param/:param2") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/api/"}, + {IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, ComparePart: "/", PartCount: 2}, + {Const: "/"}, + {IsParam: true, ParamName: "param", ComparePart: "/", PartCount: 1}, + {Const: "/"}, + {IsParam: true, ParamName: "param2", IsLast: true}, + }, + params: []string{"*1", "param", "param2"}, + wildCardCount: 1, + plusCount: 0, + }, rp) + + rp = parseRoute("/test:optional?:optional2?") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/test"}, + {IsParam: true, ParamName: "optional", IsOptional: true}, + {IsParam: true, ParamName: "optional2", IsOptional: true, IsLast: true}, + }, + params: []string{"optional", "optional2"}, + wildCardCount: 0, + plusCount: 0, + }, 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}, + {Const: ".json", IsLast: true}, + }, + params: []string{"+1"}, + wildCardCount: 0, + plusCount: 1, + }, rp) + + rp = parseRoute("/api/:day.:month?.:year?") + utils.AssertEqual(t, routeParser{ + segs: []routeSegment{ + {Const: "/api/"}, + {IsParam: true, ParamName: "day", IsOptional: false, ComparePart: ".", PartCount: 2}, + {Const: "."}, + {IsParam: true, ParamName: "month", IsOptional: true, ComparePart: ".", PartCount: 1}, + {Const: "."}, + {IsParam: true, ParamName: "year", IsOptional: true, IsLast: true}, + }, + params: []string{"day", "month", "year"}, + wildCardCount: 0, + plusCount: 0, + }, rp) +} + // go test -race -run Test_Path_matchParams func Test_Path_matchParams(t *testing.T) { t.Parallel()