Caddyfile 支持
Caddy 模块在被注册后,会根据其命名空间自动添加到 本地 JSON 配置 中,从而使它们既可用又有文档说明。这使得 Caddyfile 支持完全可选,但经常有偏好使用 Caddyfile 的用户提出需求。
Unmarshaler
要为你的模块添加 Caddyfile 支持,只需实现 caddyfile.Unmarshaler 接口。你可以通过解析标记来决定模块采用的 Caddyfile 语法。
Unmarshaler 的工作仅仅是设置你的模块类型,例如通过填充其字段,使用传入的 caddyfile.Dispenser。例如,名为 Gizmo 的模块类型可能有如下方法:
// UnmarshalCaddyfile 实现了 caddyfile.Unmarshaler。语法:
//
// gizmo <name> [<option>]
//
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // 消耗指令名
if !d.Args(&g.Name) {
// 参数不足
return d.ArgErr()
}
if d.NextArg() {
// 可选参数
g.Option = d.Val()
}
if d.NextArg() {
// 参数过多
return d.ArgErr()
}
return nil
}
在方法的 godoc 注释中记录语法是个好主意。关于解析 Caddyfile 的更多信息,请参阅 caddyfile 包的 godoc。
指令名标记可以通过简单的 d.Next() 调用来消费/跳过。
确保使用 d.NextArg() 或 d.RemainingArgs() 检查缺失和/或过多的参数。对于简单的“无效情况”消息,使用 d.ArgErr(),或者使用 d.Errf("some message") 来构造带有问题说明(并且理想情况下带有建议解决方案)的有帮助错误信息。
你还应该添加一个 接口守卫 以确保接口被正确实现:
var _ caddyfile.Unmarshaler = (*Gizmo)(nil)
块
为了接受超出单行的更多配置,你可能希望允许带有子指令的块。这可以使用 d.NextBlock() 并迭代直到回到原始嵌套级别来完成:
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "sub_directive_1":
// ...
case "sub_directive_2":
// ...
}
}
只要循环的每次迭代都消费完整个段(行或块),这就是处理块的一种优雅方式。
HTTP 指令
HTTP Caddyfile 是 Caddy 的默认 Caddyfile 适配器语法(或“服务器类型”)。它是可扩展的,这意味着你可以为你的模块注册 自己的“顶层”指令:
func init() {
httpcaddyfile.RegisterDirective("gizmo", parseCaddyfile)
}
如果你的指令只返回单个 HTTP 处理器(这是常见情况),你可能会发现使用 RegisterHandlerDirective 更简单:
func init() {
httpcaddyfile.RegisterHandlerDirective("gizmo", parseCaddyfileHandler)
}
基本思路是,你关联到指令的解析函数返回一个或多个 ConfigValue 值。(或者,如果使用 RegisterHandlerDirective,它直接返回已填充的 caddyhttp.MiddlewareHandler 值。)每个配置值都与一个“类”关联,这有助于 HTTP Caddyfile 适配器知道它可以用于最终 JSON 配置的哪些部分。所有配置值都会被丢入一个堆中,适配器在构造最终 JSON 配置时从中取出所需内容。
这种设计允许你的指令为任何已识别的类返回任意配置值,这意味着它可以影响 HTTP Caddyfile 适配器为其指定类的任何配置部分。
如果你已经实现了 UnmarshalCaddyfile() 方法,那么你的解析函数可以像下面这样简单:
// parseCaddyfileHandler 从 h 中解组标记到一个新的中间件处理器值中。
func parseCaddyfileHandler(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var g Gizmo
err := g.UnmarshalCaddyfile(h.Dispenser)
return g, err
}
有关如何使用 httpcaddyfile.Helper 类型的更多信息,请参阅 httpcaddyfile 包的 godoc。
处理器顺序
所有返回 HTTP 中间件/处理器值的指令都需要按正确的顺序进行评估。例如,一个设置站点根目录的处理器必须位于访问根目录的处理器之前,以便后者知道目录路径是什么。
HTTP Caddyfile 为标准指令有一个硬编码的顺序。这确保了用户不需要了解其 Web 服务器最常见功能的实现细节,并使他们更容易编写正确的配置。单一的硬编码列表也能防止在 Caddyfile 可扩展性的情况下出现非确定性行为。
当你注册一个新的处理器指令时,必须在它被使用之前将其添加到该列表中(在 route 块之外)。 这可以通过三种方法之一来完成:
-
(推荐)插件作者可以在
init()中在注册指令之后调用httpcaddyfile.RegisterDirectiveOrder,将该指令相对于另一个标准指令插入顺序中。这样,用户就可以在他们的站点中直接使用该指令,而无需额外设置。例如,要将你的指令gizmo插入为在header处理器之后评估:httpcaddyfile.RegisterDirectiveOrder("gizmo", httpcaddyfile.After, "header") -
用户可以添加
order全局选项 来修改其 Caddyfile 的标准顺序。例如:order gizmo before respond将把新指令gizmo插入到在respond处理器之前评估的位置。然后该指令可以正常使用。 -
用户可以将指令放在
route块 中。因为 route 块中的指令不会被重新排序,所以 route 块中使用的指令不需要出现在列表中。
如果你选择后两种中的任意一种,请为你的用户记录建议,说明你的指令在列表中的哪个位置是合适的,以便他们可以正确使用它。
类
下表描述了 HTTP Caddyfile 适配器识别的、具有导出类型的每个类:
| Class name | Expected type | Description |
|---|---|---|
| bind | []string |
服务器监听绑定地址 |
| route | caddyhttp.Route |
HTTP 处理器路由 |
| error_route | *caddyhttp.Subroute |
HTTP 错误处理路由 |
| tls.connection_policy | *caddytls.ConnectionPolicy |
TLS 连接策略 |
| tls.cert_issuer | certmagic.Issuer |
TLS 证书颁发者 |
| tls.cert_loader | caddytls.CertificateLoader |
TLS 证书加载器 |
服务器类型
从结构上看,Caddyfile 是一种简单的格式,因此可以有不同类型的 Caddyfile 格式(有时称为“服务器类型”)以满足不同需求。
默认的 Caddyfile 格式是你可能熟悉的 HTTP Caddyfile。该格式主要配置 http 应用,同时仅在 Caddy 配置结构的其他部分(例如 tls 应用以加载和自动化证书)中点缀一些配置。
要配置除 HTTP 之外的应用,你可能希望实现自己的配置适配器,该适配器使用你自己的服务器类型。Caddyfile 适配器实际上会为你解析输入并给出服务器块和选项的列表,而由你的适配器来理解该结构并将其转换为 JSON 配置。