mirror of https://github.com/gofiber/fiber.git
add support for -. chars in the route
parent
bbcff3f375
commit
f816c6706f
36
utils.go
36
utils.go
|
@ -166,6 +166,8 @@ var getBytesImmutable = func(s string) (b []byte) {
|
||||||
return []byte(s)
|
return []byte(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//region route registration and matching #-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||||
|
|
||||||
// ⚠️ This path parser was based on urlpath by @ucarion (MIT License).
|
// ⚠️ This path parser was based on urlpath by @ucarion (MIT License).
|
||||||
// 💖 Modified for the Fiber router by @renanbastos93 & @renewerner87
|
// 💖 Modified for the Fiber router by @renanbastos93 & @renewerner87
|
||||||
// 🤖 ucarion/urlpath - renanbastos93/fastpath - renewerner87/fastpath
|
// 🤖 ucarion/urlpath - renanbastos93/fastpath - renewerner87/fastpath
|
||||||
|
@ -186,16 +188,21 @@ type paramSeg struct {
|
||||||
EndChar byte
|
EndChar byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// list of possible parameter and segment delimiter
|
||||||
|
// slash has a special role, unlike the other parameters it must not be interpreted as a parameter
|
||||||
|
var routeDelimiter = []byte{'/', '-', '.'}
|
||||||
|
|
||||||
const wildcardParam string = "*"
|
const wildcardParam string = "*"
|
||||||
|
|
||||||
// New ...
|
// 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
|
||||||
func parseRoute(pattern string) (p routeParser) {
|
func parseRoute(pattern string) (p routeParser) {
|
||||||
var out []paramSeg
|
var out []paramSeg
|
||||||
var params []string
|
var params []string
|
||||||
|
|
||||||
part, delimiterPos := "", 0
|
part, delimiterPos := "", 0
|
||||||
for len(pattern) > 0 && delimiterPos != -1 {
|
for len(pattern) > 0 && delimiterPos != -1 {
|
||||||
delimiterPos = findNextRouteDelimiterPosition(pattern)
|
delimiterPos = findNextRouteSegmentEnd(pattern)
|
||||||
if delimiterPos != -1 {
|
if delimiterPos != -1 {
|
||||||
part = pattern[:delimiterPos]
|
part = pattern[:delimiterPos]
|
||||||
} else {
|
} else {
|
||||||
|
@ -246,9 +253,8 @@ func parseRoute(pattern string) (p routeParser) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var routeDelimiter = []byte{'/', '-', '.'}
|
// findNextRouteSegmentEnd searches in the route for the next end position for a segment
|
||||||
|
func findNextRouteSegmentEnd(search string) int {
|
||||||
func findNextRouteDelimiterPosition(search string) int {
|
|
||||||
nextPosition := -1
|
nextPosition := -1
|
||||||
for _, delimiter := range routeDelimiter {
|
for _, delimiter := range routeDelimiter {
|
||||||
if pos := strings.IndexByte(search, delimiter); pos != -1 && (pos < nextPosition || nextPosition == -1) {
|
if pos := strings.IndexByte(search, delimiter); pos != -1 && (pos < nextPosition || nextPosition == -1) {
|
||||||
|
@ -259,7 +265,7 @@ func findNextRouteDelimiterPosition(search string) int {
|
||||||
return nextPosition
|
return nextPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match ...
|
// 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 (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
lenKeys := len(p.params)
|
lenKeys := len(p.params)
|
||||||
paramsPositions := getAllocFreeParamsPos(lenKeys)
|
paramsPositions := getAllocFreeParamsPos(lenKeys)
|
||||||
|
@ -288,6 +294,9 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
|
|
||||||
if !segment.IsOptional && i == 0 {
|
if !segment.IsOptional && i == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
|
// special case for not slash end character
|
||||||
|
} else if i > 0 && partLen >= i && segment.EndChar != '/' && s[i-1] == '/' {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsPositions[paramsIterator][0], paramsPositions[paramsIterator][1] = paramStart, paramStart+i
|
paramsPositions[paramsIterator][0], paramsPositions[paramsIterator][1] = paramStart, paramStart+i
|
||||||
|
@ -318,7 +327,7 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
|
||||||
return paramsPositions, true
|
return paramsPositions, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (p *routeParser) paramsForPos(path string, paramsPositions [][2]int) []string {
|
||||||
size := len(paramsPositions)
|
size := len(paramsPositions)
|
||||||
params := getAllocFreeParams(size)
|
params := getAllocFreeParams(size)
|
||||||
|
@ -338,7 +347,6 @@ func (p *routeParser) paramsForPos(path string, paramsPositions [][2]int) []stri
|
||||||
func findWildcardParamLen(s string, segments []paramSeg, currIndex int) int {
|
func findWildcardParamLen(s string, segments []paramSeg, currIndex int) int {
|
||||||
// "/api/*/:param" - "/api/joker/batman/robin/1" -> "joker/batman/robin", "1"
|
// "/api/*/:param" - "/api/joker/batman/robin/1" -> "joker/batman/robin", "1"
|
||||||
// "/api/*/:param" - "/api/joker/batman" -> "joker", "batman"
|
// "/api/*/:param" - "/api/joker/batman" -> "joker", "batman"
|
||||||
// "/api/*/:param" - "/api/joker/batman/robin" -> "joker/batman", "robin"
|
|
||||||
// "/api/*/:param" - "/api/joker-batman-robin/1" -> "joker-batman-robin", "1"
|
// "/api/*/:param" - "/api/joker-batman-robin/1" -> "joker-batman-robin", "1"
|
||||||
endChar := segments[currIndex].EndChar
|
endChar := segments[currIndex].EndChar
|
||||||
neededEndChars := 0
|
neededEndChars := 0
|
||||||
|
@ -364,10 +372,14 @@ func findWildcardParamLen(s string, segments []paramSeg, currIndex int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// performance tricks
|
// performance tricks
|
||||||
var paramsDummy = make([]string, 100000)
|
// creates predefined arrays that are used to match the request routes so that no allocations need to be made
|
||||||
var paramsPosDummy = make([][2]int, 100000)
|
var paramsDummy, paramsPosDummy = make([]string, 100000), make([][2]int, 100000)
|
||||||
|
|
||||||
|
// positions parameter that moves further and further to the right and remains atomic over all simultaneous requests
|
||||||
|
// to assign a separate range to each request
|
||||||
var startParamList, startParamPosList uint32 = 0, 0
|
var startParamList, startParamPosList uint32 = 0, 0
|
||||||
|
|
||||||
|
// getAllocFreeParamsPos fetches a slice area from the predefined slice, which is currently not in use
|
||||||
func getAllocFreeParamsPos(allocLen int) [][2]int {
|
func getAllocFreeParamsPos(allocLen int) [][2]int {
|
||||||
size := uint32(allocLen)
|
size := uint32(allocLen)
|
||||||
start := atomic.AddUint32(&startParamPosList, size)
|
start := atomic.AddUint32(&startParamPosList, size)
|
||||||
|
@ -380,6 +392,8 @@ func getAllocFreeParamsPos(allocLen int) [][2]int {
|
||||||
paramsPositions := paramsPosDummy[start:allocLen:allocLen]
|
paramsPositions := paramsPosDummy[start:allocLen:allocLen]
|
||||||
return paramsPositions
|
return paramsPositions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
start := atomic.AddUint32(&startParamList, size)
|
start := atomic.AddUint32(&startParamList, size)
|
||||||
|
@ -393,6 +407,8 @@ func getAllocFreeParams(allocLen int) []string {
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion #-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||||
|
|
||||||
// HTTP methods and their unique INTs
|
// HTTP methods and their unique INTs
|
||||||
var methodINT = map[string]int{
|
var methodINT = map[string]int{
|
||||||
MethodGet: 0,
|
MethodGet: 0,
|
||||||
|
|
|
@ -169,14 +169,17 @@ func Test_Utils_matchParams(t *testing.T) {
|
||||||
testCase("/api/:day/:month?/:year?", []testparams{
|
testCase("/api/:day/:month?/:year?", []testparams{
|
||||||
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
||||||
{url: "/api/1/", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1/", params: []string{"1", "", ""}, match: true},
|
||||||
|
{url: "/api/1//", params: []string{"1", "", ""}, match: true},
|
||||||
|
{url: "/api/1/-/", params: []string{"1", "-", ""}, match: true},
|
||||||
{url: "/api/1-", params: []string{"1-", "", ""}, match: true},
|
{url: "/api/1-", params: []string{"1-", "", ""}, match: true},
|
||||||
|
{url: "/api/1.", params: []string{"1.", "", ""}, match: true},
|
||||||
{url: "/api/1/2", params: []string{"1", "2", ""}, match: true},
|
{url: "/api/1/2", params: []string{"1", "2", ""}, match: true},
|
||||||
{url: "/api/1/2/3", params: []string{"1", "2", "3"}, match: true},
|
{url: "/api/1/2/3", params: []string{"1", "2", "3"}, match: true},
|
||||||
{url: "/api/", params: nil, match: false},
|
{url: "/api/", params: nil, match: false},
|
||||||
})
|
})
|
||||||
testCase("/api/:day.:month?.:year?", []testparams{
|
testCase("/api/:day.:month?.:year?", []testparams{
|
||||||
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
||||||
//{url: "/api/1/", params: nil, match: false},// TODO: check it later
|
{url: "/api/1/", params: nil, match: false},
|
||||||
{url: "/api/1.", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1.", params: []string{"1", "", ""}, match: true},
|
||||||
{url: "/api/1.2", params: []string{"1", "2", ""}, match: true},
|
{url: "/api/1.2", params: []string{"1", "2", ""}, match: true},
|
||||||
{url: "/api/1.2.3", params: []string{"1", "2", "3"}, match: true},
|
{url: "/api/1.2.3", params: []string{"1", "2", "3"}, match: true},
|
||||||
|
@ -184,8 +187,10 @@ func Test_Utils_matchParams(t *testing.T) {
|
||||||
})
|
})
|
||||||
testCase("/api/:day-:month?-:year?", []testparams{
|
testCase("/api/:day-:month?-:year?", []testparams{
|
||||||
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1", params: []string{"1", "", ""}, match: true},
|
||||||
//{url: "/api/1/", params: nil, match: false},// TODO: check it later
|
{url: "/api/1/", params: nil, match: false},
|
||||||
{url: "/api/1-", params: []string{"1", "", ""}, match: true},
|
{url: "/api/1-", params: []string{"1", "", ""}, match: true},
|
||||||
|
{url: "/api/1-/", params: nil, match: false},
|
||||||
|
{url: "/api/1-/-", params: nil, match: false},
|
||||||
{url: "/api/1-2", params: []string{"1", "2", ""}, match: true},
|
{url: "/api/1-2", params: []string{"1", "2", ""}, match: true},
|
||||||
{url: "/api/1-2-3", params: []string{"1", "2", "3"}, match: true},
|
{url: "/api/1-2-3", params: []string{"1", "2", "3"}, match: true},
|
||||||
{url: "/api/", params: nil, match: false},
|
{url: "/api/", params: nil, match: false},
|
||||||
|
@ -202,6 +207,7 @@ func Test_Utils_matchParams(t *testing.T) {
|
||||||
{url: "/api/", params: []string{"", ""}, match: true},
|
{url: "/api/", params: []string{"", ""}, match: true},
|
||||||
{url: "/api/joker", params: []string{"joker", ""}, match: true},
|
{url: "/api/joker", params: []string{"joker", ""}, match: true},
|
||||||
{url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true},
|
{url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true},
|
||||||
|
{url: "/api/joker//batman", params: []string{"joker/", "batman"}, match: true},
|
||||||
{url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true},
|
{url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true},
|
||||||
{url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true},
|
{url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true},
|
||||||
{url: "/api/joker/batman/robin/1/", params: []string{"joker/batman/robin/1", ""}, match: true},
|
{url: "/api/joker/batman/robin/1/", params: []string{"joker/batman/robin/1", ""}, match: true},
|
||||||
|
|
Loading…
Reference in New Issue