LCOV - code coverage report
Current view: top level - http/server - http_stream.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 93.8 % 64 60
Test Date: 2026-11-10 10:38:07 Functions: 89.5 % 19 17

            Line data    Source code
       1              : #include "http_stream.hpp"
       2              : #include "request.hpp"
       3              : #include "../common/http_request.hpp"
       4              : #include "../../asio/io_context_of.hpp"
       5              : #include "../../util/logger.hpp"
       6              : #include <boost/asio/dispatch.hpp>
       7              : #include <boost/asio/post.hpp>
       8              : #include <string_view>
       9              : 
      10              : namespace thinger::http {
      11              : 
      12          150 :     void detail::continue_frame::to_buffer(std::vector<boost::asio::const_buffer>& buffer) const {
      13              :         static constexpr std::string_view head = "HTTP/1.1 100 Continue\r\n\r\n";
      14          150 :         buffer.emplace_back(boost::asio::buffer(head.data(), head.size()));
      15          150 :     }
      16              : 
      17          150 :     void detail::continue_frame::log(const char* scope, const body_logging&) const {
      18          150 :         LOG_DEBUG("[{}] HTTP/1.1 100 Continue", scope);
      19          150 :     }
      20              : 
      21        27679 :     http_stream::http_stream(stream_id stream_id, const http_request& request, boost::asio::any_io_executor executor)
      22        27679 :         : stream_id_(stream_id), executor_(std::move(executor)), cancelled_(cancel_reason::none) {
      23              :         // A client that does not keep the connection: by its Connection header, or by default
      24              :         // before HTTP/1.1
      25        27679 :         if (!request.keep_alive()) {
      26         7334 :             bool http11 = request.get_http_version_major() > 1 ||
      27         3667 :                           (request.get_http_version_major() == 1 && request.get_http_version_minor() >= 1);
      28         3667 :             must_close(http11 ? detail::close_reason::client_asked : detail::close_reason::http10);
      29              :         }
      30        27679 :     }
      31              : 
      32          387 :     http_stream::http_stream(stream_id stream_id, detail::close_reason reason, boost::asio::any_io_executor executor)
      33          387 :         : stream_id_(stream_id), close_reason_(reason), executor_(std::move(executor)),
      34          774 :           cancelled_(cancel_reason::none) {}
      35              : 
      36         3667 :     void http_stream::cancel(cancel_reason reason) {
      37         4259 :         if (reason == cancel_reason::none) return;
      38              :         // The first reason wins, whatever thread gives it
      39         3664 :         auto none = cancel_reason::none;
      40         3664 :         if (!cancelled_.compare_exchange_strong(none, reason, std::memory_order_acq_rel)) {
      41              :             // The scope stays as it is, but a shutdown still interrupts a handler that the first
      42              :             // reason let go on (once: what it does about an interruption is up to it)
      43          592 :             if (reason != cancel_reason::shutdown) return;
      44          488 :             boost::asio::dispatch(executor_, [self = shared_from_this()] {
      45          488 :                 if (!self->handler_running_ || self->handler_interrupted_) return;
      46          101 :                 self->observer_signal_.emit(boost::asio::cancellation_type::terminal);
      47          101 :                 self->interrupt_handler();
      48              :             });
      49          488 :             return;
      50              :         }
      51         3072 :         boost::asio::dispatch(executor_, [self = shared_from_this(), reason] {
      52         3069 :             if (self->cancel_event_) self->cancel_event_->set();
      53         3069 :             if (!self->handler_running_) return;
      54         1856 :             self->observer_signal_.emit(boost::asio::cancellation_type::terminal);
      55              :             // A drain deadline interrupts every handler; another reason, those declaring it
      56         1856 :             if (reason == cancel_reason::shutdown || (self->cancel_on_ & (1u << static_cast<unsigned>(reason)))) {
      57         1360 :                 self->interrupt_handler();
      58              :             }
      59              :         });
      60              :     }
      61              : 
      62         1461 :     void http_stream::interrupt_handler() {
      63         1461 :         handler_interrupted_ = true;
      64         1461 :         auto& signal = bound_signal_ ? *bound_signal_ : handler_signal_;
      65         1461 :         signal.emit(boost::asio::cancellation_type::terminal);
      66         1461 :     }
      67              : 
      68         2040 :     void http_stream::end_input(bool follows) {
      69         2040 :         if (asio::running_in_this_thread(executor_)) [[likely]] {
      70         1986 :             set_input_pending(false, follows);
      71         1986 :             return;
      72              :         }
      73          108 :         boost::asio::post(executor_, [self = shared_from_this(), follows] { self->set_input_pending(false, follows); });
      74              :     }
      75              : 
      76          678 :     awaitable<cancel_reason> http_stream::wait_cancelled() {
      77              :         // Created cancelled if the reason is there: cancel() may have come from another thread,
      78              :         // with its dispatched half (which sets the event, if any) still to run here
      79              :         if (!cancel_event_) {
      80              :             cancel_event_.emplace(executor_);
      81              :             if (cancelled() != cancel_reason::none) cancel_event_->set();
      82              :         }
      83              :         if (!co_await cancel_event_->wait()) co_return cancel_reason::none;
      84              :         co_return cancelled();
      85         1356 :     }
      86              : 
      87            0 :     size_t http_stream::get_queue_size() const {
      88            0 :         return queue_.size();
      89              :     }
      90              : 
      91        64594 :     bool http_stream::empty_queue() const {
      92        64594 :         return queue_.empty();
      93              :     }
      94              : 
      95        39117 :     std::shared_ptr<http_frame> http_stream::current_frame() const {
      96        39117 :         return queue_.front();
      97              :     }
      98              : 
      99        39120 :     void http_stream::pop_frame() {
     100        39120 :         queue_.pop();
     101        39120 :     }
     102              : 
     103        39105 :     void http_stream::add_frame(std::shared_ptr<http_frame> frame) {
     104        39105 :         queue_.push(std::move(frame));
     105        39105 :         responded_ = true;
     106        39105 :     }
     107              : 
     108          150 :     void http_stream::add_interim_frame(std::shared_ptr<http_frame> frame) {
     109          150 :         queue_.push(std::move(frame));
     110          150 :     }
     111              : 
     112            0 :     size_t http_stream::get_queued_frames() const {
     113            0 :         return queue_.size();
     114              :     }
     115              : 
     116        78204 :     stream_id http_stream::id() const {
     117        78204 :         return stream_id_;
     118              :     }
     119              : }
        

Generated by: LCOV version 2.0-1