LCOV - code coverage report
Current view: top level - libs/beast2/src/server - basic_router.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.9 % 384 376
Test Date: 2025-11-20 15:35:53 Functions: 97.2 % 36 35

            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/beast2
       8              : //
       9              : 
      10              : #include "src/server/route_rule.hpp"
      11              : #include <boost/beast2/server/basic_router.hpp>
      12              : #include <boost/beast2/server/route_handler.hpp>
      13              : #include <boost/beast2/error.hpp>
      14              : #include <boost/beast2/detail/except.hpp>
      15              : #include <boost/url/grammar/ci_string.hpp>
      16              : #include <boost/url/grammar/hexdig_chars.hpp>
      17              : #include <boost/assert.hpp>
      18              : #include <atomic>
      19              : #include <string>
      20              : #include <vector>
      21              : 
      22              : namespace boost {
      23              : namespace beast2 {
      24              : 
      25              : namespace detail {
      26              : 
      27              : // VFALCO Temporarily here until Boost.URL merges the fix
      28              : static
      29              : bool
      30           96 : ci_is_equal(
      31              :     core::string_view s0,
      32              :     core::string_view s1) noexcept
      33              : {
      34           96 :     auto n = s0.size();
      35           96 :     if(s1.size() != n)
      36            0 :         return false;
      37           96 :     auto p1 = s0.data();
      38           96 :     auto p2 = s1.data();
      39              :     char a, b;
      40              :     // fast loop
      41          387 :     while(n--)
      42              :     {
      43          313 :         a = *p1++;
      44          313 :         b = *p2++;
      45          313 :         if(a != b)
      46           22 :             goto slow;
      47              :     }
      48           74 :     return true;
      49              :     do
      50              :     {
      51            3 :         a = *p1++;
      52            3 :         b = *p2++;
      53           25 :     slow:
      54           50 :         if( grammar::to_lower(a) !=
      55           25 :             grammar::to_lower(b))
      56           19 :             return false;
      57              :     }
      58            6 :     while(n--);
      59            3 :     return true;
      60              : }
      61              : 
      62              : 
      63              : //------------------------------------------------
      64              : /*
      65              : 
      66              : pattern     target      path(use)    path(get) 
      67              : -------------------------------------------------
      68              : /           /           /
      69              : /           /api        /api
      70              : /api        /api        /            /api
      71              : /api        /api/       /            /api/
      72              : /api        /api/       /            no-match       strict
      73              : /api        /api/v0     /v0          no-match
      74              : /api/       /api        /            /api
      75              : /api/       /api        /            no-match       strict
      76              : /api/       /api/       /            /api/
      77              : /api/       /api/v0     /v0          no-match
      78              : 
      79              : */
      80              : 
      81              : //------------------------------------------------
      82              : 
      83          329 : any_router::any_handler::~any_handler() = default;
      84              : 
      85              : //------------------------------------------------
      86              : 
      87              : /*
      88              : static
      89              : void
      90              : make_lower(std::string& s)
      91              : {
      92              :     for(auto& c : s)
      93              :         c = grammar::to_lower(c);
      94              : }
      95              : */
      96              : 
      97              : // decode all percent escapes
      98              : static
      99              : std::string
     100          247 : pct_decode(
     101              :     urls::pct_string_view s)
     102              : {
     103          247 :     std::string result;
     104          247 :     core::string_view sv(s);
     105          247 :     result.reserve(s.size());
     106          247 :     auto it = sv.data();
     107          247 :     auto const end = it + sv.size();
     108              :     for(;;)
     109              :     {
     110          769 :         if(it == end)
     111          247 :             break;
     112          522 :         if(*it != '%')
     113              :         {
     114          520 :             result.push_back(*it++);
     115          520 :             continue;
     116              :         }
     117            2 :         ++it;
     118              : #if 0
     119              :         // pct_string_view can never have invalid pct-encodings
     120              :         if(it == end)
     121              :             goto invalid;
     122              : #endif
     123            2 :         auto d0 = urls::grammar::hexdig_value(*it++);
     124              : #if 0
     125              :         // pct_string_view can never have invalid pct-encodings
     126              :         if( d0 < 0 ||
     127              :             it == end)
     128              :             goto invalid;
     129              : #endif
     130            2 :         auto d1 = urls::grammar::hexdig_value(*it++);
     131              : #if 0
     132              :         // pct_string_view can never have invalid pct-encodings
     133              :         if(d1 < 0)
     134              :             goto invalid;
     135              : #endif
     136            2 :         result.push_back(d0 * 16 + d1);
     137          522 :     }
     138          494 :     return result;
     139              : #if 0
     140              : invalid:
     141              :     // can't get here, as received a pct_string_view
     142              :     detail::throw_invalid_argument();
     143              : #endif
     144            0 : }
     145              : 
     146              : // decode all percent escapes except slashes '/' and '\'
     147              : static
     148              : std::string
     149          174 : pct_decode_path(
     150              :     urls::pct_string_view s)
     151              : {
     152          174 :     std::string result;
     153          174 :     core::string_view sv(s);
     154          174 :     result.reserve(s.size());
     155          174 :     auto it = sv.data();
     156          174 :     auto const end = it + sv.size();
     157              :     for(;;)
     158              :     {
     159          603 :         if(it == end)
     160          174 :             break;
     161          429 :         if(*it != '%')
     162              :         {
     163          425 :             result.push_back(*it++);
     164          425 :             continue;
     165              :         }
     166            4 :         ++it;
     167              : #if 0
     168              :         // pct_string_view can never have invalid pct-encodings
     169              :         if(it == end)
     170              :             goto invalid;
     171              : #endif
     172            4 :         auto d0 = urls::grammar::hexdig_value(*it++);
     173              : #if 0
     174              :         // pct_string_view can never have invalid pct-encodings
     175              :         if( d0 < 0 ||
     176              :             it == end)
     177              :             goto invalid;
     178              : #endif
     179            4 :         auto d1 = urls::grammar::hexdig_value(*it++);
     180              : #if 0
     181              :         // pct_string_view can never have invalid pct-encodings
     182              :         if(d1 < 0)
     183              :             goto invalid;
     184              : #endif
     185            4 :         char c = d0 * 16 + d1;
     186            4 :         if( c != '/' &&
     187              :             c != '\\')
     188              :         {
     189            2 :             result.push_back(c);
     190            2 :             continue;
     191              :         }
     192            2 :         result.append(it - 3, 3);
     193          429 :     }
     194          348 :     return result;
     195              : #if 0
     196              : invalid:
     197              :     // can't get here, as received a pct_string_view
     198              :     detail::throw_invalid_argument();
     199              : #endif
     200            0 : }
     201              : 
     202              : //------------------------------------------------
     203              : 
     204              : } // detail
     205              : 
     206              : struct basic_request::
     207              :     match_result
     208              : {
     209          247 :     void adjust_path(
     210              :         basic_request& req,
     211              :         std::size_t n)
     212              :     {
     213          247 :         n_ = n;
     214          247 :         if(n_ == 0)
     215          166 :             return;
     216           81 :         req.base_path = {
     217              :             req.base_path.data(),
     218           81 :             req.base_path.size() + n_ };
     219           81 :         if(n_ < req.path.size())
     220              :         {
     221           27 :             req.path.remove_prefix(n_);
     222              :         }
     223              :         else
     224              :         {
     225              :             // append a soft slash
     226           54 :             req.path = { req.decoded_path_.data() +
     227           54 :                 req.decoded_path_.size() - 1, 1};
     228           54 :             BOOST_ASSERT(req.path == "/");
     229              :         }
     230              :     }
     231              : 
     232          115 :     void restore_path(
     233              :         basic_request& req)
     234              :     {
     235          256 :         if( n_ > 0 &&
     236          140 :             req.addedSlash_ &&
     237           25 :             req.path.data() ==
     238           25 :                 req.decoded_path_.data() +
     239           25 :                 req.decoded_path_.size() - 1)
     240              :         {
     241              :             // remove soft slash
     242           19 :             req.path = {
     243           19 :                 req.base_path.data() +
     244           19 :                 req.base_path.size(), 0 };
     245              :         }
     246          115 :         req.base_path.remove_suffix(n_);
     247          345 :         req.path = {
     248          115 :             req.path.data() - n_,
     249          115 :             req.path.size() + n_ };
     250          115 :     }
     251              : 
     252              : private:
     253              :     std::size_t n_ = 0; // chars moved from path to base_path
     254              : };
     255              : 
     256              : //------------------------------------------------
     257              : 
     258              : namespace detail {
     259              : 
     260              : // Matches a path against a pattern
     261              : struct any_router::matcher
     262              : {
     263              :     bool const end; // false for middleware
     264              : 
     265          247 :     matcher(
     266              :         core::string_view pat,
     267              :         bool end_)
     268          247 :         : end(end_)
     269          247 :         , decoded_pat_(
     270            0 :             [&pat]
     271              :             {
     272          247 :                 auto s = pct_decode(pat);
     273          247 :                 if( s.size() > 1
     274          247 :                     && s.back() == '/')
     275            6 :                     s.pop_back();
     276          247 :                 return s;
     277          494 :             }())
     278          247 :         , slash_(pat == "/")
     279              :     {
     280          247 :         if(! slash_)
     281          116 :             pv_ = grammar::parse(
     282          116 :                 decoded_pat_, path_rule).value();
     283          247 :     }
     284              : 
     285              :     /** Return true if req.path is a match
     286              :     */
     287          282 :     bool operator()(
     288              :         basic_request& req,
     289              :         match_result& mr) const
     290              :     {
     291          282 :         BOOST_ASSERT(! req.path.empty());
     292          448 :         if( slash_ && (
     293          220 :             ! end ||
     294          336 :             req.path == "/"))
     295              :         {
     296              :             // params = {};
     297          166 :             mr.adjust_path(req, 0);
     298          166 :             return true;
     299              :         }
     300          116 :         auto it = req.path.data();
     301          116 :         auto pit = pv_.segs.begin();
     302          116 :         auto const end_ = it + req.path.size();
     303          116 :         auto const pend = pv_.segs.end();
     304          197 :         while(it != end_ && pit != pend)
     305              :         {
     306              :             // prefix has to match
     307          116 :             auto s = core::string_view(it, end_);
     308          116 :             if(! req.case_sensitive)
     309              :             {
     310          110 :                 if(pit->prefix.size() > s.size())
     311           35 :                     return false;
     312           96 :                 s = s.substr(0, pit->prefix.size());
     313              :                 //if(! grammar::ci_is_equal(s, pit->prefix))
     314           96 :                 if(! ci_is_equal(s, pit->prefix))
     315           19 :                     return false;
     316              :             }
     317              :             else
     318              :             {
     319            6 :                 if(! s.starts_with(pit->prefix))
     320            2 :                     return false;
     321              :             }
     322           81 :             it += pit->prefix.size();
     323           81 :             ++pit;
     324              :         }
     325           81 :         if(end)
     326              :         {
     327              :             // require full match
     328           42 :             if( it != end_ ||
     329           21 :                 pit != pend)
     330            0 :                 return false;
     331              :         }
     332           60 :         else if(pit != pend)
     333              :         {
     334            0 :             return false;
     335              :         }
     336              :         // number of matching characters
     337           81 :         auto const n = it - req.path.data();
     338           81 :         mr.adjust_path(req, n);
     339           81 :         return true;
     340              :     }
     341              : 
     342              : private:
     343              :     stable_string decoded_pat_;
     344              :     path_rule_t::value_type pv_;
     345              :     bool slash_;
     346              : };
     347              : 
     348              : //------------------------------------------------
     349              : 
     350              : struct any_router::layer
     351              : {
     352              :     struct entry
     353              :     {
     354              :         handler_ptr handler;
     355              : 
     356              :         // only for end routes
     357              :         http_proto::method verb =
     358              :             http_proto::method::unknown;
     359              :         std::string verb_str;
     360              :         bool all;
     361              : 
     362          249 :         explicit entry(
     363              :             handler_ptr h) noexcept
     364          249 :             : handler(std::move(h))
     365          249 :             , all(true)
     366              :         {
     367          249 :         }
     368              : 
     369           70 :         entry(
     370              :             http_proto::method verb_,
     371              :             handler_ptr h) noexcept
     372           70 :             : handler(std::move(h))
     373           70 :             , verb(verb_)
     374           70 :             , all(false)
     375              :         {
     376           70 :             BOOST_ASSERT(verb !=
     377              :                 http_proto::method::unknown);
     378           70 :         }
     379              : 
     380            9 :         entry(
     381              :             core::string_view verb_str_,
     382              :             handler_ptr h) noexcept
     383            9 :             : handler(std::move(h))
     384            9 :             , verb(http_proto::string_to_method(verb_str_))
     385            9 :             , all(false)
     386              :         {
     387            9 :             if(verb != http_proto::method::unknown)
     388            2 :                 return;
     389            7 :             verb_str = verb_str_;
     390              :         }
     391              : 
     392          107 :         bool match_method(
     393              :             basic_request const& req) const noexcept
     394              :         {
     395          107 :             if(all)
     396           12 :                 return true;
     397           95 :             if(verb != http_proto::method::unknown)
     398           80 :                 return req.verb_ == verb;
     399           15 :             if(req.verb_ != http_proto::method::unknown)
     400            1 :                 return false;
     401           14 :             return req.verb_str_ == verb_str;
     402              :         }
     403              :     };
     404              : 
     405              :     matcher match;
     406              :     std::vector<entry> entries;
     407              : 
     408              :     // middleware layer
     409          185 :     layer(
     410              :         core::string_view pat,
     411              :         handler_list handlers)
     412          185 :         : match(pat, false)
     413              :     {
     414          185 :         entries.reserve(handlers.n);
     415          420 :         for(std::size_t i = 0; i < handlers.n; ++i)
     416          235 :             entries.emplace_back(std::move(handlers.p[i]));
     417          185 :     }
     418              : 
     419              :     // route layer
     420           62 :     explicit layer(
     421              :         core::string_view pat)
     422           62 :         : match(pat, true)
     423              :     {
     424           62 :     }
     425              : 
     426           45 :     std::size_t count() const noexcept
     427              :     {
     428           45 :         std::size_t n = 0;
     429           96 :         for(auto const& e : entries)
     430           51 :             n += e.handler->count();
     431           45 :         return n;
     432              :     }   
     433              : };
     434              : 
     435              : //------------------------------------------------
     436              : 
     437              : struct any_router::impl
     438              : {
     439              :     std::atomic<std::size_t> refs{1};
     440              :     std::vector<layer> layers;
     441              :     opt_flags opt;
     442              : 
     443          136 :     explicit impl(
     444              :         opt_flags opt_) noexcept
     445          136 :         : opt(opt_)
     446              :     {
     447          136 :     }
     448              : };
     449              : 
     450              : //------------------------------------------------
     451              : 
     452          152 : any_router::
     453          136 : ~any_router()
     454              : {
     455          152 :     if(! impl_)
     456           16 :         return;
     457          136 :     if(--impl_->refs == 0)
     458          134 :         delete impl_;
     459          152 : }
     460              : 
     461          136 : any_router::
     462              : any_router(
     463          136 :     opt_flags opt)
     464          136 :     : impl_(new impl(opt))
     465              : {
     466          136 : }
     467              : 
     468           15 : any_router::
     469           15 : any_router(any_router&& other) noexcept
     470           15 :     :impl_(other.impl_)
     471              : {
     472           15 :     other.impl_ = nullptr;
     473           15 : }
     474              : 
     475            1 : any_router::
     476            1 : any_router(any_router const& other) noexcept
     477              : {
     478            1 :     impl_ = other.impl_;
     479            1 :     ++impl_->refs;
     480            1 : }
     481              : 
     482              : any_router&
     483            1 : any_router::
     484              : operator=(any_router&& other) noexcept
     485              : {
     486            1 :     auto p = impl_;
     487            1 :     impl_ = other.impl_;
     488            1 :     other.impl_ = nullptr;
     489            1 :     if(p && --p->refs == 0)
     490            1 :         delete p;
     491            1 :     return *this;
     492              : }
     493              : 
     494              : any_router&
     495            1 : any_router::
     496              : operator=(any_router const& other) noexcept
     497              : {
     498            1 :     auto p = impl_;
     499            1 :     impl_ = other.impl_;
     500            1 :     ++impl_->refs;
     501            1 :     if(p && --p->refs == 0)
     502            1 :         delete p;
     503            1 :     return *this;
     504              : }
     505              : 
     506              : //------------------------------------------------
     507              : 
     508              : std::size_t
     509            4 : any_router::
     510              : count() const noexcept
     511              : {
     512            4 :     std::size_t n = 0;
     513            8 :     for(auto const& i : impl_->layers)
     514           20 :         for(auto const& e : i.entries)
     515           16 :             n += e.handler->count();
     516            4 :     return n;
     517              : }
     518              : 
     519              : auto
     520           63 : any_router::
     521              : new_layer(
     522              :     core::string_view pattern) -> layer&
     523              : {
     524              :     // the pattern must not be empty
     525           63 :     if(pattern.empty())
     526            1 :         detail::throw_invalid_argument();
     527              :     // delete the last route if it is empty,
     528              :     // this happens if they call route() without
     529              :     // adding anything
     530           92 :     if(! impl_->layers.empty() &&
     531           30 :         impl_->layers.back().entries.empty())
     532            1 :         impl_->layers.pop_back();
     533           62 :     impl_->layers.emplace_back(pattern);
     534           62 :     return impl_->layers.back();
     535              : };
     536              : 
     537              : void
     538          185 : any_router::
     539              : add_impl(
     540              :     core::string_view pattern,
     541              :     handler_list const& handlers)
     542              : {
     543          185 :     if( pattern.empty())
     544          105 :         pattern = "/";
     545          185 :     impl_->layers.emplace_back(
     546          185 :         pattern, std::move(handlers));
     547          185 : }
     548              : 
     549              : void
     550           68 : any_router::
     551              : add_impl(
     552              :     layer& e,
     553              :     http_proto::method verb,
     554              :     handler_list const& handlers)
     555              : {
     556              :     // cannot be unknown
     557           68 :     if(verb == http_proto::method::unknown)
     558            1 :         detail::throw_invalid_argument();
     559              : 
     560           67 :     e.entries.reserve(e.entries.size() + handlers.n);
     561          137 :     for(std::size_t i = 0; i < handlers.n; ++i)
     562           70 :         e.entries.emplace_back(verb,
     563           70 :             std::move(handlers.p[i]));
     564           67 : }
     565              : 
     566              : void
     567           23 : any_router::
     568              : add_impl(
     569              :     layer& e,
     570              :     core::string_view verb_str,
     571              :     handler_list const& handlers)
     572              : {
     573           23 :     e.entries.reserve(e.entries.size() + handlers.n);
     574              : 
     575           23 :     if(verb_str.empty())
     576              :     {
     577              :         // all
     578           28 :         for(std::size_t i = 0; i < handlers.n; ++i)
     579           14 :             e.entries.emplace_back(
     580           14 :                 std::move(handlers.p[i]));
     581           14 :         return;
     582              :     }
     583              : 
     584              :     // possibly custom string
     585           18 :     for(std::size_t i = 0; i < handlers.n; ++i)
     586            9 :         e.entries.emplace_back(verb_str,
     587            9 :             std::move(handlers.p[i]));
     588              : }
     589              : 
     590              : //------------------------------------------------
     591              : 
     592              : auto
     593            9 : any_router::
     594              : resume_impl(
     595              :     basic_request& req, basic_response& res,
     596              :     route_result const& ec) const ->
     597              :         route_result
     598              : {
     599            9 :     BOOST_ASSERT(res.resume_ > 0);
     600           17 :     if( ec == route::send ||
     601           17 :         ec == route::close ||
     602           16 :         ec == route::complete)
     603            3 :         return ec;
     604            6 :     if(&ec.category() != &detail::route_cat)
     605              :     {
     606              :         // must indicate failure
     607            2 :         if(! ec.failed())
     608            2 :             detail::throw_invalid_argument();
     609              :     }
     610              : 
     611              :     // restore base_path and path
     612            4 :     req.base_path = { req.decoded_path_.data(), 0 };
     613            4 :     req.path = req.decoded_path_;
     614            4 :     if(req.addedSlash_)
     615            1 :         req.path.remove_suffix(1);
     616              : 
     617              :     // resume_ was set in the handler's wrapper
     618            4 :     BOOST_ASSERT(res.resume_ == res.pos_);
     619            4 :     res.pos_ = 0;
     620            4 :     res.ec_ = ec;
     621            4 :     return do_dispatch(req, res);
     622              : }
     623              : 
     624              : // top-level dispatch that gets called first
     625              : route_result
     626          174 : any_router::
     627              : dispatch_impl(
     628              :     http_proto::method verb,
     629              :     core::string_view verb_str,
     630              :     urls::url_view const& url,
     631              :     basic_request& req,
     632              :     basic_response& res) const
     633              : {
     634              :     // VFALCO we could reuse the string storage by not clearing them
     635              :     // set req.case_sensitive, req.strict to default of false
     636          174 :     req = {};
     637          174 :     if(verb == http_proto::method::unknown)
     638              :     {
     639           33 :         BOOST_ASSERT(! verb_str.empty());
     640           33 :         verb = http_proto::string_to_method(verb_str);
     641           33 :         if(verb == http_proto::method::unknown)
     642           21 :             req.verb_str_ = verb_str;
     643              :     }
     644              :     else
     645              :     {
     646          141 :         BOOST_ASSERT(verb_str.empty());
     647              :     }
     648          174 :     req.verb_ = verb;
     649              :     // VFALCO use reusing-StringToken
     650              :     req.decoded_path_ =
     651          174 :         pct_decode_path(url.encoded_path());
     652          174 :     BOOST_ASSERT(! req.decoded_path_.empty());
     653          174 :     req.base_path = { req.decoded_path_.data(), 0 };
     654          174 :     req.path = req.decoded_path_;
     655          174 :     if(req.decoded_path_.back() != '/')
     656              :     {
     657           55 :         req.decoded_path_.push_back('/');
     658           55 :         req.addedSlash_ = true;
     659              :     }
     660          174 :     BOOST_ASSERT(req.case_sensitive == false);
     661          174 :     BOOST_ASSERT(req.strict == false);
     662              : 
     663          174 :     res = {};
     664              : 
     665              :     // we cannot do anything after do_dispatch returns,
     666              :     // other than return the route_result, or else we
     667              :     // could race with the detached operation trying to resume.
     668          174 :     return do_dispatch(req, res);
     669              : }
     670              : 
     671              : // recursive dispatch
     672              : route_result
     673          194 : any_router::
     674              : dispatch_impl(
     675              :     basic_request& req,
     676              :     basic_response& res) const
     677              : {
     678              :     // options are recursive and need to be restored on
     679              :     // exception or when returning to a calling router.
     680              :     struct option_saver
     681              :     {
     682          194 :         option_saver(
     683              :             basic_request& req) noexcept
     684          194 :             : req_(&req)
     685          194 :             , case_sensitive_(req.case_sensitive)
     686          194 :             , strict_(req.strict)
     687              :         {
     688          194 :         }
     689              : 
     690          194 :         ~option_saver()
     691          180 :         {
     692          194 :             if(! req_)
     693           14 :                 return;
     694          180 :             req_->case_sensitive = case_sensitive_;
     695          180 :             req_->strict = strict_;
     696          194 :         };
     697              : 
     698           14 :         void cancel() noexcept
     699              :         {
     700           14 :             req_ = nullptr;
     701           14 :         }
     702              : 
     703              :     private:
     704              :         basic_request* req_;
     705              :         bool case_sensitive_;
     706              :         bool strict_;
     707              :     };
     708              : 
     709          194 :     option_saver restore_options(req);
     710              : 
     711              :     // inherit or apply options
     712          194 :     if((impl_->opt & 2) != 0)
     713            4 :         req.case_sensitive = true;
     714          190 :     else if((impl_->opt & 4) != 0)
     715            2 :         req.case_sensitive = false;
     716              : 
     717          194 :     if((impl_->opt & 8) != 0)
     718            0 :         req.strict = true;
     719          194 :     else if((impl_->opt & 16) != 0)
     720            0 :         req.strict = false;
     721              : 
     722          194 :     match_result mr;
     723          348 :     for(auto const& i : impl_->layers)
     724              :     {
     725          286 :         if(res.resume_ > 0)
     726              :         {
     727            9 :             auto const n = i.count(); // handlers in layer
     728            9 :             if(res.pos_ + n < res.resume_)
     729              :             {
     730            3 :                 res.pos_ += n; // skip layer
     731            3 :                 continue;
     732              :             }
     733              :             // repeat match to recreate the stack
     734            6 :             bool is_match = i.match(req, mr);
     735            6 :             BOOST_ASSERT(is_match);
     736              :             (void)is_match;
     737              :         }
     738              :         else
     739              :         {
     740          277 :             if(i.match.end && res.ec_.failed())
     741              :             {
     742              :                 // routes can't have error handlers
     743            1 :                 res.pos_ += i.count(); // skip layer
     744            1 :                 continue;
     745              :             }
     746          276 :             if(! i.match(req, mr))
     747              :             {
     748              :                 // not a match
     749           35 :                 res.pos_ += i.count(); // skip layer
     750           35 :                 continue;
     751              :             }
     752              :         }
     753          247 :         for(auto it = i.entries.begin();
     754          427 :             it != i.entries.end(); ++it)
     755              :         {
     756          318 :             auto const& e(*it);
     757          318 :             if(res.resume_)
     758              :             {
     759            8 :                 auto const n = e.handler->count();
     760            8 :                 if(res.pos_ + n < res.resume_)
     761              :                 {
     762            2 :                     res.pos_ += n; // skip entry
     763          180 :                     continue;
     764              :                 }
     765            6 :                 BOOST_ASSERT(e.match_method(req));
     766              :             }
     767          310 :             else if(i.match.end)
     768              :             {
     769              :                 // check verb for match 
     770          101 :                 if(! e.match_method(req))
     771              :                 {
     772           51 :                     res.pos_ += e.handler->count(); // skip entry
     773           51 :                     continue;
     774              :                 }
     775              :             }
     776              : 
     777          265 :             route_result rv;
     778              :             // increment before invoke
     779          265 :             ++res.pos_;
     780          265 :             if(res.pos_ != res.resume_)
     781              :             {
     782              :                 // call the handler
     783          261 :                 rv = e.handler->invoke(req, res);
     784              :                 // res.pos_ can be incremented further
     785              :                 // inside the above call to invoke.
     786          261 :                 if(rv == route::detach)
     787              :                 {
     788              :                     // It is essential that we return immediately, without
     789              :                     // doing anything after route::detach is returned,
     790              :                     // otherwise we could race with the detached operation
     791              :                     // attempting to call resume().
     792           14 :                     restore_options.cancel();
     793          128 :                     return rv;
     794              :                 }
     795              :             }
     796              :             else
     797              :             {
     798              :                 // a subrouter never detaches on its own
     799            4 :                 BOOST_ASSERT(e.handler->count() == 1);
     800              :                 // can't detach on resume
     801            4 :                 if(res.ec_ == route::detach)
     802            1 :                     detail::throw_invalid_argument();
     803              :                 // do resume
     804            3 :                 res.resume_ = 0;
     805            3 :                 rv = res.ec_;
     806            3 :                 res.ec_ = {};
     807              :             }
     808          388 :             if( rv == route::send ||
     809          388 :                 rv == route::complete ||
     810          387 :                 rv == route::close)
     811          114 :                 return rv;
     812          136 :             if(rv.failed())
     813              :             {
     814              :                 // error handling mode
     815           34 :                 res.ec_ = rv;
     816           34 :                 if(! i.match.end)
     817           29 :                     continue; // next entry
     818              :                 // routes don't have error handlers
     819           11 :                 while(++it != i.entries.end())
     820            6 :                     res.pos_ += it->handler->count();
     821            6 :                 break; // skip remaining entries
     822              :             }
     823          102 :             if(rv == route::next)
     824           98 :                 continue; // next entry
     825            4 :             if(rv == route::next_route)
     826              :             {
     827              :                 // middleware can't return next_route
     828            2 :                 if(! i.match.end)
     829            1 :                     detail::throw_invalid_argument();
     830            6 :                 while(++it != i.entries.end())
     831            5 :                     res.pos_ += it->handler->count();
     832            1 :                 break; // skip remaining entries
     833              :             }
     834              :             // we must handle all route enums
     835            2 :             BOOST_ASSERT(&rv.category() != &detail::route_cat);
     836              :             // handler must return non-successful error_code
     837            2 :             detail::throw_invalid_argument();
     838              :         }
     839              : 
     840          115 :         mr.restore_path(req);
     841              :     }
     842              : 
     843           62 :     return route::next;
     844          194 : }
     845              : 
     846              : route_result
     847          178 : any_router::
     848              : do_dispatch(
     849              :     basic_request& req,
     850              :     basic_response& res) const
     851              : {
     852          178 :     auto rv = dispatch_impl(req, res);
     853          174 :     BOOST_ASSERT(&rv.category() == &detail::route_cat);
     854          174 :     BOOST_ASSERT(rv != route::next_route);
     855          174 :     if(rv != route::next)
     856              :     {
     857              :         // when rv == route::detach we must return immediately,
     858              :         // without attempting to perform any additional operations.
     859          120 :         return rv;
     860              :     }
     861           54 :     if(! res.ec_.failed())
     862              :     {
     863              :         // unhandled route
     864           48 :         return route::next;
     865              :     }
     866              :     // error condition
     867            6 :     return res.ec_;
     868              : }
     869              : 
     870              : } // detail
     871              : } // beast2
     872              : } // boost
        

Generated by: LCOV version 2.1