從零開始一個http服務器-模擬cgi(五)
從零開始一個http服務器-模擬cgi(五)
代碼地址 : https://github.com/flamedancer/cserver
git checkout step5
運行:
make clean && make && ./myserver.out
測試
瀏覽器打開 http://127.0.0.1:9734/action/show_date
模擬cgi:用外部程序來優化 動態 response
- cgi解釋
- 調用外部程序
cgi解釋
上一節中,我們確實是實現了動態的response:我們不需要修改我們的代碼,也不需要中斷server,只需要修改我們的html頁面文件就可以實時的更改返回內容。
但是當需要返回更加靈活的內容,比如當前時間的時候,我們不可能每隔一秒鐘就去改下頁面文件。這時候就可以借助外部的程序,比如shell,來為我們生產返回內容。這個外部程序,就類似於常說的cgi程序。所謂cgi,維基百科是這麽解釋:通用網關接口(Common Gateway Interface/CGI)是一種重要的互聯網技術,可以讓一個客戶端,從網頁瀏覽器向執行在網絡服務器上的程序請求數據。CGI描述了服務器和請求處理程序之間傳輸數據的一種標準。
這是說CGI是一種標準,只要服務器 和 外部的程序 都實現了這個標準,就可以相互通信。
我們這比較粗魯一點,我們不管正規的CGI標準是什麽,我們定義一個我們自己簡單粗暴的標準,這個標準只有兩條:
- 外部的程序 可以產生 標準輸出
- 服務器可以獲得外部的程序產生的標準輸出
這樣的話我們就可以把外部的程序的標準輸出直接作為response的body。
調用外部程序
執行外部程序的核心函數為popen ,它會開啟一個新進程執行傳入的外部命令,返回一個管道文件流,讀取這個管道文件流就可以讀取到外部命令的輸出。管道文件流需要用pclose關閉,而不是fclose。
void doCgi(char * filePath, struct http_response * response) { char fileName[100]; char cmd[100]; sprintf(fileName, "cgi/%s", filePath + strlen(action_url + 1)); sprintf(cmd, "%s 2>&1", fileName); FILE *fstream = NULL; if (access(fileName, F_OK) == -1 || NULL == (fstream = popen(cmd, "r"))) { // file doesn't exist or FILE cannot be exec setResponseMsg(response, errorMsg); return; } response->body = (char *)malloc((5000)); int len = 0; char *buff = response->body; do { buff += len; len = fread(buff, 1024, 1, fstream); // printf("%d\n", len); } while (len); pclose(fstream); response->body_size = strlen(response->body); struct Item *item2 = newItem( "Content-Type", "text/html; charset=utf-8"); mapPush(response->headers, item2); return; }
建一個目錄名為cgi,把我們寫的外部程序放到這個目錄,例如我們寫個 cgi/show_date 程序:
#! /bin/bash
echo $(date)
make clean && make && ./myserver.out 打開 http://127.0.0.1:9734/action/show_date 可以看到實時的時間
從零開始一個http服務器-模擬cgi(五)