跳到內容

設定模型

Pingclairfile 在載入時編譯一次,成為伺服器實際執行的執行期狀態。由此產生兩個結果,也解釋了這個專案大部分的行為:設定能決定的事都在第一個請求之前完成;而無法被滿足的設定會讓伺服器停止,而不是在請求當下妥協。

檔案包含一個選用的 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 用來選出某個 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 是可重複使用的片段,以 (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 語法,但仍然會被拒絕,而且理由正確:它指名的憑證路徑不存在於執行檢查的機器上。

同樣的檢查會在載入時執行,因此重新載入時若設定驗證失敗,先前的狀態會保持不變。

pc service reload 會重新讀取設定,而不重啟行程。於啟動階段建立的行程級策略,例如 trusted_proxies,要在重啟後才會生效。