add support for -. chars in the route

pull/466/head
wernerr 2020-06-14 12:06:14 +02:00
parent bbcff3f375
commit f816c6706f
2 changed files with 34 additions and 12 deletions

View File

@ -166,6 +166,8 @@ var getBytesImmutable = func(s string) (b []byte) {
return []byte(s)
}
//region route registration and matching #-#-#-#-#-#-#-#-#-#-#-#-#-#
// ⚠️ This path parser was based on urlpath by @ucarion (MIT License).
// 💖 Modified for the Fiber router by @renanbastos93 & @renewerner87
// 🤖 ucarion/urlpath - renanbastos93/fastpath - renewerner87/fastpath
@ -186,16 +188,21 @@ type paramSeg struct {
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 = "*"
// 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) {
var out []paramSeg
var params []string
part, delimiterPos := "", 0
for len(pattern) > 0 && delimiterPos != -1 {
delimiterPos = findNextRouteDelimiterPosition(pattern)
delimiterPos = findNextRouteSegmentEnd(pattern)
if delimiterPos != -1 {
part = pattern[:delimiterPos]
} else {
@ -246,9 +253,8 @@ func parseRoute(pattern string) (p routeParser) {
return
}
var routeDelimiter = []byte{'/', '-', '.'}
func findNextRouteDelimiterPosition(search string) int {
// findNextRouteSegmentEnd searches in the route for the next end position for a segment
func findNextRouteSegmentEnd(search string) int {
nextPosition := -1
for _, delimiter := range routeDelimiter {
if pos := strings.IndexByte(search, delimiter); pos != -1 && (pos < nextPosition || nextPosition == -1) {
@ -259,7 +265,7 @@ func findNextRouteDelimiterPosition(search string) int {
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) {
lenKeys := len(p.params)
paramsPositions := getAllocFreeParamsPos(lenKeys)
@ -288,6 +294,9 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
if !segment.IsOptional && i == 0 {
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
@ -318,7 +327,7 @@ func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool) {
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 {
size := len(paramsPositions)
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 {
// "/api/*/:param" - "/api/joker/batman/robin/1" -> "joker/batman/robin", "1"
// "/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"
endChar := segments[currIndex].EndChar
neededEndChars := 0
@ -364,10 +372,14 @@ func findWildcardParamLen(s string, segments []paramSeg, currIndex int) int {
}
// performance tricks
var paramsDummy = make([]string, 100000)
var paramsPosDummy = make([][2]int, 100000)
// creates predefined arrays that are used to match the request routes so that no allocations need to be made
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
// getAllocFreeParamsPos fetches a slice area from the predefined slice, which is currently not in use
func getAllocFreeParamsPos(allocLen int) [][2]int {
size := uint32(allocLen)
start := atomic.AddUint32(&startParamPosList, size)
@ -380,6 +392,8 @@ func getAllocFreeParamsPos(allocLen int) [][2]int {
paramsPositions := paramsPosDummy[start:allocLen:allocLen]
return paramsPositions
}
// getAllocFreeParams fetches a slice area from the predefined slice, which is currently not in use
func getAllocFreeParams(allocLen int) []string {
size := uint32(allocLen)
start := atomic.AddUint32(&startParamList, size)
@ -393,6 +407,8 @@ func getAllocFreeParams(allocLen int) []string {
return params
}
//endregion #-#-#-#-#-#-#-#-#-#-#-#-#-#
// HTTP methods and their unique INTs
var methodINT = map[string]int{
MethodGet: 0,

View File

@ -169,14 +169,17 @@ func Test_Utils_matchParams(t *testing.T) {
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/2", params: []string{"1", "2", ""}, match: true},
{url: "/api/1/2/3", params: []string{"1", "2", "3"}, match: true},
{url: "/api/", params: nil, match: false},
})
testCase("/api/:day.:month?.:year?", []testparams{
{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.2", params: []string{"1", "2", ""}, 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{
{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: nil, match: false},
{url: "/api/1-/-", params: nil, match: false},
{url: "/api/1-2", params: []string{"1", "2", ""}, match: true},
{url: "/api/1-2-3", params: []string{"1", "2", "3"}, match: true},
{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/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/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},