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

Generated by: LCOV version 2.1