Go API Documentation

github.com/caddyserver/caddy/v2/caddyconfig

No package summary is available.

Package

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

Vars

var _ caddy.ConfigLoader = (*HTTPLoader)(nil)
var bufPool = sync.Pool{
	New: func() any {
		return new(bytes.Buffer)
	},
}
var configAdapters = make(map[string]Adapter)

Types

Adapter

Adapter is a type which can adapt a configuration to Caddy JSON. It returns the results and any warnings, or an error.

type Adapter interface {
	Adapt(body []byte, options map[string]any) ([]byte, []Warning, error)
}

HTTPLoader

HTTPLoader can load Caddy configs over HTTP(S).

If the response is not a JSON config, a config adapter must be specified either in the loader config (adapter), or in the Content-Type HTTP header returned in the HTTP response from the server. The Content-Type header is read just like the admin API's /load endpoint. If you don't have control over the HTTP server (but can still trust its response), you can override the Content-Type header by setting the adapter property in this config.

type HTTPLoader struct {
	// The method for the request. Default: GET
	Method	string	`json:"method,omitempty"`

	// The URL of the request.
	URL	string	`json:"url,omitempty"`

	// HTTP headers to add to the request.
	Headers	http.Header	`json:"header,omitempty"`

	// Maximum time allowed for a complete connection and request.
	Timeout	caddy.Duration	`json:"timeout,omitempty"`

	// The name of the config adapter to use, if any. Only needed
	// if the HTTP response is not a JSON config and if the server's
	// Content-Type header is missing or incorrect.
	Adapter	string	`json:"adapter,omitempty"`

	TLS	*struct {
		// Present this instance's managed remote identity credentials to the server.
		UseServerIdentity	bool	`json:"use_server_identity,omitempty"`

		// PEM-encoded client certificate filename to present to the server.
		ClientCertificateFile	string	`json:"client_certificate_file,omitempty"`

		// PEM-encoded key to use with the client certificate.
		ClientCertificateKeyFile	string	`json:"client_certificate_key_file,omitempty"`

		// List of PEM-encoded CA certificate files to add to the same trust
		// store as RootCAPool (or root_ca_pool in the JSON).
		RootCAPEMFiles	[]string	`json:"root_ca_pem_files,omitempty"`
	}	`json:"tls,omitempty"`
}

Warning

Warning represents a warning or notice related to conversion.

type Warning struct {
	File		string	`json:"file,omitempty"`
	Line		int	`json:"line,omitempty"`
	Directive	string	`json:"directive,omitempty"`
	Message		string	`json:"message,omitempty"`
}

adapterModule

adapterModule is a wrapper type that can turn any config adapter into a Caddy module, which has the benefit of being counted with other modules, even though they do not technically extend the Caddy configuration structure. See caddyserver/caddy#3132.

type adapterModule struct {
	name	string
	Adapter
}

adminLoad

adminLoad is a module that provides the /load endpoint for the Caddy admin API. The only reason it's not baked into the caddy package directly is because of the import of the caddyconfig package for its GetAdapter function. If the caddy package depends on the caddyconfig package, then the caddyconfig package will not be able to import the caddy package, and it can more easily cause backward edges in the dependency tree (i.e. import cycle). Fortunately, the admin API has first-class support for adding endpoints from modules.

type adminLoad struct{}

Functions

func GetAdapter

GetAdapter returns the adapter with the given name, or nil if one with that name is not registered.

func GetAdapter(name string) Adapter {
	return configAdapters[name]
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func JSON

JSON encodes val as JSON, returning it as a json.RawMessage. Any marshaling errors (which are highly unlikely with correct code) are converted to warnings. This is convenient when filling config structs that require a json.RawMessage, without having to worry about errors.

func JSON(val any, warnings *[]Warning) json.RawMessage {
	b, err := json.Marshal(val)
	if err != nil {
		if warnings != nil {
			*warnings = append(*warnings, Warning{Message: err.Error()})
		}
		return nil
	}
	return b
}

Cognitive complexity: 5, Cyclomatic complexity: 3

Uses: json.Marshal.

func JSONModuleObject

JSONModuleObject is like JSON(), except it marshals val into a JSON object with an added key named fieldName with the value fieldVal. This is useful for encoding module values where the module name has to be described within the object by a certain key; for example, "handler": "file_server" for a file server HTTP handler (fieldName="handler" and fieldVal="file_server"). The val parameter must encode into a map[string]any (i.e. it must be a struct or map). Any errors are converted into warnings.

func JSONModuleObject(val any, fieldName, fieldVal string, warnings *[]Warning) json.RawMessage {
	// encode to a JSON object first
	enc, err := json.Marshal(val)
	if err != nil {
		if warnings != nil {
			*warnings = append(*warnings, Warning{Message: err.Error()})
		}
		return nil
	}

	// then decode the object
	var tmp map[string]any
	err = json.Unmarshal(enc, &tmp)
	if err != nil {
		if warnings != nil {
			*warnings = append(*warnings, Warning{Message: err.Error()})
		}
		return nil
	}

	// so we can easily add the module's field with its appointed value
	tmp[fieldName] = fieldVal

	// then re-marshal as JSON
	result, err := json.Marshal(tmp)
	if err != nil {
		if warnings != nil {
			*warnings = append(*warnings, Warning{Message: err.Error()})
		}
		return nil
	}

	return result
}

Cognitive complexity: 15, Cyclomatic complexity: 7

Uses: json.Marshal, json.Unmarshal.

func RegisterAdapter

RegisterAdapter registers a config adapter with the given name. This should usually be done at init-time. It panics if the adapter cannot be registered successfully.

func RegisterAdapter(name string, adapter Adapter) {
	if _, ok := configAdapters[name]; ok {
		panic(fmt.Errorf("%s: already registered", name))
	}
	configAdapters[name] = adapter
	caddy.RegisterModule(adapterModule{name, adapter})
}

Cognitive complexity: 3, Cyclomatic complexity: 2

Uses: fmt.Errorf.

func (HTTPLoader) LoadConfig

LoadConfig loads a Caddy config.

func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
	repl := caddy.NewReplacer()

	client, err := hl.makeClient(ctx)
	if err != nil {
		return nil, err
	}

	method := repl.ReplaceAll(hl.Method, "")
	if method == "" {
		method = http.MethodGet
	}

	url := repl.ReplaceAll(hl.URL, "")
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	for key, vals := range hl.Headers {
		for _, val := range vals {
			req.Header.Add(repl.ReplaceAll(key, ""), repl.ReplaceKnown(val, ""))
		}
	}

	resp, err := doHttpCallWithRetries(ctx, client, req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// adapt the config based on either manually-configured adapter or server's response header
	ct := resp.Header.Get("Content-Type")
	if hl.Adapter != "" {
		ct = "text/" + hl.Adapter
	}
	result, warnings, err := adaptByContentType(ct, body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger().Warn(warn.String())
	}

	return result, nil
}

Cognitive complexity: 25, Cyclomatic complexity: 12

Uses: fmt.Errorf, http.MethodGet, http.NewRequest, io.ReadAll.

func (Warning) String

func (w Warning) String() string {
	var directive string
	if w.Directive != "" {
		directive = fmt.Sprintf(" (%s)", w.Directive)
	}
	return fmt.Sprintf("%s:%d%s: %s", w.File, w.Line, directive, w.Message)
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: fmt.Sprintf.

func (adapterModule) CaddyModule

func (am adapterModule) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:	caddy.ModuleID("caddy.adapters." + am.name),
		New:	func() caddy.Module { return am },
	}
}

Cognitive complexity: 2, Cyclomatic complexity: 1

func (adminLoad) Routes

Routes returns a route for the /load endpoint.

func (al adminLoad) Routes() []caddy.AdminRoute {
	return []caddy.AdminRoute{
		{
			Pattern:	"/load",
			Handler:	caddy.AdminHandlerFunc(al.handleLoad),
		},
		{
			Pattern:	"/adapt",
			Handler:	caddy.AdminHandlerFunc(al.handleAdapt),
		},
	}
}

Cognitive complexity: 3, Cyclomatic complexity: 1

Private functions

func adaptByContentType

adaptByContentType adapts body to Caddy JSON using the adapter specified by contentType. If contentType is empty or ends with "/json", the input will be returned, as a no-op.

adaptByContentType (contentType string, body []byte) ([]byte, []Warning, error)
References: fmt.Errorf, http.StatusBadRequest, mime.ParseMediaType, strings.Cut, strings.HasSuffix.

func attemptHttpCall

attemptHttpCall (client *http.Client, request *http.Request) (*http.Response, error)
References: fmt.Errorf.

func doHttpCallWithRetries

doHttpCallWithRetries (ctx caddy.Context, client *http.Client, request *http.Request) (*http.Response, error)
References: http.Response, time.After, time.Millisecond.

func init

init ()

func makeClient

makeClient (ctx caddy.Context) (*http.Client, error)
References: fmt.Errorf, http.Client, http.Transport, os.ReadFile, time.Duration, tls.Certificate, tls.Config, tls.LoadX509KeyPair, x509.NewCertPool.

func handleAdapt

handleAdapt adapts the given Caddy config to JSON and responds with the result.

handleAdapt (w http.ResponseWriter, r *http.Request) error
References: bytes.Buffer, fmt.Errorf, http.MethodPost, http.StatusBadRequest, http.StatusMethodNotAllowed, io.Copy, json.NewEncoder, json.RawMessage.

func handleLoad

handleLoad replaces the entire current configuration with a new one provided in the response body. It supports config adapters through the use of the Content-Type header. A config that is identical to the currently-running config will be a no-op unless Cache-Control: must-revalidate is set.

handleLoad (w http.ResponseWriter, r *http.Request) error
References: bytes.Buffer, fmt.Errorf, http.MethodPost, http.StatusBadRequest, http.StatusMethodNotAllowed, io.Copy, json.Marshal.