Go API Documentation

github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile

No package summary is available.

Package

Files: 9. Third party imports: 4. Imports from organisation: 1. Tests: 0. Benchmarks: 0.

Constants

const (
	Before	Positional	= "before"
	After	Positional	= "after"
	First	Positional	= "first"
	Last	Positional	= "last"
)
const (
	matcherPrefix	= "@"
	namedRouteKey	= "named_route"
)

Vars

Interface guard

var _ caddyfile.ServerType = (*ServerType)(nil)

defaultDirectiveOrder specifies the default order to apply directives in HTTP routes. This must only consist of directives that are included in Caddy's standard distribution.

e.g. The 'root' directive goes near the start in case rewrites or redirects depend on existence of files, i.e. the file matcher, which must know the root first.

e.g. The 'header' directive goes before 'redir' so that headers can be manipulated before doing redirects.

e.g. The 'respond' directive is near the end because it writes a response and terminates the middleware chain.

var defaultDirectiveOrder = []string{
	"tracing",

	// set variables that may be used by other directives
	"map",
	"vars",
	"fs",
	"root",
	"log_append",
	"skip_log",	// TODO: deprecated, renamed to log_skip
	"log_skip",
	"log_name",

	"header",
	"copy_response_headers",	// only in reverse_proxy's handle_response
	"request_body",

	"redir",

	// incoming request manipulation
	"method",
	"rewrite",
	"uri",
	"try_files",

	// middleware handlers; some wrap responses
	"basicauth",	// TODO: deprecated, renamed to basic_auth
	"basic_auth",
	"forward_auth",
	"request_header",
	"encode",
	"push",
	"intercept",
	"templates",

	// special routing & dispatching directives
	"invoke",
	"handle",
	"handle_path",
	"route",

	// handlers that typically respond to requests
	"abort",
	"error",
	"copy_response",	// only in reverse_proxy's handle_response
	"respond",
	"metrics",
	"reverse_proxy",
	"php_fastcgi",
	"file_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
var registeredDirectives = make(map[string]UnmarshalFunc)
var registeredGlobalOptions = make(map[string]UnmarshalGlobalFunc)

Types

Address

Address represents a site address. It contains the original input value, and the component parts of an address. The component parts may be updated to the correct values as setup proceeds, but the original value should never be changed.

The Host field must be in a normalized form.

type Address struct {
	Original, Scheme, Host, Port, Path string
}

App

App represents the configuration for a non-standard Caddy app module (e.g. third-party plugin) which was parsed from a global options block.

type App struct {
	// The JSON key for the app being configured
	Name	string

	// The raw app config as JSON
	Value	json.RawMessage
}

ComplexShorthandReplacer

This type doesn't have documentation.

type ComplexShorthandReplacer struct {
	search	*regexp.Regexp
	replace	string
}

ConfigValue

ConfigValue represents a value to be added to the final configuration, or a value to be consulted when building the final configuration.

type ConfigValue struct {
	// The kind of value this is. As the config is
	// being built, the adapter will look in the
	// "pile" for values belonging to a certain
	// class when it is setting up a certain part
	// of the config. The associated value will be
	// type-asserted and placed accordingly.
	Class	string

	// The value to be used when building the config.
	// Generally its type is associated with the
	// name of the Class.
	Value	any

	directive	string
}

Helper

Helper is a type which helps setup a value from Caddyfile tokens.

type Helper struct {
	*caddyfile.Dispenser
	// State stores intermediate variables during caddyfile adaptation.
	State		map[string]any
	options		map[string]any
	warnings	*[]caddyconfig.Warning
	matcherDefs	map[string]caddy.ModuleMap
	parentBlock	caddyfile.ServerBlock
	groupCounter	counter
}

Positional

Positional are the supported modes for ordering directives.

type Positional string

ServerType

ServerType can set up a config from an HTTP Caddyfile.

type ServerType struct{}

ShorthandReplacer

This type doesn't have documentation.

type ShorthandReplacer struct {
	complex	[]ComplexShorthandReplacer
	simple	*strings.Replacer
}

UnmarshalFunc, UnmarshalHandlerFunc, UnmarshalGlobalFunc

This type doesn't have documentation.

type (
	// UnmarshalFunc is a function which can unmarshal Caddyfile
	// tokens into zero or more config values using a Helper type.
	// These are passed in a call to RegisterDirective.
	UnmarshalFunc	func(h Helper) ([]ConfigValue, error)

	// UnmarshalHandlerFunc is like UnmarshalFunc, except the
	// output of the unmarshaling is an HTTP handler. This
	// function does not need to deal with HTTP request matching
	// which is abstracted away. Since writing HTTP handlers
	// with Caddyfile support is very common, this is a more
	// convenient way to add a handler to the chain since a lot
	// of the details common to HTTP handlers are taken care of
	// for you. These are passed to a call to
	// RegisterHandlerDirective.
	UnmarshalHandlerFunc	func(h Helper) (caddyhttp.MiddlewareHandler, error)

	// UnmarshalGlobalFunc is a function which can unmarshal Caddyfile
	// tokens from a global option. It is passed the tokens to parse and
	// existing value from the previous instance of this global option
	// (if any). It returns the value to associate with this global option.
	UnmarshalGlobalFunc	func(d *caddyfile.Dispenser, existingVal any) (any, error)
)

acmeCapable

This type doesn't have documentation.

type acmeCapable interface{ GetACMEIssuer() *caddytls.ACMEIssuer }

addressWithProtocols

addressWithProtocols associates a listen address with the protocols to serve it with

type addressWithProtocols struct {
	address		string
	protocols	[]string
}

addressesWithProtocols

addressesWithProtocols associates a list of listen addresses with a list of protocols to serve them with

type addressesWithProtocols struct {
	addresses	[]string
	protocols	[]string
}

counter

This type doesn't have documentation.

type counter struct {
	n *int
}

namedCustomLog

This type doesn't have documentation.

type namedCustomLog struct {
	name		string
	hostnames	[]string
	log		*caddy.CustomLog
	noHostname	bool
}

sbAddrAssociation

sbAddrAssociation is a mapping from a list of addresses with protocols, and a list of server blocks that are served on those addresses.

type sbAddrAssociation struct {
	addressesWithProtocols	[]addressWithProtocols
	serverBlocks		[]serverBlock
}

serverBlock

serverBlock pairs a Caddyfile server block with a "pile" of config values, keyed by class name, as well as its parsed keys for convenience.

type serverBlock struct {
	block		caddyfile.ServerBlock
	pile		map[string][]ConfigValue	// config values obtained from directives
	parsedKeys	[]Address
}

serverOptions

serverOptions collects server config overrides parsed from Caddyfile global options

type serverOptions struct {
	// If set, will only apply these options to servers that contain a
	// listener address that matches exactly. If empty, will apply to all
	// servers that were not already matched by another serverOptions.
	ListenerAddress	string

	// These will all map 1:1 to the caddyhttp.Server struct
	Name			string
	ListenerWrappersRaw	[]json.RawMessage
	ReadTimeout		caddy.Duration
	ReadHeaderTimeout	caddy.Duration
	WriteTimeout		caddy.Duration
	IdleTimeout		caddy.Duration
	KeepAliveInterval	caddy.Duration
	MaxHeaderBytes		int
	EnableFullDuplex	bool
	Protocols		[]string
	StrictSNIHost		*bool
	TrustedProxiesRaw	json.RawMessage
	TrustedProxiesStrict	int
	ClientIPHeaders		[]string
	ShouldLogCredentials	bool
	Metrics			*caddyhttp.Metrics
	Trace			bool	// TODO: EXPERIMENTAL
}

Functions

func NewShorthandReplacer

func NewShorthandReplacer() ShorthandReplacer {
	// replace shorthand placeholders (which are convenient
	// when writing a Caddyfile) with their actual placeholder
	// identifiers or variable names
	replacer := strings.NewReplacer(placeholderShorthands()...)

	// these are placeholders that allow a user-defined final
	// parameters, but we still want to provide a shorthand
	// for those, so we use a regexp to replace
	regexpReplacements := []ComplexShorthandReplacer{
		{regexp.MustCompile(`{header\.([\w-]*)}`), "{http.request.header.$1}"},
		{regexp.MustCompile(`{cookie\.([\w-]*)}`), "{http.request.cookie.$1}"},
		{regexp.MustCompile(`{labels\.([\w-]*)}`), "{http.request.host.labels.$1}"},
		{regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
		{regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
		{regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
		{regexp.MustCompile(`{re\.([\w-\.]*)}`), "{http.regexp.$1}"},
		{regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
		{regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
		{regexp.MustCompile(`{resp\.([\w-\.]*)}`), "{http.intercept.$1}"},
		{regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
		{regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
	}

	return ShorthandReplacer{
		complex:	regexpReplacements,
		simple:		replacer,
	}
}

Cognitive complexity: 26, Cyclomatic complexity: 1

Uses: regexp.MustCompile, strings.NewReplacer.

func ParseAddress

ParseAddress parses an address string into a structured format with separate scheme, host, port, and path portions, as well as the original input string.

func ParseAddress(str string) (Address, error) {
	const maxLen = 4096
	if len(str) > maxLen {
		str = str[:maxLen]
	}
	remaining := strings.TrimSpace(str)
	a := Address{Original: remaining}

	// extract scheme
	splitScheme := strings.SplitN(remaining, "://", 2)
	switch len(splitScheme) {
	case 0:
		return a, nil
	case 1:
		remaining = splitScheme[0]
	case 2:
		a.Scheme = splitScheme[0]
		remaining = splitScheme[1]
	}

	// extract host and port
	hostSplit := strings.SplitN(remaining, "/", 2)
	if len(hostSplit) > 0 {
		host, port, err := net.SplitHostPort(hostSplit[0])
		if err != nil {
			host, port, err = net.SplitHostPort(hostSplit[0] + ":")
			if err != nil {
				host = hostSplit[0]
			}
		}
		a.Host = host
		a.Port = port
	}
	if len(hostSplit) == 2 {
		// all that remains is the path
		a.Path = "/" + hostSplit[1]
	}

	// make sure port is valid
	if a.Port != "" {
		if portNum, err := strconv.Atoi(a.Port); err != nil {
			return Address{}, fmt.Errorf("invalid port '%s': %v", a.Port, err)
		} else if portNum < 0 || portNum > 65535 {
			return Address{}, fmt.Errorf("port %d is out of range", portNum)
		}
	}

	return a, nil
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: fmt.Errorf, net.SplitHostPort, strconv.Atoi, strings.SplitN, strings.TrimSpace.

func ParseSegmentAsSubroute

ParseSegmentAsSubroute parses the segment such that its subdirectives are themselves treated as directives, from which a subroute is built and returned.

func ParseSegmentAsSubroute(h Helper) (caddyhttp.MiddlewareHandler, error) {
	allResults, err := parseSegmentAsConfig(h)
	if err != nil {
		return nil, err
	}

	return buildSubroute(allResults, h.groupCounter, true)
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func RegisterDirective

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 unmarshal its tokens.

func RegisterDirective(dir string, setupFunc UnmarshalFunc) {
	if _, ok := registeredDirectives[dir]; ok {
		panic("directive " + dir + " already registered")
	}
	registeredDirectives[dir] = setupFunc
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func RegisterDirectiveOrder

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 slices.Contains(directiveOrder, 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 := slices.Contains(defaultDirectiveOrder, standardDir)
	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
}

Cognitive complexity: 17, Cyclomatic complexity: 9

Uses: slices.Contains.

func RegisterGlobalOption

RegisterGlobalOption registers a unique global option opt with an associated unmarshaling (setup) function. When the global option opt is encountered in a Caddyfile, setupFunc will be called to unmarshal its tokens.

func RegisterGlobalOption(opt string, setupFunc UnmarshalGlobalFunc) {
	if _, ok := registeredGlobalOptions[opt]; ok {
		panic("global option " + opt + " already registered")
	}
	registeredGlobalOptions[opt] = setupFunc
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func RegisterHandlerDirective

RegisterHandlerDirective is like RegisterDirective, but for directives which specifically output only an HTTP handler. Directives registered with this function will always have an optional matcher token as the first argument.

func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
	RegisterDirective(dir, func(h Helper) ([]ConfigValue, error) {
		if !h.Next() {
			return nil, h.ArgErr()
		}

		matcherSet, err := h.ExtractMatcherSet()
		if err != nil {
			return nil, err
		}

		val, err := setupFunc(h)
		if err != nil {
			return nil, err
		}

		return h.NewRoute(matcherSet, val), nil
	})
}

Cognitive complexity: 7, Cyclomatic complexity: 4

func WasReplacedPlaceholderShorthand

WasReplacedPlaceholderShorthand checks if a token string was likely a replaced shorthand of the known Caddyfile placeholder replacement outputs. Useful to prevent some user-defined map output destinations from overlapping with one of the predefined shorthands.

func WasReplacedPlaceholderShorthand(token string) string {
	prev := ""
	for i, item := range placeholderShorthands() {
		// only look at every 2nd item, which is the replacement
		if i%2 == 0 {
			prev = item
			continue
		}
		if strings.Trim(token, "{}") == strings.Trim(item, "{}") {
			// we return the original shorthand so it
			// can be used for an error message
			return prev
		}
	}
	return ""
}

Cognitive complexity: 7, Cyclomatic complexity: 4

Uses: strings.Trim.

func (Address) Normalize

Normalize returns a normalized version of a.

func (a Address) Normalize() Address {
	path := a.Path

	// ensure host is normalized if it's an IP address
	host := strings.TrimSpace(a.Host)
	if ip, err := netip.ParseAddr(host); err == nil {
		if ip.Is6() && !ip.Is4() && !ip.Is4In6() {
			host = ip.String()
		}
	}

	return Address{
		Original:	a.Original,
		Scheme:		lowerExceptPlaceholders(a.Scheme),
		Host:		lowerExceptPlaceholders(host),
		Port:		a.Port,
		Path:		path,
	}
}

Cognitive complexity: 5, Cyclomatic complexity: 5

Uses: netip.ParseAddr, strings.TrimSpace.

func (Address) String

String returns a human-readable form of a. It will be a cleaned-up and filled-out URL string.

func (a Address) String() string {
	if a.Host == "" && a.Port == "" {
		return ""
	}
	scheme := a.Scheme
	if scheme == "" {
		if a.Port == strconv.Itoa(certmagic.HTTPSPort) {
			scheme = "https"
		} else {
			scheme = "http"
		}
	}
	s := scheme
	if s != "" {
		s += "://"
	}
	if a.Port != "" &&
		((scheme == "https" && a.Port != strconv.Itoa(caddyhttp.DefaultHTTPSPort)) ||
			(scheme == "http" && a.Port != strconv.Itoa(caddyhttp.DefaultHTTPPort))) {
		s += net.JoinHostPort(a.Host, a.Port)
	} else {
		s += a.Host
	}
	if a.Path != "" {
		s += a.Path
	}
	return s
}

Cognitive complexity: 11, Cyclomatic complexity: 6

Uses: caddyhttp.DefaultHTTPPort, caddyhttp.DefaultHTTPSPort, certmagic.HTTPSPort, net.JoinHostPort, strconv.Itoa.

func (Helper) Caddyfiles

Caddyfiles returns the list of config files from which tokens in the current server block were loaded.

func (h Helper) Caddyfiles() []string {
	// first obtain set of names of files involved
	// in this server block, without duplicates
	files := make(map[string]struct{})
	for _, segment := range h.parentBlock.Segments {
		for _, token := range segment {
			files[token.File] = struct{}{}
		}
	}
	// then convert the set into a slice
	filesSlice := make([]string, 0, len(files))
	for file := range files {
		filesSlice = append(filesSlice, file)
	}
	sort.Strings(filesSlice)
	return filesSlice
}

Cognitive complexity: 12, Cyclomatic complexity: 4

Uses: sort.Strings.

func (Helper) ExtractMatcherSet

ExtractMatcherSet is like MatcherToken, except this is a higher-level method that returns the matcher set described by the matcher token, or nil if there is none, and deletes the matcher token from the dispenser and resets it as if this look-ahead never happened. Useful when wrapping a route (one or more handlers) in a user-defined matcher.

func (h Helper) ExtractMatcherSet() (caddy.ModuleMap, error) {
	matcherSet, hasMatcher, err := h.MatcherToken()
	if err != nil {
		return nil, err
	}
	if hasMatcher {
		// strip matcher token; we don't need to
		// use the return value here because a
		// new dispenser should have been made
		// solely for this directive's tokens,
		// with no other uses of same slice
		h.Dispenser.Delete()
	}
	h.Dispenser.Reset()	// pretend this lookahead never happened
	return matcherSet, nil
}

Cognitive complexity: 4, Cyclomatic complexity: 3

func (Helper) GroupRoutes

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
		}
	}
}

Cognitive complexity: 14, Cyclomatic complexity: 7

Uses: caddyhttp.Route.

func (Helper) JSON

JSON converts val into JSON. Any errors are added to warnings.

func (h Helper) JSON(val any) json.RawMessage {
	return caddyconfig.JSON(val, h.warnings)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: caddyconfig.JSON.

func (Helper) MatcherToken

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) {
	if !h.NextArg() {
		return nil, false, nil
	}
	return matcherSetFromMatcherToken(h.Dispenser.Token(), h.matcherDefs, h.warnings)
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func (Helper) NewRoute

NewRoute returns config values relevant to creating a new HTTP route.

func (h Helper) NewRoute(matcherSet caddy.ModuleMap,
	handler caddyhttp.MiddlewareHandler,
) []ConfigValue {
	mod, err := caddy.GetModule(caddy.GetModuleID(handler))
	if err != nil {
		*h.warnings = append(*h.warnings, caddyconfig.Warning{
			File:		h.File(),
			Line:		h.Line(),
			Message:	err.Error(),
		})
		return nil
	}
	var matcherSetsRaw []caddy.ModuleMap
	if matcherSet != nil {
		matcherSetsRaw = append(matcherSetsRaw, matcherSet)
	}
	return []ConfigValue{
		{
			Class:	"route",
			Value: caddyhttp.Route{
				MatcherSetsRaw:	matcherSetsRaw,
				HandlersRaw:	[]json.RawMessage{caddyconfig.JSONModuleObject(handler, "handler", mod.ID.Name(), h.warnings)},
			},
		},
	}
}

Cognitive complexity: 9, Cyclomatic complexity: 3

Uses: caddyconfig.JSONModuleObject, caddyconfig.Warning, caddyhttp.Route, json.RawMessage.

func (Helper) Option

Option gets the option keyed by name.

func (h Helper) Option(name string) any {
	return h.options[name]
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (Helper) WithDispenser

WithDispenser returns a new instance based on d. All others Helper fields are copied, so typically maps are shared with this new instance.

func (h Helper) WithDispenser(d *caddyfile.Dispenser) Helper {
	h.Dispenser = d
	return h
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (ServerType) Setup

Setup makes a config from the tokens.

func (st ServerType) Setup(
	inputServerBlocks []caddyfile.ServerBlock,
	options map[string]any,
) (*caddy.Config, []caddyconfig.Warning, error) {
	var warnings []caddyconfig.Warning
	gc := counter{new(int)}
	state := make(map[string]any)

	// load all the server blocks and associate them with a "pile" of config values
	originalServerBlocks := make([]serverBlock, 0, len(inputServerBlocks))
	for _, sblock := range inputServerBlocks {
		for j, k := range sblock.Keys {
			if j == 0 && strings.HasPrefix(k.Text, "@") {
				return nil, warnings, fmt.Errorf("%s:%d: cannot define a matcher outside of a site block: '%s'", k.File, k.Line, k.Text)
			}
			if _, ok := registeredDirectives[k.Text]; ok {
				return nil, warnings, fmt.Errorf("%s:%d: parsed '%s' as a site address, but it is a known directive; directives must appear in a site block", k.File, k.Line, k.Text)
			}
		}
		originalServerBlocks = append(originalServerBlocks, serverBlock{
			block:	sblock,
			pile:	make(map[string][]ConfigValue),
		})
	}

	// apply any global options
	var err error
	originalServerBlocks, err = st.evaluateGlobalOptionsBlock(originalServerBlocks, options)
	if err != nil {
		return nil, warnings, err
	}

	// this will replace both static and user-defined placeholder shorthands
	// with actual identifiers used by Caddy
	replacer := NewShorthandReplacer()

	originalServerBlocks, err = st.extractNamedRoutes(originalServerBlocks, options, &warnings, replacer)
	if err != nil {
		return nil, warnings, err
	}

	for _, sb := range originalServerBlocks {
		for i := range sb.block.Segments {
			replacer.ApplyToSegment(&sb.block.Segments[i])
		}

		if len(sb.block.Keys) == 0 {
			return nil, warnings, fmt.Errorf("server block without any key is global configuration, and if used, it must be first")
		}

		// extract matcher definitions
		matcherDefs := make(map[string]caddy.ModuleMap)
		for _, segment := range sb.block.Segments {
			if dir := segment.Directive(); strings.HasPrefix(dir, matcherPrefix) {
				d := sb.block.DispenseDirective(dir)
				err := parseMatcherDefinitions(d, matcherDefs)
				if err != nil {
					return nil, warnings, err
				}
			}
		}

		// evaluate each directive ("segment") in this block
		for _, segment := range sb.block.Segments {
			dir := segment.Directive()

			if strings.HasPrefix(dir, matcherPrefix) {
				// matcher definitions were pre-processed
				continue
			}

			dirFunc, ok := registeredDirectives[dir]
			if !ok {
				tkn := segment[0]
				message := "%s:%d: unrecognized directive: %s"
				if !sb.block.HasBraces {
					message += "\nDid you mean to define a second site? If so, you must use curly braces around each site to separate their configurations."
				}
				return nil, warnings, fmt.Errorf(message, tkn.File, tkn.Line, dir)
			}

			h := Helper{
				Dispenser:	caddyfile.NewDispenser(segment),
				options:	options,
				warnings:	&warnings,
				matcherDefs:	matcherDefs,
				parentBlock:	sb.block,
				groupCounter:	gc,
				State:		state,
			}

			results, err := dirFunc(h)
			if err != nil {
				return nil, warnings, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
			}

			dir = normalizeDirectiveName(dir)

			for _, result := range results {
				result.directive = dir
				sb.pile[result.Class] = append(sb.pile[result.Class], result)
			}

			// specially handle named routes that were pulled out from
			// the invoke directive, which could be nested anywhere within
			// some subroutes in this directive; we add them to the pile
			// for this server block
			if state[namedRouteKey] != nil {
				for name := range state[namedRouteKey].(map[string]struct{}) {
					result := ConfigValue{Class: namedRouteKey, Value: name}
					sb.pile[result.Class] = append(sb.pile[result.Class], result)
				}
				state[namedRouteKey] = nil
			}
		}
	}

	// map
	sbmap, err := st.mapAddressToProtocolToServerBlocks(originalServerBlocks, options)
	if err != nil {
		return nil, warnings, err
	}

	// reduce
	pairings := st.consolidateAddrMappings(sbmap)

	// each pairing of listener addresses to list of server
	// blocks is basically a server definition
	servers, err := st.serversFromPairings(pairings, options, &warnings, gc)
	if err != nil {
		return nil, warnings, err
	}

	// hoist the metrics config from per-server to global
	metrics, _ := options["metrics"].(*caddyhttp.Metrics)
	for _, s := range servers {
		if s.Metrics != nil {
			metrics = cmp.Or[*caddyhttp.Metrics](metrics, &caddyhttp.Metrics{})
			metrics = &caddyhttp.Metrics{
				PerHost: metrics.PerHost || s.Metrics.PerHost,
			}
			s.Metrics = nil	// we don't need it anymore
		}
	}

	// now that each server is configured, make the HTTP app
	httpApp := caddyhttp.App{
		HTTPPort:	tryInt(options["http_port"], &warnings),
		HTTPSPort:	tryInt(options["https_port"], &warnings),
		GracePeriod:	tryDuration(options["grace_period"], &warnings),
		ShutdownDelay:	tryDuration(options["shutdown_delay"], &warnings),
		Metrics:	metrics,
		Servers:	servers,
	}

	// then make the TLS app
	tlsApp, warnings, err := st.buildTLSApp(pairings, options, warnings)
	if err != nil {
		return nil, warnings, err
	}

	// then make the PKI app
	pkiApp, warnings, err := st.buildPKIApp(pairings, options, warnings)
	if err != nil {
		return nil, warnings, err
	}

	// extract any custom logs, and enforce configured levels
	var customLogs []namedCustomLog
	var hasDefaultLog bool
	addCustomLog := func(ncl namedCustomLog) {
		if ncl.name == "" {
			return
		}
		if ncl.name == caddy.DefaultLoggerName {
			hasDefaultLog = true
		}
		if _, ok := options["debug"]; ok && ncl.log != nil && ncl.log.Level == "" {
			ncl.log.Level = zap.DebugLevel.CapitalString()
		}
		customLogs = append(customLogs, ncl)
	}

	// Apply global log options, when set
	if options["log"] != nil {
		for _, logValue := range options["log"].([]ConfigValue) {
			addCustomLog(logValue.Value.(namedCustomLog))
		}
	}

	if !hasDefaultLog {
		// if the default log was not customized, ensure we
		// configure it with any applicable options
		if _, ok := options["debug"]; ok {
			customLogs = append(customLogs, namedCustomLog{
				name:	caddy.DefaultLoggerName,
				log: &caddy.CustomLog{
					BaseLog: caddy.BaseLog{Level: zap.DebugLevel.CapitalString()},
				},
			})
		}
	}

	// Apply server-specific log options
	for _, p := range pairings {
		for _, sb := range p.serverBlocks {
			for _, clVal := range sb.pile["custom_log"] {
				addCustomLog(clVal.Value.(namedCustomLog))
			}
		}
	}

	// annnd the top-level config, then we're done!
	cfg := &caddy.Config{AppsRaw: make(caddy.ModuleMap)}

	// loop through the configured options, and if any of
	// them are an httpcaddyfile App, then we insert them
	// into the config as raw Caddy apps
	for _, opt := range options {
		if app, ok := opt.(App); ok {
			cfg.AppsRaw[app.Name] = app.Value
		}
	}

	// insert the standard Caddy apps into the config
	if len(httpApp.Servers) > 0 {
		cfg.AppsRaw["http"] = caddyconfig.JSON(httpApp, &warnings)
	}
	if !reflect.DeepEqual(tlsApp, &caddytls.TLS{CertificatesRaw: make(caddy.ModuleMap)}) {
		cfg.AppsRaw["tls"] = caddyconfig.JSON(tlsApp, &warnings)
	}
	if !reflect.DeepEqual(pkiApp, &caddypki.PKI{CAs: make(map[string]*caddypki.CA)}) {
		cfg.AppsRaw["pki"] = caddyconfig.JSON(pkiApp, &warnings)
	}
	if filesystems, ok := options["filesystem"].(caddy.Module); ok {
		cfg.AppsRaw["caddy.filesystems"] = caddyconfig.JSON(
			filesystems,
			&warnings)
	}

	if storageCvtr, ok := options["storage"].(caddy.StorageConverter); ok {
		cfg.StorageRaw = caddyconfig.JSONModuleObject(storageCvtr,
			"module",
			storageCvtr.(caddy.Module).CaddyModule().ID.Name(),
			&warnings)
	}
	if adminConfig, ok := options["admin"].(*caddy.AdminConfig); ok && adminConfig != nil {
		cfg.Admin = adminConfig
	}
	if pc, ok := options["persist_config"].(string); ok && pc == "off" {
		if cfg.Admin == nil {
			cfg.Admin = new(caddy.AdminConfig)
		}
		if cfg.Admin.Config == nil {
			cfg.Admin.Config = new(caddy.ConfigSettings)
		}
		cfg.Admin.Config.Persist = new(bool)
	}

	if len(customLogs) > 0 {
		if cfg.Logging == nil {
			cfg.Logging = &caddy.Logging{
				Logs: make(map[string]*caddy.CustomLog),
			}
		}

		// Add the default log first if defined, so that it doesn't
		// accidentally get re-created below due to the Exclude logic
		for _, ncl := range customLogs {
			if ncl.name == caddy.DefaultLoggerName && ncl.log != nil {
				cfg.Logging.Logs[caddy.DefaultLoggerName] = ncl.log
				break
			}
		}

		// Add the rest of the custom logs
		for _, ncl := range customLogs {
			if ncl.log == nil || ncl.name == caddy.DefaultLoggerName {
				continue
			}
			if ncl.name != "" {
				cfg.Logging.Logs[ncl.name] = ncl.log
			}
			// most users seem to prefer not writing access logs
			// to the default log when they are directed to a
			// file or have any other special customization
			if ncl.name != caddy.DefaultLoggerName && len(ncl.log.Include) > 0 {
				defaultLog, ok := cfg.Logging.Logs[caddy.DefaultLoggerName]
				if !ok {
					defaultLog = new(caddy.CustomLog)
					cfg.Logging.Logs[caddy.DefaultLoggerName] = defaultLog
				}
				defaultLog.Exclude = append(defaultLog.Exclude, ncl.log.Include...)

				// avoid duplicates by sorting + compacting
				sort.Strings(defaultLog.Exclude)
				defaultLog.Exclude = slices.Compact[[]string, string](defaultLog.Exclude)
			}
		}
		// we may have not actually added anything, so remove if empty
		if len(cfg.Logging.Logs) == 0 {
			cfg.Logging = nil
		}
	}

	return cfg, warnings, nil
}

Cognitive complexity: 146, Cyclomatic complexity: 67

Uses: caddyconfig.JSON, caddyconfig.JSONModuleObject, caddyconfig.Warning, caddyfile.NewDispenser, caddyhttp.App, caddyhttp.Metrics, caddypki.CA, caddypki.PKI, caddytls.TLS, cmp.Or, fmt.Errorf, reflect.DeepEqual, slices.Compact, sort.Strings, strings.HasPrefix, zap.DebugLevel.

func (ShorthandReplacer) ApplyToSegment

ApplyToSegment replaces shorthand placeholder to its full placeholder, understandable by Caddy.

func (s ShorthandReplacer) ApplyToSegment(segment *caddyfile.Segment) {
	if segment != nil {
		for i := 0; i < len(*segment); i++ {
			// simple string replacements
			(*segment)[i].Text = s.simple.Replace((*segment)[i].Text)
			// complex regexp replacements
			for _, r := range s.complex {
				(*segment)[i].Text = r.search.ReplaceAllString((*segment)[i].Text, r.replace)
			}
		}
	}
}

Cognitive complexity: 7, Cyclomatic complexity: 4

Private functions

func appendSubrouteToRouteList

appendSubrouteToRouteList appends the routes in subroute to the routeList, optionally qualified by matchers.

appendSubrouteToRouteList (routeList caddyhttp.RouteList, subroute *caddyhttp.Subroute, matcherSetsEnc []caddy.ModuleMap, p sbAddrAssociation, warnings *[]caddyconfig.Warning) caddyhttp.RouteList
References: caddyconfig.JSONModuleObject, caddyhttp.Route, json.RawMessage.

func applyServerOptions

applyServerOptions sets the server options on the appropriate servers

applyServerOptions (servers map[string]*caddyhttp.Server, options map[string]any, _ *[]caddyconfig.Warning) error
References: caddyhttp.ServerLogConfig, fmt.Errorf, slices.Contains, slices.IndexFunc.

func automationPolicyHasAllPublicNames

automationPolicyHasAllPublicNames returns true if all the names on the policy do NOT qualify for public certs OR are tailscale domains.

automationPolicyHasAllPublicNames (ap *caddytls.AutomationPolicy) bool
References: slices.ContainsFunc.

func automationPolicyIsSubset

automationPolicyIsSubset returns true if a's subjects are a subset of b's subjects.

automationPolicyIsSubset (a,b *caddytls.AutomationPolicy) bool
References: certmagic.MatchWildcard, slices.ContainsFunc.

func automationPolicyShadows

automationPolicyShadows returns the index of a policy that aps[i] shadows; in other words, for all policies after position i, if that policy covers the same subjects but is less specific, that policy's position is returned, or -1 if no shadowing is found. For example, if policy i is for "foo.example.com" and policy i+2 is for "*.example.com", then i+2 will be returned, since that policy is shadowed by i, which is in front.

automationPolicyShadows (i int, aps []*caddytls.AutomationPolicy) int

func buildSubroute

buildSubroute turns the config values, which are expected to be routes into a clean and orderly subroute that has all the routes within it.

buildSubroute (routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error)
References: caddyhttp.Route, caddyhttp.Subroute, fmt.Errorf, slices.Contains, sort.Strings.

func consolidateAutomationPolicies

consolidateAutomationPolicies combines automation policies that are the same, for a cleaner overall output.

consolidateAutomationPolicies (aps []*caddytls.AutomationPolicy) []*caddytls.AutomationPolicy
References: bytes.Equal, caddytls.AutomationPolicy, reflect.DeepEqual, slices.Contains, slices.Delete, sort.SliceStable.

func consolidateConnPolicies

consolidateConnPolicies sorts any catch-all policy to the end, removes empty TLS connection policies, and combines equivalent ones for a cleaner overall output.

consolidateConnPolicies (cps caddytls.ConnectionPolicies) (caddytls.ConnectionPolicies, error)
References: fmt.Errorf, reflect.DeepEqual, slices.Contains, sort.SliceStable.

func consolidateRoutes

consolidateRoutes combines routes with the same properties (same matchers, same Terminal and Group settings) for a cleaner overall output.

consolidateRoutes (routes caddyhttp.RouteList) caddyhttp.RouteList
References: reflect.DeepEqual.

func detectConflictingSchemes

detectConflictingSchemes (srv *caddyhttp.Server, serverBlocks []serverBlock, options map[string]any) error
References: caddyhttp.DefaultHTTPPort, caddyhttp.DefaultHTTPSPort, fmt.Errorf, strconv.Itoa.

func encodeMatcherSet

encodeMatcherSet (matchers map[string]caddyhttp.RequestMatcherWithError) (caddy.ModuleMap, error)
References: fmt.Errorf, json.Marshal.

func fillInGlobalACMEDefaults

fillInGlobalACMEDefaults (issuer certmagic.Issuer, options map[string]any) error
References: acme.EAB, caddyconfig.JSONModuleObject, caddytls.ChainPreference, caddytls.ChallengesConfig, caddytls.DNSChallengeConfig, caddytls.HTTPChallengeConfig, caddytls.TLSALPNChallengeConfig, slices.Contains.

func hostsCoveredByWildcard

hostsCoveredByWildcard (hosts []string, wildcards []string) bool
References: certmagic.MatchWildcard, strings.HasPrefix.

func init

init ()

func isTailscaleDomain

isTailscaleDomain (name string) bool
References: strings.HasSuffix, strings.ToLower.

func listenersUseAnyPortOtherThan

listenersUseAnyPortOtherThan returns true if there are any listeners in addresses that use a port which is not otherPort. Mostly borrowed from unexported method in caddyhttp package.

listenersUseAnyPortOtherThan (addresses []string, otherPort string) bool
References: strconv.Atoi.

func lowerExceptPlaceholders

lowerExceptPlaceholders lowercases s except within placeholders (substrings in non-escaped '{ }' spans). See https://github.com/caddyserver/caddy/issues/3264

lowerExceptPlaceholders (s string) string
References: strings.Builder, unicode.ToLower.

func matcherSetFromMatcherToken

matcherSetFromMatcherToken (tkn caddyfile.Token, matcherDefs map[string]caddy.ModuleMap, warnings *[]caddyconfig.Warning) (caddy.ModuleMap, bool, error)
References: caddyconfig.JSON, caddyhttp.MatchPath, fmt.Errorf, strings.HasPrefix.

func newBaseAutomationPolicy

newBaseAutomationPolicy returns a new TLS automation policy that gets its values from the global options map. It should be used as the base for any other automation policies. A nil policy (and no error) will be returned if there are no default/global options. However, if always is true, a non-nil value will always be returned (unless there is an error).

newBaseAutomationPolicy (options map[string]any, _ []caddyconfig.Warning, always bool) (*caddytls.AutomationPolicy, error)
References: caddytls.AutomationPolicy, caddytls.InternalIssuer, certmagic.Issuer, certmagic.OCSPConfig, fmt.Errorf.

func normalizeDirectiveName

normalizeDirectiveName ensures directives that should be sorted at the same level are named the same before sorting happens.

normalizeDirectiveName (directive string) string

func parseAbort

parseAbort parses the abort directive.

parseAbort (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.StaticResponse.

func parseBind

parseBind parses the bind directive. Syntax:

	bind <addresses...> [{
   protocols [h1|h2|h2c|h3] [...]
 }]

parseBind (h Helper) ([]ConfigValue, error)

func parseError

parseError parses the error directive.

parseError (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.StaticError.

func parseFilesystem

parseFilesystem parses the fs directive. Syntax:

fs <filesystem>

parseFilesystem (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.VarsMiddleware.

func parseHandle

parseHandle (h Helper) (caddyhttp.MiddlewareHandler, error)

func parseHandleErrors

parseHandleErrors (h Helper) ([]ConfigValue, error)
References: caddyhttp.MatchExpression, caddyhttp.Subroute, fmt.Sprintf, strconv.Atoi, strings.HasSuffix, strings.Join.

func parseInvoke

parseInvoke parses the invoke directive.

parseInvoke (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.Invoke.

func parseLog

parseLog parses the log directive. Syntax:

log <logger_name> {
    hostnames <hostnames...>
    output <writer_module> ...
    core   <core_module> ...
    format <encoder_module> ...
    level  <level>
}

parseLog (h Helper) ([]ConfigValue, error)

func parseLogHelper

parseLogHelper is used both for the parseLog directive within Server Blocks, as well as the global "log" option for configuring loggers at the global level. The parseAsGlobalOption parameter is used to distinguish any differing logic between the two.

parseLogHelper (h Helper, globalLogNames map[string]struct{}) ([]ConfigValue, error)
References: caddyconfig.JSONModuleObject, caddyfile.UnmarshalModule, fmt.Sprintf, reflect.DeepEqual, strconv.Atoi, time.ParseDuration, zapcore.Core, zapcore.Encoder.

func parseLogName

parseLogName parses the log_name directive. Syntax:

log_name <names...>

parseLogName (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.AccessLoggerNameVarKey, caddyhttp.VarsMiddleware.

func parseLogOptions

parseLogOptions parses the global log option. Syntax:

log [name] {
    output  <writer_module> ...
    format  <encoder_module> ...
    level   <level>
    include <namespaces...>
    exclude <namespaces...>
}

When the name argument is unspecified, this directive modifies the default logger.

parseLogOptions (d *caddyfile.Dispenser, existingVal any) (any, error)
References: caddyconfig.Warning.

func parseLogSkip

parseLogSkip parses the log_skip directive. Syntax:

log_skip [<matcher>]

parseLogSkip (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.VarsMiddleware.

func parseMatcherDefinitions

parseMatcherDefinitions (d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error
References: caddyconfig.JSON, caddyfile.MatcherNameCtxKey, caddyfile.NewDispenser, caddyfile.Token, caddyfile.Unmarshaler, caddyhttp.RequestMatcher, caddyhttp.RequestMatcherWithError, fmt.Errorf.

func parseMetricsOptions

parseMetricsOptions (d *caddyfile.Dispenser, _ any) (any, error)

func parseOCSPStaplingOptions

parseOCSPStaplingOptions (d *caddyfile.Dispenser, _ any) (any, error)
References: certmagic.OCSPConfig.

func parseOptACMEDNS

parseOptACMEDNS (d *caddyfile.Dispenser, _ any) (any, error)
References: caddyfile.UnmarshalModule, certmagic.DNSProvider.

func parseOptACMEEAB

parseOptACMEEAB (d *caddyfile.Dispenser, _ any) (any, error)
References: acme.EAB.

func parseOptAdmin

parseOptAdmin (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptAutoHTTPS

parseOptAutoHTTPS (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptCertIssuer

parseOptCertIssuer (d *caddyfile.Dispenser, existing any) (any, error)
References: caddyfile.UnmarshalModule, certmagic.Issuer.

func parseOptDefaultBind

parseOptDefaultBind (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptDuration

parseOptDuration (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptHTTPPort

parseOptHTTPPort (d *caddyfile.Dispenser, _ any) (any, error)
References: strconv.Atoi.

func parseOptHTTPSPort

parseOptHTTPSPort (d *caddyfile.Dispenser, _ any) (any, error)
References: strconv.Atoi.

func parseOptOnDemand

parseOptOnDemand (d *caddyfile.Dispenser, _ any) (any, error)
References: caddyconfig.JSONModuleObject, caddyfile.UnmarshalModule, caddytls.OnDemandConfig, caddytls.OnDemandPermission, caddytls.PermissionByHTTP.

func parseOptOrder

parseOptOrder (d *caddyfile.Dispenser, _ any) (any, error)
References: slices.DeleteFunc, slices.Index, slices.Insert.

func parseOptPersistConfig

parseOptPersistConfig (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptPreferredChains

parseOptPreferredChains (d *caddyfile.Dispenser, _ any) (any, error)
References: caddytls.ParseCaddyfilePreferredChainsOptions.

func parseOptSingleString

parseOptSingleString (d *caddyfile.Dispenser, _ any) (any, error)

func parseOptStorage

parseOptStorage (d *caddyfile.Dispenser, _ any) (any, error)
References: caddyfile.UnmarshalModule.

func parseOptTrue

parseOptTrue (d *caddyfile.Dispenser, _ any) (any, error)

func parsePKIApp

parsePKIApp parses the global log option. Syntax:

pki {
    ca [<id>] {
        name                  <name>
        root_cn               <name>
        intermediate_cn       <name>
        intermediate_lifetime <duration>
        root {
            cert   <path>
            key    <path>
            format <format>
        }
        intermediate {
            cert   <path>
            key    <path>
            format <format>
        }
    }
}

When the CA ID is unspecified, 'local' is assumed.

parsePKIApp (d *caddyfile.Dispenser, existingVal any) (any, error)
References: caddypki.CA, caddypki.DefaultCAID, caddypki.KeyPair, caddypki.PKI.

func parseRedir

parseRedir parses the redir directive. Syntax:

redir [<matcher>] <to> [<code>]

<code> can be "permanent" for 301, "temporary" for 302 (default), a placeholder, or any number in the 3xx range or 401. The special code "html" can be used to redirect only browser clients (will respond with HTTP 200 and no Location header; redirect is performed with JS and a meta tag).

parseRedir (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.StaticResponse, caddyhttp.WeakString, fmt.Sprintf, html.EscapeString, http.Header, strconv.Atoi, strings.HasPrefix.

func parseRespond

parseRespond parses the respond directive.

parseRespond (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.StaticResponse.

func parseRoot

parseRoot parses the root directive. Syntax:

root [<matcher>] <path>

parseRoot (h Helper) ([]ConfigValue, error)
References: caddyhttp.VarsMiddleware.

func parseRoute

parseRoute parses the route directive.

parseRoute (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.Route, caddyhttp.Subroute.

func parseSegmentAsConfig

parseSegmentAsConfig parses the segment such that its subdirectives are themselves treated as directives, including named matcher definitions, and the raw Config structs are returned.

parseSegmentAsConfig (h Helper) ([]ConfigValue, error)
References: caddyfile.NewDispenser, caddyfile.Segment, strings.HasPrefix.

func parseServerOptions

parseServerOptions (d *caddyfile.Dispenser, _ any) (any, error)

func parseStorageCheck

parseStorageCheck (d *caddyfile.Dispenser, _ any) (any, error)

func parseStorageCleanInterval

parseStorageCleanInterval (d *caddyfile.Dispenser, _ any) (any, error)

func parseTLS

parseTLS parses the tls directive. Syntax:

tls [<email>|internal]|[<cert_file> <key_file>] {
    protocols <min> [<max>]
    ciphers   <cipher_suites...>
    curves    <curves...>
    client_auth {
        mode                   [request|require|verify_if_given|require_and_verify]
        trust_pool			   <module_name> [...]
        trusted_leaf_cert      <base64_der>
        trusted_leaf_cert_file <filename>
    }
    alpn                          <values...>
    load                          <paths...>
    ca                            <acme_ca_endpoint>
    ca_root                       <pem_file>
    key_type                      [ed25519|p256|p384|rsa2048|rsa4096]
    dns                           <provider_name> [...]
    propagation_delay             <duration>
    propagation_timeout           <duration>
    resolvers                     <dns_servers...>
    dns_ttl                       <duration>
    dns_challenge_override_domain <domain>
    on_demand
    reuse_private_keys
    eab                           <key_id> <mac_key>
    issuer                        <module_name> [...]
    get_certificate               <module_name> [...]
    insecure_secrets_log          <log_file>
}

parseTLS (h Helper) ([]ConfigValue, error)
References: acme.EAB, caddyconfig.JSONModuleObject, caddyfile.UnmarshalModule, caddytls.ACMEIssuer, caddytls.CertKeyFilePair, caddytls.ChallengesConfig, caddytls.CipherSuiteNameSupported, caddytls.ClientAuthentication, caddytls.ConnectionPolicy, caddytls.CustomCertSelectionPolicy, caddytls.DNSChallengeConfig, caddytls.DefaultIssuers, caddytls.FileLoader, caddytls.FolderLoader, caddytls.InternalIssuer, caddytls.SupportedCurves, caddytls.SupportedProtocols, certmagic.Issuer, certmagic.Manager, fmt.Sprintf, strings.Contains, time.Duration.

func parseVars

parseVars parses the vars directive. See its UnmarshalCaddyfile method for syntax.

parseVars (h Helper) (caddyhttp.MiddlewareHandler, error)
References: caddyhttp.VarsMiddleware.

func placeholderShorthands

placeholderShorthands returns a slice of old-new string pairs, where the left of the pair is a placeholder shorthand that may be used in the Caddyfile, and the right is the replacement.

placeholderShorthands () []string

func sortRoutes

sortRoutes (routes []ConfigValue)
References: caddyhttp.MatchPath, caddyhttp.Route, json.Unmarshal, sort.SliceStable, strings.TrimSuffix.

func specificity

specificity returns len(s) minus any wildcards () and placeholders ({...}). Basically, it's a length count that penalizes the use of wildcards and placeholders. This is useful for comparing hostnames and paths. However, wildcards in paths are not a sure answer to the question of specificity. For example, '.example.com' is clearly less specific than 'a.example.com', but is '/a' more or less specific than '/a*'?

specificity (s string) int
References: strings.Count, strings.Index.

func subjectQualifiesForPublicCert

subjectQualifiesForPublicCert is like certmagic.SubjectQualifiesForPublicCert() except that this allows domains with multiple wildcard levels like '..example.com' to qualify if the automation policy has OnDemand enabled (i.e. this function is more lenient).

IP subjects are considered as non-qualifying for public certs. Technically, there are now public ACME CAs as well as non-ACME CAs that issue IP certificates. But this function is used solely for implicit automation (defaults), where it gets really complicated to keep track of which issuers support IP certificates in which circumstances. Currently, issuers that support IP certificates are very few, and all require some sort of config from the user anyway (such as an account credential). Since we cannot implicitly and automatically get public IP certs without configuration from the user, we treat IPs as not qualifying for public certificates. Users should expressly configure an issuer that supports IP certs for that purpose.

subjectQualifiesForPublicCert (ap *caddytls.AutomationPolicy, subj string) bool
References: certmagic.SubjectIsIP, certmagic.SubjectIsInternal, strings.Count.

func tryDuration

tryDuration (val any, warnings *[]caddyconfig.Warning) caddy.Duration
References: caddyconfig.Warning.

func tryInt

tryInt tries to convert val to an integer. If it fails, it downgrades the error to a warning and returns 0.

tryInt (val any, warnings *[]caddyconfig.Warning) int
References: caddyconfig.Warning.

func tryString

tryString (val any, warnings *[]caddyconfig.Warning) string
References: caddyconfig.Warning.

func unmarshalCaddyfileMetricsOptions

unmarshalCaddyfileMetricsOptions (d *caddyfile.Dispenser) (any, error)
References: caddyhttp.Metrics.

func unmarshalCaddyfileServerOptions

unmarshalCaddyfileServerOptions (d *caddyfile.Dispenser) (any, error)
References: caddyconfig.JSONModuleObject, caddyfile.UnmarshalModule, caddyhttp.IPRangeSource, caddyhttp.Metrics, fmt.Errorf, slices.Contains.

func compileEncodedMatcherSets

compileEncodedMatcherSets (sblock serverBlock) ([]caddy.ModuleMap, error)
References: caddyhttp.MatchHost, caddyhttp.MatchPath, caddyhttp.RequestMatcherWithError, fmt.Errorf, slices.Contains.

func consolidateAddrMappings

consolidateAddrMappings eliminates repetition of identical server blocks in a mapping of single listener addresses to protocols to lists of server blocks. Since multiple addresses may serve multiple protocols to identical sites (server block contents), this function turns a 1:many mapping into a many:many mapping. Server block contents (tokens) must be exactly identical so that reflect.DeepEqual returns true in order for the addresses to be combined. Identical entries are deleted from the addrToServerBlocks map. Essentially, each pairing (each association from multiple addresses to multiple server blocks; i.e. each element of the returned slice) becomes a server definition in the output JSON.

consolidateAddrMappings (addrToProtocolToServerBlocks map[string]map[string][]serverBlock) []sbAddrAssociation
References: reflect.DeepEqual, sort.Strings.

func listenersForServerBlockAddress

listenersForServerBlockAddress essentially converts the Caddyfile site addresses to a map from Caddy listener addresses and the protocols to serve them with to the parsed address for each server block.

listenersForServerBlockAddress (sblock serverBlock, addr Address, options map[string]any) (map[string]map[string]struct{}, error)
References: caddyhttp.DefaultHTTPPort, caddyhttp.DefaultHTTPSPort, fmt.Errorf, strconv.Itoa.

func mapAddressToProtocolToServerBlocks

mapAddressToProtocolToServerBlocks returns a map of listener address to list of server blocks that will be served on that address. To do this, each server block is expanded so that each one is considered individually, although keys of a server block that share the same address stay grouped together so the config isn't repeated unnecessarily. For example, this Caddyfile:

example.com {
	bind 127.0.0.1
}
www.example.com, example.net/path, localhost:9999 {
	bind 127.0.0.1 1.2.3.4
}

has two server blocks to start with. But expressed in this Caddyfile are actually 4 listener addresses: 127.0.0.1:443, 1.2.3.4:443, 127.0.0.1:9999, and 127.0.0.1:9999. This is because the bind directive is applied to each key of its server block (specifying the host part), and each key may have a different port. And we definitely need to be sure that a site which is bound to be served on a specific interface is not served on others just because that is more convenient: it would be a potential security risk if the difference between interfaces means private vs. public.

So what this function does for the example above is iterate each server block, and for each server block, iterate its keys. For the first, it finds one key (example.com) and determines its listener address (127.0.0.1:443 - because of 'bind' and automatic HTTPS). It then adds the listener address to the map value returned by this function, with the first server block as one of its associations.

It then iterates each key on the second server block and associates them with one or more listener addresses. Indeed, each key in this block has two listener addresses because of the 'bind' directive. Once we know which addresses serve which keys, we can create a new server block for each address containing the contents of the server block and only those specific keys of the server block which use that address.

It is possible and even likely that some keys in the returned map have the exact same list of server blocks (i.e. they are identical). This happens when multiple hosts are declared with a 'bind' directive and the resulting listener addresses are not shared by any other server block (or the other server blocks are exactly identical in their token contents). This happens with our example above because 1.2.3.4:443 and 1.2.3.4:9999 are used exclusively with the second server block. This repetition may be undesirable, so call consolidateAddrMappings() to map multiple addresses to the same lists of server blocks (a many:many mapping). (Doing this is essentially a map-reduce technique.)

mapAddressToProtocolToServerBlocks (originalServerBlocks []serverBlock, options map[string]any) (map[string]map[string][]serverBlock, error)
References: caddyfile.ServerBlock, caddyfile.Token, fmt.Errorf, sort.Strings.

func serversFromPairings

serversFromPairings creates the servers for each pairing of addresses to server blocks. Each pairing is essentially a server definition.

serversFromPairings (pairings []sbAddrAssociation, options map[string]any, warnings *[]caddyconfig.Warning, groupCounter counter) (map[string]*caddyhttp.Server, error)
References: caddyconfig.JSON, caddyconfig.JSONModuleObject, caddyhttp.AutoHTTPSConfig, caddyhttp.DefaultHTTPPort, caddyhttp.DefaultHTTPSPort, caddyhttp.HTTPErrorConfig, caddyhttp.Route, caddyhttp.Server, caddyhttp.ServerLogConfig, caddyhttp.StringArray, caddyhttp.Subroute, caddytls.ConnectionPolicy, fmt.Errorf, fmt.Sprintf, net.SplitHostPort, slices.Contains, slices.Sort, sort.SliceStable, strconv.Itoa, strings.Contains, strings.HasPrefix, strings.Index, zap.String.

func buildPKIApp

buildPKIApp (pairings []sbAddrAssociation, options map[string]any, warnings []caddyconfig.Warning) (*caddypki.PKI, []caddyconfig.Warning, error)
References: caddypki.CA, caddypki.DefaultCAID, caddypki.PKI.

func buildTLSApp

buildTLSApp (pairings []sbAddrAssociation, options map[string]any, warnings []caddyconfig.Warning) (*caddytls.TLS, []caddyconfig.Warning, error)
References: caddyconfig.JSON, caddyconfig.JSONModuleObject, caddyhttp.DefaultHTTPPort, caddytls.ACMEIssuer, caddytls.AutomateLoader, caddytls.AutomationConfig, caddytls.AutomationPolicy, caddytls.CertificateLoader, caddytls.ChallengesConfig, caddytls.DefaultIssuers, caddytls.InternalIssuer, caddytls.OnDemandConfig, caddytls.TLS, certmagic.Issuer, certmagic.OCSPConfig, certmagic.SubjectQualifiesForCert, certmagic.SubjectQualifiesForPublicCert, fmt.Errorf, json.RawMessage, reflect.Append, reflect.DeepEqual, reflect.New, reflect.Slice, reflect.TypeOf, reflect.ValueOf, slices.Contains, sort.Strings, strconv.Itoa, strings.HasPrefix.

func evaluateGlobalOptionsBlock

evaluateGlobalOptionsBlock evaluates the global options block, which is expected to be the first server block if it has zero keys. It returns the updated list of server blocks with the global options block removed, and updates options accordingly.

evaluateGlobalOptionsBlock (serverBlocks []serverBlock, options map[string]any) ([]serverBlock, error)
References: caddyfile.NewDispenser, fmt.Errorf, sort.Slice.

func extractNamedRoutes

extractNamedRoutes pulls out any named route server blocks so they don't get parsed as sites, and stores them in options for later.

extractNamedRoutes (serverBlocks []serverBlock, options map[string]any, warnings *[]caddyconfig.Warning, replacer ShorthandReplacer) ([]serverBlock, error)
References: caddyconfig.JSONModuleObject, caddyfile.NewDispenser, caddyfile.Segment, caddyhttp.Route, caddyhttp.Subroute, json.RawMessage.

func nextGroup

nextGroup () string
References: fmt.Sprintf.

func hasHostCatchAllKey

hasHostCatchAllKey returns true if sb has a key that omits a host portion, i.e. it "catches all" hosts.

hasHostCatchAllKey () bool
References: slices.ContainsFunc.

func hostsFromKeys

hostsFromKeys returns a list of all the non-empty hostnames found in the keys of the server block sb. If logger mode is false, a key with an empty hostname portion will return an empty slice, since that server block is interpreted to effectively match all hosts. An empty string is never added to the slice.

If loggerMode is true, then the non-standard ports of keys will be joined to the hostnames. This is to effectively match the Host header of requests that come in for that key.

The resulting slice is not sorted but will never have duplicates.

hostsFromKeys (loggerMode bool) []string
References: caddyhttp.DefaultHTTPPort, caddyhttp.DefaultHTTPSPort, net.JoinHostPort, strconv.Itoa.

func hostsFromKeysNotHTTP

hostsFromKeysNotHTTP (httpPort string) []string

func isAllHTTP

isAllHTTP returns true if all sb keys explicitly specify the http:// scheme

isAllHTTP () bool
References: slices.ContainsFunc.


Tests

Files: 6. Third party imports: 0. Imports from organisation: 0. Tests: 13. Benchmarks: 0.

Test functions

TestAddressString

TestAutomationPolicyIsSubset

References: caddytls.AutomationPolicy.

TestGlobalLogOptionSyntax

References: caddyfile.Adapter.

TestGlobalOptions

References: caddyfile.Adapter.

TestHostsFromKeys

References: reflect.DeepEqual, sort.Strings.

TestImportErrorLine

References: caddyfile.Adapter, strings.Contains.

TestKeyNormalization

TestLogDirectiveSyntax

References: caddyfile.Adapter.

TestMatcherSyntax

References: caddyfile.Adapter.

TestNestedImport

References: caddyfile.Adapter.

TestParseAddress

TestRedirDirectiveSyntax

References: caddyfile.Adapter.

TestSpecificity