Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/boost/http_proto/detail/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ namespace boost {
namespace http_proto {
namespace detail {

template<class...> struct make_void { typedef void type; };
template<class... Ts> using void_t = typename make_void<Ts...>::type;

template<class T>
struct remove_cvref
{
Expand Down
6 changes: 5 additions & 1 deletion include/boost/http_proto/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ enum class error

/** A dynamic buffer's maximum size would be exceeded.
*/
buffer_overflow
buffer_overflow,

/** An unhandled exception occurred while routing a request
*/
unhandled_exception
};

// VFALCO we need a bad_message condition?
Expand Down
218 changes: 196 additions & 22 deletions include/boost/http_proto/server/basic_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/http_proto/server/route_handler.hpp>
#include <boost/http_proto/method.hpp>
#include <boost/url/url_view.hpp>
#include <boost/capy/detail/call_traits.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/core/detail/static_assert.hpp>
#include <type_traits>
Expand Down Expand Up @@ -354,6 +355,37 @@ class basic_router : public /*detail::*/any_router
std::false_type
>::type {};

template<class H, class = void>
struct except_type : std::false_type {};

template<class H>
struct except_type<H, typename std::enable_if<
capy::detail::call_traits<H>{} && (
capy::detail::type_list_size<typename
capy::detail::call_traits<H>::arg_types>{} == 3) &&
std::is_convertible<Req&, typename capy::detail::type_at<0, typename
capy::detail::call_traits<H>::arg_types>::type>::value &&
std::is_convertible<Res&, typename capy::detail::type_at<1, typename
capy::detail::call_traits<H>::arg_types>::type>{}
>::type>
: std::true_type
{
using type = typename std::decay<typename
capy::detail::type_at<2,typename
capy::detail::call_traits<H>::arg_types>::type>::type;
};

template<int, class... Hs>
struct except_types;

template<int N>
struct except_types<N> : std::true_type {};

template<int N, class H1, class... HN>
struct except_types<N, H1, HN...> : std::integral_constant<bool,
except_type<H1>{} && except_types<N, HN...>{}>
{};

public:
/** The type of request object used in handlers
*/
Expand Down Expand Up @@ -524,6 +556,32 @@ class basic_router : public /*detail::*/any_router
std::forward<H1>(h1), std::forward<HN>(hn)...);
}

/** Add a global exception handler.
*/
template<class H1, class... HN>
void except(
core::string_view pattern,
H1&& h1, HN... hn)
{
// If you get a compile error on this line it means that one or
// more of the provided types is not a valid exception handler
BOOST_CORE_STATIC_ASSERT(except_types<0, H1, HN...>::value);
add_impl(pattern, make_except_list(
std::forward<H1>(h1), std::forward<HN>(hn)...));
}

template<class H1, class... HN
, class = typename std::enable_if<
! std::is_convertible<H1, core::string_view>::value>::type>
void except(H1&& h1, HN&&... hn)
{
// If you get a compile error on this line it means that one or
// more of the provided types is not a valid exception handler
BOOST_CORE_STATIC_ASSERT(except_types<0, H1, HN...>::value);
except(core::string_view(),
std::forward<H1>(h1), std::forward<HN>(hn)...);
}

/** Add handlers for all HTTP methods matching a path pattern.

This registers regular handlers for the specified path pattern,
Expand Down Expand Up @@ -736,8 +794,31 @@ class basic_router : public /*detail::*/any_router
}

private:
struct undo_resume
{
std::size_t& resume;
bool undo_ = true;
~undo_resume()
{
if(undo_)
resume = 0;
}
undo_resume(
std::size_t& resume_) noexcept
: resume(resume_)
{
}
void cancel() noexcept
{
undo_ = false;
}
};

// wrapper for route handlers
template<class H>
template<
class H,
class Ty = handler_type<typename
std::decay<H>::type > >
struct handler_impl : any_handler
{
typename std::decay<H>::type h;
Expand All @@ -751,8 +832,7 @@ class basic_router : public /*detail::*/any_router
std::size_t
count() const noexcept override
{
return count(
handler_type<decltype(h)>{});
return count(Ty{});
}

route_result
Expand All @@ -762,8 +842,7 @@ class basic_router : public /*detail::*/any_router
{
return invoke(
static_cast<Req&>(req),
static_cast<Res&>(res),
handler_type<decltype(h)>{});
static_cast<Res&>(res), Ty{});
}

private:
Expand Down Expand Up @@ -795,16 +874,20 @@ class basic_router : public /*detail::*/any_router
route_result invoke(Req& req, Res& res,
std::integral_constant<int, 1>) const
{
auto const& ec = static_cast<
basic_response const&>(res).ec_;
if(ec.failed())
auto& res_ = static_cast<
basic_response&>(res);
if( res_.ec_.failed() ||
res_.ep_)
return http_proto::route::next;
// avoid racing on res.resume_
res.resume_ = res.pos_;
// avoid racing on res_.resume_
undo_resume u(res_.resume_);
res_.resume_ = res_.pos_;
auto rv = h(req, res);
if(rv == http_proto::route::detach)
{
u.cancel();
return rv;
res.resume_ = 0; // revert
}
return rv;
}

Expand All @@ -813,32 +896,96 @@ class basic_router : public /*detail::*/any_router
invoke(Req& req, Res& res,
std::integral_constant<int, 2>) const
{
auto const& ec = static_cast<
basic_response const&>(res).ec_;
if(! ec.failed())
auto& res_ = static_cast<
basic_response&>(res);
if(! res_.ec_.failed())
return http_proto::route::next;
// avoid racing on res.resume_
res.resume_ = res.pos_;
auto rv = h(req, res, ec);
res_.resume_ = res_.pos_;
undo_resume u(res_.resume_);
auto rv = h(req, res, res_.ec_);
if(rv == http_proto::route::detach)
{
u.cancel();
return rv;
res.resume_ = 0; // revert
}
return rv;
}

// any_router
route_result invoke(Req& req, Res& res,
std::integral_constant<int, 4>) const
{
auto const& ec = static_cast<
basic_response const&>(res).ec_;
if( res.resume_ > 0 ||
! ec.failed())
auto const& res_ = static_cast<
basic_response const&>(res);
if( res_.resume_ > 0 ||
( ! res_.ec_.failed() &&
! res_.ep_))
return h.dispatch_impl(req, res);
return http_proto::route::next;
}
};

template<class H, class E = typename
except_type<typename std::decay<H>::type>::type>
struct except_impl : any_handler
{
typename std::decay<H>::type h;

template<class... Args>
explicit except_impl(Args&&... args)
: h(std::forward<Args>(args)...)
{
}

std::size_t
count() const noexcept override
{
return 1;
}

route_result
invoke(Req& req, Res& res) const override
{
#ifndef BOOST_NO_EXCEPTIONS
auto& res_ = static_cast<
basic_response&>(res);
if( ! res_.ec_.failed() &&
! res_.ep_)
return http_proto::route::next;
volatile int dummy = sizeof(E);
(void)(dummy);
try
{
std::rethrow_exception(res_.ep_);
}
catch(E const& ex)
{
// avoid racing on res.resume_
res_.resume_ = res_.pos_;
undo_resume u(res_.resume_);
// VFALCO What if h throws?
auto rv = h(req, res, ex);
if(rv == http_proto::route::detach)
{
u.cancel();
return rv;
}
return rv;
}
catch(...)
{
res_.ep_ = std::current_exception();
return http_proto::route::next;
}
#else
(void)req;
(void)res;
return http_proto::route::next;
#endif
}
};

template<std::size_t N>
struct handler_list_impl : handler_list
{
Expand All @@ -850,6 +997,15 @@ class basic_router : public /*detail::*/any_router
assign<0>(std::forward<HN>(hn)...);
}

// exception handlers
template<class... HN>
explicit handler_list_impl(int, HN&&... hn)
{
n = sizeof...(HN);
p = v;
assign<0>(0, std::forward<HN>(hn)...);
}

private:
template<std::size_t I, class H1, class... HN>
void assign(H1&& h1, HN&&... hn)
Expand All @@ -859,8 +1015,17 @@ class basic_router : public /*detail::*/any_router
assign<I+1>(std::forward<HN>(hn)...);
}

// exception handlers
template<std::size_t I, class H1, class... HN>
void assign(int, H1&& h1, HN&&... hn)
{
v[I] = handler_ptr(new except_impl<H1>(
std::forward<H1>(h1)));
assign<I+1>(0, std::forward<HN>(hn)...);
}

template<std::size_t>
void assign()
void assign(int = 0)
{
}

Expand All @@ -876,6 +1041,15 @@ class basic_router : public /*detail::*/any_router
std::forward<HN>(hn)...);
}

template<class... HN>
static auto
make_except_list(HN&&... hn) ->
handler_list_impl<sizeof...(HN)>
{
return handler_list_impl<sizeof...(HN)>(
0, std::forward<HN>(hn)...);
}

void append(layer& e,
http_proto::method verb,
handler_list const& handlers)
Expand Down
53 changes: 53 additions & 0 deletions include/boost/http_proto/server/cors.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http_proto
//

#ifndef BOOST_HTTP_PROTO_SERVER_CORS_HPP
#define BOOST_HTTP_PROTO_SERVER_CORS_HPP

#include <boost/http_proto/detail/config.hpp>
#include <boost/http_proto/server/route_handler.hpp>
#include <boost/http_proto/status.hpp>
#include <chrono>

namespace boost {
namespace http_proto {

struct cors_options
{
std::string origin;
std::string methods;
std::string allowedHeaders;
std::string exposedHeaders;
std::chrono::seconds max_age{ 0 };
status result = status::no_content;
bool preFligthContinue = false;
bool credentials = false;
};

class cors
{
public:
BOOST_HTTP_PROTO_DECL
explicit cors(
cors_options options = {}) noexcept;

BOOST_HTTP_PROTO_DECL
route_result
operator()(
Request& req,
Response& res) const;

private:
cors_options options_;
};

} // http_proto
} // boost

#endif
Loading
Loading