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.9 % 384 376
Test Date: 2025-12-01 03:49:40 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/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          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          173 : pct_decode_path(
     151              :     urls::pct_string_view s)
     152              : {
     153          173 :     std::string result;
     154          173 :     core::string_view sv(s);
     155          173 :     result.reserve(s.size());
     156          173 :     auto it = sv.data();
     157          173 :     auto const end = it + sv.size();
     158              :     for(;;)
     159              :     {
     160          601 :         if(it == end)
     161          173 :             break;
     162          428 :         if(*it != '%')
     163              :         {
     164          424 :             result.push_back(*it++);
     165          424 :             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          428 :     }
     195          346 :     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          246 :     void adjust_path(
     211              :         basic_request& req,
     212              :         std::size_t n)
     213              :     {
     214          246 :         n_ = n;
     215          246 :         if(n_ == 0)
     216          165 :             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          281 :     bool operator()(
     289              :         basic_request& req,
     290              :         match_result& mr) const
     291              :     {
     292          281 :         BOOST_ASSERT(! req.path.empty());
     293          446 :         if( slash_ && (
     294          219 :             ! end ||
     295          335 :             req.path == "/"))
     296              :         {
     297              :             // params = {};
     298          165 :             mr.adjust_path(req, 0);
     299          165 :             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 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(! is_route_result(ec))
     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          173 : 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          173 :     req = {};
     638          173 :     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          140 :         BOOST_ASSERT(verb_str.empty());
     648              :     }
     649          173 :     req.verb_ = verb;
     650              :     // VFALCO use reusing-StringToken
     651              :     req.decoded_path_ =
     652          173 :         pct_decode_path(url.encoded_path());
     653          173 :     BOOST_ASSERT(! req.decoded_path_.empty());
     654          173 :     req.base_path = { req.decoded_path_.data(), 0 };
     655          173 :     req.path = req.decoded_path_;
     656          173 :     if(req.decoded_path_.back() != '/')
     657              :     {
     658           55 :         req.decoded_path_.push_back('/');
     659           55 :         req.addedSlash_ = true;
     660              :     }
     661          173 :     BOOST_ASSERT(req.case_sensitive == false);
     662          173 :     BOOST_ASSERT(req.strict == false);
     663              : 
     664          173 :     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          173 :     return do_dispatch(req, res);
     670              : }
     671              : 
     672              : // recursive dispatch
     673              : route_result
     674          193 : 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          193 :         option_saver(
     684              :             basic_request& req) noexcept
     685          193 :             : req_(&req)
     686          193 :             , case_sensitive_(req.case_sensitive)
     687          193 :             , strict_(req.strict)
     688              :         {
     689          193 :         }
     690              : 
     691          193 :         ~option_saver()
     692          179 :         {
     693          193 :             if(! req_)
     694           14 :                 return;
     695          179 :             req_->case_sensitive = case_sensitive_;
     696          179 :             req_->strict = strict_;
     697          193 :         };
     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          193 :     option_saver restore_options(req);
     711              : 
     712              :     // inherit or apply options
     713          193 :     if((impl_->opt & 2) != 0)
     714            4 :         req.case_sensitive = true;
     715          189 :     else if((impl_->opt & 4) != 0)
     716            2 :         req.case_sensitive = false;
     717              : 
     718          193 :     if((impl_->opt & 8) != 0)
     719            0 :         req.strict = true;
     720          193 :     else if((impl_->opt & 16) != 0)
     721            0 :         req.strict = false;
     722              : 
     723              :     // nested routers count as 1 call
     724              :     //++res.pos_;
     725              : 
     726          193 :     match_result mr;
     727          347 :     for(auto const& i : impl_->layers)
     728              :     {
     729          285 :         if(res.resume_ > 0)
     730              :         {
     731            9 :             auto const n = i.count(); // handlers in layer
     732            9 :             if(res.pos_ + n < res.resume_)
     733              :             {
     734            3 :                 res.pos_ += n; // skip layer
     735            3 :                 continue;
     736              :             }
     737              :             // repeat match to recreate the stack
     738            6 :             bool is_match = i.match(req, mr);
     739            6 :             BOOST_ASSERT(is_match);
     740              :             (void)is_match;
     741              :         }
     742              :         else
     743              :         {
     744          276 :             if(i.match.end && res.ec_.failed())
     745              :             {
     746              :                 // routes can't have error handlers
     747            1 :                 res.pos_ += i.count(); // skip layer
     748            1 :                 continue;
     749              :             }
     750          275 :             if(! i.match(req, mr))
     751              :             {
     752              :                 // not a match
     753           35 :                 res.pos_ += i.count(); // skip layer
     754           35 :                 continue;
     755              :             }
     756              :         }
     757          246 :         for(auto it = i.entries.begin();
     758          426 :             it != i.entries.end(); ++it)
     759              :         {
     760          317 :             auto const& e(*it);
     761          317 :             if(res.resume_)
     762              :             {
     763            8 :                 auto const n = e.handler->count();
     764            8 :                 if(res.pos_ + n < res.resume_)
     765              :                 {
     766            2 :                     res.pos_ += n; // skip entry
     767          180 :                     continue;
     768              :                 }
     769            6 :                 BOOST_ASSERT(e.match_method(req));
     770              :             }
     771          309 :             else if(i.match.end)
     772              :             {
     773              :                 // check verb for match 
     774          101 :                 if(! e.match_method(req))
     775              :                 {
     776           51 :                     res.pos_ += e.handler->count(); // skip entry
     777           51 :                     continue;
     778              :                 }
     779              :             }
     780              : 
     781          264 :             route_result rv;
     782              :             // increment before invoke
     783          264 :             ++res.pos_;
     784          264 :             if(res.pos_ != res.resume_)
     785              :             {
     786              :                 // call the handler
     787          260 :                 rv = e.handler->invoke(req, res);
     788              :                 // res.pos_ can be incremented further
     789              :                 // inside the above call to invoke.
     790          260 :                 if(rv == route::detach)
     791              :                 {
     792              :                     // It is essential that we return immediately, without
     793              :                     // doing anything after route::detach is returned,
     794              :                     // otherwise we could race with the detached operation
     795              :                     // attempting to call resume().
     796           14 :                     restore_options.cancel();
     797          127 :                     return rv;
     798              :                 }
     799              :             }
     800              :             else
     801              :             {
     802              :                 // a subrouter never detaches on its own
     803            4 :                 BOOST_ASSERT(e.handler->count() == 1);
     804              :                 // can't detach on resume
     805            4 :                 if(res.ec_ == route::detach)
     806            1 :                     detail::throw_invalid_argument();
     807              :                 // do resume
     808            3 :                 res.resume_ = 0;
     809            3 :                 rv = res.ec_;
     810            3 :                 res.ec_ = {};
     811              :             }
     812          387 :             if( rv == route::send ||
     813          387 :                 rv == route::complete ||
     814          386 :                 rv == route::close)
     815          113 :                 return rv;
     816          136 :             if(rv == route::next)
     817           98 :                 continue; // next entry
     818           38 :             if(rv == route::next_route)
     819              :             {
     820              :                 // middleware can't return next_route
     821            2 :                 if(! i.match.end)
     822            1 :                     detail::throw_invalid_argument();
     823            6 :                 while(++it != i.entries.end())
     824            5 :                     res.pos_ += it->handler->count();
     825            6 :                 break; // skip remaining entries
     826              :             }
     827              :             // we must handle all route enums
     828           36 :             BOOST_ASSERT(! is_route_result(rv));
     829           36 :             if(! rv.failed())
     830              :             {
     831              :                 // handler must return non-successful error_code
     832            2 :                 detail::throw_invalid_argument();
     833              :             }
     834              :             // error handling mode
     835           34 :             res.ec_ = rv;
     836           34 :             if(! i.match.end)
     837           29 :                 continue; // next entry
     838              :             // routes don't have error handlers
     839           11 :             while(++it != i.entries.end())
     840            6 :                 res.pos_ += it->handler->count();
     841            5 :             break; // skip remaining entries
     842              :         }
     843              : 
     844          115 :         mr.restore_path(req);
     845              :     }
     846              : 
     847           62 :     return route::next;
     848          193 : }
     849              : 
     850              : route_result
     851          177 : any_router::
     852              : do_dispatch(
     853              :     basic_request& req,
     854              :     basic_response& res) const
     855              : {
     856          177 :     auto rv = dispatch_impl(req, res);
     857          173 :     BOOST_ASSERT(is_route_result(rv));
     858          173 :     BOOST_ASSERT(rv != route::next_route);
     859          173 :     if(rv != route::next)
     860              :     {
     861              :         // when rv == route::detach we must return immediately,
     862              :         // without attempting to perform any additional operations.
     863          119 :         return rv;
     864              :     }
     865           54 :     if(! res.ec_.failed())
     866              :     {
     867              :         // unhandled route
     868           48 :         return route::next;
     869              :     }
     870              :     // error condition
     871            6 :     return res.ec_;
     872              : }
     873              : 
     874              : //} // detail
     875              : 
     876              : } // http_proto
     877              : } // boost
        

Generated by: LCOV version 2.1