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