diff --git a/src/docs/markdown/caddyfile/concepts.md b/src/docs/markdown/caddyfile/concepts.md index c93627c..845a64f 100644 --- a/src/docs/markdown/caddyfile/concepts.md +++ b/src/docs/markdown/caddyfile/concepts.md @@ -320,46 +320,55 @@ Matcher tokens can be omitted entirely to match all requests; for example, `*` d ## Placeholders -You can use any [Caddy placeholders](/docs/conventions#placeholders) in the Caddyfile, but for convenience you can also use some equivalent shorthand ones: +[Placeholders](/docs/conventions#placeholders) are a simple way to inject dynamic values into your static configuration. They can be used as arguments to directives and subdirectives. -| Shorthand | Replaces | -|-----------------|-----------------------------------| -| `{cookie.*}` | `{http.request.cookie.*}` | -| `{client_ip}` | `{http.vars.client_ip}` | -| `{dir}` | `{http.request.uri.path.dir}` | -| `{err.*}` | `{http.error.*}` | -| `{file_match.*}` | `{http.matchers.file.*}` | -| `{file.base}` | `{http.request.uri.path.file.base}` | -| `{file.ext}` | `{http.request.uri.path.file.ext}` | -| `{file}` | `{http.request.uri.path.file}` | -| `{header.*}` | `{http.request.header.*}` | -| `{host}` | `{http.request.host}` | -| `{hostport}` | `{http.request.hostport}` | -| `{labels.*}` | `{http.request.host.labels.*}` | -| `{method}` | `{http.request.method}` | -| `{path.*}` | `{http.request.uri.path.*}` | -| `{path}` | `{http.request.uri.path}` | -| `{port}` | `{http.request.port}` | -| `{query.*}` | `{http.request.uri.query.*}` | -| `{query}` | `{http.request.uri.query}` | -| `{re.*}` | `{http.regexp.*}` | -| `{remote_host}` | `{http.request.remote.host}` | -| `{remote_port}` | `{http.request.remote.port}` | -| `{remote}` | `{http.request.remote}` | -| `{rp.*}` | `{http.reverse_proxy.*}` | -| `{resp.*}` | `{http.intercept.*}` | -| `{scheme}` | `{http.request.scheme}` | -| `{tls_cipher}` | `{http.request.tls.cipher_suite}` | +Placeholders are bounded on either side by curly braces `{ }` and contain the identifier inside, for example: `{foo.bar}`. The opening placeholder brace can be escaped `\{like.this}` to prevent replacement. Placeholder identifiers are typically namespaced with dots to avoid collisions across modules. + +Which placeholders are available depends on the context. Not all placeholders are available in all parts of the config. For example, [the HTTP app sets placeholders](/docs/json/apps/http/#docs) that are only available in areas of the config related to handling HTTP requests (i.e. in HTTP handler [directives](#directives) and [matchers](#matchers), but _not_ in [`tls` configuration](/docs/caddyfile/directives/tls)). Some directives or matchers may set their own placeholders too which can be used by anything that follows them. Some placeholders [are globally available](/docs/conventions#placeholders). + +You can use any placeholders in the Caddyfile, but for convenience you can also use some of these equivalent shorthands which are expanded when the Caddyfile is parsed: + +| Shorthand | Replaces | +|------------------|-------------------------------------| +| `{cookie.*}` | `{http.request.cookie.*}` | +| `{client_ip}` | `{http.vars.client_ip}` | +| `{dir}` | `{http.request.uri.path.dir}` | +| `{err.*}` | `{http.error.*}` | +| `{file_match.*}` | `{http.matchers.file.*}` | +| `{file.base}` | `{http.request.uri.path.file.base}` | +| `{file.ext}` | `{http.request.uri.path.file.ext}` | +| `{file}` | `{http.request.uri.path.file}` | +| `{header.*}` | `{http.request.header.*}` | +| `{host}` | `{http.request.host}` | +| `{hostport}` | `{http.request.hostport}` | +| `{labels.*}` | `{http.request.host.labels.*}` | +| `{method}` | `{http.request.method}` | +| `{path.*}` | `{http.request.uri.path.*}` | +| `{path}` | `{http.request.uri.path}` | +| `{port}` | `{http.request.port}` | +| `{query.*}` | `{http.request.uri.query.*}` | +| `{query}` | `{http.request.uri.query}` | +| `{re.*}` | `{http.regexp.*}` | +| `{remote_host}` | `{http.request.remote.host}` | +| `{remote_port}` | `{http.request.remote.port}` | +| `{remote}` | `{http.request.remote}` | +| `{rp.*}` | `{http.reverse_proxy.*}` | +| `{resp.*}` | `{http.intercept.*}` | +| `{scheme}` | `{http.request.scheme}` | +| `{tls_cipher}` | `{http.request.tls.cipher_suite}` | | `{tls_client_certificate_der_base64}` | `{http.request.tls.client.certificate_der_base64}` | | `{tls_client_certificate_pem}` | `{http.request.tls.client.certificate_pem}` | | `{tls_client_fingerprint}` | `{http.request.tls.client.fingerprint}` | | `{tls_client_issuer}` | `{http.request.tls.client.issuer}` | | `{tls_client_serial}` | `{http.request.tls.client.serial}` | | `{tls_client_subject}` | `{http.request.tls.client.subject}` | -| `{tls_version}` | `{http.request.tls.version}` | +| `{tls_version}` | `{http.request.tls.version}` | | `{upstream_hostport}` | `{http.reverse_proxy.upstream.hostport}` | -| `{uri}` | `{http.request.uri}` | -| `{vars.*}` | `{http.vars.*}` | +| `{uri}` | `{http.request.uri}` | +| `{vars.*}` | `{http.vars.*}` | + +Not all config fields support placeholders, but most do where you would expect it. Support for placeholders needs to have been explicitly added to those fields. Plugin authors can [read this article](/docs/extending-caddy/placeholders) to learn how to add support for placeholders in their own modules. + diff --git a/src/docs/markdown/caddyfile/directives/vars.md b/src/docs/markdown/caddyfile/directives/vars.md index 2497c2c..e002138 100644 --- a/src/docs/markdown/caddyfile/directives/vars.md +++ b/src/docs/markdown/caddyfile/directives/vars.md @@ -6,9 +6,11 @@ title: vars (Caddyfile directive) Sets one or more variables to a particular value, to be used later in the request handling chain. -The primary way to access variables is with placeholders, which have the form `{vars.variable_name}`, or with the [`vars`](/docs/caddyfile/matchers#vars) and [`vars_regexp`](/docs/caddyfile/matchers#vars_regexp) request matchers. You may use variables with the [`templates`](templates) directive using the `placeholder` function, for example: `{{placeholder "http.vars.variable_name"}}` +The primary way to access variables is with placeholders, which have the form `{vars.variable_name}`, or with the [`vars`](/docs/caddyfile/matchers#vars) and [`vars_regexp`](/docs/caddyfile/matchers#vars_regexp) request matchers. -As a special case, it's possible to override the variable named `http.auth.user.id`, which is stored in the replacer, to update the `user_id` field in access logs. +You may use variables with the [`templates`](templates) directive using the `placeholder` function, for example: `{{ "{{placeholder \"http.vars.variable_name\"}}" }}` + +As a special case, it's possible to override the variable named `http.auth.user.id`, which is stored in the replacer, to update the `user_id` field in [access logs](log). ## Syntax @@ -24,7 +26,7 @@ vars [] [ ] { - **<value>** is the value of the variable. - The value will be type converted if possible; `true` and `false` will be converted to boolean types, and numeric values will be converted to integer or float accordingly. To avoid this conversion, you may wrap the output with [quotes](/docs/caddyfile/concepts#tokens-and-quotes) and they will stay strings. + The value will be type converted if possible; `true` and `false` will be converted to boolean types, and numeric values will be converted to integer or float accordingly. To avoid this conversion and keep them as strings, you may wrap them with [quotes](/docs/caddyfile/concepts#tokens-and-quotes). ## Examples diff --git a/src/docs/markdown/caddyfile/matchers.md b/src/docs/markdown/caddyfile/matchers.md index 344822e..7b321f8 100644 --- a/src/docs/markdown/caddyfile/matchers.md +++ b/src/docs/markdown/caddyfile/matchers.md @@ -874,6 +874,12 @@ Match an output of the [`map` directive](/docs/caddyfile/directives/map) named ` vars {magic_number} 3 5 ``` +Match an arbitrary placeholder's value, i.e. the authenticated user's ID, either `Bob` or `Alice`: + +```caddy-d +vars {http.auth.user.id} Bob Alice +``` + --- diff --git a/src/docs/markdown/conventions.md b/src/docs/markdown/conventions.md index 4ddef28..9a62741 100644 --- a/src/docs/markdown/conventions.md +++ b/src/docs/markdown/conventions.md @@ -79,20 +79,20 @@ Caddy network addresses are not URLs. URLs couple the lower and higher layers of ## Placeholders -Caddy's configuration supports the use of _placeholders_ (variables). Using placeholders is a simple way to inject dynamic values into a static configuration. +Caddy's configuration supports the use of _placeholders_. Using placeholders is a simple way to inject dynamic values into a static configuration. -Placeholders are bounded on either side by curly braces `{ }` and contain the variable name inside, for example: `{foo.bar}`. The opening placeholder brace can be escaped `\{like-this}` to prevent replacement. Variable names are typically namespaced with dots to avoid collisions across modules. +Placeholders are bounded on either side by curly braces `{ }` and contain the identifier inside, for example: `{foo.bar}`. The opening placeholder brace can be escaped `\{like.this}` to prevent replacement. Placeholder identifiers are typically namespaced with dots to avoid collisions across modules. Which placeholders are available depends on the context. Not all placeholders are available in all parts of the config. For example, [the HTTP app sets placeholders](/docs/json/apps/http/#docs) that are only available in areas of the config related to handling HTTP requests. -The following placeholders are always available: +The following placeholders are always available (global): Placeholder | Description ------------|------------- @@ -110,7 +110,7 @@ Placeholder | Description `{time.now.common_log}` | The current time in Common Log Format `{time.now.year}` | The current year in YYYY format -Not all config fields support placeholders, but most do where you would expect it. +Not all config fields support placeholders, but most do where you would expect it. Support for placeholders needs to have been explicitly added to those fields. Plugin authors can [read this article](/docs/extending-caddy/placeholders) to learn how to add support for placeholders in their own modules. diff --git a/src/docs/markdown/extending-caddy.md b/src/docs/markdown/extending-caddy.md index 71cb55c..13c2d77 100644 --- a/src/docs/markdown/extending-caddy.md +++ b/src/docs/markdown/extending-caddy.md @@ -138,7 +138,7 @@ type Gizmo struct { } ``` -Using struct tags in this way will ensure that config properties are consisently named across all of Caddy. +Using the `omitempty` option in the struct tag will omit the field from the JSON output if it is the zero value for its type. This is useful to keep the JSON config clean and concise when marshaled (e.g. adapting from Caddyfile to JSON). When a module is initialized, it will already have its configuration filled out. It is also possible to perform additional [provisioning](#provisioning) and [validation](#validating) steps after a module is initialized. @@ -158,7 +158,7 @@ Note that multiple loaded instances of your module may overlap at a given time! ### Provisioning -A module's configuration will be unmarshaled into its value automatically. This means, for example, that struct fields will be filled out for you. +A module's configuration will be unmarshaled into its value automatically (when loading the JSON config). This means, for example, that struct fields will be filled out for you. However, if your module requires additional provisioning steps, you can implement the (optional) [`caddy.Provisioner`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#Provisioner) interface: @@ -170,7 +170,9 @@ func (g *Gizmo) Provision(ctx caddy.Context) error { } ``` -This is typically where host modules will load their guest/child modules, but it can be used for pretty much anything. Module provisioning is done in an arbitrary order. +This is where you should set default values for fields that were not provided by the user (fields that are not their zero value). If a field is required, you may return an error if it is not set. For numeric fields where the zero value has meaning (e.g. some timeout duration), you may want to support `-1` to mean "off" rather than `0`, so you may set a default value if the user did not configure it. + +This is also typically where host modules will load their guest/child modules. A module may access other apps by calling `ctx.App()`, but modules must not have circular dependencies. In other words, a module loaded by the `http` app cannot depend on the `tls` app if a module loaded by the `tls` app depends on the `http` app. (Very similar to rules forbidding import cycles in Go.) diff --git a/src/docs/markdown/extending-caddy/placeholders.md b/src/docs/markdown/extending-caddy/placeholders.md index efb4743..190e6d8 100644 --- a/src/docs/markdown/extending-caddy/placeholders.md +++ b/src/docs/markdown/extending-caddy/placeholders.md @@ -8,18 +8,18 @@ In Caddy, placeholders are processed by each individual plugin as needed; they d This means that if you wish for your plugin to support placeholders, you must explicitly add support for them. -If you are not yet familiar with placeholders, start by reading [here](/docs/conventions#placeholders)! +If you are not yet familiar with placeholders, start by [reading here](/docs/conventions#placeholders)! ## Placeholders Overview -Placeholders are a string in the format `{foo.bar}` used as dynamic configuration values, which is later evaluated at runtime. +[Placeholders](/docs/conventions#placeholders) are a string in the format `{foo.bar}` used as dynamic configuration values, which is later evaluated at runtime. -Placeholders-like strings which start with a dollar sign (`{$FOO}`), are evaulated at Caddyfile parse time, and do not need to be dealt with by your plugin. This is because these are not placeholders, but Caddyfile-specific [environmental variable substitution](/docs/caddyfile/concepts#environment-variables), they just happen to share the `{}` syntax. +Caddyfile [environment variables substitutions](/docs/caddyfile/concepts#environment-variables) which start with a dollar sign like `{$FOO}` are evaluated at Caddyfile-parse time, and do not need to be handled by your plugin. These are _not_ placeholders, despite sharing the same `{ }` syntax. -It is therefore important to understand that `{env.HOST}` is inherently different from something like `{$HOST}`. +It is therefore important to understand that `{env.HOST}` (a [global placeholder](/docs/conventions#placeholders)) is inherently different from `{$HOST}` (a Caddyfile env-var substitution). As an example, see the following Caddyfile: -```caddyfile +```caddy :8080 { respond {$HOST} 200 } @@ -29,16 +29,15 @@ As an example, see the following Caddyfile: } ``` -When you adapt this Caddyfile with `HOST=example caddy adapt` you will get +When you adapt this Caddyfile to JSON with `HOST=example caddy adapt` you will get: + ```json { "apps": { "http": { "servers": { "srv0": { - "listen": [ - ":8080" - ], + "listen": [":8080"], "routes": [ { "handle": [ @@ -52,9 +51,7 @@ When you adapt this Caddyfile with `HOST=example caddy adapt` you will get ] }, "srv1": { - "listen": [ - ":8081" - ], + "listen": [":8081"], "routes": [ { "handle": [ @@ -73,23 +70,23 @@ When you adapt this Caddyfile with `HOST=example caddy adapt` you will get } ``` -Importantly, look at the `"body"` field in both `srv0` and `srv1`. +In particular, look at the `"body"` field in both `srv0` and `srv1`. -Since `srv0` used `{$HOST}`, the special environmental variable replacement with `$`, the value became `example`, as it was processed during Caddyfile parse time. +Since `srv0` used `{$HOST}` (Caddyfile env-var substitution), the value became `example`, as it was processed during Caddyfile parse time when producing the JSON config. -Since `srv1` used `{env.HOST}`, a normal placeholder, it was parsed as its own raw string value, `{env.HOST}` +Since `srv1` used `{env.HOST}` (a global placeholder), it remains untouched when adapting to JSON. -Some users may immediately notice that this means it is impossible to use the `{$ENV}` syntax in a JSON config. The solution to this is to process such placeholders at Provision time, which is covered below. +This does mean that users writing JSON config (not using Caddyfile) cannot use the `{$ENV}` syntax. For that reason, it's important that plugin authors implement support for replacing placeholders when the config is provisioned. This is explained below. ## Implementing placeholder support -You should not process placeholders when ummarshaling your Caddyfile. Instead, unmarshal the placeholders as strings in your configuration and evaluate them during either your module's execution (e.g. `ServeHTTP()` for HTTP handlers, `Match()` for matchers, etc.) or in the `Provision()` step, using a `caddy.Replacer`. +You should not process placeholders in [`UnmarshalCaddyfile()`](/docs/extending-caddy/caddyfile). Instead, placeholders should be replaced later, either in the [`Provision()`](/docs/extending-caddy#provisioning) step, or during your module's execution (e.g. `ServeHTTP()` for HTTP handlers, `Match()` for matchers, etc.), using a `caddy.Replacer`. ### Examples -In this example, we are using a newly constructed replacer to process placeholders. It has access to [global placeholders](/docs/conventions#placeholders) such as `{env.HOST}`, but NOT request placeholder such as `{http.request.uri}` +Here, we are using a newly constructed replacer to process placeholders. It has access to [global placeholders](/docs/conventions#placeholders) such as `{env.HOST}`, but _not_ HTTP placeholders such as `{http.request.uri}` because provisoning happens when the config is loaded, and not during a request. ```go func (g *Gizmo) Provision(ctx caddy.Context) error { @@ -99,7 +96,7 @@ func (g *Gizmo) Provision(ctx caddy.Context) error { } ``` -Here, we extract a replacer out of the `context.Context` inside the `*http.Request`. This replacer not only has access to global placeholders, but also request placeholders such as `{http.request.uri}`. +Here, we fetch the replacer from the request context `r.Context()` during `ServeHTTP`. This replacer has access to both global placeholders _and_ per-request HTTP placeholders such as `{http.request.uri}`. ```go func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {