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