LCOV - code coverage report
Current view: top level - boost/http/server - router_types.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 22 22
Test Date: 2026-02-02 17:02:49 Functions: 100.0 % 9 9

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/http
       8              : //
       9              : 
      10              : #ifndef BOOST_HTTP_SERVER_ROUTER_TYPES_HPP
      11              : #define BOOST_HTTP_SERVER_ROUTER_TYPES_HPP
      12              : 
      13              : #include <boost/http/detail/config.hpp>
      14              : #include <boost/http/method.hpp>
      15              : #include <boost/http/detail/except.hpp>
      16              : #include <boost/core/detail/string_view.hpp>
      17              : #include <boost/capy/io_result.hpp>
      18              : #include <boost/capy/task.hpp>
      19              : #include <boost/system/error_category.hpp>
      20              : #include <boost/system/error_code.hpp>
      21              : #include <exception>
      22              : #include <string>
      23              : #include <type_traits>
      24              : #include <utility>
      25              : #include <vector>
      26              : 
      27              : namespace boost {
      28              : namespace http {
      29              : 
      30              : /** Directive values for route handler results.
      31              : 
      32              :     These values indicate how the router should proceed
      33              :     after a handler completes. Handlers return one of
      34              :     the predefined constants (@ref route_done, @ref route_next,
      35              :     @ref route_next_route, @ref route_close) or an error code.
      36              : 
      37              :     @see route_result, route_task
      38              : */
      39              : enum class route_what
      40              : {
      41              :     /// Handler completed successfully, response was sent
      42              :     done,
      43              : 
      44              :     /// Handler declined, try next handler in the route
      45              :     next,
      46              : 
      47              :     /// Handler declined, skip to next matching route
      48              :     next_route,
      49              : 
      50              :     /// Handler requests connection closure
      51              :     close,
      52              : 
      53              :     /// Handler encountered an error
      54              :     error
      55              : };
      56              : 
      57              : //------------------------------------------------
      58              : 
      59              : /** The result type returned by route handlers.
      60              : 
      61              :     This class represents the outcome of a route handler.
      62              :     Handlers return this type to indicate how the router
      63              :     should proceed. Construct from a directive constant
      64              :     or an error code:
      65              : 
      66              :     @code
      67              :     route_task my_handler(route_params& p)
      68              :     {
      69              :         if(! authorized(p))
      70              :             co_return route_next;        // try next handler
      71              : 
      72              :         if(auto ec = process(p); ec)
      73              :             co_return ec;                // return error
      74              : 
      75              :         co_return route_done;            // success
      76              :     }
      77              :     @endcode
      78              : 
      79              :     @par Checking Results
      80              : 
      81              :     Use @ref what() to determine the directive, and
      82              :     @ref error() to retrieve any error code:
      83              : 
      84              :     @code
      85              :     route_result rv = co_await handler(p);
      86              :     if(rv.what() == route_what::error)
      87              :         handle_error(rv.error());
      88              :     @endcode
      89              : 
      90              :     @see route_task, route_what, route_done, route_next
      91              : */
      92              : class BOOST_HTTP_DECL
      93              :     route_result
      94              : {
      95              :     system::error_code ec_;
      96              : 
      97              :     template<route_what T>
      98              :     struct what_t {};
      99              : 
     100              :     route_result(system::error_code ec);
     101              :     void set(route_what w);
     102              : 
     103              : public:
     104           49 :     route_result() = default;
     105              : 
     106              :     /** Construct from a directive constant.
     107              : 
     108              :         This constructor allows implicit conversion from
     109              :         the predefined constants (@ref route_done, @ref route_next,
     110              :         @ref route_next_route, @ref route_close).
     111              : 
     112              :         @code
     113              :         route_task handler(route_params& p)
     114              :         {
     115              :             co_return route_done;  // implicitly converts
     116              :         }
     117              :         @endcode
     118              :     */
     119              :     template<route_what W>
     120          120 :     route_result(what_t<W>)
     121          120 :     {
     122              :         static_assert(W != route_what::error);
     123          120 :         set(W);
     124          120 :     }
     125              : 
     126              :     /** Return the directive for this result.
     127              : 
     128              :         Call this to determine how the router should proceed:
     129              : 
     130              :         @code
     131              :         route_result rv = co_await handler(p);
     132              :         switch(rv.what())
     133              :         {
     134              :         case route_what::done:
     135              :             // response sent, done with request
     136              :             break;
     137              :         case route_what::next:
     138              :             // try next handler
     139              :             break;
     140              :         case route_what::error:
     141              :             log_error(rv.error());
     142              :             break;
     143              :         }
     144              :         @endcode
     145              : 
     146              :         @return The directive value.
     147              :     */
     148              :     auto
     149              :     what() const noexcept ->
     150              :         route_what;
     151              : 
     152              :     /** Return the error code, if any.
     153              : 
     154              :         If @ref what() returns `route_what::error`, this
     155              :         returns the underlying error code. Otherwise returns
     156              :         a default-constructed (non-failing) error code.
     157              : 
     158              :         @return The error code, or a non-failing code.
     159              :     */
     160              :     auto
     161              :     error() const noexcept ->
     162              :         system::error_code;
     163              : 
     164              :     /** Return true if the result indicates an error.
     165              : 
     166              :         @return `true` if @ref what() equals `route_what::error`.
     167              :     */
     168              :     bool failed() const noexcept
     169              :     {
     170              :         return what() == route_what::error;
     171              :     }
     172              : 
     173              :     static constexpr route_result::what_t<route_what::done> route_done{};
     174              :     static constexpr route_result::what_t<route_what::next> route_next{};
     175              :     static constexpr route_result::what_t<route_what::next_route> route_next_route{};
     176              :     static constexpr route_result::what_t<route_what::close> route_close{};
     177              :     friend route_result route_error(system::error_code ec) noexcept;
     178              : 
     179              :     template<class E>
     180              :     friend auto route_error(E e) noexcept ->
     181              :         std::enable_if_t<
     182              :             system::is_error_code_enum<E>::value,
     183              :             route_result>;
     184              : };
     185              : 
     186              : //------------------------------------------------
     187              : 
     188              : /** Handler completed successfully.
     189              : 
     190              :     Return this from a handler to indicate the response
     191              :     was sent and the request is complete:
     192              : 
     193              :     @code
     194              :     route_task handler(route_params& p)
     195              :     {
     196              :         p.res.set(field::content_type, "text/plain");
     197              :         co_await p.send("Hello, World!");
     198              :         co_return route_done;
     199              :     }
     200              :     @endcode
     201              : */
     202              : inline constexpr decltype(auto) route_done = route_result::route_done;
     203              : 
     204              : /** Handler declined, try next handler.
     205              : 
     206              :     Return this from a handler to decline processing
     207              :     and allow the next handler in the route to try:
     208              : 
     209              :     @code
     210              :     route_task auth_handler(route_params& p)
     211              :     {
     212              :         if(! p.req.exists(field::authorization))
     213              :             co_return route_next;  // let another handler try
     214              : 
     215              :         // process authenticated request...
     216              :         co_return route_done;
     217              :     }
     218              :     @endcode
     219              : */
     220              : inline constexpr decltype(auto) route_next = route_result::route_next;
     221              : 
     222              : /** Handler declined, skip to next route.
     223              : 
     224              :     Return this from a handler to skip all remaining
     225              :     handlers in the current route and proceed to the
     226              :     next matching route:
     227              : 
     228              :     @code
     229              :     route_task version_check(route_params& p)
     230              :     {
     231              :         if(p.req.version() < 11)
     232              :             co_return route_next_route;  // skip this route
     233              : 
     234              :         co_return route_next;  // continue with this route
     235              :     }
     236              :     @endcode
     237              : */
     238              : inline constexpr decltype(auto) route_next_route = route_result::route_next_route;
     239              : 
     240              : /** Handler requests connection closure.
     241              : 
     242              :     Return this from a handler to immediately close
     243              :     the connection without sending a response:
     244              : 
     245              :     @code
     246              :     route_task ban_check(route_params& p)
     247              :     {
     248              :         if(is_banned(p.req.remote_address()))
     249              :             co_return route_close;  // drop connection
     250              : 
     251              :         co_return route_next;
     252              :     }
     253              :     @endcode
     254              : */
     255              : inline constexpr decltype(auto) route_close = route_result::route_close;
     256              : 
     257              : /** Construct from an error code.
     258              : 
     259              :     Use this constructor to return an error from a handler.
     260              :     The error code must represent a failure condition.
     261              : 
     262              :     @param ec The error code to return.
     263              : 
     264              :     @throw std::invalid_argument if `!ec` (non-failing code).
     265              : */
     266           11 : inline route_result route_error(system::error_code ec) noexcept
     267              : {
     268           11 :     return route_result(ec);
     269              : }
     270              : 
     271              : /** Construct from an error enum.
     272              : 
     273              :     Use this overload to return an error from a handler
     274              :     using any type satisfying `is_error_code_enum`.
     275              : 
     276              :     @param e The error enum value to return.
     277              : */
     278              : template<class E>
     279            2 : auto route_error(E e) noexcept ->
     280              :     std::enable_if_t<
     281              :         system::is_error_code_enum<E>::value,
     282              :         route_result>
     283              : {
     284            2 :     return route_result(make_error_code(e));
     285              : }
     286              : 
     287              : //------------------------------------------------
     288              : 
     289              : /** Convenience alias for route handler return type.
     290              : 
     291              :     Route handlers are coroutines that return a @ref route_result
     292              :     indicating how the router should proceed. This alias simplifies
     293              :     handler declarations:
     294              : 
     295              :     @code
     296              :     route_task my_handler(route_params& p)
     297              :     {
     298              :         // process request...
     299              :         co_return route_done;
     300              :     }
     301              : 
     302              :     route_task auth_middleware(route_params& p)
     303              :     {
     304              :         if(! check_token(p))
     305              :         {
     306              :             p.res.set_status(status::unauthorized);
     307              :             co_await p.send();
     308              :             co_return route_done;
     309              :         }
     310              :         co_return route_next;  // continue to next handler
     311              :     }
     312              :     @endcode
     313              : 
     314              :     @see route_result, route_params
     315              : */
     316              : using route_task = capy::task<route_result>;
     317              : 
     318              : //------------------------------------------------
     319              : 
     320              : namespace detail {
     321              : class router_base;
     322              : } // detail
     323              : template<class> class basic_router;
     324              : 
     325              : struct route_params_base_privates
     326              : {
     327              :     struct match_result;
     328              : 
     329              :     std::string verb_str_;
     330              :     std::string decoded_path_;
     331              :     system::error_code ec_;
     332              :     std::exception_ptr ep_;
     333              :     std::size_t pos_ = 0;
     334              :     std::size_t resume_ = 0;
     335              :     http::method verb_ =
     336              :         http::method::unknown;
     337              :     bool addedSlash_ = false;
     338              :     bool case_sensitive = false;
     339              :     bool strict = false;
     340              :     char kind_ = 0;  // dispatch mode, initialized by flat_router::dispatch()
     341              : };
     342              : 
     343              : /** Base class for request objects
     344              : 
     345              :     This is a required public base for any `Request`
     346              :     type used with @ref router.
     347              : */
     348              : class route_params_base : public route_params_base_privates
     349              : {
     350              : public:
     351              :     /** Return true if the request method matches `m`
     352              :     */
     353              :     bool is_method(
     354              :         http::method m) const noexcept
     355              :     {
     356              :         return verb_ == m;
     357              :     }
     358              : 
     359              :     /** Return true if the request method matches `s`
     360              :     */
     361              :     BOOST_HTTP_DECL
     362              :     bool is_method(
     363              :         core::string_view s) const noexcept;
     364              : 
     365              :     /** The mount path of the current router
     366              : 
     367              :         This is the portion of the request path
     368              :         which was matched to select the handler.
     369              :         The remaining portion is available in
     370              :         @ref path.
     371              :     */
     372              :     core::string_view base_path;
     373              : 
     374              :     /** The current pathname, relative to the base path
     375              :     */
     376              :     core::string_view path;
     377              : 
     378              :     /** Captured route parameters
     379              : 
     380              :         Contains name-value pairs extracted from the path
     381              :         by matching :param and *wildcard tokens.
     382              :     */
     383              :     std::vector<std::pair<std::string, std::string>> params;
     384              : 
     385              :    struct match_result;
     386              : 
     387              : private:
     388              :     template<class>
     389              :     friend class basic_router;
     390              :     friend struct route_params_access;
     391              : 
     392              :     route_params_base& operator=(
     393              :         route_params_base const&) = delete;
     394              : };
     395              : 
     396              : struct route_params_base::
     397              :     match_result
     398              : {
     399              :     std::vector<std::pair<std::string, std::string>> params_;
     400              : 
     401          127 :     void adjust_path(
     402              :         route_params_base& p,
     403              :         std::size_t n)
     404              :     {
     405          127 :         n_ = n;
     406          127 :         if(n_ == 0)
     407           41 :             return;
     408           86 :         p.base_path = {
     409              :             p.base_path.data(),
     410           86 :             p.base_path.size() + n_ };
     411           86 :         if(n_ < p.path.size())
     412              :         {
     413           28 :             p.path.remove_prefix(n_);
     414              :         }
     415              :         else
     416              :         {
     417              :             // append a soft slash
     418           58 :             p.path = { p.decoded_path_.data() +
     419           58 :                 p.decoded_path_.size() - 1, 1};
     420           58 :             BOOST_ASSERT(p.path == "/");
     421              :         }
     422              :     }
     423              : 
     424              :     void restore_path(
     425              :         route_params_base& p)
     426              :     {
     427              :         if( n_ > 0 &&
     428              :             p.addedSlash_ &&
     429              :             p.path.data() ==
     430              :                 p.decoded_path_.data() +
     431              :                 p.decoded_path_.size() - 1)
     432              :         {
     433              :             // remove soft slash
     434              :             p.path = {
     435              :                 p.base_path.data() +
     436              :                 p.base_path.size(), 0 };
     437              :         }
     438              :         p.base_path.remove_suffix(n_);
     439              :         p.path = {
     440              :             p.path.data() - n_,
     441              :             p.path.size() + n_ };
     442              :     }
     443              : 
     444              : private:
     445              :     std::size_t n_ = 0; // chars moved from path to base_path
     446              : };
     447              : 
     448              : 
     449              : namespace detail {
     450              : 
     451              : struct route_params_access
     452              : {
     453              :     route_params_base& rp;
     454              : 
     455           62 :     route_params_base_privates* operator->() const noexcept
     456              :     {
     457           62 :         return &rp;
     458              :     }
     459              : };
     460              : 
     461              : } // detail
     462              : 
     463              : } // http
     464              : } // boost
     465              : 
     466              : #endif
        

Generated by: LCOV version 2.3