在开发Caddy插件的时候,很多现成的配置可以为我们所用,这样我们插件中的某些配置就可以遵循整个Caddyfile的全局统一配置,用户体验会更好,比如使用统一的debug来控制自己开发插件的日志输出等等。

什么是全局配置

Caddyfile的全局配置和整个Caddy有关,并不局限于某个指令、请求等。在Caddy中有一些配置使用了默认的配置,但是Caddy同时也提供了全局的配置,可以让我们修改某些配置的默认值。

比如在Caddyfile中,debug默认是关闭的,我们可以通过全局配置来启用它。

1
2
3
{
  debug
}

在整个Caddyfile的最顶部,使用一个单独的 {} 块,这个块就是自定义全局配置的地方,比如以上示例代码,我们开启了 debug 模式,这将会把所有的日志级别都设置为 debug 。 还有一个我们曾经用过的 order 其实也是一个全局配置,用它来自定义指令的顺序:

1
2
3
4
{
  order hello_world last
  debug
}

Caddyfile提供的全局配置有很多, orderdebug 只是其中的两个,比如还有 http_port 用来设置http的端口,默认是80。

这里给出所有的配置列表:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{
	# General Options
	debug
	http_port  <port>
	https_port <port>
	order <dir1> first|last|[before|after <dir2>]
	storage <module_name> {
		<options...>
	}
	storage_clean_interval <duration>
	admin   off|<addr> {
		origins <origins...>
		enforce_origin
	}
	log [name] {
		output  <writer_module> ...
		format  <encoder_module> ...
		level   <level>
		include <namespaces...>
		exclude <namespaces...>
	}
	grace_period <duration>
	# TLS Options
	auto_https off|disable_redirects|ignore_loaded_certs
	email <yours>
	default_sni <name>
	local_certs
	skip_install_trust
	acme_ca <directory_url>
	acme_ca_root <pem_file>
	acme_eab <key_id> <mac_key>
	acme_dns <provider> ...
	on_demand_tls {
		ask      <endpoint>
		interval <duration>
		burst    <n>
	}
	key_type ed25519|p256|p384|rsa2048|rsa4096
	cert_issuer <name> ...
	ocsp_stapling off
	preferred_chains [smallest] {
		root_common_name <common_names...>
		any_common_name  <common_names...>
	}
	# Server Options
	servers [<listener_address>] {
		listener_wrappers {
			<listener_wrappers...>
		}
		timeouts {
			read_body   <duration>
			read_header <duration>
			write       <duration>
			idle        <duration>
		}
		max_header_size <size>
		protocol {
			allow_h2c
			experimental_http3
			strict_sni_host
		}
	}
}

以上就是Caddyfile内置支持的全局配置,关于他们的说明可以从字面意思上推测出来,当然你也可以查看这个 https://caddyserver.com/docs/caddyfile/options 文档,了解更详细的说明 。

使用全局配置

以上这些全局配置,我们也可以在我们自己定义的插件中使用的,现在我就为你讲解如何使用他们,下面就以 debug 这个配置为例。

1
2
3
4
type HelloWorld struct {
   Text  string `json:"text,omitempty"`
   Debug bool   `json:"debug,omitempty"`
}

首先增加一个 Debug 字段用于存储是否需要Debug。 这个字段并不是我们控制,而是要用过全局配置来控制,需要我们需要读取全局配置中 debug 的值,用于控制是否需要Debug。

1
2
3
4
5
6
7
8
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
   hw := new(HelloWorld)
   if debug, ok := h.Option("debug").(bool); ok {
      hw.Debug = debug
   }
   err := hw.UnmarshalCaddyfile(h.Dispenser)
   return hw, err
}

在解析Caddyfile文件的时候,通过 h.Option 方法即可获取指定全局配置的值,这里我们获取的是 debug ,然后赋予 HelloWorldDebug 字段。 好了,现在我们的插件也可以通过全局配置 debug 开关Debug模式了。

自定义全局配置

除了可以使用Caddy自带的全局配置外,我们还可以自定义自己的全局配置,Caddy的这种能力为我们提供了更强大的扩展性。

下面我还以这个debug模式为例,使用我们自定义的全局配置,看如何实现。需要注意的是这个纯属演示自定义全局配置的能力,实践中还是要使用内置的debug这个全局配置。

要先自定义自己的全局配置,首先我们需要注册它:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func init() {
   caddy.RegisterModule(&HelloWorld{})
   httpcaddyfile.RegisterGlobalOption("hello_debug", parseOptHelloDebug)
   httpcaddyfile.RegisterHandlerDirective("hello_world", parseCaddyfile)
}
func parseOptHelloDebug(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
   var debug int
   for d.Next() {
      var debugStr string
      if !d.AllArgs(&debugStr) {
         return 0, d.ArgErr()
      }
      var err error
      debug, err = strconv.Atoi(debugStr)
      if err != nil {
         return 0, d.Errf("converting port '%s' to integer value: %v", debugStr, err)
      }
   }
   return debug, nil
}

是的,就在我们的 init 方法中,使用函数 httpcaddyfile.RegisterGlobalOption 就可以新增一个自定义的全局配置。 parseOptHelloDebug 用来解析出Caddyfile全局配置中 hello_debug 的值,这里为了演示,我定义的hello_debug 的值是 int 类型, 1 表示开启debug模式, 0 表示不开启。

现在解析Caddyfile得到 hello_debug 的值,就可以在刚刚 parseCaddyfile 函数中使用它了,我们稍微修改下即可:

1
2
3
4
5
6
7
8
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
   hw := new(HelloWorld)
   if debug, ok := h.Option("hello_debug").(int); ok {
      hw.Debug = debug == 1
   }
   err := hw.UnmarshalCaddyfile(h.Dispenser)
   return hw, err
}

只需要把原来的 bool 断言换成 int 断言,并且值为 1 才被认为是开启Debug模式。

小结

现在,你已经拿到了 Debug 的值,你可以在你插件的任何地方使用它,来打印你想打印的日志,一般我们会这样:

1
2
3
if (h.Debug){
  // 日志输出
}

Caddy 提供的全局配置以及可以自定义的能力,为Caddy的能力扩展以及插件开发都提供了很好的扩展,我们可以利用这个能力,大大丰富自己插件的可自定义化行为。

但是最终我想提醒的是,如果Caddy已经定义了你需要的全局配置,那么最好和他们保持一致。

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站 https://www.flysnow.org/ ,第一时间看后续精彩文章。觉得好的话,请猛击文章右下角「在看」,感谢支持。

扫码关注