一個極簡、無負擔的網頁框架,專為現代 C++ 環境設計。快速、結構化且嚴格型別。

Vermell 是為現代 C++ 環境設計的網頁框架:一個標頭檔引入,一個靜態函式庫連結,僅此而已。沒有運行時,沒有垃圾回收器,沒有框架特定的 DSL,沒有外部依賴 — 您編寫的是 C++,運行的是 C++。

底層是一個事件驅動引擎:一個非阻塞的 epoll 迴圈讀取請求並將工作分配給一個工作執行緒池。這種分離使得 Vermell 在負載下表現快速,並能抵禦慢速客戶端。

📚 完整文件:vermell.cc — 雙語(EN/ES)手冊,涵蓋此 README 的每個部分,包含範例和圖表。

適用於 amd64、arm64 和 armhf 的套件位於 GitHub Pages,已簽署並可供添加:

金鑰指紋:022D 56AA 7A6B 2028 B005 3629 F616 54D8 8AD1 C323

Vermell 伺服器是一個 Router:為一個路由註冊一個處理器,選擇一個埠,然後呼叫 listen()。

處理器周圍的 { ... } 很重要。router.get(...) 的第二個參數是 MiddlewareList,因此處理器總是作為一個大括號列表傳遞:router.get("/", { [](Query &http) { ... } })

然後將您的瀏覽器或 curl 指向它:

router.listen() 會阻塞並永遠服務。listenOne() 服務單一請求後返回 — 對於測試和一次性伺服器非常方便。

單一 g++ 調用即可編譯和連結所有內容 — 無需額外旗標,無需處理連結順序:

對於較大的專案,請使用 CMake,但伺服器始終只需一個命令。

可移植性。Vermell 沒有依賴,因此任何基於 Linux 並擁有 C++20 g++ 的系統都可以建置它:x86_64、ARM (aarch64, armv7)、透過 Termux 的 Android、WSL、Raspberry Pi、容器。macOS 和 Windows 不支援(epoll)。

沒有 root?沒問題。在 Termux(或任何沒有 root 的系統)上,您無法將其安裝到 /usr/local。透過相對路徑引入標頭檔(#include "../include/vermell/vermell.h")並直接連結靜態函式庫 — 將 libvermell.a 複製到您的原始碼旁邊,並使用 -L. -lvermell 編譯:

路由器為每個 HTTP verb 提供一個註冊方法。靜態路由透過 O(1) 的透明雜湊路由映射進行分派。

注意 deleteX() 名稱:delete 是 C++ 的關鍵字。對於較大的應用程式,請單獨宣告路由並使用 router.use() 掛載它們:

每個處理器都是一個 C++ lambda void(Query&)。[ 和 ] 之間的捕獲列表決定了外部狀態如何到達它:

執行緒安全。處理器在工作執行緒上運行,並在整個伺服器生命週期內存在。[&] 捕獲是對註冊範圍的引用:對於比 listen() 壽命長的變數來說沒問題,但切勿捕獲早於其壽命的堆疊局部變數 — 那是懸空引用。由於請求是並發運行的,透過引用捕獲的共享可變狀態需要互斥鎖;偏好使用 [=] 進行不可變快照。

請求/響應管道的每個旋鈕都位於 vermell::Config (include/vermell/config.hpp) 中。透過 router.configure({...}) 整個傳遞(預設值保留了舊行為):

加固的預設值:read_chunk 被限制在 [1, 1 MiB] 之間,max_events 在 [1, 65536] 之間,threads 在 [0, 256] 之間,每個超時時間在 [1ms, INT_MAX ms] 之間 — 荒謬的值是記憶體/DoS 的陷阱,而不是功能。對於沒有確切一個 Host 標頭的 HTTP/1.1(或更新版本)請求,將以 400 回應(RFC 9112 §3.2,代理同步/請求偽裝向量);HTTP/1.0 的舊版客戶端仍可正常工作。預設情況下,max_connections 是有界限的(1024),因此連接洪水無法耗盡記憶體。

Slowloris 不再是 DoS:請求位元組在事件迴圈(非阻塞)上讀取,因此一個涓涓細流的客戶端會佔用一個 epoll fd — 受 max_connections 和 read_timeout / request_timeout 截止時間的限制 — 而不是工作執行緒。一個每隔幾秒發送 1 個位元組數小時的客戶端,一旦整個請求超過 request_timeout 就會以 408 被丟棄。當任務佇列已滿時,調度器會捨棄連線(503),而不是停滯接受迴圈。

configure() 會取代**整個**配置(建議使用指定初始化器):先前使用設定器所做的設定將被捨棄,因此請一次性傳遞所有內容。configure() 也適用於正在運行的伺服器 — 超時時間、限制和執行緒計數會被事件迴圈和工作集區即時拾取 (RequestIO::ApplyConfig);只有網路端的旋鈕(port、backlog、reuse_port)需要重新啟動。

運行時可透過 router.config() 讀取目前的配置。一個完整的註釋範例位於 examples/configuration。

Vermell 從檔案的最終副檔名檢測 Content-Type。這意味著 kevin.txt.html 會以 text/html 服務,且匹配不區分大小寫。查詢字串和片段在確定類型時會被忽略。未知副檔名使用 application/octet-stream。

傳遞給 readFile 的顯式類型始終優先。註冊表包含常見的文字、資料、文件、圖像、音訊、視訊、字型、封存和可執行檔格式。

Node 的 express.static 作為 Vermell 的掛載點:使用一個呼叫將磁碟目錄 — Vue、React 或 Angular 的 dist 資料夾、純資產,任何東西 — 綁定到一個 URL 前綴。static 是 C++ 的關鍵字,因此有 X 後綴(與 deleteX 相同約定)。

完整選項位於 vermell::StaticOptions (include/vermell/util/static_files.h)。一個完整的範例 — SPA dist + 經典掛載 + 一個 JSON API 並排 — 位於 examples/static。

compose() 從模板中引用的 #[name] 組件組裝一個 HTML 頁面。一個(遞歸)包含自身的頁面會回應 413 而不是耗盡記憶體:

render() 透過 dataRender 回呼函數填入 HTML 模板中的 [[variable]] 佔位符:

檔案渲染方法(readFile、file、compose、render)透過 Config::render 加固:

僅透過引入 vermell/vermell.h 即可獲得 Node.js 風格的運行時資訊和配置。

vermell::process 在第一次使用時捕獲進程數據一次:

vermell::environment 會自動載入可執行檔旁邊的 .env 檔案,並持有運行時「會話」值。來自檔案和 set() 的值優先於作業系統環境;所有方法都是執行緒安全的。

.env 語法支援 # 註釋、export KEY=VALUE、引號值和尾部註釋。請參閱 examples/process 和 examples/environment。

在 examples/ 資料夾中,您會找到針對不同使用案例的獨立伺服器:

tests/debug.cpp 是一個用於測試已安裝函式庫的 Vermell 的草稿板 — 它故意不包含在 CMake 建置中。將 libvermell.a 複製到它旁邊並手動編譯:

並自由編輯 tests/debug.cpp 檔案。

歡迎貢獻!如果您想為 Vermell 做出貢獻,請遵循這些準則:

請在貢獻前閱讀 CODE_OF_CONDUCT.md,並在提交錯誤報告和功能請求時使用 .github/ISSUE_TEMPLATE 中的問題範本。

此專案採用 MIT 授權。

一個極簡、無負擔的網頁框架,專為現代 C++ 環境設計。快速、結構化且嚴格型別。(Vibe 的後繼者。)