docs: Expand "Extending Caddy" to new Developers section

This commit is contained in:
Matthew Holt 2020-05-28 14:19:14 -06:00
parent 06fef09ff6
commit 2c8897f6a7
No known key found for this signature in database
GPG key ID: 2A349DD577D586A5
5 changed files with 272 additions and 27 deletions

View file

@ -0,0 +1,124 @@
---
title: "Caddyfile Support"
---
# Caddyfile Support
Caddy modules are automatically added to the [native JSON config](/docs/json/) by virtue of their namespace when they are [registered](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#RegisterModule), making them both usable and documented. This makes Caddyfile support purely optional, but it is often requested by users who prefer the Caddyfile.
## Unmarshaler
To add Caddyfile support for your module, simply implement the [`caddyfile.Unmarshaler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc#Unmarshaler) interface. You get to choose the Caddyfile syntax your module has by how you parse the tokens.
An unmarshaler's job is simply to set up your module's type, e.g. by populating its fields, using the [`caddyfile.Dispenser`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc#Dispenser) passed to it. For example, a module type named `Gizmo` might have this method:
```go
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
//
// gizmo <name> [<option>]
//
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if !d.Args(&g.Name) {
// not enough args
return d.ArgErr()
}
if d.NextArg() {
// optional arg
g.Option = d.Val()
}
if d.NextArg() {
// too many args
return d.ArgErr()
}
}
return nil
}
```
It is a good idea to document the syntax in the godoc comment for the method. See the [godoc for the `caddyfile` package](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc) for more information about parsing the Caddyfile.
It is also important for an unmarshaler to accept multiple occurrences of its directive (rare, but can happen in some cases). Since the first token will typically be the module's name or directive (and can often be skipped by the unmarshaler), this usually means wrapping your parsing logic in a `for d.Next() { ... }` loop.
Make sure to check for missing or excess arguments.
You should also add an [interface guard](/docs/extending-caddy#interface-guards) to ensure the interface is satisfied properly:
```go
var _ caddyfile.Unmarshaler = (*Gizmo)(nil)
```
### Blocks
To accept more configuration than can fit on a single line, you may wish to allow a block with subdirectives. This can be done using `d.NextBlock()` and iterating until you return to the original nesting level:
```go
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "sub_directive_1":
// ...
case "sub_directive_2":
// ...
}
}
```
As long as each iteration of the loop consumes the entire segment (line or block), then this is an elegant way to handle blocks.
## HTTP Directives
The HTTP Caddyfile is Caddy's default Caddyfile adapter syntax (or "server type"). It is extensible, meaning you can [register](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc#RegisterDirective) your own "top-level" directives for your module:
```go
func init() {
httpcaddyfile.RegisterDirective("gizmo", parseCaddyfile)
}
```
If your directive only returns a single HTTP handler (as is common), you may find [`RegisterHandlerDirective`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc#RegisterHandlerDirective) easier:
```go
func init() {
httpcaddyfile.RegisterHandlerDirective("gizmo", parseCaddyfileHandler)
}
```
The basic idea is that [the parsing function](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc#UnmarshalFunc) you associate with your directive returns one or more [`ConfigValue`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc#ConfigValue) values. (Or, if using `RegisterHandlerDirective`, it simply returns the populated `caddyhttp.MiddlewareHandler` value directly.) Each config value is associated with a "class" which helps the HTTP Caddyfile adapter to know which part(s) of the final JSON config it can be used in. All the config values get dumped into a pile from which the adapter draws when constructing the final JSON config.
This design allows your directive to return any config values for any recognized classes, which means it can influence any parts of the config that the HTTP Caddyfile adapter has a designated class for.
If you've already implemented the `UnmarshalCaddyfile()` method, then your parse function could be as simple as:
```go
// parseCaddyfileHandler unmarshals tokens from h into a new middleware handler value.
func parseCaddyfileHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var g Gizmo
err := g.UnmarshalCaddyfile(h.Dispenser)
return g, err
}
```
See the [`httpcaddyfile` package godoc](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile?tab=doc) for more information about how to use the `httpcaddyfile.Helper` type.
### Classes
This table describes each class with exported types that is recognized by the HTTP Caddyfile adapter:
Class name | Expected type | Description
---------- | ------------- | -----------
bind | `[]string` | Server listener bind addresses
tls.connection_policy | `*caddytls.ConnectionPolicy` | TLS connection policy
route | `caddyhttp.Route` | HTTP handler route
error_route | `*caddyhttp.Subroute` | HTTP error handling route
tls.cert_issuer | `certmagic.Issuer` | TLS certificate issuer
tls.cert_loader | `caddytls.CertificateLoader` | TLS certificate loader
## Server Types
Structurally, the Caddyfile is a simple format, so there can be different types of Caddyfile formats (sometimes called "server types") to suit different needs.
The default Caddyfile format is the HTTP Caddyfile, which you are probably familiar with. This format primarily configures the [`http` app](/docs/modules/http) while only potentially sprinkling some config in other parts of the Caddy config structure (e.g. the `tls` app to load and automate certificates).
To configure apps other than HTTP, you may want to implement your own config adate

View file

@ -0,0 +1,62 @@
---
title: "Writing Config Adapters"
---
# Writing Config Adapters
For various reasons, you may wish to configure Caddy using a format that is not [JSON](/docs/json/). Caddy has first-class support for this through [config adapters](/docs/config-adapters).
If one does not already exist for the language/syntax/format you prefer, you can write one!
## Template
Here's a template you can start with:
```go
package myadapter
import (
"fmt"
"github.com/caddyserver/caddy/v2/caddyconfig"
)
func init() {
caddyconfig.RegisterAdapter("adapter_name", MyAdapter{})
}
// MyAdapter adapts ____ to Caddy JSON.
type MyAdapter struct{
}
// Adapt adapts the body to Caddy JSON.
func (a MyAdapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []caddyconfig.Warning, error) {
// TODO: parse body and convert it to JSON
return nil, nil, fmt.Errorf("not implemented")
}
```
- See godoc for [`RegisterAdapter()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig?tab=doc#RegisterAdapter)
- See godoc for ['Adapter'](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig?tab=doc#Adapter) interface
The returned JSON should **not** be indented; it should always be compact. The caller can always prettify it if they want to.
Note that while config adapters are Caddy _plugins_, they are not Caddy _modules_ because they do not integrate into a part of the config (but they will show up in `list-modules` for convenience). Thus, they do not have `Provision()` or `Validate()` methods or follow the rest of the module lifecycle. They need only implement the `Adapter` interface and be registered as adapters.
When populating fields of the config that are `json.RawMessage` types (i.e. module fields), use the `JSON()` and `JSONModuleObject()` functions:
- [`caddyconfig.JSON()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig?tab=doc#JSON) is for marshaling module values without the module name embedded. (Often used for ModuleMap fields where the module name is the map key.)
- [`caddyconfig.JSONModuleObject()`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig?tab=doc#JSONModuleObject) is for marshaling module values with the module name added to the object. (Used pretty much everywhere else.)
## Caddyfile Server Types
It is also possible to implement a custom Caddyfile format. The Caddyfile adapter is a single adapter implementation and its default "server type" is HTTP, but it supports alternate "server types" at registration. For example, the HTTP Caddyfile is registered like so:
```go
func init() {
caddyconfig.RegisterAdapter("caddyfile", caddyfile.Adapter{ServerType: ServerType{}})
}
```
You would implement the [`caddyfile.ServerType` interface](https://pkg.go.dev/github.com/caddyserver/caddy/v2/caddyconfig/caddyfile?tab=doc#ServerType) and register your own adapter accordingly.

View file

@ -0,0 +1,31 @@
---
title: "Module Namespaces"
---
# Module Namespaces
Caddy guest modules are loaded generically as `interface{}` types. In order for the host modules to be able to use them, the loaded guest modules are usually type-asserted to a known type first. This page describes the mapping from module namespaces to Go types for all the standard modules.
Documentation for non-standard module namespaces can be found with the documentation for the host module that defines them.
<aside class="tip">
One way to read this table is, "If your module is in &lt;namespace&gt;, then it should compile as &lt;type&gt;."
</aside>
Namespace | Expected Type | Description
--------- | ------------- | -----------
| | [`caddy.App`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#App) | Caddy app
caddy.logging.encoders.filter | [`logging.LogFieldFilter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/logging?tab=doc#LogFieldFilter) | Log field filter</i>
caddy.logging.writers | [`caddy.WriterOpener`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#WriterOpener) | Log writers
caddy.storage | [`caddy.StorageConverter`](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#StorageConverter) | Storage backends
http.authentication.hashes | [`caddyauth.Comparer`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth?tab=doc#Comparer) | Password hashers/comparers
http.authentication.providers | [`caddyauth.Authenticator`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/caddyauth?tab=doc#Authenticator) | HTTP authentication providers
http.handlers | [`caddyhttp.MiddlewareHandler`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp?tab=doc#MiddlewareHandler) | HTTP handlers
http.matchers | [`caddyhttp.RequestMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp?tab=doc#RequestMatcher) | HTTP request matchers
http.reverse_proxy.circuit_breakers | [`reverseproxy.CircuitBreaker`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy?tab=doc#CircuitBreaker) | Reverse proxy circuit breakers
http.reverse_proxy.selection_policies | [`reverseproxy.Selector`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy?tab=doc#Selector) | Load balancing selection policies<br><i>⚠️ Subject to change</i>
http.reverse_proxy.transport | [`http.RoundTripper`](https://pkg.go.dev/net/http?tab=doc#RoundTripper) | HTTP reverse proxy transports
tls.certificates | [`caddytls.CertificateLoader`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#CertificateLoader) | TLS certificate source</i>
tls.handshake_match | [`caddytls.ConnectionMatcher`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#ConnectionMatcher) | TLS connection matcher</i>
tls.issuance | [`certmagic.Issuer`](https://pkg.go.dev/github.com/caddyserver/certmagic?tab=doc#Issuer) | TLS certificate issuer<br><i>⚠️ Subject to change</i>
tls.stek | [`caddytls.STEKProvider`](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddytls?tab=doc#STEKProvider) | TLS session ticket key source</i>