Skip to content

Commit c558d66

Browse files
committed
feat: exception route handlers
1 parent 4233c34 commit c558d66

9 files changed

Lines changed: 401 additions & 31 deletions

File tree

include/boost/http_proto/detail/type_traits.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ namespace boost {
1717
namespace http_proto {
1818
namespace detail {
1919

20+
template<class...> struct make_void { typedef void type; };
21+
template<class... Ts> using void_t = typename make_void<Ts...>::type;
22+
2023
template<class T>
2124
struct remove_cvref
2225
{

include/boost/http_proto/error.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ enum class error
197197

198198
/** A dynamic buffer's maximum size would be exceeded.
199199
*/
200-
buffer_overflow
200+
buffer_overflow,
201+
202+
/** An unhandled exception occurred while routing a request
203+
*/
204+
unhandled_exception
201205
};
202206

203207
// VFALCO we need a bad_message condition?

include/boost/http_proto/server/basic_router.hpp

Lines changed: 194 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
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+
357389
public:
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

738796
private:
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)

include/boost/http_proto/server/router_types.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <boost/http_proto/detail/except.hpp>
1616
#include <boost/core/detail/string_view.hpp>
1717
#include <boost/system/error_code.hpp>
18+
#include <exception>
1819
#include <string>
1920
#include <type_traits>
2021

@@ -340,7 +341,7 @@ class basic_response
340341
std::size_t pos_ = 0;
341342
std::size_t resume_ = 0;
342343
system::error_code ec_;
343-
unsigned int opt_ = 0;
344+
std::exception_ptr ep_;
344345
};
345346

346347
} // http_proto

src/error.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const char*
2121
error_cat_type::
2222
name() const noexcept
2323
{
24-
return "boost.http.proto";
24+
return "boost.http";
2525
}
2626

2727
std::string
@@ -75,6 +75,7 @@ message(
7575
case error::numeric_overflow: return "numeric overflow";
7676
case error::multiple_content_length: return "multiple Content-Length";
7777
case error::buffer_overflow: return "buffer overflow";
78+
case error::unhandled_exception: return "unhandled exception";
7879
default:
7980
return "unknown";
8081
}
@@ -86,7 +87,7 @@ const char*
8687
condition_cat_type::
8788
name() const noexcept
8889
{
89-
return "boost.http.proto";
90+
return "boost.http";
9091
}
9192

9293
std::string

0 commit comments

Comments
 (0)