1616#include < boost/http_proto/server/route_handler.hpp>
1717#include < boost/http_proto/method.hpp>
1818#include < boost/url/url_view.hpp>
19+ #include < boost/capy/detail/call_traits.hpp>
1920#include < boost/core/detail/string_view.hpp>
2021#include < boost/core/detail/static_assert.hpp>
2122#include < type_traits>
@@ -354,6 +355,37 @@ class basic_router : public /*detail::*/any_router
354355 std::false_type
355356 >::type {};
356357
358+ template <class H , class = void >
359+ struct except_type : std::false_type {};
360+
361+ template <class H >
362+ struct except_type <H, typename std::enable_if<
363+ capy::detail::call_traits<H>{} && (
364+ capy::detail::type_list_size<typename
365+ capy::detail::call_traits<H>::arg_types>{} == 3 ) &&
366+ std::is_convertible<Req&, typename capy::detail::type_at<0 , typename
367+ capy::detail::call_traits<H>::arg_types>::type>::value &&
368+ std::is_convertible<Res&, typename capy::detail::type_at<1 , typename
369+ capy::detail::call_traits<H>::arg_types>::type>{}
370+ >::type>
371+ : std::true_type
372+ {
373+ using type = typename std::decay<typename
374+ capy::detail::type_at<2 ,typename
375+ capy::detail::call_traits<H>::arg_types>::type>::type;
376+ };
377+
378+ template <int , class ... Hs>
379+ struct except_types ;
380+
381+ template <int N>
382+ struct except_types <N> : std::true_type {};
383+
384+ template <int N, class H1 , class ... HN >
385+ struct except_types <N, H1 , HN ...> : std::integral_constant<bool ,
386+ except_type<H1 >{} && except_types<N, HN ...>{}>
387+ {};
388+
357389public:
358390 /* * The type of request object used in handlers
359391 */
@@ -524,6 +556,32 @@ class basic_router : public /*detail::*/any_router
524556 std::forward<H1 >(h1), std::forward<HN >(hn)...);
525557 }
526558
559+ /* * Add a global exception handler.
560+ */
561+ template <class H1 , class ... HN >
562+ void except (
563+ core::string_view pattern,
564+ H1 && h1, HN ... hn)
565+ {
566+ // If you get a compile error on this line it means that one or
567+ // more of the provided types is not a valid exception handler
568+ BOOST_CORE_STATIC_ASSERT (except_types<0 , H1 , HN ...>::value);
569+ add_impl (pattern, make_except_list (
570+ std::forward<H1 >(h1), std::forward<HN >(hn)...));
571+ }
572+
573+ template <class H1 , class ... HN
574+ , class = typename std::enable_if<
575+ ! std::is_convertible<H1 , core::string_view>::value>::type>
576+ void except (H1 && h1, HN &&... hn)
577+ {
578+ // If you get a compile error on this line it means that one or
579+ // more of the provided types is not a valid exception handler
580+ BOOST_CORE_STATIC_ASSERT (except_types<0 , H1 , HN ...>::value);
581+ except (core::string_view (),
582+ std::forward<H1 >(h1), std::forward<HN >(hn)...);
583+ }
584+
527585 /* * Add handlers for all HTTP methods matching a path pattern.
528586
529587 This registers regular handlers for the specified path pattern,
@@ -736,8 +794,31 @@ class basic_router : public /*detail::*/any_router
736794 }
737795
738796private:
797+ struct undo_resume
798+ {
799+ std::size_t & resume;
800+ bool undo_ = true ;
801+ ~undo_resume ()
802+ {
803+ if (undo_)
804+ resume = 0 ;
805+ }
806+ undo_resume (
807+ std::size_t & resume_) noexcept
808+ : resume(resume_)
809+ {
810+ }
811+ void cancel () noexcept
812+ {
813+ undo_ = false ;
814+ }
815+ };
816+
739817 // wrapper for route handlers
740- template <class H >
818+ template <
819+ class H ,
820+ class Ty = handler_type<typename
821+ std::decay<H>::type > >
741822 struct handler_impl : any_handler
742823 {
743824 typename std::decay<H>::type h;
@@ -751,8 +832,7 @@ class basic_router : public /*detail::*/any_router
751832 std::size_t
752833 count () const noexcept override
753834 {
754- return count (
755- handler_type<decltype (h)>{});
835+ return count (Ty{});
756836 }
757837
758838 route_result
@@ -762,8 +842,7 @@ class basic_router : public /*detail::*/any_router
762842 {
763843 return invoke (
764844 static_cast <Req&>(req),
765- static_cast <Res&>(res),
766- handler_type<decltype (h)>{});
845+ static_cast <Res&>(res), Ty{});
767846 }
768847
769848 private:
@@ -795,16 +874,20 @@ class basic_router : public /*detail::*/any_router
795874 route_result invoke (Req& req, Res& res,
796875 std::integral_constant<int , 1 >) const
797876 {
798- auto const & ec = static_cast <
799- basic_response const &>(res).ec_ ;
800- if (ec.failed ())
877+ auto & res_ = static_cast <
878+ basic_response&>(res);
879+ if ( res_.ec_ .failed () ||
880+ res_.ep_ )
801881 return http_proto::route::next;
802- // avoid racing on res.resume_
803- res.resume_ = res.pos_ ;
882+ // avoid racing on res_.resume_
883+ undo_resume u (res_.resume_ );
884+ res_.resume_ = res_.pos_ ;
804885 auto rv = h (req, res);
805886 if (rv == http_proto::route::detach)
887+ {
888+ u.cancel ();
806889 return rv;
807- res. resume_ = 0 ; // revert
890+ }
808891 return rv;
809892 }
810893
@@ -813,32 +896,94 @@ class basic_router : public /*detail::*/any_router
813896 invoke (Req& req, Res& res,
814897 std::integral_constant<int , 2 >) const
815898 {
816- auto const & ec = static_cast <
817- basic_response const &>(res). ec_ ;
818- if (! ec .failed ())
899+ auto & res_ = static_cast <
900+ basic_response&>(res);
901+ if (! res_. ec_ .failed ())
819902 return http_proto::route::next;
820903 // avoid racing on res.resume_
821- res.resume_ = res.pos_ ;
822- auto rv = h (req, res, ec);
904+ res_.resume_ = res_.pos_ ;
905+ undo_resume u (res_.resume_ );
906+ auto rv = h (req, res, res_.ec_ );
823907 if (rv == http_proto::route::detach)
908+ {
909+ u.cancel ();
824910 return rv;
825- res. resume_ = 0 ; // revert
911+ }
826912 return rv;
827913 }
828914
829915 // any_router
830916 route_result invoke (Req& req, Res& res,
831917 std::integral_constant<int , 4 >) const
832918 {
833- auto const & ec = static_cast <
834- basic_response const &>(res).ec_ ;
835- if ( res.resume_ > 0 ||
836- ! ec.failed ())
919+ auto const & res_ = static_cast <
920+ basic_response const &>(res);
921+ if ( res_.resume_ > 0 ||
922+ ( ! res_.ec_ .failed () &&
923+ ! res_.ep_ ))
837924 return h.dispatch_impl (req, res);
838925 return http_proto::route::next;
839926 }
840927 };
841928
929+ template <class H , class E = typename
930+ except_type<typename std::decay<H>::type>::type>
931+ struct except_impl : any_handler
932+ {
933+ typename std::decay<H>::type h;
934+
935+ template <class ... Args>
936+ explicit except_impl (Args&&... args)
937+ : h(std::forward<Args>(args)...)
938+ {
939+ }
940+
941+ std::size_t
942+ count () const noexcept override
943+ {
944+ return 1 ;
945+ }
946+
947+ route_result
948+ invoke (Req& req, Res& res) const override
949+ {
950+ #ifndef BOOST_NO_EXCEPTIONS
951+ auto & res_ = static_cast <
952+ basic_response&>(res);
953+ if ( ! res_.ec_ .failed () &&
954+ ! res_.ep_ )
955+ return http_proto::route::next;
956+ try
957+ {
958+ std::rethrow_exception (res_.ep_ );
959+ }
960+ catch (E const & ex)
961+ {
962+ // avoid racing on res.resume_
963+ res_.resume_ = res_.pos_ ;
964+ undo_resume u (res_.resume_ );
965+ // VFALCO What if h throws?
966+ auto rv = h (req, res, ex);
967+ if (rv == http_proto::route::detach)
968+ {
969+ u.cancel ();
970+ return rv;
971+ }
972+ return rv;
973+ }
974+ catch (...)
975+ {
976+ res_.ep_ = std::current_exception ();
977+ return http_proto::route::next;
978+ }
979+ #else
980+ (void )req;
981+ (void )res;
982+ return http_proto::route::next;
983+ #endif
984+ }
985+ };
986+
842987 template <std::size_t N>
843988 struct handler_list_impl : handler_list
844989 {
@@ -850,6 +995,15 @@ class basic_router : public /*detail::*/any_router
850995 assign<0 >(std::forward<HN >(hn)...);
851996 }
852997
998+ // exception handlers
999+ template <class ... HN >
1000+ explicit handler_list_impl (int , HN &&... hn)
1001+ {
1002+ n = sizeof ...(HN );
1003+ p = v;
1004+ assign<0 >(0 , std::forward<HN >(hn)...);
1005+ }
1006+
8531007 private:
8541008 template <std::size_t I, class H1 , class ... HN >
8551009 void assign (H1 && h1, HN &&... hn)
@@ -859,8 +1013,17 @@ class basic_router : public /*detail::*/any_router
8591013 assign<I+1 >(std::forward<HN >(hn)...);
8601014 }
8611015
1016+ // exception handlers
1017+ template <std::size_t I, class H1 , class ... HN >
1018+ void assign (int , H1 && h1, HN &&... hn)
1019+ {
1020+ v[I] = handler_ptr (new except_impl<H1 >(
1021+ std::forward<H1 >(h1)));
1022+ assign<I+1 >(0 , std::forward<HN >(hn)...);
1023+ }
1024+
8621025 template <std::size_t >
863- void assign ()
1026+ void assign (int = 0 )
8641027 {
8651028 }
8661029
@@ -876,6 +1039,15 @@ class basic_router : public /*detail::*/any_router
8761039 std::forward<HN >(hn)...);
8771040 }
8781041
1042+ template <class ... HN >
1043+ static auto
1044+ make_except_list (HN &&... hn) ->
1045+ handler_list_impl<sizeof...(HN )>
1046+ {
1047+ return handler_list_impl<sizeof ...(HN )>(
1048+ 0 , std::forward<HN >(hn)...);
1049+ }
1050+
8791051 void append (layer& e,
8801052 http_proto::method verb,
8811053 handler_list const & handlers)
0 commit comments