日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser

發(fā)布時間:2024/1/23 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載地址:https://blog.csdn.net/jiange_zh/article/details/50639178

對于http服務(wù)器,http request的解析是比較麻煩的,由于我們的重點(diǎn)并不在這上面,所以這一部分不打算自己編寫,而是使用開源的http-parser庫,下面我們將使用該庫來構(gòu)建項目中處理http的類。

HTTP Parser簡介

http-parser是一個用C編寫的HTTP消息解析器,可以解析HTTP請求或者回應(yīng)消息。

這個解析器常常在高性能的HTTP應(yīng)用中使用。

在解析的過程中,它不會調(diào)用任何系統(tǒng)調(diào)用,不會在HEAP上申請內(nèi)存,不會緩存數(shù)據(jù),并且可以在任意時刻打斷解析過程,而不會產(chǎn)生任何影響。

對于每個HTTP消息(在WEB服務(wù)器中就是每個請求),它只需要40字節(jié)的內(nèi)存占用(解析器本身的基本數(shù)據(jù)結(jié)構(gòu))。

特性:

無第三方依賴

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

支持解碼chunk編碼的消息

支持Upgrade協(xié)議升級(如無例外就是WebSocket)

可以防御緩沖區(qū)溢出攻擊

解析器可以處理以下類型的HTTP消息:

頭部的字段和值

Content-Length

請求方法

響應(yīng)的狀態(tài)碼

Transfer-Encoding

HTTP版本

請求的URL

消息主體

Github鏈接:

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

下面我們將根據(jù)它提供的接口來編寫我們自己的類。

準(zhǔn)備工作

首先,我們根據(jù)上一節(jié)所講的http知識,定義我們的http request和response結(jié)構(gòu)體:

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 parsingstd::string http_body; };struct HttpResponse {//std::string http_versionint http_code;std::string http_phrase;header_t http_headers;std::string http_body;std::string GetResponse();void ResetResponse(); };

幾點(diǎn)說明:

? ? ?1.我們先假定http_version為HTTP/1.1,所以暫時不對該字段進(jìn)行處理;
? ? ?2.http_header_field是在使用http-parser用來暫存header的field的;
? ? ?3.GetResponse()函數(shù)利用數(shù)據(jù)成員生成并返回一則response消息字符串(可直接用于發(fā)送給客戶端);
? ? ?4.ResetResponse()函數(shù)清空所有數(shù)據(jù),準(zhǔn)備下一次響應(yīng)時重用;

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類中添加下面幾個數(shù)據(jù)成員:

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.

根據(jù)上面的信息,我們便可以知道我們的HttpParser類需要哪些東西了:

?一個parser成員;

?一個settings成員;

?一個初始化函數(shù),用于初始化parser和settings;

?一個解析函數(shù),用于調(diào)用http_parser_execute();

七個回調(diào)函數(shù);

具體代碼如下:

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; };

之后便是具體實(shí)現(xiàn)了:

/** 調(diào)用http_parser_execute* 在該函數(shù)執(zhí)行期間,將調(diào)用一系列回調(diào)函數(shù)*/ 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一一對應(yīng)*/ 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; }/*本函數(shù)可能被調(diào)用不止一次,因此使用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函數(shù),參照官方文檔:

? ? ?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.?
(一些可擴(kuò)展的信息字段,例如status_code、method和HTTP版本號,它們都存儲在解析器的數(shù)據(jù)結(jié)構(gòu)中。 這些數(shù)據(jù)被臨時的存儲在http_parser中,并且會在每個連接到來后被重置(當(dāng)多個連接的HTTP數(shù)據(jù)使用同一個解析器時); 如果需要保留這些數(shù)據(jù),必須要在on_headers_complete返回之前保存它們。)
? ? ?事實(shí)上,我們每一個http連接都有一個解析器,所以不存在以上問題,但是我們還是按照步驟在on_headers_complete中保存它們。

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

在下一節(jié)中,我們將開始接觸本項目的核心部分——狀態(tài)機(jī)!

總結(jié)

以上是生活随笔為你收集整理的【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。