"SLIGHTTPD" server project based on LIGHTTPD Architecture Combat (7)-http-parser

Source: Internet
Author: User
Tags message queue

For HTTP server, the parsing of HTTP request is rather troublesome, because our focus is not on it, so this part is not intended to be written by itself, but instead uses the open source Http-parser library, which we will use to build the class that handles HTTP in the project.

Introduction to HTTP Parser

Http-parser is an HTTP message parser written in C that can parse HTTP requests or respond to messages.

This parser is often used in high-performance HTTP applications.

During parsing, it does not invoke any system calls, does not request memory on the heap, does not cache the data, and can interrupt the parsing process at any time without any impact.

For each HTTP message (every request in the Web server), it only requires a 40-byte memory footprint (the basic data structure of the parser itself).

Characteristics:

无第三方依赖可以处理持久消息(keep-alive)支持解码chunk编码的消息支持Upgrade协议升级(如无例外就是WebSocket)可以防御缓冲区溢出攻击

The parser can handle the following types of HTTP messages:

头部的字段和值Content-Length请求方法响应的状态码Transfer-EncodingHTTP版本请求的URL消息主体

GitHub Links:

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

Below we will write our own class based on the interface it provides.

Preparatory work

First, we define our HTTP request and response structure according to the HTTP knowledge described in the previous section:

typedef STD:: Map<std::string, std::string>header_t;typedefHeader_t::iterator header_iter_t;structhttprequest{STD::stringHttp_method;STD::stringHttp_url;//std::string http_version;header_t http_headers;STD::stringHttp_header_field;//field is waiting for value while parsing    STD::stringHttp_body;};structhttpresponse{//std::string http_version;    intHttp_code;STD::stringHttp_phrase; header_t http_headers;STD::stringHttp_body;STD::stringGetResponse ();voidResetresponse ();};

A few notes:

    1. We first assume that http_version is http/1.1, so the field is not processed for the time being;
    2. Http_header_field is the field in which Http-parser is used to hold the header;
    3. The GetResponse () function generates and returns a response message string using a data member (which can be used to send to the client directly);
    4. The Resetresponse () function empties all data and prepares the next response for reuse;
Std:: StringHttpResponse:: GetResponse() {STD:: OstringstreamOstream 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;returnOstream.STR ();}voidHttpResponse:: Resetresponse(){//http_version = "http/1.1";Http_code=  $; Http_phrase= "OK"; Http_body.Clear (); Http_headers.Clear ();}

Next, we add the following data members in the connection class:

        HttpRequest        *http_request_parser;    //解析时用        HttpRequest        *http_request_process;   //处理请求时用        HttpResponse        http_response;                  HttpParser          http_parser;            

Where Http_request_parser will be used at the time of parsing (http-parser);
The http_request_process will be used when processing the request (in connection).

Httpparser

Let's take a look at some of the official documentation:

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

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

The example given above is the process of configuring, initializing, and in Settings we need to set up a series of callback functions:

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

There is 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.

Based on the above information, we can know what our Httpparser class needs:

a parser member;
A settings member;
An initialization function for initializing parser and settings;
An analytic function to invoke Http_parser_execute ();
Seven callback functions;

The specific code is as follows:

Class httpparser{ Public:void Initparser(Connection *con);intHttpparserequest (ConstSTD::string&AMP;INBUF);Static intOnmessagebegincallback (Http_parser *parser);Static intOnurlcallback (Http_parser *parser,Const Char*at, size_t length);Static intOnheaderfieldcallback (Http_parser *parser,Const Char*at, size_t length);Static intOnheadervaluecallback (Http_parser *parser,Const Char*at, size_t length);Static intOnheaderscompletecallback (Http_parser *parser);Static intOnbodycallback (Http_parser *parser,Const Char*at, size_t length);Static intOnmessagecompletecallback (Http_parser *parser);Private: Http_parser parser; http_parser_settings settings;};

After that, it is implemented in detail:

/* * Call Http_parser_execute * During the function execution, a series of callback functions will be called */intHttpparser::httpparserequest (Const STD::string&AMP;INBUF) {intnparsed = Http_parser_execute (&parser, &settings, Inbuf.c_str (), inbuf.size ());if(Parser.http_errno! = HPE_OK) {return-1; }returnnparsed;}/ * Initialize Http_request_parser * /intHttpparser::onmessagebegincallback (Http_parser *parser) {Connection *con = (connection*) parser->data; Con->http_request_parser =NewHttpRequest ();return 0;}/* Assign the parsed URL to Http_url * /intHttpparser::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 will be resolved to a temporary presence in Http_header_field * /intHttpparser::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;}/ * will resolve to the Header_value with Header_field one by one * /intHttpparser::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;}/ * Refer to official documentation * /intHttpparser::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;}/ * This function may be called more than once, so use append * /intHttpparser::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;}/ * Put the parsed message in the message queue * /intHttpparser::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;}

For the Onheaderscompletecallback function, refer to the official documentation:

Scalar valued message information such as Status_code, method, and the HTTP version is stored in the parser structure. This data was only temporally stored in Http_parser and gets reset on each new message. If This information are needed later, copy it out of the structure during the headers_complete.
(Some extensible information fields, such as Status_code, method, and HTTP version numbers, are stored in the parser's data structure.) This data is temporarily stored in Http_parser and is reset after each connection arrives (when multiple connected HTTP data uses the same parser); If you need to preserve this data, you must save them before On_headers_complete returns. )

In fact, each of our HTTP connections has a parser, so there is no problem, but we still follow the steps to save them in On_headers_complete.

At this point, we define our own Httpparser class through the interface provided by Http-parser, then we can use the ~

In the next section, we'll start contacting the core of the project-state machine!

"SLIGHTTPD" server project based on LIGHTTPD Architecture Combat (7)-http-parser

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.