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

            Line data    Source code
       1              : #include "route_handler.hpp"
       2              : #include "../response.hpp"
       3              : #include "../../../util/logger.hpp"
       4              : 
       5              : namespace thinger::http {
       6              : 
       7        11913 : route_handler::route_handler() = default;
       8              : 
       9        34202 : route_builder route_handler::operator[](method http_method) {
      10        34202 :     return route_builder(http_method, routes_[http_method], trees_[http_method], &schema_components_);
      11              : }
      12              : 
      13        89548 : const route* route_handler::find_route(std::shared_ptr<request> req) {
      14        89548 :     auto http_request = req->get_http_request();
      15        89548 :     const auto& request_method = http_request->get_method();
      16        89548 :     std::string_view path = http_request->get_uri();
      17        89548 :     path = path.substr(0, path.find('?'));
      18              : 
      19        89548 :     LOG_DEBUG("Finding route for {} {}", get_method(request_method), path);
      20              : 
      21        89548 :     detail::route_captures captures;
      22        89548 :     const auto* found = match(request_method, path, captures);
      23              :     // HEAD is answered by the GET route of the path when no HEAD route matches it (the
      24              :     // response drops the body): only searched once HEAD missed, so GET pays nothing for it
      25        89548 :     if (!found && request_method == method::HEAD) {
      26          294 :         captures.clear();
      27          294 :         found = match(method::GET, path, captures);
      28              :     }
      29        89548 :     if (!found) {
      30        60256 :         LOG_DEBUG("No matching route found for {} {}", get_method(request_method), path);
      31        60256 :         return nullptr;
      32              :     }
      33        29292 :     LOG_DEBUG("Matched route: {}", found->target->get_pattern());
      34              : 
      35              :     // Path parameters, as captured (raw, not percent-decoded)
      36        35282 :     for (size_t i = 0; i < captures.size() && i < found->names.size(); ++i) {
      37         5990 :         if (!found->names[i].empty() && captures[i].data()) {
      38        17970 :             req->set_uri_parameter(found->names[i], std::string(captures[i]));
      39              :         }
      40              :     }
      41              : 
      42        29292 :     req->set_matched_route(found->target);
      43        29292 :     return found->target;
      44        89548 : }
      45              : 
      46         1219 : thinger::awaitable<void> route_handler::handle_unmatched(request& req, response& res) {
      47              :     if (fallback_handler_) {
      48              :         co_await fallback_handler_(req, res);
      49              :         co_return;
      50              :     }
      51              : 
      52              :     // A method with routes (HEAD has those of GET too) → 404
      53              :     auto http_request = req.get_http_request();
      54              :     const auto& request_method = http_request->get_method();
      55              :     if (routes_.contains(request_method) || (request_method == method::HEAD && routes_.contains(method::GET))) {
      56              :         res.error(http_response::status::not_found);
      57              :         co_return;
      58              :     }
      59              : 
      60              :     // A method without routes at all → 405, with the methods of the routes matching the path
      61              :     // (RFC 9110, 15.5.6), HEAD wherever GET: empty if none does
      62              :     std::string_view path = http_request->get_uri();
      63              :     path = path.substr(0, path.find('?'));
      64              :     std::string allowed;
      65              :     for (auto candidate : {method::GET, method::HEAD, method::POST, method::PUT, method::DELETE,
      66              :                            method::TRACE, method::OPTIONS, method::CONNECT, method::PATCH}) {
      67              :         detail::route_captures captures;
      68              :         if (!match(candidate, path, captures) && !(candidate == method::HEAD && match(method::GET, path, captures))) {
      69              :             continue;
      70              :         }
      71              :         if (!allowed.empty()) allowed += ", ";
      72              :         allowed += get_method(candidate);
      73              :     }
      74              :     res.header("Allow", allowed);
      75              :     res.error(http_response::status::not_allowed);
      76         2438 : }
      77              : 
      78              : } // namespace thinger::http
        

Generated by: LCOV version 2.0-1