設定模型
Pingclairfile 在載入時編譯一次,成為伺服器實際執行的執行期狀態。由此產生兩個結果,也解釋了這個專案大部分的行為:設定能決定的事都在第一個請求之前完成;而無法被滿足的設定會讓伺服器停止,而不是在請求當下妥協。
🗂️ 檔案結構
Section titled “🗂️ 檔案結構”檔案包含一個選用的 global options 區塊,後面接著一或多個 site block。
{ email admin@example.com}
example.com { encode zstd gzip reverse_proxy 10.0.0.10:8080 10.0.0.11:8080}
:8080 { file_server ./public}- Global options 寫在檔案最前面、沒有名稱的區塊裡,用於設定不屬於單一 site 的狀態:ACME 帳號信箱、Admin API、自動 HTTPS 行為、trusted proxies,以及主機名上游的 DNS 重新解析。可用選項列在指令參考。
- Site block 以位址命名:主機、連接埠,或兩者兼具。連接埠屬於位址的一部分,而不是另一個獨立指令,因此只有一個地方需要保持兩者一致。
- Directive 是 site block 內的敘述句。有些接受參數串列,有些接受巢狀區塊,有些兩者都接受。
- 註解以
#開始,延伸到行尾。 - 含空白的值要加引號。 時間長度要帶單位:
30s是三十秒,而在需要時間長度的地方寫裸數字30會被拒絕。
🧭 Matcher
Section titled “🧭 Matcher”Matcher 用來選出某個 directive 要套用到哪些請求。具名 matcher 以 @name 宣告,之後以名稱引用:
example.com { @api path /api/* header @api Cache-Control "no-store"
@assets path /assets/* header @assets Cache-Control "public, max-age=31536000, immutable"}handle 區塊依路由群組化行為,並支援不帶 matcher 的 fallback:
example.com { handle /assets/* { file_server ./assets }
handle { respond "Page Not Found" 404 }}🧩 Snippet 與 import
Section titled “🧩 Snippet 與 import”Snippet 是可重複使用的片段,以 (name) { ... } 宣告,並用 import name 引入。Snippet 也能接收呼叫端提供的區塊,並把它插入到片段中寫 {block} 的位置:
(site) { https://{args[0]} { {block} }}
import site example.com { reverse_proxy 127.0.0.1:3000}在被 import 的檔案裡定義的 snippet,對之後的 import 是可見的。放在參數串列中的 placeholder 會被拒絕,因為 directive 樹無法像 token 層那樣在插入後重新解析該行。
pingclair validate 會編譯檔案並套用語意檢查:指令參數、matcher 語法、憑證與金鑰路徑,以及諸如「哪些對端可以宣稱用戶端身分標頭」之類的策略限制。
失敗一律明確且封閉:
- 未實作的名稱會以名稱拒絕。 格式定義的每個名稱都會被辨識,伺服器未實作的會產生「功能不存在」的訊息。它不會被當成拼字錯誤,也不會被忽略:含有這類名稱的設定不會啟動。
- 無法兌現的選項會被拒絕,而不是降級。 例如在
encode中指定 Brotli 會是編譯錯誤,因為代理沒有串流 Brotli 編碼器;伺服器不會悄悄改用 gzip。 - 語法正確但引用不存在素材的檔案仍會被拒絕。 倉庫中的
examples/full_featured.pingclair是合法的 Caddyfile 語法,但仍然會被拒絕,而且理由正確:它指名的憑證路徑不存在於執行檢查的機器上。
同樣的檢查會在載入時執行,因此重新載入時若設定驗證失敗,先前的狀態會保持不變。
🔁 重新載入
Section titled “🔁 重新載入”pc service reload 會重新讀取設定,而不重啟行程。於啟動階段建立的行程級策略,例如 trusted_proxies,要在重啟後才會生效。
