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
|