LCOV - code coverage report
Current view: top level - http/client - response_factory.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 80.0 % 20 16
Test Date: 2026-02-20 15:38:22 Functions: 100.0 % 2 2

            Line data    Source code
       1              : #ifndef THINGER_HTTP_RESPONSE_FACTORY_HPP
       2              : #define THINGER_HTTP_RESPONSE_FACTORY_HPP
       3              : 
       4              : #include <memory>
       5              : #include <functional>
       6              : #include <string_view>
       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_response;
      14              : 
      15              :     /// Parser for incoming requests.
      16              :     class response_factory {
      17              :     public:
      18              :         /// Specify the maximum response size to be read
      19              :         const size_t MAX_CONTENT_SIZE = 8*1048576;  // 1MB
      20              :         const size_t MAX_HEADERS_SIZE = 8*1024;     // 8KB
      21              : 
      22              :         /// Construct ready to parse the http_request method.
      23       767572 :         response_factory() = default;
      24              : 
      25              :         /// Parse some data. The tribool return value is true when a complete http_request
      26              :         /// has been parsed, false if the data is invalid, indeterminate when more
      27              :         /// data is required. The InputIterator return value indicates how much of the
      28              :         /// input has been consumed.
      29              :         template<typename InputIterator>
      30          948 :         boost::tribool parse(InputIterator begin, InputIterator end, bool head_request = false) {
      31              :             // iterate over all input chars
      32       127394 :             while (begin != end) {
      33              :                 // Optimization: batch process content in streaming mode
      34       127265 :                 if (on_streaming_ && state_ == lenght_delimited_content && tempInt_ > 0) {
      35            2 :                     size_t available = static_cast<size_t>(std::distance(begin, end));
      36            2 :                     size_t to_read = std::min(available, tempInt_);
      37              : 
      38              :                     // Call streaming callback with batch data
      39              :                     // Need to cast to char* since buffer may be uint8_t*
      40            2 :                     std::string_view data(reinterpret_cast<const char*>(&*begin), to_read);
      41            2 :                     streaming_downloaded_ += to_read;
      42              : 
      43            2 :                     if (!on_streaming_(data, streaming_downloaded_, get_content_length())) {
      44            0 :                         streaming_aborted_ = true;
      45            0 :                         return false;
      46              :                     }
      47              : 
      48              :                     std::advance(begin, to_read);
      49            2 :                     tempInt_ -= to_read;
      50              : 
      51            2 :                     if (tempInt_ == 0) {
      52            2 :                         return true;  // Done reading content
      53              :                     }
      54            0 :                     continue;
      55            0 :                 }
      56              : 
      57              :                 // consume character
      58       127263 :                 boost::tribool result = consume(*begin++, head_request);
      59              : 
      60              :                 // parse completed/failed
      61       127263 :                 if (result || !result){
      62          817 :                     return result;
      63              :                 }
      64              :             }
      65              :             // still not finished
      66          129 :             return boost::indeterminate;
      67              :         }
      68              : 
      69              :         std::shared_ptr<http_response> consume_response();
      70              :         void reset();
      71              : 
      72              :         void on_http_major_version(uint8_t major);
      73              : 
      74              :         void on_http_minor_version(uint16_t minor);
      75              : 
      76              :         void on_http_status_code(unsigned short status_code);
      77              : 
      78              :         void on_http_reason_phrase(const std::string& reason);
      79              : 
      80              :         void on_http_header(const std::string& name, const std::string& value);
      81              : 
      82              :         void on_content_data(char content);
      83              : 
      84              :         bool on_chunk_read(size_t size);
      85              : 
      86              :         bool on_length_delimited_content(size_t size);
      87              : 
      88              :         size_t get_content_length();
      89              : 
      90              :         size_t get_content_read();
      91              : 
      92              :         bool empty_headers();
      93              : 
      94              :         /**
      95              :          * Get the HTTP status code (available after headers are parsed).
      96              :          */
      97              :         int get_status_code() const;
      98              : 
      99              :         /**
     100              :          * Get the response object without consuming it.
     101              :          * Note: The response is still being built, so don't modify it.
     102              :          */
     103              :         std::shared_ptr<http_response> get_response() const { return resp; }
     104              : 
     105              :         void setOnChunked(const std::function<void(int, const std::string&)>& onChunked);
     106              : 
     107              :         /**
     108              :          * Set streaming callback for both chunked and length-delimited responses.
     109              :          * @param callback Function called with (data, downloaded_so_far, total_size).
     110              :          *                 total_size is 0 for chunked responses.
     111              :          *                 Return false to abort download.
     112              :          */
     113              :         void setOnStreaming(std::function<bool(const std::string_view&, size_t, size_t)> callback);
     114              : 
     115              :         /**
     116              :          * Check if streaming mode is enabled.
     117              :          */
     118              :         bool is_streaming() const { return on_streaming_ != nullptr; }
     119              : 
     120              :     private:
     121              :         /// Handle the next character of input.
     122              :         boost::tribool consume(char input, bool head_request=false);
     123              : 
     124              :         /// Check if a byte is an HTTP character.
     125              :         static bool is_char(char c);
     126              : 
     127              :         /// Check if a byte is an HTTP control character.
     128              :         static bool is_ctl(char c);
     129              : 
     130              :         /// Check if a byte is defined as an HTTP special character.
     131              :         static bool is_tspecial(char c);
     132              : 
     133              :         /// Check if a byte is a digit.
     134              :         static bool is_digit(char c);
     135              : 
     136              :         /// Check if a byte is hexadecimal digit
     137              :         bool is_hexadecimal(char c);
     138              : 
     139              :         uint8_t get_hex_value(char c);
     140              : 
     141              :         std::shared_ptr<http_response> resp;
     142              : 
     143              :         std::string tempString1_;
     144              :         std::string tempString2_;
     145              :         size_t tempInt_         = 0;
     146              :         size_t headers_size_    = 0;
     147              :         bool lastChunk_         = false;
     148              :         std::function<void(int, const std::string&)> on_chunked_;
     149              :         std::function<bool(const std::string_view&, size_t, size_t)> on_streaming_;
     150              :         size_t streaming_downloaded_ = 0;
     151              :         bool streaming_aborted_ = false;
     152              : 
     153              :         enum content_type{
     154              :             none,
     155              :             lenght_delimited,
     156              :             chunked
     157              :         };
     158              : 
     159              :         content_type content_ = content_type::none;
     160              : 
     161              :         /// The current state of the parser.
     162              :         enum state {
     163              :             http_version_h,
     164              :             http_version_t_1,
     165              :             http_version_t_2,
     166              :             http_version_p,
     167              :             http_version_slash,
     168              :             http_version_major_start,
     169              :             http_version_major,
     170              :             http_version_minor_start,
     171              :             http_version_minor,
     172              :             status_code,
     173              :             reason_phrase,
     174              :             expecting_newline_1,
     175              :             header_line_start,
     176              :             header_lws,
     177              :             header_name,
     178              :             space_before_header_value,
     179              :             header_value,
     180              :             expecting_newline_2,
     181              :             expecting_newline_3,
     182              :             lenght_delimited_content,
     183              :             chunked_content_size,
     184              :             chunked_content_size_expecting_n,
     185              :             chunked_content,
     186              :             chunked_content_expecting_n
     187              :         } state_ = http_version_h;
     188              :     };
     189              : 
     190              : }
     191              : 
     192              : #endif
        

Generated by: LCOV version 2.0-1