1. 程式人生 > >從零開始一個http服務器-模擬cgi(五)

從零開始一個http服務器-模擬cgi(五)

pclose github 網頁瀏覽 erro malloc 地址 pen bash doc

從零開始一個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標準是什麽,我們定義一個我們自己簡單粗暴的標準,這個標準只有兩條:

  1. 外部的程序 可以產生 標準輸出
  2. 服務器可以獲得外部的程序產生的標準輸出

這樣的話我們就可以把外部的程序的標準輸出直接作為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(五)