diff --git a/helpers.go b/helpers.go index f5e1ee55..b4046b47 100644 --- a/helpers.go +++ b/helpers.go @@ -698,18 +698,21 @@ const ( // Route Constraints const ( - ConstraintInt = "int" - ConstraintBool = "bool" - ConstraintFloat = "float" - ConstraintAlpha = "alpha" - ConstraintGuid = "guid" - ConstraintMinLen = "minLen" - ConstraintMaxLen = "maxLen" - ConstraintExactLen = "exactLen" - ConstraintBetweenLen = "betweenLen" - ConstraintMin = "min" - ConstraintMax = "max" - ConstraintRange = "range" - ConstraintDatetime = "datetime" - ConstraintRegex = "regex" + ConstraintInt = "int" + ConstraintBool = "bool" + ConstraintFloat = "float" + ConstraintAlpha = "alpha" + ConstraintGuid = "guid" + ConstraintMinLen = "minLen" + ConstraintMaxLen = "maxLen" + ConstraintLen = "len" + ConstraintBetweenLen = "betweenLen" + ConstraintMinLenLower = "minlen" + ConstraintMaxLenLower = "maxlen" + ConstraintBetweenLenLower = "betweenlen" + ConstraintMin = "min" + ConstraintMax = "max" + ConstraintRange = "range" + ConstraintDatetime = "datetime" + ConstraintRegex = "regex" ) diff --git a/path.go b/path.go index 16d1b2a3..db2818dd 100644 --- a/path.go +++ b/path.go @@ -7,6 +7,7 @@ package fiber import ( + "fmt" "regexp" "strconv" "strings" @@ -79,7 +80,7 @@ const ( guidConstraint minLenConstraint maxLenConstraint - exactLenConstraint + lenConstraint betweenLenConstraint minConstraint maxConstraint @@ -261,7 +262,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint { constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd] - userconstraints := strings.Split(constraintString, string(parameterConstraintSeparatorChars)) + userconstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars)) constraints = make([]*Constraint, 0, len(userconstraints)) for _, c := range userconstraints { @@ -272,7 +273,15 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r if start != -1 && end != -1 { constraint := &Constraint{ ID: getParamConstraintType(c[:start]), - Data: strings.Split(RemoveEscapeChar(c[start+1:end]), string(parameterConstraintDataSeparatorChars)), + Data: splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)), + } + + // remove escapes from data + if len(constraint.Data) == 1 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + } else if len(constraint.Data) == 2 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) } // Precompile regex if has regex constraint @@ -384,6 +393,21 @@ func findNextNonEscapedCharsetPosition(search string, charset []byte) int { return pos } +// splitNonEscaped slices s into all substrings separated by sep and returns a slice of the substrings between those separators +// This function also takes a care of escape char when splitting. +func splitNonEscaped(s, sep string) []string { + var result []string + i := findNextNonEscapedCharsetPosition(s, []byte(sep)) + + for i > -1 { + result = append(result, s[:i]) + s = s[i+len(sep):] + i = findNextNonEscapedCharsetPosition(s, []byte(sep)) + } + + return append(result, s) +} + // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { var i, paramsIterator, partLen int @@ -526,13 +550,13 @@ func getParamConstraintType(constraintPart string) TypeConstraint { return alphaConstraint case ConstraintGuid: return guidConstraint - case ConstraintMinLen: + case ConstraintMinLen, ConstraintMinLenLower: return minLenConstraint - case ConstraintMaxLen: + case ConstraintMaxLen, ConstraintMaxLenLower: return maxLenConstraint - case ConstraintExactLen: - return exactLenConstraint - case ConstraintBetweenLen: + case ConstraintLen: + return lenConstraint + case ConstraintBetweenLen, ConstraintBetweenLenLower: return betweenLenConstraint case ConstraintMin: return minConstraint @@ -555,7 +579,7 @@ func (c *Constraint) CheckConstraint(param string) bool { var num int // check data exists - needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, exactLenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint} + needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint} needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint} for _, data := range needOneData { @@ -570,6 +594,8 @@ func (c *Constraint) CheckConstraint(param string) bool { } } + fmt.Print(c.Data) + // check constraints switch c.ID { case intConstraint: @@ -598,7 +624,7 @@ func (c *Constraint) CheckConstraint(param string) bool { if len(param) > data { return false } - case exactLenConstraint: + case lenConstraint: data, _ := strconv.Atoi(c.Data[0]) if len(param) != data { diff --git a/path_test.go b/path_test.go index 7ad51409..4ea2a1ec 100644 --- a/path_test.go +++ b/path_test.go @@ -475,7 +475,7 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) - testCase("/api/v1/:param", []testparams{ + testCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, @@ -549,6 +549,19 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/true", params: []string{"true"}, match: false}, }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/1200", params: []string{"1200"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) } func Test_Utils_GetTrimmedParam(t *testing.T) { @@ -681,7 +694,7 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) - benchCase("/api/v1/:param", []testparams{ + benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, @@ -755,28 +768,17 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/true", params: []string{"true"}, match: false}, }) -} - -func Test_Path_matchParams0(t *testing.T) { - t.Parallel() - type testparams struct { - url string - params []string - match bool - partialCheck bool - } - var ctxParams [maxParams]string - testCase := func(r string, cases []testparams) { - parser := parseRoute(r) - for _, c := range cases { - match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) - if match && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) - } - } - } - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/1200", params: []string{"1200"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, }) }