如何爲 caddy 添寫自定義插件

如何爲 caddy 添寫自定義插件

項目地址:https://github.com/yhyddr/quicksilver/tree/master/gosample/caddy-plugingit


前言

Caddy附帶一個HTTP服務器,可是你能夠實現其餘服務器類型並將它們插入Caddy中。其餘類型的服務器能夠是SSH、SFTP、TCP、內部使用的其餘東西等等。


對於Caddy來講,服務器的概念是任何能夠Listen()Serve()的東西。這意味着什麼、如何運做都取決於你。你能夠自由地發揮你的創造力去使用它。


那麼怎樣去擴展 Caddy 呢?
不一樣的服務器類型,能夠根據本身的須要定製不一樣的插件。咱們在這裏,經過添加最簡單的不作任何事的插件,來熟悉如何擴展 Caddy 服務器。
github

Plugin for HTTP

咱們會一步一步構建出一個 HTTP Plugin 的框架,到時候你只須要填充本身處理邏輯便可!那還等什麼,讓咱們開始吧。


構建一個 HTTP Plugin ,代碼部分僅須要兩步,注意事項也有兩個。
golang

建立一個 Go Package

首先爲 caddy 建立一個 插件的 Go Package ,你能夠新建一個文件夾達到這個效果。好比web

├── caddy-plugin
│   ├── gizmo.go
│   └── setup.go

這裏分爲了兩個 Go 文件,接下來詳細講每個 Go 文件的做用。bash

代碼🀙:註冊 caddy plugin

首先咱們看到 setup.go服務器

setup.go

建立 setup.go 文件並寫入如下信息app

import "github.com/mholt/caddy"

func init() {
    caddy.RegisterPlugin("gizmo", caddy.Plugin{
        ServerType: "http",
        Action:     setup,
    })
}

這裏是 創建了一個新插件,caddy 包來作到插件的註冊。框架

  1. 注意到 「gizmo」 這是 插件的名字,同時也是指令的名字,請爲你的插件取一個獨一無二的名字吧。(注意:名字須要是單詞小寫哦。)
  2. 由於是針對 HTTP 服務器的插件,因此 ServerType 字段值是 「http」
  3. 另外一個設置的字段是 setup ,實際上,咱們接下來會填充這個函數的邏輯。它的做用就是將咱們插件的處理邏輯安裝到 Caddy 中。

setup

如今咱們來實現 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以瞭解如何更充分地使用分發器,並查看任何其餘現有插件。

代碼 🀚:Handler 實現

gizmo.go:

查看httpserver包的godoc。最重要的兩種類型是httpserver.Handlerhttpserver.Middleware

  1. Handler是一個處理HTTP請求的函數。
  2. Middleware是一種鏈接Handler的方式。

Caddy將負責爲你設置HTTP服務器的全部簿記(bookkeeping)工做,可是你須要實現這兩種類型。

Struct

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

爲了實現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 實現。

第二步,註冊 Middleware

而後咱們能夠進行第二步,將這個 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.goimport部分的尾部完成的:

_ "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

參考

image.png
我剛編輯過哦
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

相關文章
相關標籤/搜索