文档
一个 项目,由 ZeroSSL 出品

Caddyfile 支持

Caddy 模块通过其命名空间自动添加到原生 JSON 配置中,当它们被注册时,使其既可用又可被文档化。这使得 Caddyfile 支持成为纯粹可选的功能,但它经常被喜欢 Caddyfile 的用户所要求。

Unmarshaler (反序列化器)

要为您的模块添加 Caddyfile 支持,只需实现 caddyfile.Unmarshaler 接口。您可以根据解析令牌的方式来选择模块的 Caddyfile 语法。

反序列化器的任务很简单,就是设置模块的类型,例如,通过使用传递给它的 caddyfile.Dispenser 来填充其字段。例如,一个名为 Gizmo 的模块类型可能具有以下方法:

// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
//
// gizmo <name> [<option>]
//
func (g *Gizmo) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
	d.Next() // consume directive name

	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
}

最好在方法的 godoc 注释中记录语法。有关解析 Caddyfile 的更多信息,请参阅 caddyfile 包的 godoc

指令名称令牌可以使用简单的 d.Next() 调用来消耗/跳过。

请务必使用 d.NextArg()d.RemainingArgs() 检查缺失和/或多余的参数。使用 d.ArgErr() 获取简单的“无效情况”消息,或使用 d.Errf("一些消息") 来制作有用的错误消息,其中包含问题的解释(以及理想情况下,建议的解决方案)。

您还应该添加一个 接口守卫,以确保正确满足接口。

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 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
}

有关如何使用 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中。由于路由块中的指令不会重新排序,因此路由块中使用的指令不需要出现在列表中。

如果您选择后两种选项之一,请为您的用户记录关于列表中哪个位置是正确的位置来排序您的指令的建议,以便他们可以正确使用它。

下表描述了 HTTP Caddyfile 适配器识别的每个类及其导出的类型:

类名 预期类型 描述
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 配置。