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