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 % 403 394
Test Date: 2025-12-02 19:05:12 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 basic_request::
     203              :     match_result
     204              : {
     205          263 :     void adjust_path(
     206              :         basic_request& req,
     207              :         std::size_t n)
     208              :     {
     209          263 :         n_ = n;
     210          263 :         if(n_ == 0)
     211          182 :             return;
     212           81 :         req.base_path = {
     213              :             req.base_path.data(),
     214           81 :             req.base_path.size() + n_ };
     215           81 :         if(n_ < req.path.size())
     216              :         {
     217           27 :             req.path.remove_prefix(n_);
     218              :         }
     219              :         else
     220              :         {
     221              :             // append a soft slash
     222           54 :             req.path = { req.decoded_path_.data() +
     223           54 :                 req.decoded_path_.size() - 1, 1};
     224           54 :             BOOST_ASSERT(req.path == "/");
     225              :         }
     226              :     }
     227              : 
     228          130 :     void restore_path(
     229              :         basic_request& req)
     230              :     {
     231          286 :         if( n_ > 0 &&
     232          155 :             req.addedSlash_ &&
     233           25 :             req.path.data() ==
     234           25 :                 req.decoded_path_.data() +
     235           25 :                 req.decoded_path_.size() - 1)
     236              :         {
     237              :             // remove soft slash
     238           19 :             req.path = {
     239           19 :                 req.base_path.data() +
     240           19 :                 req.base_path.size(), 0 };
     241              :         }
     242          130 :         req.base_path.remove_suffix(n_);
     243          390 :         req.path = {
     244          130 :             req.path.data() - n_,
     245          130 :             req.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 req.path is a match
     282              :     */
     283          298 :     bool operator()(
     284              :         basic_request& req,
     285              :         match_result& mr) const
     286              :     {
     287          298 :         BOOST_ASSERT(! req.path.empty());
     288          480 :         if( slash_ && (
     289          236 :             ! end ||
     290          352 :             req.path == "/"))
     291              :         {
     292              :             // params = {};
     293          182 :             mr.adjust_path(req, 0);
     294          182 :             return true;
     295              :         }
     296          116 :         auto it = req.path.data();
     297          116 :         auto pit = pv_.segs.begin();
     298          116 :         auto const end_ = it + req.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(! req.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 - req.path.data();
     334           81 :         mr.adjust_path(req, 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              :             basic_request const& req) const noexcept
     390              :         {
     391          107 :             if(all)
     392           12 :                 return true;
     393           95 :             if(verb != http_proto::method::unknown)
     394           80 :                 return req.verb_ == verb;
     395           15 :             if(req.verb_ != http_proto::method::unknown)
     396            1 :                 return false;
     397           14 :             return req.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              :     basic_request& req, basic_response& res,
     592              :     route_result ec) const ->
     593              :         route_result
     594              : {
     595            9 :     BOOST_ASSERT(res.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 :     req.base_path = { req.decoded_path_.data(), 0 };
     609            4 :     req.path = req.decoded_path_;
     610            4 :     if(req.addedSlash_)
     611            1 :         req.path.remove_suffix(1);
     612              : 
     613              :     // resume_ was set in the handler's wrapper
     614            4 :     BOOST_ASSERT(res.resume_ == res.pos_);
     615            4 :     res.pos_ = 0;
     616            4 :     res.ec_ = ec;
     617            4 :     return do_dispatch(req, res);
     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              :     basic_request& req,
     628              :     basic_response& res) const
     629              : {
     630              :     // VFALCO we could reuse the string storage by not clearing them
     631              :     // set req.case_sensitive, req.strict to default of false
     632          180 :     req = {};
     633          180 :     if(verb == http_proto::method::unknown)
     634              :     {
     635           33 :         BOOST_ASSERT(! verb_str.empty());
     636           33 :         verb = http_proto::string_to_method(verb_str);
     637           33 :         if(verb == http_proto::method::unknown)
     638           21 :             req.verb_str_ = verb_str;
     639              :     }
     640              :     else
     641              :     {
     642          147 :         BOOST_ASSERT(verb_str.empty());
     643              :     }
     644          180 :     req.verb_ = verb;
     645              :     // VFALCO use reusing-StringToken
     646              :     req.decoded_path_ =
     647          180 :         pct_decode_path(url.encoded_path());
     648          180 :     BOOST_ASSERT(! req.decoded_path_.empty());
     649          180 :     req.base_path = { req.decoded_path_.data(), 0 };
     650          180 :     req.path = req.decoded_path_;
     651          180 :     if(req.decoded_path_.back() != '/')
     652              :     {
     653           55 :         req.decoded_path_.push_back('/');
     654           55 :         req.addedSlash_ = true;
     655              :     }
     656          180 :     BOOST_ASSERT(req.case_sensitive == false);
     657          180 :     BOOST_ASSERT(req.strict == false);
     658              : 
     659          180 :     res = {};
     660              : 
     661              :     // we cannot do anything after do_dispatch returns,
     662              :     // other than return the route_result, or else we
     663              :     // could race with the detached operation trying to resume.
     664          180 :     auto rv = do_dispatch(req, res);
     665          177 :     if(rv == route::detach)
     666           12 :         return rv;
     667          165 :     if(res.ep_)
     668              :     {
     669            4 :         res.ep_ = nullptr;
     670            4 :         return error::unhandled_exception;
     671              :     }
     672          161 :     if( res.ec_.failed())
     673            6 :         res.ec_ = {};
     674          161 :     return rv;
     675              : }
     676              : 
     677              : // recursive dispatch
     678              : route_result
     679          200 : any_router::
     680              : dispatch_impl(
     681              :     basic_request& req,
     682              :     basic_response& res) const
     683              : {
     684              :     // options are recursive and need to be restored on
     685              :     // exception or when returning to a calling router.
     686              :     struct option_saver
     687              :     {
     688          200 :         option_saver(
     689              :             basic_request& req) noexcept
     690          200 :             : req_(&req)
     691          200 :             , case_sensitive_(req.case_sensitive)
     692          200 :             , strict_(req.strict)
     693              :         {
     694          200 :         }
     695              : 
     696          200 :         ~option_saver()
     697          186 :         {
     698          200 :             if(! req_)
     699           14 :                 return;
     700          186 :             req_->case_sensitive = case_sensitive_;
     701          186 :             req_->strict = strict_;
     702          200 :         };
     703              : 
     704           14 :         void cancel() noexcept
     705              :         {
     706           14 :             req_ = nullptr;
     707           14 :         }
     708              : 
     709              :     private:
     710              :         basic_request* req_;
     711              :         bool case_sensitive_;
     712              :         bool strict_;
     713              :     };
     714              : 
     715          200 :     option_saver restore_options(req);
     716              : 
     717              :     // inherit or apply options
     718          200 :     if((impl_->opt & 2) != 0)
     719            4 :         req.case_sensitive = true;
     720          196 :     else if((impl_->opt & 4) != 0)
     721            2 :         req.case_sensitive = false;
     722              : 
     723          200 :     if((impl_->opt & 8) != 0)
     724            0 :         req.strict = true;
     725          200 :     else if((impl_->opt & 16) != 0)
     726            0 :         req.strict = false;
     727              : 
     728          200 :     match_result mr;
     729          369 :     for(auto const& i : impl_->layers)
     730              :     {
     731          302 :         if(res.resume_ > 0)
     732              :         {
     733            9 :             auto const n = i.count(); // handlers in layer
     734            9 :             if(res.pos_ + n < res.resume_)
     735              :             {
     736            3 :                 res.pos_ += n; // skip layer
     737            3 :                 continue;
     738              :             }
     739              :             // repeat match to recreate the stack
     740            6 :             bool is_match = i.match(req, mr);
     741            6 :             BOOST_ASSERT(is_match);
     742              :             (void)is_match;
     743              :         }
     744              :         else
     745              :         {
     746          293 :             if(i.match.end && res.ec_.failed())
     747              :             {
     748              :                 // routes can't have error handlers
     749            1 :                 res.pos_ += i.count(); // skip layer
     750            1 :                 continue;
     751              :             }
     752          292 :             if(! i.match(req, mr))
     753              :             {
     754              :                 // not a match
     755           35 :                 res.pos_ += i.count(); // skip layer
     756           35 :                 continue;
     757              :             }
     758              :         }
     759          263 :         for(auto it = i.entries.begin();
     760          459 :             it != i.entries.end(); ++it)
     761              :         {
     762          335 :             auto const& e(*it);
     763          335 :             if(res.resume_)
     764              :             {
     765            8 :                 auto const n = e.handler->count();
     766            8 :                 if(res.pos_ + n < res.resume_)
     767              :                 {
     768            2 :                     res.pos_ += n; // skip entry
     769          196 :                     continue;
     770              :                 }
     771            6 :                 BOOST_ASSERT(e.match_method(req));
     772              :             }
     773          327 :             else if(i.match.end)
     774              :             {
     775              :                 // check verb for match 
     776          101 :                 if(! e.match_method(req))
     777              :                 {
     778           51 :                     res.pos_ += e.handler->count(); // skip entry
     779           51 :                     continue;
     780              :                 }
     781              :             }
     782              : 
     783          282 :             route_result rv;
     784              :             // increment before invoke
     785          282 :             ++res.pos_;
     786          282 :             if(res.pos_ != res.resume_)
     787              :             {
     788              :                 // call the handler
     789              :             #ifdef BOOST_NO_EXCEPTIONS
     790              :                 rv = e.handler->invoke(req, res);
     791              :             #else
     792              :                 try
     793              :                 {
     794          278 :                     rv = e.handler->invoke(req, res);
     795          272 :                     if(res.ec_.failed())
     796           57 :                         res.ep_ = {}; // transition to error mode
     797              :                 }
     798            6 :                 catch(...)
     799              :                 {
     800            6 :                     if(res.ec_.failed())
     801            0 :                         res.ec_ = {}; // transition to except mode
     802            6 :                     res.ep_ = std::current_exception();
     803            6 :                     rv = route::next;
     804            6 :                 }
     805              :             #endif
     806              : 
     807              :                 // res.pos_ can be incremented further
     808              :                 // inside the above call to invoke.
     809          278 :                 if(rv == route::detach)
     810              :                 {
     811              :                     // It is essential that we return immediately, without
     812              :                     // doing anything after route::detach is returned,
     813              :                     // otherwise we could race with the detached operation
     814              :                     // attempting to call resume().
     815           14 :                     restore_options.cancel();
     816          129 :                     return rv;
     817              :                 }
     818              :             }
     819              :             else
     820              :             {
     821              :                 // a subrouter never detaches on its own
     822            4 :                 BOOST_ASSERT(e.handler->count() == 1);
     823              :                 // can't detach on resume
     824            4 :                 if(res.ec_ == route::detach)
     825            1 :                     detail::throw_invalid_argument();
     826              :                 // do resume
     827            3 :                 res.resume_ = 0;
     828            3 :                 rv = res.ec_;
     829            3 :                 res.ec_ = {};
     830              :             }
     831          421 :             if( rv == route::send ||
     832          421 :                 rv == route::complete ||
     833          420 :                 rv == route::close)
     834              :             {
     835          115 :                 if( res.ec_.failed())
     836           19 :                     res.ec_ = {};
     837          115 :                 if( res.ep_)
     838            2 :                     res.ep_ = nullptr;
     839          115 :                 return rv;
     840              :             }
     841          152 :             if(rv == route::next)
     842          114 :                 continue; // next entry
     843           38 :             if(rv == route::next_route)
     844              :             {
     845              :                 // middleware can't return next_route
     846            2 :                 if(! i.match.end)
     847            1 :                     detail::throw_invalid_argument();
     848            6 :                 while(++it != i.entries.end())
     849            5 :                     res.pos_ += it->handler->count();
     850            6 :                 break; // skip remaining entries
     851              :             }
     852              :             // we must handle all route enums
     853           36 :             BOOST_ASSERT(! is_route_result(rv));
     854           36 :             if(! rv.failed())
     855              :             {
     856              :                 // handler must return non-successful error_code
     857            2 :                 detail::throw_invalid_argument();
     858              :             }
     859              :             // error handling mode
     860           34 :             res.ec_ = rv;
     861           34 :             if(! i.match.end)
     862           29 :                 continue; // next entry
     863              :             // routes don't have error handlers
     864           11 :             while(++it != i.entries.end())
     865            6 :                 res.pos_ += it->handler->count();
     866            5 :             break; // skip remaining entries
     867              :         }
     868              : 
     869          130 :         mr.restore_path(req);
     870              :     }
     871              : 
     872           67 :     return route::next;
     873          200 : }
     874              : 
     875              : route_result
     876          184 : any_router::
     877              : do_dispatch(
     878              :     basic_request& req,
     879              :     basic_response& res) const
     880              : {
     881          184 :     auto rv = dispatch_impl(req, res);
     882          180 :     BOOST_ASSERT(is_route_result(rv));
     883          180 :     BOOST_ASSERT(rv != route::next_route);
     884          180 :     if(rv != route::next)
     885              :     {
     886              :         // when rv == route::detach we must return immediately,
     887              :         // without attempting to perform any additional operations.
     888          121 :         return rv;
     889              :     }
     890           59 :     if(! res.ec_.failed())
     891              :     {
     892              :         // unhandled route
     893           53 :         return route::next;
     894              :     }
     895              :     // error condition
     896            6 :     return res.ec_;
     897              : }
     898              : 
     899              : //} // detail
     900              : 
     901              : } // http_proto
     902              : } // boost
        

Generated by: LCOV version 2.1