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

            Line data    Source code
       1              : #ifndef THINGER_HTTP_SERVER_UPGRADE_HPP
       2              : #define THINGER_HTTP_SERVER_UPGRADE_HPP
       3              : 
       4              : // What a connection upgraded or taken over from the server is handed with (see
       5              : // response::upgrade_websocket(), upgrade_websocket_stream(), start_sse() and take_over()), and
       6              : // the options of those upgrades. Light: only declarations of the connection types, so that
       7              : // response.hpp does not bring them in.
       8              : 
       9              : #include <chrono>
      10              : #include <cstddef>
      11              : #include <cstdint>
      12              : #include <functional>
      13              : #include <memory>
      14              : #include <optional>
      15              : #include <string>
      16              : #include <vector>
      17              : #include "../../util/types.hpp"
      18              : 
      19              : namespace thinger::asio {
      20              : class socket;
      21              : class websocket;
      22              : }
      23              : 
      24              : namespace thinger::http {
      25              : 
      26              : // What a connection of a server serves (see connection_tracker.hpp)
      27              : enum class connection_type : uint8_t;
      28              : 
      29              : class websocket_connection;
      30              : class sse_connection;
      31              : 
      32              : namespace detail {
      33              : // The server's tracking of a connection taken over (connection_tracker.hpp), not public
      34              : class taken_over_tracking;
      35              : }
      36              : 
      37              : /**
      38              :  * The link between a connection taken over (or upgraded) and the server it came from: its
      39              :  * statistics and its shutdown. A handle: copies share it. It does not keep the server alive;
      40              :  * with no server (a socket that does not come from one, a request dispatched in memory, a
      41              :  * default-constructed scope), or once the server is gone, its methods do nothing.
      42              :  *
      43              :  * The server counts the connection while the socket handed out (or a WebSocket built on it)
      44              :  * lives, or until closed() is called; whatever is still open at the end of a shutdown's grace
      45              :  * period is closed. On the thread of the socket.
      46              :  */
      47              : class server_scope {
      48              : public:
      49          189 :     server_scope() = default;
      50              : 
      51              :     // What the connection serves now, in the server statistics
      52              :     void set_type(connection_type type) const;
      53              : 
      54              :     // Be told when the server shuts down gracefully: `callback` is called once, on the
      55              :     // io_context of the socket, when the drain starts, so the owner can finish and close the
      56              :     // connection (e.g. with a WebSocket close 1001 Going Away). Set after the drain started, it
      57              :     // is still called, posted. Released before it is called: it may own the socket. What it
      58              :     // throws is logged
      59              :     void on_shutdown(sync_callback<void()> callback) const;
      60              : 
      61              :     // The new owner closed the connection: it no longer counts, and is not told of a shutdown.
      62              :     // Once; calling it again does nothing
      63              :     void closed() const;
      64              : 
      65              : private:
      66              :     friend class detail::taken_over_tracking;
      67         4306 :     explicit server_scope(std::weak_ptr<detail::taken_over_tracking> tracking)
      68         4306 :         : tracking_(std::move(tracking)) {}
      69              : 
      70              :     std::weak_ptr<detail::taken_over_tracking> tracking_;
      71              : };
      72              : 
      73              : // A connection taken over from the server (see response::take_over()): its socket, any bytes
      74              : // the server had already read past the request that took it over, and its scope
      75              : struct upgraded_connection {
      76              :     std::shared_ptr<asio::socket> socket;
      77              :     std::string buffered;
      78              :     server_scope scope;
      79              : };
      80              : 
      81              : using takeover_handler = sync_callback<void(upgraded_connection)>;
      82              : // The same, as a coroutine (run on the io_context of the socket)
      83              : using takeover_coroutine = std::function<awaitable<void>(upgraded_connection)>;
      84              : 
      85              : // Options of a WebSocket upgrade, with a message interface (response::upgrade_websocket()) or
      86              : // as a byte stream (response::upgrade_websocket_stream())
      87              : struct websocket_accept_options {
      88              :     // Subprotocols the handler speaks, alternatives included (e.g. "mqtt", "mqttv3.1",
      89              :     // "mqttv3.1.1"). The first one the client offers (Sec-WebSocket-Protocol, in its order of
      90              :     // preference) that is in this list is accepted. With the message interface, a client
      91              :     // offering subprotocols none of which is here is rejected with 400 (also with an empty
      92              :     // list); as a byte stream, it is accepted without one, unless protocol_required
      93              :     std::vector<std::string> protocols;
      94              :     // With protocols: a client offering none of them is rejected with 400. Otherwise it is
      95              :     // accepted without a subprotocol (and the handler gets an empty one)
      96              :     bool protocol_required = true;
      97              :     // As a byte stream: messages written binary, or text (the message interface writes each
      98              :     // message as it is sent)
      99              :     bool binary = true;
     100              :     // With the message interface: the largest message received (at least 1 byte); a larger one
     101              :     // closes with 1009 (Message Too Big). A byte stream has no messages, nor this limit
     102              :     size_t max_message_size = size_t{16} * 1024 * 1024;
     103              :     // Ping the client once nothing was received from it for this long, and close the connection
     104              :     // once nothing was received for idle_timeout (pongs included); 0 turns each off. Unset, the
     105              :     // default of the upgrade: 60 s and 120 s with the message interface, off as a byte stream
     106              :     std::optional<std::chrono::milliseconds> ping_interval;
     107              :     std::optional<std::chrono::milliseconds> idle_timeout;
     108              : };
     109              : 
     110              : // A WebSocket upgraded as a byte stream (see response::upgrade_websocket_stream()): the
     111              : // WebSocket (holding any frames the client sent right after its request), the subprotocol
     112              : // negotiated (empty if none), and its scope
     113              : struct upgraded_websocket {
     114              :     std::shared_ptr<asio::websocket> stream;
     115              :     std::string protocol;
     116              :     server_scope scope;
     117              : };
     118              : 
     119              : using websocket_stream_handler = sync_callback<void(upgraded_websocket)>;
     120              : using websocket_stream_coroutine = std::function<awaitable<void>(upgraded_websocket)>;
     121              : 
     122              : // A WebSocket with a message interface, and an event stream
     123              : using websocket_handler = sync_callback<void(std::shared_ptr<websocket_connection>)>;
     124              : using websocket_coroutine = std::function<awaitable<void>(std::shared_ptr<websocket_connection>)>;
     125              : using sse_handler = sync_callback<void(std::shared_ptr<sse_connection>)>;
     126              : using sse_coroutine = std::function<awaitable<void>(std::shared_ptr<sse_connection>)>;
     127              : 
     128              : // Options of an event stream (response::start_sse())
     129              : struct sse_options {
     130              :     // Interval of the keep-alive comments written while nothing else is (0: none); see
     131              :     // sse_connection::set_keepalive()
     132          513 :     std::chrono::milliseconds keepalive{15000};
     133              : };
     134              : 
     135              : }
     136              : 
     137              : #endif
        

Generated by: LCOV version 2.0-1