mirror of https://github.com/gofiber/fiber.git
🚀 improve routing behavior
parent
b8ca4c9b8a
commit
d9c09fa68e
3
ctx.go
3
ctx.go
|
@ -646,6 +646,9 @@ func (ctx *Ctx) OriginalURL() string {
|
||||||
// Returned value is only valid within the handler. Do not store any references.
|
// Returned value is only valid within the handler. Do not store any references.
|
||||||
// Make copies or use the Immutable setting to use the value outside the Handler.
|
// Make copies or use the Immutable setting to use the value outside the Handler.
|
||||||
func (ctx *Ctx) Params(key string, defaultValue ...string) string {
|
func (ctx *Ctx) Params(key string, defaultValue ...string) string {
|
||||||
|
if key == "*" || key == "+" {
|
||||||
|
key += "1"
|
||||||
|
}
|
||||||
for i := range ctx.route.Params {
|
for i := range ctx.route.Params {
|
||||||
if len(key) != len(ctx.route.Params[i]) {
|
if len(key) != len(ctx.route.Params[i]) {
|
||||||
continue
|
continue
|
||||||
|
|
14
ctx_test.go
14
ctx_test.go
|
@ -913,16 +913,26 @@ func Test_Ctx_Params(t *testing.T) {
|
||||||
app.Get("/test2/*", func(c *Ctx) {
|
app.Get("/test2/*", func(c *Ctx) {
|
||||||
utils.AssertEqual(t, "im/a/cookie", c.Params("*"))
|
utils.AssertEqual(t, "im/a/cookie", c.Params("*"))
|
||||||
})
|
})
|
||||||
app.Get("/test3/:optional?", func(c *Ctx) {
|
app.Get("/test3/*/blafasel/*", func(c *Ctx) {
|
||||||
|
utils.AssertEqual(t, "1111", c.Params("*1"))
|
||||||
|
utils.AssertEqual(t, "2222", c.Params("*2"))
|
||||||
|
})
|
||||||
|
app.Get("/test4/:optional?", func(c *Ctx) {
|
||||||
utils.AssertEqual(t, "", c.Params("optional"))
|
utils.AssertEqual(t, "", c.Params("optional"))
|
||||||
})
|
})
|
||||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil))
|
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil))
|
||||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||||
|
|
||||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/im/a/cookie", nil))
|
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/im/a/cookie", nil))
|
||||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test3", nil))
|
|
||||||
|
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test3/1111/blafasel/2222", nil))
|
||||||
|
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||||
|
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||||
|
|
||||||
|
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil))
|
||||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||||
}
|
}
|
||||||
|
|
147
path.go
147
path.go
|
@ -7,6 +7,7 @@
|
||||||
package fiber
|
package fiber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
@ -17,13 +18,18 @@ import (
|
||||||
type routeParser struct {
|
type routeParser struct {
|
||||||
segs []routeSegment
|
segs []routeSegment
|
||||||
params []string
|
params []string
|
||||||
|
wildCardCount int
|
||||||
|
plusCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramsSeg holds the segment metadata
|
// paramsSeg holds the segment metadata
|
||||||
type routeSegment struct {
|
type routeSegment struct {
|
||||||
ParamName string
|
|
||||||
Const string
|
Const string
|
||||||
|
|
||||||
IsParam bool
|
IsParam bool
|
||||||
|
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
|
IsWildcard bool
|
||||||
IsGreedy bool
|
IsGreedy bool
|
||||||
IsOptional bool
|
IsOptional bool
|
||||||
|
@ -55,19 +61,18 @@ var (
|
||||||
// parseRoute analyzes the route and divides it into segments for constant areas and parameters,
|
// parseRoute analyzes the route and divides it into segments for constant areas and parameters,
|
||||||
// this information is needed later when assigning the requests to the declared routes
|
// this information is needed later when assigning the requests to the declared routes
|
||||||
func parseRoute(pattern string) routeParser {
|
func parseRoute(pattern string) routeParser {
|
||||||
var segList []routeSegment
|
parser := routeParser{}
|
||||||
var params []string
|
|
||||||
|
|
||||||
part := ""
|
part := ""
|
||||||
for len(pattern) > 0 {
|
for len(pattern) > 0 {
|
||||||
nextParamPosition := findNextParamPosition(pattern)
|
nextParamPosition := findNextParamPosition(pattern)
|
||||||
// handle the parameter part
|
// handle the parameter part
|
||||||
if nextParamPosition == 0 {
|
if nextParamPosition == 0 {
|
||||||
processedPart, seg := analyseParameterPart(pattern)
|
processedPart, seg := parser.analyseParameterPart(pattern)
|
||||||
params, segList, part = append(params, seg.ParamName), append(segList, seg), processedPart
|
parser.params, parser.segs, part = append(parser.params, seg.ParamName), append(parser.segs, seg), processedPart
|
||||||
} else {
|
} else {
|
||||||
processedPart, seg := analyseConstantPart(pattern, nextParamPosition)
|
processedPart, seg := parser.analyseConstantPart(pattern, nextParamPosition)
|
||||||
segList, part = append(segList, seg), processedPart
|
parser.segs, part = append(parser.segs, seg), processedPart
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduce the pattern by the processed parts
|
// reduce the pattern by the processed parts
|
||||||
|
@ -77,11 +82,44 @@ func parseRoute(pattern string) routeParser {
|
||||||
pattern = pattern[len(part):]
|
pattern = pattern[len(part):]
|
||||||
}
|
}
|
||||||
// mark last segment
|
// mark last segment
|
||||||
if len(segList) > 0 {
|
if len(parser.segs) > 0 {
|
||||||
segList[len(segList)-1].IsLast = true
|
parser.segs[len(parser.segs)-1].IsLast = true
|
||||||
|
}
|
||||||
|
parser.segs = addParameterMetaInfo(parser.segs)
|
||||||
|
|
||||||
|
return parser
|
||||||
}
|
}
|
||||||
|
|
||||||
return routeParser{segs: segList, params: params}
|
// addParameterMetaInfo add important meta information to the parameter segments
|
||||||
|
// to simplify the search for the end of the parameter
|
||||||
|
func addParameterMetaInfo(segs []routeSegment) []routeSegment {
|
||||||
|
comparePart := ""
|
||||||
|
// loop from end to begin
|
||||||
|
for i := len(segs) - 1; i >= 0; i-- {
|
||||||
|
// set the compare part for the parameter
|
||||||
|
if segs[i].IsParam {
|
||||||
|
segs[i].ComparePart = comparePart
|
||||||
|
} else {
|
||||||
|
comparePart = segs[i].Const
|
||||||
|
if len(comparePart) > 1 {
|
||||||
|
comparePart = utils.TrimRight(comparePart, slashDelimiter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop from begin to end
|
||||||
|
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++ {
|
||||||
|
if !segs[j].IsParam {
|
||||||
|
segs[i].PartCount += strings.Count(segs[j].Const, segs[i].ComparePart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return segs
|
||||||
}
|
}
|
||||||
|
|
||||||
// findNextParamPosition search for the next possible parameter start position
|
// findNextParamPosition search for the next possible parameter start position
|
||||||
|
@ -102,7 +140,7 @@ func findNextParamPosition(pattern string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyseConstantPart find the end of the constant part and create the route segment
|
// analyseConstantPart find the end of the constant part and create the route segment
|
||||||
func analyseConstantPart(pattern string, nextParamPosition int) (string, routeSegment) {
|
func (routeParser *routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, routeSegment) {
|
||||||
// handle the constant part
|
// handle the constant part
|
||||||
processedPart := pattern
|
processedPart := pattern
|
||||||
if nextParamPosition != -1 {
|
if nextParamPosition != -1 {
|
||||||
|
@ -115,7 +153,7 @@ func analyseConstantPart(pattern string, nextParamPosition int) (string, routeSe
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyseParameterPart find the parameter end and create the route segment
|
// analyseParameterPart find the parameter end and create the route segment
|
||||||
func analyseParameterPart(pattern string) (string, routeSegment) {
|
func (routeParser *routeParser) analyseParameterPart(pattern string) (string, routeSegment) {
|
||||||
isWildCard := pattern[0] == wildcardParam
|
isWildCard := pattern[0] == wildcardParam
|
||||||
isPlusParam := pattern[0] == plusParam
|
isPlusParam := pattern[0] == plusParam
|
||||||
parameterEndPosition := findNextCharsetPosition(pattern[1:], parameterEndChars)
|
parameterEndPosition := findNextCharsetPosition(pattern[1:], parameterEndChars)
|
||||||
|
@ -130,8 +168,20 @@ func analyseParameterPart(pattern string) (string, routeSegment) {
|
||||||
// cut params part
|
// cut params part
|
||||||
processedPart := pattern[0 : parameterEndPosition+1]
|
processedPart := pattern[0 : parameterEndPosition+1]
|
||||||
|
|
||||||
|
paramName := processedPart
|
||||||
|
// add access iterator to wildcard and plus
|
||||||
|
if isWildCard {
|
||||||
|
routeParser.wildCardCount++
|
||||||
|
paramName += strconv.Itoa(routeParser.wildCardCount)
|
||||||
|
} else if isPlusParam {
|
||||||
|
routeParser.plusCount++
|
||||||
|
paramName += strconv.Itoa(routeParser.plusCount)
|
||||||
|
} else {
|
||||||
|
paramName = utils.GetTrimmedParam(paramName)
|
||||||
|
}
|
||||||
|
|
||||||
return processedPart, routeSegment{
|
return processedPart, routeSegment{
|
||||||
ParamName: utils.GetTrimmedParam(processedPart),
|
ParamName: paramName,
|
||||||
IsParam: true,
|
IsParam: true,
|
||||||
IsOptional: isWildCard || pattern[parameterEndPosition] == optionalParam,
|
IsOptional: isWildCard || pattern[parameterEndPosition] == optionalParam,
|
||||||
IsGreedy: isWildCard || isPlusParam,
|
IsGreedy: isWildCard || isPlusParam,
|
||||||
|
@ -160,31 +210,21 @@ func findNextCharsetPosition(search string, charset []byte) int {
|
||||||
return nextPosition
|
return nextPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check performance
|
|
||||||
// getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions
|
// getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions
|
||||||
func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
func (routeParser *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
lenKeys := len(p.params)
|
lenKeys := len(routeParser.params)
|
||||||
paramsPositions := getAllocFreeParamsPos(lenKeys)
|
paramsPositions := getAllocFreeParamsPos(lenKeys)
|
||||||
var i, paramsIterator, partLen, paramStart int
|
var i, paramsIterator, partLen, paramStart int
|
||||||
for index, segment := range p.segs {
|
for index, segment := range routeParser.segs {
|
||||||
partLen = len(s)
|
partLen = len(s)
|
||||||
// check parameter
|
// check parameter
|
||||||
if segment.IsParam {
|
if !segment.IsParam {
|
||||||
// determine parameter length
|
|
||||||
i = findParamLen(s, p.segs, index)
|
|
||||||
if !segment.IsOptional && i == 0 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
// take over the params positions
|
|
||||||
paramsPositions[paramsIterator][0], paramsPositions[paramsIterator][1] = paramStart, paramStart+i
|
|
||||||
paramsIterator++
|
|
||||||
} else {
|
|
||||||
// check const segment
|
// check const segment
|
||||||
optionalPart := false
|
optionalPart := false
|
||||||
i = len(segment.Const)
|
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
|
// check if the end of the segment is a optional slash and then if the segement is optional or the last one
|
||||||
if i > 0 && partLen == i-1 && segment.Const[i-1] == slashDelimiter && s[:i-1] == segment.Const[:i-1] {
|
if i > 0 && partLen == i-1 && segment.Const[i-1] == slashDelimiter && s[:i-1] == segment.Const[:i-1] {
|
||||||
if segment.IsLast || p.segs[index+1].IsOptional {
|
if segment.IsLast || routeParser.segs[index+1].IsOptional {
|
||||||
i--
|
i--
|
||||||
optionalPart = true
|
optionalPart = true
|
||||||
}
|
}
|
||||||
|
@ -193,6 +233,15 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
if optionalPart == false && (partLen < i || (i == 0 && partLen > 0) || s[:i] != segment.Const) {
|
if optionalPart == false && (partLen < i || (i == 0 && partLen > 0) || s[:i] != segment.Const) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// determine parameter length
|
||||||
|
i = findParamLen(s, routeParser.segs, index)
|
||||||
|
if !segment.IsOptional && i == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
// take over the params positions
|
||||||
|
paramsPositions[paramsIterator][0], paramsPositions[paramsIterator][1] = paramStart, paramStart+i
|
||||||
|
paramsIterator++
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduce founded part from the string
|
// reduce founded part from the string
|
||||||
|
@ -213,7 +262,7 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramsForPos get parameters for the given positions from the given path
|
// paramsForPos get parameters for the given positions from the given path
|
||||||
func (p *routeParser) paramsForPos(path string, paramsPositions [][2]int) []string {
|
func (routeParser *routeParser) paramsForPos(path string, paramsPositions [][2]int) []string {
|
||||||
size := len(paramsPositions)
|
size := len(paramsPositions)
|
||||||
params := getAllocFreeParams(size)
|
params := getAllocFreeParams(size)
|
||||||
for i, positions := range paramsPositions {
|
for i, positions := range paramsPositions {
|
||||||
|
@ -235,48 +284,31 @@ func findParamLen(s string, segments []routeSegment, currIndex int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
compareSeg := segments[currIndex+1]
|
compareSeg := segments[currIndex+1]
|
||||||
nextConstSegInd := currIndex + 1
|
|
||||||
// check if parameter segments are directly after each other
|
// check if parameter segments are directly after each other
|
||||||
if compareSeg.IsParam {
|
if compareSeg.IsParam {
|
||||||
// and if one of them is greedy
|
// and if one of them is greedy
|
||||||
if segments[currIndex].IsGreedy || compareSeg.IsGreedy {
|
if !segments[currIndex].IsGreedy && !compareSeg.IsGreedy && len(s) > 0 {
|
||||||
// search for the next segment that contains a constant part, so that it can be used later
|
|
||||||
for i := currIndex + 1; i < len(segments); i++ {
|
|
||||||
if false == segments[i].IsParam {
|
|
||||||
nextConstSegInd = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if len(s) > 0 {
|
|
||||||
// in case the next parameter or the current parameter is not a wildcard its not greedy, we only want one character
|
// in case the next parameter or the current parameter is not a wildcard its not greedy, we only want one character
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return findParamLenUntilNextConstSeg(s, currIndex, nextConstSegInd, segments)
|
return findParamLenUntilNextConstSeg(s, segments[currIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
// findParamLenUntilNextConstSeg Search the parameters until the next constant part
|
// findParamLenUntilNextConstSeg Search the parameters until the next constant part
|
||||||
func findParamLenUntilNextConstSeg(s string, currIndex, nextConstSegInd int, segments []routeSegment) int {
|
func findParamLenUntilNextConstSeg(s string, segment routeSegment) int {
|
||||||
compareSeg := segments[nextConstSegInd]
|
|
||||||
// get the length to the next constant part
|
|
||||||
if false == compareSeg.IsParam {
|
|
||||||
searchString := compareSeg.Const
|
|
||||||
if len(searchString) > 1 {
|
|
||||||
searchString = utils.TrimRight(compareSeg.Const, slashDelimiter)
|
|
||||||
}
|
|
||||||
// special logic for greedy params
|
// special logic for greedy params
|
||||||
if segments[currIndex].IsGreedy {
|
if segment.IsGreedy {
|
||||||
searchCount := strings.Count(s, searchString)
|
searchCount := strings.Count(s, segment.ComparePart)
|
||||||
if searchCount > 1 {
|
if searchCount > 1 {
|
||||||
return findGreedyParamLen(s, searchString, searchCount, nextConstSegInd, segments)
|
return findGreedyParamLen(s, searchCount, segment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if constPosition := strings.Index(s, searchString); constPosition != -1 {
|
if constPosition := strings.Index(s, segment.ComparePart); constPosition != -1 {
|
||||||
return constPosition
|
return constPosition
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return len(s)
|
return len(s)
|
||||||
}
|
}
|
||||||
|
@ -294,18 +326,16 @@ func findParamLenForLastSegment(s string, seg routeSegment) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// findGreedyParamLen get the length of the parameter for greedy segments from right to left
|
// findGreedyParamLen get the length of the parameter for greedy segments from right to left
|
||||||
func findGreedyParamLen(s, searchString string, searchCount, compareSegIndex int, segments []routeSegment) int {
|
func findGreedyParamLen(s string, searchCount int, segment routeSegment) int {
|
||||||
// check all from right to left segments
|
// check all from right to left segments
|
||||||
for i := len(segments) - 1; i >= compareSegIndex && searchCount > 0; i-- {
|
for i := segment.PartCount; i > 0 && searchCount > 0; i-- {
|
||||||
if false == segments[i].IsParam && segments[i].Const == segments[compareSegIndex].Const {
|
|
||||||
searchCount--
|
searchCount--
|
||||||
if constPosition := strings.LastIndex(s, searchString); constPosition != -1 {
|
if constPosition := strings.LastIndex(s, segment.ComparePart); constPosition != -1 {
|
||||||
s = s[:constPosition]
|
s = s[:constPosition]
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return len(s)
|
return len(s)
|
||||||
}
|
}
|
||||||
|
@ -332,7 +362,6 @@ func getAllocFreeParamsPos(allocLen int) [][2]int {
|
||||||
return paramsPositions
|
return paramsPositions
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace it with bytebufferpool and release the parameter buffers in ctx release function
|
|
||||||
// getAllocFreeParams fetches a slice area from the predefined slice, which is currently not in use
|
// getAllocFreeParams fetches a slice area from the predefined slice, which is currently not in use
|
||||||
func getAllocFreeParams(allocLen int) []string {
|
func getAllocFreeParams(allocLen int) []string {
|
||||||
size := uint32(allocLen)
|
size := uint32(allocLen)
|
||||||
|
|
43
path_test.go
43
path_test.go
|
@ -297,6 +297,49 @@ func Test_Path_matchParams(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go test -race -run Test_Path_matchParams
|
||||||
|
func Benchmark_Path_matchParams(t *testing.B) {
|
||||||
|
type testparams struct {
|
||||||
|
url string
|
||||||
|
params []string
|
||||||
|
match bool
|
||||||
|
partialCheck bool
|
||||||
|
}
|
||||||
|
benchCase := func(r string, cases []testparams) {
|
||||||
|
parser := parseRoute(r)
|
||||||
|
for _, c := range cases {
|
||||||
|
|
||||||
|
var params []string
|
||||||
|
var matchRes bool
|
||||||
|
t.Run(r+" | "+c.url, func(b *testing.B) {
|
||||||
|
params = nil
|
||||||
|
for i := 0; i <= b.N; i++ {
|
||||||
|
if paramPos, match := parser.getMatch(c.url, c.partialCheck); match {
|
||||||
|
// Get params from the original path
|
||||||
|
matchRes = true
|
||||||
|
params = parser.paramsForPos(c.url, paramPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", r, c.url))
|
||||||
|
if matchRes && params != nil {
|
||||||
|
utils.AssertEqual(t, c.params, params, fmt.Sprintf("route: '%s', url: '%s'", r, c.url))
|
||||||
|
} else {
|
||||||
|
utils.AssertEqual(t, true, nil == params, fmt.Sprintf("route: '%s', url: '%s'", r, c.url))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
benchCase("/api/v1/:param/*", []testparams{
|
||||||
|
{url: "/api/v1/entity", params: []string{"entity", ""}, match: true},
|
||||||
|
{url: "/api/v1/entity/", params: []string{"entity", ""}, match: true},
|
||||||
|
{url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true},
|
||||||
|
{url: "/api/v", params: nil, match: false},
|
||||||
|
{url: "/api/v2", params: nil, match: false},
|
||||||
|
{url: "/api/v1/", params: nil, match: false},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// go test -race -run Test_Reset_StartParamPosList
|
// go test -race -run Test_Reset_StartParamPosList
|
||||||
func Test_Reset_StartParamPosList(t *testing.T) {
|
func Test_Reset_StartParamPosList(t *testing.T) {
|
||||||
atomic.StoreUint32(&startParamPosList, uint32(len(paramsPosDummy))-10)
|
atomic.StoreUint32(&startParamPosList, uint32(len(paramsPosDummy))-10)
|
||||||
|
|
Loading…
Reference in New Issue