1. 程式人生 > 程式設計 >如何為 caddy 添寫自定義外掛

如何為 caddy 添寫自定義外掛

如何為 caddy 添寫自定義外掛

專案地址:github.com/yhyddr/quic…


前言

Caddy附帶一個HTTP伺服器,但是你可以實現其他伺服器型別並將它們插入Caddy中。其他型別的伺服器可以是SSH、SFTP、TCP、內部使用的其他東西等等。

對於Caddy來說,伺服器的概念是任何可以Listen()Serve()的東西。這意味著什麼、如何運作都取決於你。你可以自由地發揮你的創造力去使用它。

那麼怎樣去擴充套件 Caddy 呢?
不同的伺服器型別,可以根據自己的需要定製不同的外掛。我們在這裡,通過新增最簡單的不做任何事的外掛,來熟悉如何擴充套件 Caddy 伺服器。

Plugin for HTTP

我們會一步一步構建出一個 HTTP Plugin 的框架,到時候你只需要填充自己處理邏輯即可!那還等什麼,讓我們開始吧。

構建一個 HTTP Plugin ,程式碼部分僅需要兩步,注意事項也有兩個。

建立一個 Go Package

首先為 caddy 建立一個 外掛的 Go Package ,你可以新建一個資料夾達到這個效果。比如

├── caddy-plugin
│   ├── gizmo.go
│   └── setup.go
複製程式碼

這裡分為了兩個 Go 檔案,接下來詳細講每一個 Go 檔案的作用。

程式碼?:註冊 caddy plugin

首先我們看到 setup.go

setup.go

建立 setup.go 檔案並寫入以下資訊

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”)的值,如下所示:

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() 的東西,只要你的創造力足夠。

現在,發揮你的想象力,填充這個框架吧,可以參考我的簡單專案地址。
專案地址:github.com/yhyddr/quic…

同時記得多多尋找別人的外掛實現方式,你會找到讓你耳目一新的實現。www.yuque.com/fengyfei/id…

參考

image.png

我剛編輯過哦
github.com/caddyserver…
github.com/caddyserver…
github.com/caddyserver…