1. 程式人生 > >【slighttpd】基於lighttpd架構的Server專案實戰(7)—http-parser

【slighttpd】基於lighttpd架構的Server專案實戰(7)—http-parser

轉載地址:https://blog.csdn.net/jiange_zh/article/details/50639178

對於http伺服器,http request的解析是比較麻煩的,由於我們的重點並不在這上面,所以這一部分不打算自己編寫,而是使用開源的http-parser庫,下面我們將使用該庫來構建專案中處理http的類。

HTTP Parser簡介

http-parser是一個用C編寫的HTTP訊息解析器,可以解析HTTP請求或者回應訊息。

這個解析器常常在高效能的HTTP應用中使用。

在解析的過程中,它不會呼叫任何系統呼叫,不會在HEAP上申請記憶體,不會快取資料,並且可以在任意時刻打斷解析過程,而不會產生任何影響。

對於每個HTTP訊息(在WEB伺服器中就是每個請求),它只需要40位元組的記憶體佔用(解析器本身的基本資料結構)。

特性:

無第三方依賴

可以處理持久訊息(keep-alive)

支援解碼chunk編碼的訊息

支援Upgrade協議升級(如無例外就是WebSocket)

可以防禦緩衝區溢位攻擊

解析器可以處理以下型別的HTTP訊息:

頭部的欄位和值

Content-Length

請求方法

響應的狀態碼

Transfer-Encoding

HTTP版本

請求的URL

訊息主體

Github連結:

https://github.com/nodejs/http-parser

下面我們將根據它提供的介面來編寫我們自己的類。

準備工作

首先,我們根據上一節所講的http知識,定義我們的http request和response結構體:

typedef std::map<std::string, std::string> header_t;
typedef header_t::iterator header_iter_t;

struct HttpRequest
{
    std::string http_method;
    std::string http_url;
    //std::string http_version;

    header_t http_headers;
    std::string http_header_field;  //field is waiting for value while parsing

    std::string http_body;
};

struct HttpResponse
{
    //std::string http_version
    int http_code;
    std::string http_phrase;

    header_t http_headers;
    
    std::string http_body;

    std::string GetResponse();
    void ResetResponse();
};

幾點說明:

     1.我們先假定http_version為HTTP/1.1,所以暫時不對該欄位進行處理;
     2.http_header_field是在使用http-parser用來暫存header的field的;
     3.GetResponse()函式利用資料成員生成並返回一則response訊息字串(可直接用於傳送給客戶端);
     4.ResetResponse()函式清空所有資料,準備下一次響應時重用;

std::string HttpResponse::GetResponse() 
{
    std::ostringstream ostream;
    ostream << "HTTP/1.1" << " " << http_code << " " << http_phrase << "\r\n"
            << "Connection: keep-alive"              <<"\r\n";

    header_iter_t iter = http_headers.begin();
    
    while(iter != http_headers.end()) {
        ostream << iter->first << ": " << iter->second << "\r\n";
        ++iter;
    }
    ostream << "Content-Length: " << http_body.size()  << "\r\n\r\n";
    ostream << http_body;

    return ostream.str();
}

void HttpResponse::ResetResponse()
{
    //http_version = "HTTP/1.1";
    http_code = 200;
    http_phrase = "OK";

    http_body.clear();
    http_headers.clear();
}

       接下來,我們在Connection類中新增下面幾個資料成員:

HttpRequest *http_request_parser;   //解析時用

HttpRequest *http_request_process;   //處理請求時用

HttpResponse http_response;  

HttpParser  http_parser;

      其中http_request_parser將在解析的時候使用(http-parser中); 
      而http_request_process將在處理請求的時候使用(在connection中)。

HttpParser

    我們先看看官方文件的一些說明:

One http_parser object is used per TCP connection. Initialize the struct using http_parser_init() and set the callbacks. That might look something like this for a request parser:

http_parser_settings settins;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */

http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;

During the http_parser_execute() call, the callbacks set in http_parser_settings will be executed. The parser maintains state and never looks behind, so buffering the data is not necessary. If you need to save certain data for later usage, you can do that from the callbacks.

There are two types of callbacks:

notification typedef int (http_cb) (http_parser); 
    Callbacks:on_message_begin,on_headers_complete,on_message_complete.
data typedef int (http_data_cb) (http_parser, const char *at, size_t length); 
    Callbacks:(requests only)on_url, 
    (common)n_header_field,on_header_value,on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates error to the parser, making it exit immediately.

根據上面的資訊,我們便可以知道我們的HttpParser類需要哪些東西了:

 一個parser成員;

 一個settings成員;

 一個初始化函式,用於初始化parser和settings;

 一個解析函式,用於呼叫http_parser_execute();

七個回撥函式;

具體程式碼如下:

class HttpParser
{
    public:
        void InitParser(Connection *con);
        int HttpParseRequest(const std::string &inbuf);

        static int OnMessageBeginCallback(http_parser *parser);
        static int OnUrlCallback(http_parser *parser, const char *at, size_t length);
        static int OnHeaderFieldCallback(http_parser *parser, const char *at, size_t length);
        static int OnHeaderValueCallback(http_parser *parser, const char *at, size_t length);
        static int OnHeadersCompleteCallback(http_parser *parser);
        static int OnBodyCallback(http_parser *parser, const char *at, size_t length);
        static int OnMessageCompleteCallback(http_parser *parser);

    private:
        http_parser parser;
        http_parser_settings settings;
};

之後便是具體實現了:

/*
 * 呼叫http_parser_execute
 * 在該函式執行期間,將呼叫一系列回撥函式
 */
int HttpParser::HttpParseRequest(const std::string &inbuf)
{
    int nparsed = http_parser_execute(&parser, &settings, inbuf.c_str(), inbuf.size());
    
    if (parser.http_errno != HPE_OK) {
        return -1;
    }
    return nparsed;
}

/*初始化http_request_parser*/
int HttpParser::OnMessageBeginCallback(http_parser *parser)
{
    Connection *con = (Connection *)parser->data;
    
    con->http_request_parser = new HttpRequest();

    return 0;
}

/*將解析好的url賦值給http_url */
int HttpParser::OnUrlCallback(http_parser *parser, const char *at, size_t length)
{
    Connection *con = (Connection *)parser->data;

    con->http_request_parser->http_url.assign(at, length);
    
    return 0;
}

/*將解析到的header_field暫存在http_header_field中*/
int HttpParser::OnHeaderFieldCallback(http_parser *parser, const char *at, size_t length) 
{
    Connection *con = (Connection *)parser->data;

    con->http_request_parser->http_header_field.assign(at, length);

    return 0;
}

/*將解析到的header_value跟header_field一一對應*/
int HttpParser::OnHeaderValueCallback(http_parser *parser, const char *at, size_t length)
{
    Connection *con = (Connection *)parser->data;
    Httprequest *request = con->http_request_parser;

    request->http_headers[request->http_header_field] = std::string(at, length);

    return 0;
}

/*參照官方文件*/
int HttpParser::OnHeadersCompleteCallback(http_parser *parser)
{
    Connection *con = (Connection *)parser->data;
    HttpRequest *request = con->http_request_parser;
    request->http_method = http_method_str((http_method)parser->method);
    return 0;
}

/*本函式可能被呼叫不止一次,因此使用append*/
int HttpParser::OnBodyCallback(http_parser *parser, const char *at, size_t length)
{
    Connection *con = (Connection *)parser->data;

    con->http_request_parser->http_body.append(at, length);

    return 0;
}

/*將解析完畢的訊息放到訊息佇列中*/
int HttpParser::OnMessageCompleteCallback(http_parser *parser) 
{
    Connection *con = (Connection *)parser->data;
    HttpRequest *request = con->http_request_parser;
    
    con->req_queue.push(request);
    con->http_request_parser = NULL;
    return 0;
}

  對於OnHeadersCompleteCallback函式,參照官方文件:

     Scalar valued message information such as status_code, method, and the HTTP version are stored in the parser structure. This data is only temporally stored in http_parser and gets reset on each new message. If this information is needed later, copy it out of the structure during the headers_complete callback. 
(一些可擴充套件的資訊欄位,例如status_code、method和HTTP版本號,它們都儲存在解析器的資料結構中。 這些資料被臨時的儲存在http_parser中,並且會在每個連線到來後被重置(當多個連線的HTTP資料使用同一個解析器時); 如果需要保留這些資料,必須要在on_headers_complete返回之前儲存它們。)
     事實上,我們每一個http連線都有一個解析器,所以不存在以上問題,但是我們還是按照步驟在on_headers_complete中儲存它們。

至此,我們通過http-parser提供的介面定義了自己的HttpParser類,接下來便可以使用了~

在下一節中,我們將開始接觸本專案的核心部分——狀態機!