caddyserver/caddy
Caddyfile parser
Package: github.com/caddyserver/caddy/v2/caddyconfig/caddyfile.
The generic Caddyfile parser — independent of the HTTP app. It produces a list of ServerBlocks and exposes a Dispenser API that any module can use to consume tokens.
Purpose
Provide a single, well-tested implementation of:
- The Caddyfile lexical grammar (tokens, quoted strings, heredocs, comments, line continuations).
- The block grammar (addresses, segments, nested blocks).
- The
importdirective with file globbing, args, and cycle detection. - A token-stream API (
Dispenser) that directive parsers in any module can use.
The HTTP-Caddyfile adapter (caddyconfig/httpcaddyfile/) builds on this; so do other downstream tools (caddy fmt, caddy validate).
Directory layout
| Path | Role |
|---|---|
lexer.go |
Lexer, token scanning |
parse.go |
Parser, ServerBlock, parseOne recursion |
dispenser.go |
The Dispenser API |
formatter.go |
caddy fmt formatting algorithm |
importargs.go |
Argument substitution for import {args.0} etc. |
importgraph.go |
Cycle detection across imports |
adapter.go |
The caddyfile Caddy adapter (delegates to the HTTP adapter for HTTP) |
*_fuzz.go |
Fuzz harnesses for lexer and formatter |
testdata/ |
Fixtures (incl. glob/ for import-glob tests) |
Key abstractions
| Type | Where | Description |
|---|---|---|
Token |
parse.go |
{File, Line, Text, …} |
ServerBlock |
parse.go |
Address keys + segments (each segment is a flat token slice for one directive call) |
Lexer |
lexer.go |
Tokenizer state machine |
Parser |
parse.go |
Block parser using the lexer |
Dispenser |
dispenser.go |
Iterator over tokens with helpers Next, NextArg, NextBlock, Nesting, Val, RemainingArgs, Err/Errf/ArgErr |
Unmarshaler |
dispenser.go |
Module interface — UnmarshalCaddyfile(*Dispenser) error |
Adapter |
adapter.go |
Implements caddyconfig.Adapter |
How it works
graph LR
Source[Caddyfile bytes] -->|Lex| Lexer
Lexer -->|Token stream| Parser
Parser -->|ServerBlocks| Adapter
Parser -->|imports| Imports[importargs/importgraph]
Imports --> Lexer
Adapter -->|hand each segment| Directive[module.UnmarshalCaddyfile]
Directive --> Dispenser
Dispenser --> Module[populated module struct]Lexer
lexer.go runs a small state machine over the input bytes:
- Comments start at
#and run to end-of-line. - Strings are quoted (
") or backticked (`); escapes\\,\",\n,\tetc. inside double-quotes. - Heredocs use
<<TAG\n...\nTAG\n— useful for inline templates. - Backslashes at end of line continue tokens onto the next line.
Tokens carry their file and line so error messages stay precise after import expansion.
Parser
parse.go consumes tokens, producing ServerBlocks. A server block is "address(es) { segments }" — segments are nested for any block bodies. The parser is recursive-descent, returning ParseError with file/line/message on failure.
Imports
import path/to/file (or a glob like import sites/*.caddy) pulls the referenced file in at parse time. importargs.go supports positional arguments (import snippet arg1 arg2 with {args.0}/{args.1} placeholders inside the snippet). importgraph.go walks the import tree and rejects cycles.
Snippets — blocks like (my_snippet) { ... } defined at the top level — are referenced by import my_snippet.
Dispenser
The interface every directive uses to read its own tokens. Typical usage:
func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() { // each call = directive invocation
if !d.NextArg() { // inline arg
return d.ArgErr()
}
h.Field = d.Val()
for d.NextBlock(0) { // block body
switch d.Val() {
case "subdir":
if !d.NextArg() { return d.ArgErr() }
h.Sub = d.Val()
default:
return d.Errf("unrecognized: %s", d.Val())
}
}
}
return nil
}d.Errf produces <file>:<line>: <message> errors automatically — that's why Caddyfile errors are usable.
Formatter
formatter.go implements caddy fmt. It re-tokenizes the input, normalizes whitespace, sorts directive blocks by registered priority (when configured), and reprints. The fuzz harness for the formatter checks that format(format(x)) == format(x) and that formatted output still parses.
Integration points
- HTTP-Caddyfile adapter: the heaviest consumer (
caddyconfig/httpcaddyfile/). - Module directive parsers: every Caddyfile-aware module under
modules/implementsUnmarshalCaddyfile. - CLI:
caddy adapt,caddy fmt,caddy validateall sit on top of this package.
Entry points for modification
- Lexer rules:
lexer.go. Tests inlexer_test.goare exhaustive; add a fuzz seed for any new edge case. - Block grammar:
parse.go. Same conventions. Dispenserergonomics:dispenser.go— but be careful, every directive in the codebase consumes this surface.
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.