caddy-website/src/docs/markdown/extending-caddy/placeholders.md

137 lines
4.1 KiB
Markdown
Raw Normal View History

2024-07-25 15:40:43 -05:00
---
title: "Placeholder Support"
---
2024-07-25 15:48:03 -05:00
# Placeholders
2024-07-25 15:40:43 -05:00
2024-07-25 16:09:42 -05:00
In Caddy, placeholders are processed by each individual plugin themselves. They are not parsed at config time, but instead preserved and processed at runtime.
2024-07-25 15:48:03 -05:00
2024-07-25 16:09:42 -05:00
This means that if you wish for your plugin to support placeholders, you must explicitly add support for them.
2024-07-25 15:48:03 -05:00
2024-07-25 15:57:39 -05:00
If you are not yet familiar with placeholders, start by reading [here](/docs/conventions#placeholders)!
2024-07-25 16:05:51 -05:00
## Placeholder Internals
2024-07-25 15:48:03 -05:00
2024-07-25 16:05:51 -05:00
Internally, placeholders are simply a string in format `{foo.bar}` used as valid configuration values, which is later parsed at runtime.
2024-07-25 15:57:39 -05:00
2024-07-25 16:05:51 -05:00
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/#environmental-variables), they just happen to share the `{}` syntax.
2024-07-25 15:48:03 -05:00
It is therefore important to understand that `{env.HOST}` is inherently different from something like `{$HOST}`
2024-07-25 15:40:43 -05:00
As an example, see the following caddyfile:
2024-07-25 15:59:00 -05:00
```caddyfile
2024-07-25 15:40:43 -05:00
:8080 {
respond {$HOST} 200
}
:8081 {
respond {env.HOST} 200
}
```
When you adapt this Caddyfile with `HOST=example caddy adapt` you will get
2024-07-25 15:59:00 -05:00
```json
2024-07-25 15:40:43 -05:00
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"body": "example",
"handler": "static_response",
"status_code": 200
}
]
}
]
},
"srv1": {
"listen": [
":8081"
],
"routes": [
{
"handle": [
{
"body": "{env.HOST}",
"handler": "static_response",
"status_code": 200
}
]
}
]
}
}
}
}
}
```
Importantly, look at the `"body"` field in both `srv0` and `srv1`.
2024-07-25 16:09:42 -05:00
Since `srv0` used `{$HOST}`, the special environmental variable replacement with `$`, the value became `example`, as it was processed during Caddyfile parse time.
2024-07-25 16:05:51 -05:00
Since `srv1` used `{env.HOST}`, a normal placeholder, it was parsed as its own raw string value, `{env.HOST}`
2024-07-25 15:40:43 -05:00
2024-07-25 16:09:42 -05:00
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.
2024-07-25 15:40:43 -05:00
2024-07-25 15:48:03 -05:00
2024-07-25 16:09:42 -05:00
## How to use Placeholders in your Plugin
2024-07-25 15:40:43 -05:00
2024-07-25 16:09:42 -05:00
#### Parse the raw Placeholder value in your unmarshaler
2024-07-25 15:40:43 -05:00
2024-07-25 16:09:42 -05:00
Placeholders should be parsed as their raw values when parsing the Cazddyfile, just like any other string value.
2024-07-25 15:40:43 -05:00
```go
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next()
if !d.Args(&g.Name) {
// not enough args
return d.ArgErr()
}
2024-07-25 15:48:03 -05:00
}
2024-07-25 15:40:43 -05:00
```
2024-07-25 15:59:00 -05:00
#### Resolve the Placeholder during Match or Serve
2024-07-25 15:40:43 -05:00
2024-07-25 15:48:03 -05:00
In order to now correctly read our `g.Name` placeholder, in a plugin matcher or middleware, we must extract the replacer from the context, and use that replacer on our saved placeholder string.
2024-07-25 15:40:43 -05:00
```go
func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
_, err := w.Write([]byte(repl.ReplaceAll(g.Name,"")))
if err != nil {
return err
}
return next.ServeHTTP(w, r)
}
```
2024-07-25 15:59:00 -05:00
#### Alternatively, resolve the Placeholder during Provision
2024-07-25 15:40:43 -05:00
2024-07-25 16:09:42 -05:00
If you only use global placeholders, like `env`, then you may initialize a global replacer at provision time, and use it to replace such values. This also allows users of config file formats other than Caddyfile to use environmental variables.
2024-07-25 15:40:43 -05:00
```go
func (g *Gizmo) Provision(ctx caddy.Context) error {
repl := caddy.NewReplacer()
g.Name = repl.ReplaceAll(g.Name,"")
return nil
}
func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// in this case, you don't need to replace at serve-time anymore
_, err := w.Write([]byte(g.Name))
if err != nil {
return err
}
return next.ServeHTTP(w, r)
}
```