LCOV - code coverage report
Current view: top level - http/common - headers.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 17 17
Test Date: 2026-11-10 10:38:07 Functions: 92.3 % 13 12

            Line data    Source code
       1              : #ifndef THINGER_HTTP_HEADERS_HPP
       2              : #define THINGER_HTTP_HEADERS_HPP
       3              : 
       4              : #include <algorithm>
       5              : #include <boost/logic/tribool.hpp>
       6              : #include <boost/algorithm/string.hpp>
       7              : #include <boost/lexical_cast.hpp>
       8              : #include "http_frame.hpp"
       9              : #include "../../util/ascii.hpp"
      10              : 
      11              : namespace thinger::http{
      12              : 
      13              :     namespace misc_strings {
      14              :         static const std::string name_value_separator{": "};
      15              :         static const std::string crlf = "\r\n";
      16              :         static const std::string lf = "\n";
      17              :         static const std::string space = " ";
      18              :         static const std::string http_1_1 = "HTTP/1.1";
      19              :         static const std::string close = "close";
      20              :     } // namespace misc_strings
      21              : 
      22              :     namespace header{
      23              :         // TODO replace any header constant in code by those strings
      24              :         const std::string content_type = "Content-Type";
      25              :         const std::string cache_control = "Cache-Control";
      26              :         const std::string connection = "Connection";
      27              :         const std::string upgrade = "Upgrade";
      28              :         const std::string accept = "Accept";
      29              :         const std::string content_length = "Content-Length";
      30              :         const std::string transfer_encoding = "Transfer-Encoding";
      31              :         const std::string user_agent = "User-Agent";
      32              :         const std::string authorization = "Authorization";
      33              :         const std::string set_cookie = "Set-Cookie";
      34              :         const std::string cookie = "Cookie";
      35              :         const std::string location = "Location";
      36              :         const std::string host = "Host";
      37              :         const std::string referer = "Referer";
      38              :         const std::string x_frame_options = "X-Frame-Options";
      39              :     }
      40              : 
      41              :     namespace connection{
      42              :         const std::string keep_alive = "keep-alive";
      43              :         const std::string close = "close";
      44              :         const std::string upgrade = "upgrade";
      45              :     }
      46              : 
      47              :     namespace accept{
      48              :         const std::string event_stream = "text/event-stream";
      49              :     }
      50              : 
      51              :     namespace content_type{
      52              :         const std::string application_form_urlencoded = "application/x-www-form-urlencoded";
      53              :         const std::string text_html = "text/html";
      54              :     }
      55              : 
      56              : class headers : public http_frame{
      57              : 
      58              : public:
      59              :     using http_header = std::pair<std::string, std::string>;
      60              : 
      61              :     // constructors
      62       566203 :     headers() = default;
      63       568800 :     ~headers() override = default;
      64              :     headers(const headers&) = default;
      65         2597 :     headers(headers&&) = default;
      66              :     headers& operator=(const headers&) = default;
      67              :     headers& operator=(headers&&) = default;
      68              : 
      69              :     // The head of a message
      70        23679 :     frame_kind kind() const override { return frame_kind::head; }
      71              : 
      72              :     // setters. A header whose name is not a token, or whose value has CR, LF, NUL or another
      73              :     // control character but HTAB (RFC 9110, section 5), is refused, without throwing: it is not
      74              :     // stored (a refused set_header leaves the message as it was), and has_refused_header() is
      75              :     // set. So a response is sent without it, and a client request with one fails, unsent. A
      76              :     // header with an empty name is ignored. Writing through get_headers() or process_header()
      77              :     // is not checked: they are for the parsers and the library.
      78              :     void add_header(std::string key, std::string value);
      79              :     void set_header(std::string key, std::string value);
      80            9 :     void set(std::string key, std::string value) { set_header(std::move(key), std::move(value)); }
      81              :     // Leave a single `key` header with this value, whatever how many there were: the first
      82              :     // one keeps its place and the spelling of its name. set_header() changes only the first.
      83              :     void replace_headers(std::string key, std::string value);
      84              :     void set_proxy(std::string key, std::string value);
      85              :     void add_proxy(std::string key, std::string value);
      86              :     // Whether a setter refused a header (see add_header)
      87         9890 :     bool has_refused_header() const { return refused_header_; }
      88              :     bool upgrade() const;
      89              :     bool stream() const;
      90              :     bool has_header(std::string_view key) const;
      91              :     bool remove_header(std::string_view key);
      92              :     // Remove every `key` header, returning how many there were
      93              :     size_t remove_headers(std::string_view key);
      94              :     void set_http_version_major(uint8_t http_version_major);
      95              :     void set_http_version_minor(uint8_t http_version_minor);
      96              :     void set_keep_alive(bool keep_alive);
      97              : 
      98              :     // getters
      99              :     std::vector<http_header>& get_headers();
     100              :     const std::vector<http_header>& get_headers() const;
     101              :     static std::string get_parameter(const std::string& key, std::string_view name) ;
     102              :     std::vector<std::string> get_headers_with_key(std::string_view key) const;
     103              :     const std::string& get_header(std::string_view key) const;
     104              : 
     105              :     const std::string& get_authorization() const;
     106              :     const std::string& get_cookie() const;
     107              :     const std::string& get_user_agent() const;
     108              :     const std::string& get_content_type() const;
     109              :     bool is_content_type(const std::string& value) const;
     110              :     bool empty_headers() const;
     111              :     size_t get_content_length() const;
     112              :     // Whether a Content-Length header was not a valid number, or repeated with different values
     113           18 :     bool has_invalid_content_length() const { return invalid_content_length_; }
     114              :     // Whether there is a Content-Length / Transfer-Encoding header (tracked as headers are added
     115              :     // and removed, so checking it for every parsed header is not a scan of all the headers)
     116        31584 :     bool has_content_length_header() const { return content_length_header_; }
     117        22438 :     bool has_transfer_encoding_header() const { return transfer_encoding_header_; }
     118              :     // Whether the body is chunked: "Transfer-Encoding: chunked" (see has_valid_framing)
     119       126004 :     bool is_chunked_transfer() const { return chunked_transfer_; }
     120              :     // Whether the body framing of a received message is unambiguous (RFC 9112, section 6.3):
     121              :     // a valid Content-Length, or "Transfer-Encoding: chunked" alone (the only coding, in a
     122              :     // single header: no other is supported), but not both, and Transfer-Encoding only from
     123              :     // HTTP/1.1 on (section 6.1: in an HTTP/1.0 message, its framing is faulty). Otherwise
     124              :     // where the message ends, and so where the next one starts, is unknown: it must be
     125              :     // rejected, and the connection closed.
     126        59407 :     bool has_valid_framing() const {
     127        59407 :         bool before_http_1_1 = http_version_major_ < 1 || (http_version_major_ == 1 && http_version_minor_ < 1);
     128        59087 :         return !invalid_content_length_ && !invalid_transfer_encoding_
     129        58854 :             && !(chunked_transfer_ && content_length_header_)
     130       118494 :             && !(before_http_1_1 && transfer_encoding_header_);
     131              :     }
     132              :     int get_http_version_major() const;
     133              :     int get_http_version_minor() const;
     134              :     bool keep_alive() const;
     135              :     // Header names (and tokens) compare case-insensitively
     136      3798410 :     static bool is_header(std::string_view key, std::string_view header) {
     137      3798410 :         return ::thinger::util::ascii::iequals(key, header);
     138              :     }
     139              : 
     140              :     // debug
     141              :     void debug_headers(std::ostream& os) const;
     142              :     void log(const char* scope, const body_logging& options = {}) const override;
     143              : 
     144              :     // Whether the value of the header `name` is a credential, never logged (Authorization,
     145              :     // Proxy-Authorization, Cookie, Set-Cookie)
     146              :     static bool is_sensitive_header(std::string_view name);
     147              : 
     148              :     // process header intended to be override to process headers
     149              :     virtual void process_header(std::string key, std::string value);
     150              : 
     151              : protected:
     152              :     // Log each header (debug): its name, and its value if `options` is enabled, but those of
     153              :     // the sensitive headers
     154              :     void log_headers(const body_logging& options) const;
     155              : 
     156              :     // Log the size of a body (trace), and its start if `options` is enabled
     157              :     static void log_body(std::string_view body, size_t size, const body_logging& options);
     158              : 
     159              :     // Keep track of the body framing (Content-Length, Transfer-Encoding) of a received header
     160              :     void process_framing_header(std::string_view key, std::string_view value);
     161              : 
     162              :     // Add, or replace the first `key` header, without checking it: for the parsers, and the
     163              :     // values the library builds itself (constants, digits)
     164              :     void store_header(std::string&& key, std::string&& value);
     165              :     void replace_header(std::string&& key, std::string&& value);
     166              : 
     167              :     // Whether a header set by a setter is valid; if not, it is logged and has_refused_header()
     168              :     // is set
     169              :     bool accept_header(std::string_view key, std::string_view value);
     170              : 
     171              :     // Keep track of the framing headers present, after adding or removing a `key` header
     172              :     void on_header_added(std::string_view key);
     173              :     void on_header_removed(std::string_view key);
     174              : 
     175              :     std::vector<http_header> headers_;
     176              :     std::vector<http_header> proxy_headers_;
     177              :     boost::tribool keep_alive_    = boost::indeterminate;
     178              :     bool upgrade_                 = false;
     179              :     bool stream_                  = false;
     180              :     size_t content_length_        = 0;
     181              :     bool invalid_content_length_  = false;
     182              :     bool content_length_header_   = false;
     183              :     bool transfer_encoding_header_ = false;
     184              :     bool chunked_transfer_         = false;
     185              :     bool invalid_transfer_encoding_ = false;
     186              :     bool refused_header_          = false;
     187              :     uint8_t http_version_major_   = 1;
     188              :     uint8_t http_version_minor_   = 1;
     189              : };
     190              : 
     191              : }
     192              : 
     193              : #endif
        

Generated by: LCOV version 2.0-1