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

            Line data    Source code
       1              : #ifndef API_REQUEST_HPP
       2              : #define API_REQUEST_HPP
       3              : 
       4              : #include <cstdint>
       5              : #include <string>
       6              : #include <memory>
       7              : #include <optional>
       8              : #include <set>
       9              : #include <vector>
      10              : #include <nlohmann/json.hpp>
      11              : #include <boost/algorithm/string.hpp>
      12              : 
      13              : #include "http_stream.hpp"
      14              : #include "body_reader.hpp"
      15              : #include "body_decoders.hpp"
      16              : #include "multipart.hpp"
      17              : #include "request_context.hpp"
      18              : #include "../common/http_request.hpp"
      19              : #include "../common/http_response.hpp"
      20              : #include "../../util/types.hpp"
      21              : 
      22              : namespace thinger::http{
      23              : 
      24              :     // Maximum request body size unless the application or the route set theirs
      25              :     inline constexpr std::uint64_t default_max_body_size = 8 * 1024 * 1024;
      26              : 
      27              :     // Why the scope of a request was cancelled (see request::cancelled()):
      28              :     // - client_gone: its client reset the connection, or a read or write on it failed, or it
      29              :     //   ended before the request was complete (a client that only closes its sending side
      30              :     //   after a complete request still gets its response: that is not client_gone);
      31              :     // - timeout: the timeout of its route (or of its in-memory dispatch) expired;
      32              :     // - shutdown: the server reached the deadline of its shutdown with the request in progress.
      33              :     // The library interrupts a handler for a reason only if its route declared it (see
      34              :     // route::cancel_on), and always for shutdown
      35              :     enum class cancel_reason : uint8_t { none, client_gone, timeout, shutdown };
      36              : 
      37              :     // Forward declarations
      38              :     class route;
      39              :     class virtual_host;
      40              :     class trusted_proxies;
      41              :     struct body_settings;
      42              :     class http_application;
      43              :     namespace proxy_detail { class proxy_exchange; }
      44              : 
      45              :     /**
      46              :      * Class that represents a single HTTP request over the API. It means, that the HTTP request
      47              :      * was matched to some of the registered endpoints in the API. It holds where its body is
      48              :      * read from (its connection, or memory), the stream associated to the HTTP request, and the
      49              :      * HTTP request itself.
      50              :      */
      51              :     class request{
      52              :     public:
      53              :         /// A request whose body comes from `source` (none: no body beyond what it was given,
      54              :         /// and no connection), answered on `stream` (none in memory)
      55              :         request(std::unique_ptr<detail::request_source> source,
      56              :                 std::shared_ptr<http_stream> http_stream,
      57              :                 std::shared_ptr<http_request> http_request);
      58              : 
      59              :         virtual ~request();
      60              : 
      61              :         // Not copyable: its body reader refers to it
      62              :         request(const request&) = delete;
      63              :         request& operator=(const request&) = delete;
      64              : 
      65              :     public:
      66              :         /// get parameter
      67              :         const std::string& operator[](const std::string& param) const;
      68              : 
      69              :         /// has parameter
      70              :         bool has(const std::string& param) const;
      71              : 
      72              :         /// erase parameter
      73              :         bool erase(const std::string& param);
      74              : 
      75              :         std::string debug_parameters() const;
      76              : 
      77              :         std::shared_ptr<http_request> get_http_request();
      78              :         std::shared_ptr<const http_request> get_http_request() const;
      79              : 
      80              :         /// Client IP: the peer address or, if the peer is a trusted proxy (see
      81              :         /// http_application::set_trusted_proxies), the client address it forwarded
      82              :         std::string get_request_ip() const;
      83              : 
      84              :         /// Address of the direct peer (the connection's remote IP), whatever it forwards
      85              :         std::string get_peer_ip() const;
      86              : 
      87              :         /// The connection the request came from: its peer and local addresses, and whether it
      88              :         /// is TLS (for requests dispatched in memory, the peer address of the dispatch). Empty
      89              :         /// once the connection is gone
      90              :         connection_info connection() const;
      91              : 
      92              :         /// TLS session of the connection (e.g. the client certificate on an mTLS route): none
      93              :         /// without TLS, in memory, or once the connection is gone
      94              :         std::optional<asio::tls_session> tls() const;
      95              : 
      96              :         /// Whether the request came on a TLS connection whose handshake (SNI) chose another
      97              :         /// client certificate mode or certificate than its host name (Host) gets: it must not be
      98              :         /// served there (421, see asio::tls_handshake_record::misdirected()). False at once without
      99              :         /// TLS, in memory, or once the connection is gone
     100              :         bool misdirected() const;
     101              : 
     102              :         /// Client scheme, "http" or "https": the one of the connection (TLS or not; for
     103              :         /// requests dispatched in memory, the one of the request, see http_request::is_ssl())
     104              :         /// or, if the peer is a trusted proxy (see http_application::set_trusted_proxies),
     105              :         /// the one it forwarded for the client of get_request_ip(): the X-Forwarded-Proto value
     106              :         /// aligned with X-Forwarded-For (or its last one), or the proto of the Forwarded element
     107              :         /// giving the client address (see trusted_proxies::resolve())
     108              :         std::string get_request_scheme() const;
     109              : 
     110              :         /// Peer address of requests without a connection (in-memory dispatch)
     111         1176 :         void set_peer_ip(std::string ip) { peer_ip_ = std::move(ip); }
     112              : 
     113              :         /// Proxies whose forwarding header get_request_ip() accepts (set by the server)
     114        27559 :         void set_trusted_proxies(std::shared_ptr<const trusted_proxies> proxies) { trusted_proxies_ = std::move(proxies); }
     115              : 
     116              :         /// Trusted proxies set by the server (null if none is trusted)
     117          675 :         const std::shared_ptr<const trusted_proxies>& get_trusted_proxies() const { return trusted_proxies_; }
     118              : 
     119              :         /// Virtual host serving the request (the application itself for the default host)
     120          477 :         const virtual_host* get_virtual_host() const { return virtual_host_; }
     121        27136 :         void set_virtual_host(const virtual_host* host) { virtual_host_ = host; }
     122              : 
     123              :         /// Captures of the host pattern that matched: [0] is the host name and [1..] the
     124              :         /// groups, e.g. the subdomain matched by "*" in "*.example.com" (the labels named
     125              :         /// with ":name" are also request parameters). Empty for exact and default hosts.
     126            9 :         const std::vector<std::string>& get_host_matches() const { return host_matches_; }
     127           27 :         void set_host_matches(std::vector<std::string> matches) { host_matches_ = std::move(matches); }
     128              : 
     129              :         // Convenience methods for accessing request data
     130              : 
     131              :         /// Get query parameter by key
     132              :         std::string query(const std::string& key) const;
     133              : 
     134              :         /// Get query parameter by key with default value
     135              :         std::string query(const std::string& key, const std::string& default_value) const;
     136              : 
     137              :         /// Get request body as string
     138              :         std::string body() const;
     139              : 
     140              :         /// Get request body parsed as JSON
     141              :         nlohmann::json json() const;
     142              : 
     143              :         /// Get request header by key
     144              :         std::string header(const std::string& key) const;
     145              : 
     146              :         std::shared_ptr<http_stream> get_http_stream() const;
     147              : 
     148              :         void set_uri_parameter(const std::string& param, const std::string& value);
     149              : 
     150              :         void add_uri_parameter(const std::string& param, const std::string& value);
     151              : 
     152              :         const std::string& get_uri_parameter(const std::string& param) const;
     153              : 
     154              :         const std::multimap<std::string, std::string>& get_uri_parameters() const;
     155              : 
     156              :         void set_auth_groups(const std::set<std::string>& groups);
     157              : 
     158              :         const std::set<std::string>& get_auth_groups() const;
     159              : 
     160              :         void set_auth_user(const std::string& auth_user);
     161              : 
     162              :         const std::string& get_auth_user() const;
     163              : 
     164              :         /// Typed values of this request (see request_context), e.g. the principal an
     165              :         /// authentication middleware stores for the route handler. Empty at first (or with the
     166              :         /// values of dispatch_options::context), they are destroyed with the request.
     167         4251 :         request_context& context() noexcept { return context_; }
     168              :         const request_context& context() const noexcept { return context_; }
     169              : 
     170              :         void set_matched_route(const route* route);
     171              :         
     172              :         const route* get_matched_route() const;
     173              : 
     174              :         // Whether the client asked to keep the connection (its Connection header and HTTP
     175              :         // version), not whether the server keeps it after the response
     176              :         bool keep_alive() const;
     177              : 
     178              :         // --- Cancellation (see cancel_reason, route::cancel_on) ---
     179              : 
     180              :         /// Why the scope of the request was cancelled: none until then (or for a request
     181              :         /// without a stream). Its handler goes on unless its route declared the reason (or
     182              :         /// the server shuts down): it looks here where it chooses
     183              :         cancel_reason cancelled() const;
     184              : 
     185              :         /// Completes once the scope of the request is cancelled, whatever the reason, and
     186              :         /// returns it; none if this wait is cancelled itself (e.g. by `||`). For a request
     187              :         /// without a stream it never completes. Await it on the executor of the handler
     188              :         thinger::awaitable<cancel_reason> client_gone();
     189              : 
     190              :         /// Slot cancelled (terminal) with the scope of the request while the handler runs,
     191              :         /// for the awaits the handler chooses to abandon then: bind it with
     192              :         /// boost::asio::bind_cancellation_slot, to one pending await at a time (asio's rule).
     193              :         /// Not connected for a request without a stream
     194              :         boost::asio::cancellation_slot cancellation_slot();
     195              : 
     196              :         // --- Body reading (see body_reader) ---
     197              : 
     198              :         /// Store read-ahead data: the bytes read past the request head (called by the
     199              :         /// connection before dispatch)
     200              :         void set_read_ahead(const uint8_t* data, size_t size);
     201              : 
     202              :         /// Read exactly `size` bytes (read-ahead first, then the source). TCP backpressure.
     203              :         /// Returns less at the end of the body, or if it fails (see get_body_error()), e.g.
     204              :         /// for a body over the maximum body size (a Content-Length one before reading it).
     205              :         thinger::awaitable<size_t> read(uint8_t* buffer, size_t size);
     206              : 
     207              :         /// Read up to `max_size` bytes (read-ahead first, then the source); 0 at the end of the
     208              :         /// body, or if it fails (see get_body_error()), as read(). A read cancelled (e.g. by
     209              :         /// `read_some() || timer`) returns 0 too, without failing: the body is still pending.
     210              :         thinger::awaitable<size_t> read_some(uint8_t* buffer, size_t max_size);
     211              : 
     212              :         /// Absolute deadline for the reads of the body, pending and later ones (read(),
     213              :         /// read_some(), read_body(), those of a multipart_reader), as Go's
     214              :         /// http.ResponseController.SetReadDeadline: the handler sets and moves it as it reads,
     215              :         /// as often as it likes, on the thread rules of those reads ("60 s more for each 64 MiB
     216              :         /// received"), which cuts a client that goes silent or trickles without cutting a long
     217              :         /// upload. Bytes already received (the read-ahead) are still returned past it.
     218              :         /// It replaces the request timeout (connection_options::request_timeout, or the one of
     219              :         /// the route) for the rest of the body; time_point::max() removes it, leaving only the
     220              :         /// idle timeout, which a deadline further away than it does not extend. It bounds no
     221              :         /// write. As it passes, the pending read returns what it read, or 0, and
     222              :         /// get_body_error() is body_error::timeout, for good. Nothing is answered for the
     223              :         /// handler (408 if it answers nothing), and the connection closes after the response,
     224              :         /// as with any body left unread. Without a connection (in memory) it does nothing: a
     225              :         /// body in memory never waits.
     226              :         void set_read_deadline(std::chrono::steady_clock::time_point deadline);
     227              : 
     228              :         /// Read full body into http_request content (for non-deferred dispatch), decoding
     229              :         /// its Content-Encoding (gzip, deflate, or a list of them; see util::decode_content).
     230              :         /// Fails if the body exceeds the maximum body size (see get_max_body_size()), a
     231              :         /// Content-Length one without reading it.
     232              :         /// Only reads what is still pending: do not mix it with read()/read_some() on
     233              :         /// the same request, or the stored body will be incomplete (and fail to
     234              :         /// decompress if Content-Encoding is set).
     235              :         thinger::awaitable<bool> read_body();
     236              : 
     237              :         /// Read a multipart/form-data body (see multipart_form): its fields in memory and its
     238              :         /// files in temporary files, within the limits of the options and the maximum body
     239              :         /// size. Check the form for errors (400 malformed, 413 over a limit, 415 not a form).
     240              :         /// To stream the parts instead, read them with a multipart_reader.
     241              :         thinger::awaitable<multipart_form> read_multipart(multipart_options options = {});
     242              : 
     243              :         /// Read and drop an unread body so the connection can be reused.
     244              :         /// Returns false if the body exceeds `max_size` or the read fails.
     245              :         thinger::awaitable<bool> discard_body(std::uint64_t max_size);
     246              : 
     247              :         /// Whether part of the body is still unread by read(), read_some() or read_body().
     248              :         bool has_pending_body() const;
     249              : 
     250              :         /// Why reading the body failed (body_error::none if it did not): too large, invalid
     251              :         /// chunked framing, connection closed before its end, undecodable Content-Encoding, or
     252              :         /// past its read deadline (see set_read_deadline())
     253              :         body_error get_body_error() const;
     254              : 
     255              :         /// Content-Length convenience (0 for chunked requests)
     256              :         size_t content_length() const;
     257              : 
     258              :         /// Whether the request uses chunked transfer encoding
     259              :         bool is_chunked() const;
     260              : 
     261              :         /// Remove and return the unconsumed read-ahead bytes (data following this request)
     262              :         std::vector<uint8_t> take_read_ahead();
     263              : 
     264              :         /// Maximum body size of every read (read_body(), read(), read_some()), also once
     265              :         /// decompressed: the one of the matched route, or of the application (set by the server)
     266        27898 :         void set_max_body_size(std::uint64_t size) { body_reader_.set_limit(size); }
     267            3 :         std::uint64_t get_max_body_size() const { return body_reader_.limit(); }
     268              : 
     269              :         /// How the matched route decodes the body (set by the server; the defaults if not)
     270        27559 :         void set_body_settings(const body_settings* settings) { body_settings_ = settings; }
     271              :         const body_settings& get_body_settings() const;
     272              : 
     273              :     private:
     274              : 
     275              :         /// After reading the body: once read to its end, none of it is left on the connection
     276              :         /// (see http_stream::input_pending())
     277              :         void note_body_read();
     278              : 
     279              :         /**
     280              :          * A stream is a channel inside the HTTP connection. i.e, while receiving multiple requests
     281              :          * and responses are generated asynchronously, it is required to know which response
     282              :          * belong to a given query, and provide an ordered response (to support HTTP pipelining).
     283              :          * With HTTP2.0 it is possible to provide responses without pipelining.
     284              :          */
     285              :         std::shared_ptr<http_stream> http_stream_;
     286              : 
     287              :         /**
     288              :          * Where the body is read from, and what is known of the connection (see
     289              :          * detail::request_source). After the stream it refers to, so destroyed before it
     290              :          */
     291              :         std::unique_ptr<detail::request_source> source_;
     292              : 
     293              :         /**
     294              :          * This is the request that originated the API Request, and required to know the body, the
     295              :          * content type, etc.
     296              :          */
     297              :         std::shared_ptr<http_request> http_request_;
     298              : 
     299              :         /**
     300              :          * Vector for storing the matched parameters found in the URL, like username, device, etc.
     301              :          */
     302              :         std::multimap<std::string, std::string> params_;
     303              : 
     304              :         std::string auth_user_;
     305              : 
     306              :         std::string peer_ip_;
     307              : 
     308              :         std::shared_ptr<const trusted_proxies> trusted_proxies_;
     309              : 
     310              :         const virtual_host* virtual_host_ = nullptr;
     311              : 
     312              :         std::vector<std::string> host_matches_;
     313              : 
     314              :         std::set<std::string> groups_;
     315              : 
     316              :         /// Values stored by the middlewares (or the in-memory dispatch) for the handler
     317              :         request_context context_;
     318              : 
     319              :         const route* matched_route_ = nullptr;
     320              : 
     321              :         /// Body framing and reading: read-ahead bytes, then the connection
     322              :         body_reader body_reader_;
     323              : 
     324              :         /// Body decoders, accepted types and limits of the application (null: defaults)
     325              :         const body_settings* body_settings_ = nullptr;
     326              : 
     327              :         /// The sink of the response, the deadline of a route timeout and the proxy watching the
     328              :         /// client reach the connection through the source
     329              :         friend class connection_sink;
     330              :         friend class http_application;
     331              :         friend class proxy_detail::proxy_exchange;
     332              : 
     333              :         /// Body parsed as JSON by the matched route, for callbacks taking it
     334              :         friend class route;
     335              :         nlohmann::json json_body_;
     336              : 
     337              :         /// Type of the body the matched route accepted (matched before the body is read):
     338              :         /// its media type (null until matched) and decoder (null if none). They point to the
     339              :         /// decoder registry for the media types its decoders list, else to body_type_.
     340              :         const media_type* body_media_type_ = nullptr;
     341              :         const body_format* body_format_ = nullptr;
     342              :         std::optional<body_type_match> body_type_;
     343              :     };
     344              : 
     345              : }
     346              : 
     347              : 
     348              : #endif
        

Generated by: LCOV version 2.0-1