mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 14:13:48 +03:00
httpcaddyfile: Add RegisterDirectiveOrder
function for plugin authors (#5865)
* httpcaddyfile: Add `RegisterDirectiveOrder` function for plugin authors * Set up Positional enum * Linter doesn't like a switch on an enum with default * Update caddyconfig/httpcaddyfile/directives.go Co-authored-by: Matt Holt <mholt@users.noreply.github.com> --------- Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
69290d232d
commit
258d906140
2 changed files with 89 additions and 15 deletions
|
@ -27,18 +27,25 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// directiveOrder specifies the order
|
// defaultDirectiveOrder specifies the default order
|
||||||
// to apply directives in HTTP routes.
|
// to apply directives in HTTP routes. This must only
|
||||||
|
// consist of directives that are included in Caddy's
|
||||||
|
// standard distribution.
|
||||||
//
|
//
|
||||||
// The root directive goes first in case rewrites or
|
// e.g. The 'root' directive goes near the start in
|
||||||
// redirects depend on existence of files, i.e. the
|
// case rewrites or redirects depend on existence of
|
||||||
// file matcher, which must know the root first.
|
// files, i.e. the file matcher, which must know the
|
||||||
|
// root first.
|
||||||
//
|
//
|
||||||
// The header directive goes second so that headers
|
// e.g. The 'header' directive goes before 'redir' so
|
||||||
// can be manipulated before doing redirects.
|
// that headers can be manipulated before doing redirects.
|
||||||
var directiveOrder = []string{
|
//
|
||||||
|
// e.g. The 'respond' directive is near the end because it
|
||||||
|
// writes a response and terminates the middleware chain.
|
||||||
|
var defaultDirectiveOrder = []string{
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
||||||
|
// set variables that may be used by other directives
|
||||||
"map",
|
"map",
|
||||||
"vars",
|
"vars",
|
||||||
"fs",
|
"fs",
|
||||||
|
@ -85,6 +92,11 @@ var directiveOrder = []string{
|
||||||
"acme_server",
|
"acme_server",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// directiveOrder specifies the order to apply directives
|
||||||
|
// in HTTP routes, after being modified by either the
|
||||||
|
// plugins or by the user via the "order" global option.
|
||||||
|
var directiveOrder = defaultDirectiveOrder
|
||||||
|
|
||||||
// directiveIsOrdered returns true if dir is
|
// directiveIsOrdered returns true if dir is
|
||||||
// a known, ordered (sorted) directive.
|
// a known, ordered (sorted) directive.
|
||||||
func directiveIsOrdered(dir string) bool {
|
func directiveIsOrdered(dir string) bool {
|
||||||
|
@ -131,6 +143,58 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterDirectiveOrder registers the default order for a
|
||||||
|
// directive from a plugin.
|
||||||
|
//
|
||||||
|
// This is useful when a plugin has a well-understood place
|
||||||
|
// it should run in the middleware pipeline, and it allows
|
||||||
|
// users to avoid having to define the order themselves.
|
||||||
|
//
|
||||||
|
// The directive dir may be placed in the position relative
|
||||||
|
// to ('before' or 'after') a directive included in Caddy's
|
||||||
|
// standard distribution. It cannot be relative to another
|
||||||
|
// plugin's directive.
|
||||||
|
//
|
||||||
|
// EXPERIMENTAL: This API may change or be removed.
|
||||||
|
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
|
||||||
|
// check if directive was already ordered
|
||||||
|
if directiveIsOrdered(dir) {
|
||||||
|
panic("directive '" + dir + "' already ordered")
|
||||||
|
}
|
||||||
|
|
||||||
|
if position != Before && position != After {
|
||||||
|
panic("the 2nd argument must be either 'before' or 'after', got '" + position + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if directive exists in standard distribution, since
|
||||||
|
// we can't allow plugins to depend on one another; we can't
|
||||||
|
// guarantee the order that plugins are loaded in.
|
||||||
|
foundStandardDir := false
|
||||||
|
for _, d := range defaultDirectiveOrder {
|
||||||
|
if d == standardDir {
|
||||||
|
foundStandardDir = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundStandardDir {
|
||||||
|
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert directive into proper position
|
||||||
|
newOrder := directiveOrder
|
||||||
|
for i, d := range newOrder {
|
||||||
|
if d != standardDir {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if position == Before {
|
||||||
|
newOrder = append(newOrder[:i], append([]string{dir}, newOrder[i:]...)...)
|
||||||
|
} else if position == After {
|
||||||
|
newOrder = append(newOrder[:i+1], append([]string{dir}, newOrder[i+1:]...)...)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
directiveOrder = newOrder
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterGlobalOption registers a unique global option opt with
|
// RegisterGlobalOption registers a unique global option opt with
|
||||||
// an associated unmarshaling (setup) function. When the global
|
// an associated unmarshaling (setup) function. When the global
|
||||||
// option opt is encountered in a Caddyfile, setupFunc will be
|
// option opt is encountered in a Caddyfile, setupFunc will be
|
||||||
|
@ -555,6 +619,16 @@ func (sb serverBlock) isAllHTTP() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Positional are the supported modes for ordering directives.
|
||||||
|
type Positional string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Before Positional = "before"
|
||||||
|
After Positional = "after"
|
||||||
|
First Positional = "first"
|
||||||
|
Last Positional = "last"
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// UnmarshalFunc is a function which can unmarshal Caddyfile
|
// UnmarshalFunc is a function which can unmarshal Caddyfile
|
||||||
// tokens into zero or more config values using a Helper type.
|
// tokens into zero or more config values using a Helper type.
|
||||||
|
|
|
@ -107,7 +107,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
if !d.Next() {
|
if !d.Next() {
|
||||||
return nil, d.ArgErr()
|
return nil, d.ArgErr()
|
||||||
}
|
}
|
||||||
pos := d.Val()
|
pos := Positional(d.Val())
|
||||||
|
|
||||||
newOrder := directiveOrder
|
newOrder := directiveOrder
|
||||||
|
|
||||||
|
@ -121,22 +121,22 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
|
|
||||||
// act on the positional
|
// act on the positional
|
||||||
switch pos {
|
switch pos {
|
||||||
case "first":
|
case First:
|
||||||
newOrder = append([]string{dirName}, newOrder...)
|
newOrder = append([]string{dirName}, newOrder...)
|
||||||
if d.NextArg() {
|
if d.NextArg() {
|
||||||
return nil, d.ArgErr()
|
return nil, d.ArgErr()
|
||||||
}
|
}
|
||||||
directiveOrder = newOrder
|
directiveOrder = newOrder
|
||||||
return newOrder, nil
|
return newOrder, nil
|
||||||
case "last":
|
case Last:
|
||||||
newOrder = append(newOrder, dirName)
|
newOrder = append(newOrder, dirName)
|
||||||
if d.NextArg() {
|
if d.NextArg() {
|
||||||
return nil, d.ArgErr()
|
return nil, d.ArgErr()
|
||||||
}
|
}
|
||||||
directiveOrder = newOrder
|
directiveOrder = newOrder
|
||||||
return newOrder, nil
|
return newOrder, nil
|
||||||
case "before":
|
case Before:
|
||||||
case "after":
|
case After:
|
||||||
default:
|
default:
|
||||||
return nil, d.Errf("unknown positional '%s'", pos)
|
return nil, d.Errf("unknown positional '%s'", pos)
|
||||||
}
|
}
|
||||||
|
@ -153,9 +153,9 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
// insert directive into proper position
|
// insert directive into proper position
|
||||||
for i, d := range newOrder {
|
for i, d := range newOrder {
|
||||||
if d == otherDir {
|
if d == otherDir {
|
||||||
if pos == "before" {
|
if pos == Before {
|
||||||
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
|
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
|
||||||
} else if pos == "after" {
|
} else if pos == After {
|
||||||
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
|
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in a new issue