This commit is contained in:
a 2024-08-21 15:51:01 -05:00
parent 985e365c9d
commit ecf57209ec
No known key found for this signature in database
GPG key ID: 374BC539FE795AF0

View file

@ -10,7 +10,7 @@ This means that if you wish for your plugin to support placeholders, you must ex
If you are not yet familiar with placeholders, start by reading [here](/docs/conventions#placeholders)!
## Placeholder Internals
## Placeholders Overview
Placeholders are a string in the format `{foo.bar}` used as dynamic configuration values, which is later evaluated at runtime.
@ -82,29 +82,25 @@ Since `srv1` used `{env.HOST}`, a normal placeholder, it was parsed as its own r
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.
## How to use placeholders in your plugin
## Implementing placeholder support
#### Parse the raw placeholder value in your unmarshaler
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 or `Provision()` using a `caddy.Replacer`.
Placeholders are not evaluated at Caddyfile parse time, and should be preserved for later use. They are used as their raw string values.
In other words, parsing a placeholder is no different from parsing any other string.
### Examples
In this example, we are using a newly constructed replacer to process placeholders. It has access to placeholders such as `{env.HOST}`, but NOT `{http.request.uri}`
```go
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next()
if !d.Args(&g.Name) {
// not enough args
return d.ArgErr()
}
func (g *Gizmo) Provision(ctx caddy.Context) error {
repl := caddy.NewReplacer()
g.Name = repl.ReplaceAll(g.Name,"")
return nil
}
```
#### Evaluate the placeholder during Match or Serve
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.
This gives us a string with all valid replacements done, which we can then use in whichever way we want. In the example, we write those bytes to output
Here, we extract a replacer out of the `context.Context` inside the `*http.Request`. This replacer has access to http placeholers, such as `{http.request.uri}`
```go
func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
@ -116,25 +112,3 @@ func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp
return next.ServeHTTP(w, r)
}
```
#### Alternatively, resolve the placeholder during Provision
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.
```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)
}
```