summaryrefslogtreecommitdiff
path: root/caddyconfig/httpcaddyfile/directives.go
diff options
context:
space:
mode:
Diffstat (limited to 'caddyconfig/httpcaddyfile/directives.go')
-rw-r--r--caddyconfig/httpcaddyfile/directives.go124
1 files changed, 109 insertions, 15 deletions
diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go
index 19ecb26..7acdb8c 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"
@@ -23,27 +24,45 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
-// defaultDirectiveOrder specifies the order
+// directiveOrder specifies the order
// to apply directives in HTTP routes.
-var defaultDirectiveOrder = []string{
+var directiveOrder = []string{
+ "root",
+
+ "redir",
"rewrite",
+
"strip_prefix",
"strip_suffix",
"uri_replace",
"try_files",
+
"basicauth",
"headers",
"request_header",
"encode",
"templates",
+
+ "handle",
"route",
- "redir",
+
"respond",
"reverse_proxy",
"php_fastcgi",
"file_server",
}
+// directiveIsOrdered returns true if dir is
+// a known, ordered (sorted) directive.
+func directiveIsOrdered(dir string) bool {
+ for _, d := range directiveOrder {
+ if d == dir {
+ return true
+ }
+ }
+ return false
+}
+
// RegisterDirective registers a unique directive dir with an
// associated unmarshaling (setup) function. When directive dir
// is encountered in a Caddyfile, setupFunc will be called to
@@ -92,10 +111,11 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
// Caddyfile tokens.
type Helper struct {
*caddyfile.Dispenser
- options map[string]interface{}
- warnings *[]caddyconfig.Warning
- matcherDefs map[string]caddy.ModuleMap
- parentBlock caddyfile.ServerBlock
+ options map[string]interface{}
+ warnings *[]caddyconfig.Warning
+ matcherDefs map[string]caddy.ModuleMap
+ parentBlock caddyfile.ServerBlock
+ groupCounter counter
}
// Option gets the option keyed by name.
@@ -127,8 +147,8 @@ func (h Helper) JSON(val interface{}) json.RawMessage {
return caddyconfig.JSON(val, h.warnings)
}
-// MatcherToken assumes the current token is (possibly) a matcher, and
-// if so, returns the matcher set along with a true value. If the current
+// MatcherToken assumes the next argument token is (possibly) a matcher,
+// and if so, returns the matcher set along with a true value. If the next
// token is not a matcher, nil and false is returned. Note that a true
// value may be returned with a nil matcher set if it is a catch-all.
func (h Helper) MatcherToken() (caddy.ModuleMap, bool, error) {
@@ -165,18 +185,39 @@ func (h Helper) NewRoute(matcherSet caddy.ModuleMap,
}
}
+// GroupRoutes adds the routes (caddyhttp.Route type) in vals to the
+// same group, if there is more than one route in vals.
+func (h Helper) GroupRoutes(vals []ConfigValue) {
+ // ensure there's at least two routes; group of one is pointless
+ var count int
+ for _, v := range vals {
+ if _, ok := v.Value.(caddyhttp.Route); ok {
+ count++
+ if count > 1 {
+ break
+ }
+ }
+ }
+ if count < 2 {
+ return
+ }
+
+ // now that we know the group will have some effect, do it
+ groupName := h.groupCounter.nextGroup()
+ for i := range vals {
+ if route, ok := vals[i].Value.(caddyhttp.Route); ok {
+ route.Group = groupName
+ vals[i].Value = route
+ }
+ }
+}
+
// NewBindAddresses returns config values relevant to adding
// listener bind addresses to the config.
func (h Helper) NewBindAddresses(addrs []string) []ConfigValue {
return []ConfigValue{{Class: "bind", Value: addrs}}
}
-// NewVarsRoute returns config values relevant to adding a
-// "vars" wrapper route to the config.
-func (h Helper) NewVarsRoute(route caddyhttp.Route) []ConfigValue {
- return []ConfigValue{{Class: "var", Value: route}}
-}
-
// ConfigValue represents a value to be added to the final
// configuration, or a value to be consulted when building
// the final configuration.
@@ -197,6 +238,59 @@ type ConfigValue struct {
directive string
}
+func sortRoutes(routes []ConfigValue) {
+ dirPositions := make(map[string]int)
+ for i, dir := range directiveOrder {
+ 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 {
+ 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 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.