mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 18:17:08 +08: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:
		@ -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
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user