狀態機是 Lighttpd 最核心的部分。它將一個連接在不同時刻劃分為不同狀態,根據當前狀態決定處理邏輯與下一狀態轉換。本文為系列首篇,建立整體認知框架。

(源碼 doc/state.dot 可用 dot -Tpng state.dot -o state.png 生成)
typedef enum {
CON_STATE_CONNECT, // 連接建立
CON_STATE_REQUEST_START, // 開始讀取請求
CON_STATE_READ, // 讀取並解析請求
CON_STATE_REQUEST_END, // 讀取請求結束
CON_STATE_READ_POST, // 讀取 POST 數據
CON_STATE_HANDLE_REQUEST, // 處理請求(核心)
CON_STATE_RESPONSE_START, // 開始響應
CON_STATE_WRITE, // 寫入響應數據
CON_STATE_RESPONSE_END, // 響應結束
CON_STATE_ERROR, // 錯誤
CON_STATE_CLOSE // 連接關閉
} connection_state_t;
每個 connection 結構體首個成員即此枚舉變量,表示當前狀態。
int connection_state_machine(server *srv, connection *con) {
int done = 0, r;
while (done == 0) {
size_t ostate = con->state;
// 大 switch 根據當前狀態執行處理與轉換
switch (con->state) {
case CON_STATE_REQUEST_START: /* transient */
case CON_STATE_REQUEST_END: /* transient */
case CON_STATE_HANDLE_REQUEST:
case CON_STATE_RESPONSE_START:
case CON_STATE_RESPONSE_END: /* transient */
case CON_STATE_CONNECT:
case CON_STATE_CLOSE:
case CON_STATE_READ_POST:
case CON_STATE_READ:
case CON_STATE_WRITE:
case CON_STATE_ERROR: /* transient */
default: break;
}
if (done == -1) { done = 0; }
else if (ostate == con->state) { done = 1; } // 狀態未變,需等待 I/O
}
return 0;
}
關鍵機制:狀態改變則繼續循環處理;狀態不變(需等待 I/O)則 done=1 跳出循環。done==-1 為 HANDLE_REQUEST 特殊情況。
connection_set_state(CON_STATE_REQUEST_START) → 呼叫一次 connection_state_machine(首次 READ 幾乎無操作,保持狀態跳出)。FDEVENT_IN。connection_handle_fdevent 標記並加入 joblist(作業隊列)。connection_state_machine 真正處理讀取。FDEVENT_OUT)。connection 結構體不銷毀,留在 server->connections 供下次複用。in_joblist 標記始終為 0,導致重複加入;處理完未從 joblist 移除指針,造成輕微記憶體洩漏(最新 1.4.26 仍未修復)。後續文章將詳細剖析各狀態具體處理邏輯。