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

            Line data    Source code
       1              : #ifndef THINGER_HTTP_SERVER_RESPONSE_OBSERVER_HPP
       2              : #define THINGER_HTTP_SERVER_RESPONSE_OBSERVER_HPP
       3              : 
       4              : #include "../common/http_response.hpp"
       5              : #include "../../util/types.hpp"
       6              : #include <atomic>
       7              : #include <chrono>
       8              : #include <cstdint>
       9              : #include <functional>
      10              : #include <memory>
      11              : 
      12              : namespace thinger::http {
      13              : 
      14              : class request;
      15              : 
      16              : // How a response ended (see response_info)
      17              : enum class response_outcome : uint8_t {
      18              :     // Written whole (for a request dispatched in memory, collected whole)
      19              :     completed,
      20              :     // Its head written, the connection was handed over: a WebSocket upgrade, an event stream
      21              :     // (start_sse()) or take_over(). What happens on the connection afterwards is not reported
      22              :     // (see websocket_connection::on_close() and sse_connection::on_close())
      23              :     taken_over,
      24              :     // Not completed: the connection was closed or failed first (the client left, the connection
      25              :     // timed out, the server shut down...), the response was aborted (abort_stream(), or a
      26              :     // handler failing once it started), or nothing answered the request
      27              :     aborted
      28              : };
      29              : 
      30              : // A response, as http_application::on_response() reports it
      31              : struct response_info {
      32              :     // Status of the response head (0 if nothing was answered)
      33              :     http_response::status status{};
      34              :     response_outcome outcome = response_outcome::completed;
      35              :     // The response that ended had its body sent in parts as it was produced: start_chunked()
      36              :     // or start_stream() (not for HEAD, answered with the head alone, nor for a response that
      37              :     // replaced it, as the 502 of a stream aborted in memory)
      38              :     bool streamed = false;
      39              :     // Bytes of this response written to the connection, head included (and an interim
      40              :     // 100 Continue), before any TLS; for a request dispatched in memory, the size of the body
      41              :     uint64_t bytes = 0;
      42              :     // From the moment the application received the request (its headers read) until the
      43              :     // response ended: written, handed over, or aborted
      44              :     std::chrono::steady_clock::duration duration{};
      45              : };
      46              : 
      47              : // See http_application::on_response()
      48              : using response_hook = sync_callback<void(const request&, const response_info&)>;
      49              : 
      50              : // The end of a request, for the response hook: its response ended (as told by the sink writing
      51              : // it) and the application is done with the request, in either order. Created only while a hook
      52              : // is set: the server costs nothing without one.
      53              : class response_observer {
      54              : public:
      55              :     // The peer address of the request is kept, for a hook called once its connection is gone
      56              :     response_observer(std::shared_ptr<request> req, std::shared_ptr<const response_hook> hook);
      57              : 
      58              :     // Reports the response if it was not yet: as aborted, unless it ended
      59              :     ~response_observer();
      60              : 
      61              :     response_observer(const response_observer&) = delete;
      62              :     response_observer& operator=(const response_observer&) = delete;
      63              : 
      64              :     // The response, from the sink writing it (one thread at a time): its head, with whether
      65              :     // its body follows in parts, the bytes written, and its end (once, later ones are ignored)
      66              :     void head(http_response::status status, bool streamed);
      67         1247 :     void add_bytes(uint64_t bytes) { info_.bytes += bytes; }
      68              :     void ended(response_outcome outcome);
      69              : 
      70              :     // From any thread: the response is aborted (it ends as such)
      71           24 :     void abort() { aborted_.store(true, std::memory_order_relaxed); }
      72              : 
      73              :     // The application is done with the request
      74              :     void handled();
      75              : 
      76              :     // `callback` runs once the hook has returned (for an in-memory dispatch, which waits for
      77              :     // it). Set before the response can end
      78          759 :     void on_reported(std::function<void()> callback) { on_reported_ = std::move(callback); }
      79              : 
      80              : private:
      81              :     // One side is done: the last one reports
      82              :     void done();
      83              :     void report() noexcept;
      84              : 
      85              :     std::shared_ptr<request> request_;
      86              :     std::shared_ptr<const response_hook> hook_;
      87              :     std::function<void()> on_reported_;
      88              :     std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
      89              :     response_info info_;
      90              :     bool ended_ = false;
      91              :     bool handled_ = false;
      92              :     bool reported_ = false;
      93              :     std::atomic<bool> aborted_{false};
      94              :     // The sides not done yet: the response and the application
      95              :     std::atomic<uint8_t> pending_{2};
      96              : };
      97              : 
      98              : } // namespace thinger::http
      99              : 
     100              : #endif // THINGER_HTTP_SERVER_RESPONSE_OBSERVER_HPP
        

Generated by: LCOV version 2.0-1