From 21643a007a2d2d90e1636ecd6b49f82560f4c939 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 16 Jan 2020 12:09:54 -0700 Subject: httpcaddyfile: Replace 'handler_order' option with 'order' This allows individual directives to be ordered relative to others, where order matters (for example HTTP handlers). Will primarily be useful when developing new directives, so you don't have to modify the Caddy source code. Can also be useful if you prefer that redir comes before rewrite, for example. Note that these are global options. The route directive can be used to give a specific order to a specific group of HTTP handler directives. --- caddyconfig/httpcaddyfile/directives.go | 15 ++++-- caddyconfig/httpcaddyfile/httptype.go | 22 ++------- caddyconfig/httpcaddyfile/options.go | 87 ++++++++++++++++++++++++++------- 3 files changed, 85 insertions(+), 39 deletions(-) (limited to 'caddyconfig/httpcaddyfile') diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go index e74a3fe..b866c7d 100644 --- a/caddyconfig/httpcaddyfile/directives.go +++ b/caddyconfig/httpcaddyfile/directives.go @@ -25,9 +25,9 @@ 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{ "rewrite", "strip_prefix", "strip_suffix", @@ -168,6 +168,8 @@ 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 @@ -185,7 +187,7 @@ func (h Helper) GroupRoutes(vals []ConfigValue) { // now that we know the group will have some effect, do it groupNum := *h.groupCounter - for i := 0; i < len(vals); i++ { + for i := range vals { if route, ok := vals[i].Value.(caddyhttp.Route); ok { route.Group = fmt.Sprintf("group%d", groupNum) vals[i].Value = route @@ -226,7 +228,12 @@ type ConfigValue struct { directive string } -func sortRoutes(handlers []ConfigValue, dirPositions map[string]int) { +func sortRoutes(handlers []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 diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go index 257e7be..6ed4e39 100644 --- a/caddyconfig/httpcaddyfile/httptype.go +++ b/caddyconfig/httpcaddyfile/httptype.go @@ -65,8 +65,8 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock, val, err = parseOptHTTPPort(disp) case "https_port": val, err = parseOptHTTPSPort(disp) - case "handler_order": - val, err = parseOptHandlerOrder(disp) + case "order": + val, err = parseOptOrder(disp) case "experimental_http3": val, err = parseOptExperimentalHTTP3(disp) case "storage": @@ -397,23 +397,9 @@ func (st *ServerType) serversFromPairings( siteSubroute.Routes = append(siteSubroute.Routes, cfgVal.Value.(caddyhttp.Route)) } - // set up each handler directive - the order of the handlers - // as they are added to the routes depends on user preference + // set up each handler directive, making sure to honor directive order dirRoutes := sblock.pile["route"] - handlerOrder, ok := options["handler_order"].([]string) - if !ok { - handlerOrder = defaultDirectiveOrder - } - if len(handlerOrder) == 1 && handlerOrder[0] == "appearance" { - handlerOrder = nil - } - if handlerOrder != nil { - dirPositions := make(map[string]int) - for i, dir := range handlerOrder { - dirPositions[dir] = i - } - sortRoutes(dirRoutes, dirPositions) - } + sortRoutes(dirRoutes) // add all the routes piled in from directives for _, r := range dirRoutes { diff --git a/caddyconfig/httpcaddyfile/options.go b/caddyconfig/httpcaddyfile/options.go index e87b30f..e81528e 100644 --- a/caddyconfig/httpcaddyfile/options.go +++ b/caddyconfig/httpcaddyfile/options.go @@ -58,27 +58,80 @@ func parseOptExperimentalHTTP3(d *caddyfile.Dispenser) (bool, error) { return true, nil } -func parseOptHandlerOrder(d *caddyfile.Dispenser) ([]string, error) { - if !d.Next() { - return nil, d.ArgErr() - } - order := d.RemainingArgs() - if len(order) == 1 && order[0] == "appearance" { - return []string{"appearance"}, nil - } - if len(order) > 0 && d.NextBlock(0) { - return nil, d.Err("cannot open block if there are arguments") - } - for d.NextBlock(0) { - order = append(order, d.Val()) +func parseOptOrder(d *caddyfile.Dispenser) ([]string, error) { + newOrder := directiveOrder + + for d.Next() { + // get directive name + if !d.Next() { + return nil, d.ArgErr() + } + dirName := d.Val() + if _, ok := registeredDirectives[dirName]; !ok { + return nil, fmt.Errorf("%s is not a registered directive", dirName) + } + + // get positional token + if !d.Next() { + return nil, d.ArgErr() + } + pos := d.Val() + + // if directive exists, first remove it + for i, d := range newOrder { + if d == dirName { + newOrder = append(newOrder[:i], newOrder[i+1:]...) + break + } + } + + // act on the positional + switch pos { + case "first": + newOrder = append([]string{dirName}, newOrder...) + if d.NextArg() { + return nil, d.ArgErr() + } + directiveOrder = newOrder + return newOrder, nil + case "last": + newOrder = append(newOrder, dirName) + if d.NextArg() { + return nil, d.ArgErr() + } + directiveOrder = newOrder + return newOrder, nil + case "before": + case "after": + default: + return nil, fmt.Errorf("unknown positional '%s'", pos) + } + + // get name of other directive + if !d.NextArg() { + return nil, d.ArgErr() + } + otherDir := d.Val() if d.NextArg() { return nil, d.ArgErr() } + + // insert directive into proper position + for i, d := range newOrder { + if d == otherDir { + if pos == "before" { + newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...) + } else if pos == "after" { + newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...) + } + break + } + } } - if len(order) == 0 { - return nil, d.ArgErr() - } - return order, nil + + directiveOrder = newOrder + + return newOrder, nil } func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) { -- cgit v1.2.3