接著我就跟他解釋說,反正你都用 C 寫了,就用 C 寫個 CGI 程式給 HTTP Server 呼叫就好,他一臉迷惑的問我「什麼是 CGI?」
後面我也忘了討論的結果了。但自以為很熟習 CGI 這老古董的我,卻是在接觸到 OpenWrt uhttpd 後才算對 CGI 有了深刻的理解。
傳統 CGI
因為接下來的討論都是以 OpenWrt uhttpd 當作範例,而 OpenWrt 是 Linux-based,小弟就不多做解釋,當作各位看官已經有 Linux 背景了。
如下圖所示,當 HTTP Server 接到一個 request,而 HTTP Server 發現要求的資源並不是 HTML/JavaScript,就會按副檔名檢查設定是否有對應的解譯器存在,有的話就 fork 一個 child process 用對應的解譯器(例如 PHP)去解譯該資源(例如下圖中的 login.php),然後把解譯的結果從 stdout 讀出,傳回給 Browser。
那 HTTP Server 要怎麼把 reqeust 傳給 script 呢?以 uhttpd 的作法就是用環境變數與 stdin 傳給 script,環境變數自然不用多言,而 stdin 則有以 anonymous pipe 替換這種最古老的招數,stdout 也是如法炮製。
那 stdin 要怎麼知道該讀多少?HTTP request header 裡有一欄 Content-Length 代表內容的長度,而這個欄位放在環境變數裡,所以只要用環境變數取得的長度讀取就保證讀好讀滿。下圖為 wireshark 掃描聯合新聞網的結果:
而 stdout 是把 script output 原封不動的照搬給 Browser?依照小弟對 uhttpd 的實驗,他還是會過濾 script 輸出的內容,在輸出 HTML/JavaScript 前至少要輸出一行 "Content-Type: text/html\r\n\r\n",否則 Browser 會收到「Bad Request」,status code(如 404 Not Found)則是有需要才填。
寫到這裡可以看到,寫 php 跟寫 uhttpd CGI 大同小異,差別只在於 php 把 GET 與 POST 預先幫你包裝成 $_GET, $_POST 陣列,而你自己寫 CGI(尤其是用 C 語言寫)則是要自己做一堆苦工(比方分析 query string)。
CGI 的繼承者們
從前面可以看到傳統 CGI 的最大缺點在於 fork,如果你用過 LinkIt Smart 7688 跑 Python CGI script,光是回傳一個簡單的 hello 字串就要 5 秒鐘。就算使用運上能力強上 n 倍的 x86,這樣做在高訪問量時也肯定 gg。
所以現代 HTTP Server,無一例外的全部都改為 event driven + async IO(M$除外),node.js 更是直白的強迫你用這種方式思考應用。
在 Programming in Lua,作者認為 script 的關鍵屬性之一就是「解譯器為程式庫的一部分」,作為程式庫呼叫(哪怕是從 script 本身也行),也就是可以在不結束 process 的情況下重複解譯不同的 script。所以我們可以依據 CPU 的核心數與記憶體的大小,預先配置一定數量的 Process Pool(或 Thread Pool),然後當 request 來臨時從中取出閒置的 process/thread,這就是 FastCGI!
你好,新手想請教CGI與JSON,二者有關連嗎
回覆刪除你把http想成卡車,JSON就是上面裝貨物的箱子,XML也是一樣
刪除