LCOV - code coverage report
Current view: top level - libs/http_proto/src/server - basic_router.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 97.8 % 408 399
Test Date: 2025-12-05 19:49:25 Functions: 100.0 % 34 34

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

Generated by: LCOV version 2.1