LCOV - code coverage report
Current view: top level - http/server - request_factory.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 98.2 % 56 55
Test Date: 2026-11-10 10:38:07 Functions: 100.0 % 14 14

            Line data    Source code
       1              : #ifndef HTTP_REQUEST_PARSER_HPP
       2              : #define HTTP_REQUEST_PARSER_HPP
       3              : 
       4              : #include <iterator>
       5              : #include <memory>
       6              : #include <string>
       7              : #include <boost/logic/tribool.hpp>
       8              : #include <boost/tuple/tuple.hpp>
       9              : #include <boost/lexical_cast.hpp>
      10              : 
      11              : namespace thinger::http {
      12              : 
      13              :     class http_request;
      14              : 
      15              :     /// Parser for incoming requests.
      16              :     class request_factory {
      17              :     public:
      18              :         /// Limits of the header section (request line and header fields): a request over
      19              :         /// them fails to parse, with header_section_too_large() set
      20              :         static constexpr size_t max_header_lines = 100;
      21              :         static constexpr size_t max_header_section_size = 16 * 1024;
      22              : 
      23              :         /// Construct ready to parse the http_request method.
      24              :         request_factory();
      25              : 
      26              :         /// Parse some data. The tribool return value is true when a complete http_request
      27              :         /// has been parsed, false if the data is invalid, indeterminate when more
      28              :         /// data is required. The InputIterator return value indicates how much of the
      29              :         /// input has been consumed.
      30              :         template<typename InputIterator>
      31        48161 :         boost::tribool parse(InputIterator& begin, InputIterator end) {
      32      1215325 :             while (begin != end) {
      33              :                 // The bytes of the uri, a header name or a header value are appended at once,
      34              :                 // as consume() would one by one (up to the header section limit: the byte
      35              :                 // over it is left to consume(), which fails)
      36      1202544 :                 if (std::string* target = run_target()) {
      37       360513 :                     size_t room = max_header_section_size - header_section_size_;
      38       360513 :                     auto run_end = begin;
      39       360513 :                     size_t size = 0;
      40      2531649 :                     while (run_end != end && size < room && is_run_char(static_cast<char>(*run_end))) {
      41      2171136 :                         ++run_end;
      42      2171136 :                         ++size;
      43              :                     }
      44       360513 :                     if (size > 0) {
      45              :                         if constexpr (std::contiguous_iterator<InputIterator>) {
      46              :                             // (from a range of other than chars, append() builds a temporary string)
      47       177726 :                             target->append(reinterpret_cast<const char*>(std::to_address(begin)), size);
      48              :                         } else {
      49              :                             target->append(begin, run_end);
      50              :                         }
      51       177726 :                         header_section_size_ += size;
      52       177726 :                         begin = run_end;
      53       177726 :                         continue;
      54              :                     }
      55              :                 }
      56      1024818 :                 boost::tribool result = consume(*begin++);
      57              :                 // parsed completed or parse failed
      58      1024818 :                 if (result || !result)
      59        35380 :                     return result;
      60              :             }
      61              :             // still not finished
      62        12781 :             return boost::indeterminate;
      63              :         }
      64              : 
      65        23974 :         void set_headers_only(bool headers_only) {
      66        23974 :             headers_only_ = headers_only;
      67        23974 :         }
      68              : 
      69            6 :         bool get_headers_only() const {
      70            6 :             return headers_only_;
      71              :         }
      72              : 
      73              :         /// Whether no part of a request was parsed since the last one
      74          252 :         bool idle() const {
      75          252 :             return state_ == method_start;
      76              :         }
      77              : 
      78              :         /// Whether the last parse failed for exceeding the header section limits
      79          372 :         bool header_section_too_large() const {
      80          372 :             return header_section_too_large_;
      81              :         }
      82              : 
      83              :         std::shared_ptr<http_request> consume_request();
      84              : 
      85              : 
      86              :         void on_http_method(const std::string& method);
      87              : 
      88              :         void on_http_status_code(unsigned short status_code);
      89              : 
      90              :         void on_http_uri(const std::string& uri);
      91              : 
      92              :         void on_http_major_version(uint8_t major);
      93              : 
      94              :         void on_http_minor_version(uint16_t minor);
      95              : 
      96              :         void on_http_header(const std::string& name, const std::string& value);
      97              : 
      98              :         void on_content(char content);
      99              : 
     100              :         size_t get_content_length();
     101              : 
     102              :         size_t get_content_read();
     103              : 
     104              :     private:
     105              :         /// Handle the next character of input.
     106              :         boost::tribool consume(char input);
     107              : 
     108              :         /// String a byte that consume() only appends in the current state would go to (the
     109              :         /// uri, a header name or a header value), null in the other states
     110      1202544 :         std::string* run_target() {
     111      1202544 :             switch (state_) {
     112        71454 :                 case uri: return &tempString1_;
     113       151224 :                 case header_name: return &tempString1_;
     114       137835 :                 case header_value: return &tempString2_;
     115       842031 :                 default: return nullptr;
     116              :             }
     117              :         }
     118              : 
     119              :         /// Whether consume() only appends `c` in the current state (see run_target)
     120      2526769 :         bool is_run_char(char c) const {
     121      2526769 :             switch (state_) {
     122       571420 :                 case uri: return c != ' ' && is_char(c) && !is_ctl(c);
     123       709725 :                 case header_name: return is_char(c) && !is_ctl(c) && !is_tspecial(c);
     124      1245624 :                 case header_value: return !is_ctl(c) || c == '\t';
     125            0 :                 default: return false;
     126              :             }
     127              :         }
     128              : 
     129              :         /// Check if a byte is optional whitespace (OWS, RFC 9110 section 5.6.3).
     130       149014 :         static bool is_ows(int c) {
     131       149014 :             return c == ' ' || c == '\t';
     132              :         }
     133              : 
     134              :         /// Check if a byte is an HTTP character.
     135      1398226 :         static bool is_char(int c) {
     136      1398226 :             return c >= 0 && c <= 127;
     137              :         }
     138              : 
     139              :         /// Check if a byte is an HTTP control character.
     140      2717146 :         static bool is_ctl(int c) {
     141      2717146 :             return (c >= 0 && c <= 31) || (c == 127);
     142              :         }
     143              : 
     144              :         /// Check if a byte is defined as an HTTP special character.
     145       896312 :         static bool is_tspecial(int c) {
     146       896312 :             switch (c) {
     147       149724 :                 case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':':
     148              :                 case '\\': case '"': case '/': case '[': case ']': case '?': case '=': case '{':
     149              :                 case '}': case ' ': case '\t':
     150       149724 :                     return true;
     151       746588 :                 default:
     152       746588 :                     return false;
     153              :             }
     154              :         }
     155              : 
     156              :         /// Check if a byte is a digit.
     157        33492 :         static bool is_digit(int c) {
     158        33492 :             return c >= '0' && c <= '9';
     159              :         }
     160              : 
     161              :         std::shared_ptr<http_request> req;
     162              : 
     163              :         std::string tempString1_;
     164              :         std::string tempString2_;
     165              :         size_t tempInt_;
     166              :         bool headers_only_ = false;
     167              :         size_t header_lines_ = 0;
     168              :         size_t header_section_size_ = 0;
     169              :         bool header_section_too_large_ = false;
     170              :         bool host_field_ = false;
     171              : 
     172              :         /// Fail the parsing for exceeding the header section limits
     173           60 :         bool header_section_exceeded() {
     174           60 :             header_section_too_large_ = true;
     175           60 :             return false;
     176              :         }
     177              : 
     178              :         /// The current state of the parser.
     179              :         enum state {
     180              :             method_start,
     181              :             method,
     182              :             uri,
     183              :             http_version_h,
     184              :             http_version_t_1,
     185              :             http_version_t_2,
     186              :             http_version_p,
     187              :             http_version_slash,
     188              :             http_version_major_start,
     189              :             http_version_major,
     190              :             http_version_minor_start,
     191              :             http_version_minor,
     192              :             expecting_newline_1,
     193              :             header_line_start,
     194              :             header_name,
     195              :             space_before_header_value,
     196              :             header_value,
     197              :             expecting_newline_2,
     198              :             expecting_newline_3,
     199              :             content
     200              :         } state_;
     201              :     };
     202              : }
     203              : 
     204              : #endif // HTTP_REQUEST_PARSER_HPP
        

Generated by: LCOV version 2.0-1