summaryrefslogtreecommitdiff
path: root/caddyconfig/httpcaddyfile/directives.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2020-01-15 13:51:12 -0700
committerMatthew Holt <mholt@users.noreply.github.com>2020-01-15 13:51:12 -0700
commita66f461201ae2b7e724b3067f74019bdf44f834e (patch)
treeba0bf0ef6f89dbd5207c87595e22d36b6b304c9d /caddyconfig/httpcaddyfile/directives.go
parent07ad4655db5d37635edf36fa91d997d166d306b0 (diff)
caddyfile: Sort site subroutes by key specificity, and make exclusive
In the v1 Caddyfile, only the first matching site definition would be used, so setting these `Terminal: true` ensures that only the first matching one is used in v2, too. We also have to sort by key specificity... Caddy 1 had a special data structure for selecting the most specific site definition, but we don't have that structure in v2, so we need to sort by length (of host and path, separately). For blocks where more than one key is present, we choose the longest host and path (independently, need not be from same key) by which to sort.
Diffstat (limited to 'caddyconfig/httpcaddyfile/directives.go')
-rw-r--r--caddyconfig/httpcaddyfile/directives.go43
1 files changed, 43 insertions, 0 deletions
diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go
index 19ecb26..50e27d6 100644
--- a/caddyconfig/httpcaddyfile/directives.go
+++ b/caddyconfig/httpcaddyfile/directives.go
@@ -16,6 +16,7 @@ package httpcaddyfile
import (
"encoding/json"
+ "sort"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -197,6 +198,48 @@ type ConfigValue struct {
directive string
}
+func sortRoutes(handlers []ConfigValue, dirPositions map[string]int) {
+ // 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(handlers))
+
+ sort.SliceStable(handlers, func(i, j int) bool {
+ iDir, jDir := handlers[i].directive, handlers[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 := handlers[i].Value.(caddyhttp.Route)
+ jRoute := handlers[j].Value.(caddyhttp.Route)
+
+ if len(iRoute.MatcherSetsRaw) == 1 && len(jRoute.MatcherSetsRaw) == 1 {
+ // use already-decoded matcher, or decode if it's the first time seeing it
+ iPM, jPM := decodedMatchers[i], decodedMatchers[j]
+ if iPM == nil {
+ var pathMatcher caddyhttp.MatchPath
+ _ = json.Unmarshal(iRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
+ decodedMatchers[i] = pathMatcher
+ iPM = pathMatcher
+ }
+ if jPM == nil {
+ var pathMatcher caddyhttp.MatchPath
+ _ = json.Unmarshal(jRoute.MatcherSetsRaw[0]["path"], &pathMatcher)
+ decodedMatchers[j] = pathMatcher
+ jPM = pathMatcher
+ }
+
+ // if there is only one path in the matcher, sort by
+ // longer path (more specific) first
+ if len(iPM) == 1 && len(jPM) == 1 {
+ return len(iPM[0]) > len(jPM[0])
+ }
+ }
+ }
+
+ return dirPositions[iDir] < dirPositions[jDir]
+ })
+}
+
// serverBlock pairs a Caddyfile server block
// with a "pile" of config values, keyed by class
// name.