From cd9317e5df6210c0530dd30ffc817dc07f9c7c0c Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Wed, 6 May 2020 19:41:37 -0600 Subject: httpcaddyfile: Fix route ordering bug https://caddy.community/t/cant-get-simple-alias-to-work/7911/8?u=matt This removes an optimization where we amortized path matcher decoding. The decoded matchers were index by... position... which obviously changes during sorting. Duh. Anyway, sorting is sliiightly slower now but the Caddyfile is not really CPU-sensitive, so this is fine. --- caddyconfig/httpcaddyfile/directives.go | 74 +++++++++++++++------------------ 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go index ff248bf..ac401e1 100644 --- a/caddyconfig/httpcaddyfile/directives.go +++ b/caddyconfig/httpcaddyfile/directives.go @@ -276,53 +276,45 @@ func sortRoutes(routes []ConfigValue) { dirPositions[dir] = i } - // while we are sorting, we will need to decode a route's path matcher - // in order to sub-sort by path length; we can amortize this operation - // for efficiency by storing the decoded matchers in a slice - decodedMatchers := make([]caddyhttp.MatchPath, len(routes)) - sort.SliceStable(routes, func(i, j int) bool { + // if the directives are different, just use the established directive order iDir, jDir := routes[i].directive, routes[j].directive - if iDir == jDir { - // directives are the same; sub-sort by path matcher length - // if there's only one matcher set and one path (common case) - iRoute, ok := routes[i].Value.(caddyhttp.Route) - if !ok { - return false - } - jRoute, ok := routes[j].Value.(caddyhttp.Route) - if !ok { - return false - } + if iDir != jDir { + return dirPositions[iDir] < dirPositions[jDir] + } - // use already-decoded matcher, or decode if it's the first time seeing it - iPM, jPM := decodedMatchers[i], decodedMatchers[j] - if iPM == nil && len(iRoute.MatcherSetsRaw) == 1 { - var pathMatcher caddyhttp.MatchPath - _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &pathMatcher) - decodedMatchers[i] = pathMatcher - iPM = pathMatcher - } - if jPM == nil && len(jRoute.MatcherSetsRaw) == 1 { - var pathMatcher caddyhttp.MatchPath - _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &pathMatcher) - decodedMatchers[j] = pathMatcher - jPM = pathMatcher - } + // directives are the same; sub-sort by path matcher length if there's + // only one matcher set and one path (this is a very common case and + // usually -- but not always -- helpful/expected, oh well; user can + // always take manual control of order using handler or route blocks) + iRoute, ok := routes[i].Value.(caddyhttp.Route) + if !ok { + return false + } + jRoute, ok := routes[j].Value.(caddyhttp.Route) + if !ok { + return false + } - // sort by longer path (more specific) first; missing - // path matchers are treated as zero-length paths - var iPathLen, jPathLen int - if iPM != nil { - iPathLen = len(iPM[0]) - } - if jPM != nil { - jPathLen = len(jPM[0]) - } - return iPathLen > jPathLen + // decode the path matchers, if there is just one of them + var iPM, jPM caddyhttp.MatchPath + if len(iRoute.MatcherSetsRaw) == 1 { + _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &iPM) + } + if len(jRoute.MatcherSetsRaw) == 1 { + _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &jPM) } - return dirPositions[iDir] < dirPositions[jDir] + // sort by longer path (more specific) first; missing path + // matchers or multi-matchers are treated as zero-length paths + var iPathLen, jPathLen int + if len(iPM) > 0 { + iPathLen = len(iPM[0]) + } + if len(jPM) > 0 { + jPathLen = len(jPM[0]) + } + return iPathLen > jPathLen }) } -- cgit v1.2.3