项目地址:https://github.com/yhyddr/quicksilver/tree/master/gosample/caddy-plugingit
Caddy附带一个HTTP服务器,可是你能够实现其余服务器类型并将它们插入Caddy中。其余类型的服务器能够是SSH、SFTP、TCP、内部使用的其余东西等等。
对于Caddy来讲,服务器的概念是任何能够Listen()
和Serve()
的东西。这意味着什么、如何运做都取决于你。你能够自由地发挥你的创造力去使用它。
那么怎样去扩展 Caddy 呢?
不一样的服务器类型,能够根据本身的须要定制不一样的插件。咱们在这里,经过添加最简单的不作任何事的插件,来熟悉如何扩展 Caddy 服务器。
github
咱们会一步一步构建出一个 HTTP Plugin 的框架,到时候你只须要填充本身处理逻辑便可!那还等什么,让咱们开始吧。
构建一个 HTTP Plugin ,代码部分仅须要两步,注意事项也有两个。
golang
首先为 caddy 建立一个 插件的 Go Package ,你能够新建一个文件夹达到这个效果。好比web
├── caddy-plugin │ ├── gizmo.go │ └── setup.go
这里分为了两个 Go 文件,接下来详细讲每个 Go 文件的做用。bash
首先咱们看到 setup.go服务器
建立 setup.go 文件并写入如下信息app
import "github.com/mholt/caddy" func init() { caddy.RegisterPlugin("gizmo", caddy.Plugin{ ServerType: "http", Action: setup, }) }
这里是 创建了一个新插件,caddy 包来作到插件的注册。框架
如今咱们来实现 setup 函数
假如咱们但愿在Caddyfile中有一行这样的行:函数
gizmo foobar
咱们能够获得刚才所说的 c.Next()
第一个参数(“foobar”)的值,以下所示:ui
for c.Next() { // skip the directive name if !c.NextArg() { // expect at least one value return c.ArgErr() // otherwise it's an error } value := c.Val() // use the value }
咱们首先注意到, c.Next() 是真正咱们读取 caddyfile 逻辑的地方,caddyfile 就是配置服务器的配置文件的名字。咱们注意到,这里的操做其实是使用 caddy.Controller 来实现的。它的存在 让编写插件的开发者只须要关注如何使用它来执行你的命令,这是一项优秀的设计,有兴趣能够看个人源码阅读部分关于 Plugin 的具体实现。
在 Caddy 解析了Caddyfile以后,它将迭代每一个指令名(按照服务器类型规定的顺序),并在每次遇到指令名时调用指令的setup函数。setup函数的职责是解析指令的标识并配置本身。
您能够经过遍历c.Next()
来解析为指令提供的标识,只要有更多的标识须要解析,那么c.Next()
就会返回true
。因为一个指令可能出现屡次,你必须遍历c.Next()
以得到全部出现的指令并使用第一个标识(即指令名)。
有关caddyfile包,请参阅godoc以了解如何更充分地使用分发器,并查看任何其余现有插件。
查看httpserver包的godoc。最重要的两种类型是httpserver.Handler和httpserver.Middleware。
Handler
是一个处理HTTP请求的函数。Middleware
是一种链接Handler
的方式。Caddy将负责为你设置HTTP服务器的全部簿记(bookkeeping)工做,可是你须要实现这两种类型。
httpserver.Handler
是一个几乎和http.Handler
彻底同样的接口,除了ServeHTTP
方法返回(int, error)
。
这个方法签名遵循Go语言博客中关于与中间件相关的错误处理的建议。int
是HTTP状态码,error
应该被处理和/或记录。有关这些返回值的详细信息,请参阅godoc。
Handler
一般是一个结构体,至少包含一个Next
字段,用来连接下一个Handler
:
type gizmoHandler struct { next httpserver.Handler }
除了这些以外,能够添加一些本身使用的参数,考虑 grpc 的 plugin 实现,解释放在代码块中的注释中
type server struct { backendAddr string // 监听地址 next httpserver.Handler // 做为中间件必须有的字段 backendIsInsecure bool // 是否启用 Insecure() 选项,是 grpc 的一项配置 backendTLS *tls.Config // 关于 TLS 的使用的证书文件 wrappedGrpc *grpcweb.WrappedGrpcServer // 经过 grpcweb 的 协议实现 HTTP 请求等 }
这就是参考的一个 字段的使用。能够根据本身的须要,调整在 caddyfile 中读取的指令应该如何配置。
为了实现httpserver.Handler
接口,咱们须要编写一个名为ServeHTTP
的方法。这个方法是实际的处理程序函数,除非它本身处理完毕请求,不然它应该调用链中的下一个Handler
:即便用 g.next.ServeHTTP(w, r)
func (g gizmoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { return g.next.ServeHTTP(w, r) }
这里只是框架,具体逻辑能够自行填充,能够参照已有的 Plugin 实现。
而后咱们能够进行第二步,将这个 handler 注册到整个 caddy 的 http 调用链上。
咱们须要回到 刚才的 setup.go 文件中,
回到设置函数。你刚刚解析了标识并使用全部适当的配置设置了中间件处理程序:
func setup(c *caddy.Controller) error { g := gizmoHandler{} // 用来实现 HTTPHandler 的 next 的结构,用来构建 中间件。也能够加入一些本身的字段 for c.Next() { // 获取配置文件,并处理 } // 如今开始注册中间件 httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { g.next = next return g }) return nil }
这样,代码部分就所有完成了。
下面咱们查看须要注意的事项。其实是关乎于怎样将写好的插件集成在 caddy 中。
要作的事情是告诉服务器类型在进程的什么地方执行你的指令。这一点很重要,由于其余指令可能会设置你所依赖的更原始的配置,所以执行指令的顺序不能是随意的。
每一个服务器类型都有一个字符串列表,其中每一个项都是一个指令的名称。例如,查看HTTP服务器支持的指令列表。将指令添加到适当的位置。
最后,不要忘记导入你的插件包!Caddy必须导入插件来注册并执行它。这一般是在run.go的import
部分的尾部完成的:
_ "your/plugin/package/here"
请注意:包名前的_
是必需的。
就是这样!能够用你的插件来构建caddy,而后用你的新指令写一个Caddyfile来查看它的运行状况。
虽然还没完善的她只是一个框架,还不能作任何事情,可是她很简单,很美不是吗?她能帮你作任何事情。由于记住 caddy 的服务器是设置的很是抽象的。她就想 net 包中 conn 同样完美的 接口设计,可以兼容和扩展任何 须要 listen() 和 serve() 的东西,只要你的创造力足够。
如今,发挥你的想象力,填充这个框架吧,能够参考个人简单项目地址。
项目地址:https://github.com/yhyddr/quicksilver/tree/master/gosample/caddy-plugin
同时记得多多寻找别人的插件实现方式,你会找到让你耳目一新的实现。https://www.yuque.com/fengyfei/idznuk/sumapn
我刚编辑过哦
https://github.com/caddyserver/caddy/wiki/Writing-a-Plugin:-Directives
https://github.com/caddyserver/caddy/wiki/Writing-a-Plugin:-Server-Type
https://github.com/caddyserver/caddy/wiki/Writing-a-Plugin:-HTTP-Middleware