Skip to content

Commit

Permalink
improve aspects (qicosmos#522)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos authored Feb 9, 2024
1 parent d9d16db commit 259b9f9
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 127 deletions.
7 changes: 3 additions & 4 deletions example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ async_simple::coro::Lazy<void> static_file_server() {
assert(result.resp_body.size() == 64);
}

struct log_t : public base_aspect {
struct log_t {
bool before(coro_http_request &, coro_http_response &) {
std::cout << "before log" << std::endl;
return true;
Expand All @@ -256,7 +256,7 @@ struct log_t : public base_aspect {
}
};

struct get_data : public base_aspect {
struct get_data {
bool before(coro_http_request &req, coro_http_response &res) {
req.set_aspect_data("hello world");
return true;
Expand All @@ -272,8 +272,7 @@ async_simple::coro::Lazy<void> use_aspects() {
assert(val[0] == "hello world");
resp.set_status_and_content(status_type::ok, "ok");
},
std::vector<std::shared_ptr<base_aspect>>{std::make_shared<log_t>(),
std::make_shared<get_data>()});
log_t{}, get_data{});

server.async_start();
std::this_thread::sleep_for(300ms); // wait for server start
Expand Down
13 changes: 6 additions & 7 deletions include/cinatra/coro_http_reverse_proxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ class reverse_proxy {
weights_.push_back(weight);
}

template <http_method... method>
void start_reverse_proxy(
std::string url_path = "/", bool sync = true,
coro_io::load_blance_algorithm type =
coro_io::load_blance_algorithm::random,
std::vector<std::shared_ptr<base_aspect>> aspects = {}) {
template <http_method... method, typename... Aspects>
void start_reverse_proxy(std::string url_path = "/", bool sync = true,
coro_io::load_blance_algorithm type =
coro_io::load_blance_algorithm::random,
Aspects &&...aspects) {
if (dest_hosts_.empty()) {
throw std::invalid_argument("not config hosts yet!");
}
Expand All @@ -52,7 +51,7 @@ class reverse_proxy {
co_await reply(client, uri.get_path(), req, response);
});
},
std::move(aspects));
std::forward<Aspects>(aspects)...);

start(sync);
}
Expand Down
188 changes: 99 additions & 89 deletions include/cinatra/coro_http_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,35 @@ constexpr inline bool is_lazy_v =
is_template_instant_of<async_simple::coro::Lazy,
std::remove_cvref_t<T>>::value;

struct base_aspect {
virtual bool before(coro_http_request& req, coro_http_response& resp) {
return true;
}
template <class, class = void>
struct has_before : std::false_type {};

virtual bool after(coro_http_request& req, coro_http_response& resp) {
return true;
}
};
template <class T>
struct has_before<T, std::void_t<decltype(std::declval<T>().before(
std::declval<coro_http_request&>(),
std::declval<coro_http_response&>()))>>
: std::true_type {};

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

template <class T>
struct has_after<T, std::void_t<decltype(std::declval<T>().after(
std::declval<coro_http_request&>(),
std::declval<coro_http_response&>()))>>
: std::true_type {};

template <class T>
constexpr bool has_before_v = has_before<T>::value;

template <class T>
constexpr bool has_after_v = has_after<T>::value;

class coro_http_router {
public:
// eg: "GET hello/" as a key
template <http_method method, typename Func>
void set_http_handler(
std::string key, Func handler,
std::vector<std::shared_ptr<base_aspect>> aspects = {}) {
template <http_method method, typename Func, typename... Aspects>
void set_http_handler(std::string key, Func handler, Aspects&&... asps) {
constexpr auto method_name = cinatra::method_name(method);
std::string whole_str;
whole_str.append(method_name).append(" ").append(key);
Expand All @@ -53,12 +65,34 @@ class coro_http_router {
// std::string_view, avoid memcpy when route
using return_type = typename util::function_traits<Func>::return_type;
if constexpr (is_lazy_v<return_type>) {
std::function<async_simple::coro::Lazy<void>(coro_http_request & req,
coro_http_response & resp)>
http_handler;
if constexpr (sizeof...(Aspects) > 0) {
http_handler = [this, handler = std::move(handler),
... asps = std::forward<Aspects>(asps)](
coro_http_request& req,
coro_http_response& resp) mutable
-> async_simple::coro::Lazy<void> {
bool ok = true;
(do_before(asps, req, resp, ok), ...);
if (ok) {
co_await handler(req, resp);

(do_after(asps, req, resp, ok), ...);
}
};
}
else {
http_handler = std::move(handler);
}

if (whole_str.find(":") != std::string::npos) {
std::vector<std::string> coro_method_names = {};
std::string coro_method_str;
coro_method_str.append(method_name);
coro_method_names.push_back(coro_method_str);
coro_router_tree_->coro_insert(key, std::move(handler),
coro_router_tree_->coro_insert(key, std::move(http_handler),
coro_method_names);
}
else {
Expand All @@ -71,29 +105,44 @@ class coro_http_router {
}

coro_regex_handles_.emplace_back(std::regex(pattern),
std::move(handler));
std::move(http_handler));
}
else {
auto [it, ok] = coro_keys_.emplace(std::move(whole_str));
if (!ok) {
CINATRA_LOG_WARNING << key << " has already registered.";
return;
}
coro_handles_.emplace(*it, std::move(handler));
if (!aspects.empty()) {
has_aspects_ = true;
aspects_.emplace(*it, std::move(aspects));
}
coro_handles_.emplace(*it, std::move(http_handler));
}
}
}
else {
std::function<void(coro_http_request & req, coro_http_response & resp)>
http_handler;
if constexpr (sizeof...(Aspects) > 0) {
http_handler = [this, handler = std::move(handler),
... asps = std::forward<Aspects>(asps)](
coro_http_request& req,
coro_http_response& resp) mutable {
bool ok = true;
(do_before(asps, req, resp, ok), ...);
if (ok) {
handler(req, resp);
(do_after(asps, req, resp, ok), ...);
}
};
}
else {
http_handler = std::move(handler);
}

if (whole_str.find(':') != std::string::npos) {
std::vector<std::string> method_names = {};
std::string method_str;
method_str.append(method_name);
method_names.push_back(method_str);
router_tree_->insert(whole_str, std::move(handler), method_names);
router_tree_->insert(whole_str, std::move(http_handler), method_names);
}
else if (whole_str.find("{") != std::string::npos ||
whole_str.find(")") != std::string::npos) {
Expand All @@ -103,20 +152,42 @@ class coro_http_router {
replace_all(pattern, "{}", "([^/]+)");
}

regex_handles_.emplace_back(std::regex(pattern), std::move(handler));
regex_handles_.emplace_back(std::regex(pattern),
std::move(http_handler));
}
else {
auto [it, ok] = keys_.emplace(std::move(whole_str));
if (!ok) {
CINATRA_LOG_WARNING << key << " has already registered.";
return;
}
map_handles_.emplace(*it, std::move(handler));
if (!aspects.empty()) {
has_aspects_ = true;
aspects_.emplace(*it, std::move(aspects));
}
map_handles_.emplace(*it, std::move(http_handler));
}
}
}

template <typename T>
void do_before(T& aspect, coro_http_request& req, coro_http_response& resp,
bool& ok) {
if constexpr (has_before_v<T>) {
if (!ok) {
return;
}
ok = aspect.before(req, resp);
}
else {
ok = true;
}
}

template <typename T>
void do_after(T& aspect, coro_http_request& req, coro_http_response& resp,
bool& ok) {
if constexpr (has_after_v<T>) {
ok = aspect.after(req, resp);
}
else {
ok = true;
}
}

Expand All @@ -139,19 +210,7 @@ class coro_http_router {

void route(auto handler, auto& req, auto& resp, std::string_view key) {
try {
if (has_aspects_) {
auto [it, ok] = handle_aspects(req, resp, key, true);
if (!ok) {
return;
}
(*handler)(req, resp);
if (it != aspects_.end()) {
handle_aspects(req, resp, it->second, false);
}
}
else {
(*handler)(req, resp);
}
(*handler)(req, resp);
} catch (const std::exception& e) {
CINATRA_LOG_WARNING << "exception in business function, reason: "
<< e.what();
Expand All @@ -165,19 +224,7 @@ class coro_http_router {
async_simple::coro::Lazy<void> route_coro(auto handler, auto& req, auto& resp,
std::string_view key) {
try {
if (has_aspects_) {
auto [it, ok] = handle_aspects(req, resp, key, true);
if (!ok) {
co_return;
}
co_await (*handler)(req, resp);
if (it != aspects_.end()) {
handle_aspects(req, resp, it->second, false);
}
}
else {
co_await (*handler)(req, resp);
}
co_await (*handler)(req, resp);
} catch (const std::exception& e) {
CINATRA_LOG_WARNING << "exception in business function, reason: "
<< e.what();
Expand All @@ -202,38 +249,6 @@ class coro_http_router {

const auto& get_regex_handlers() { return regex_handles_; }

bool handle_aspects(auto& req, auto& resp, auto& aspects, bool before) {
bool r = true;
for (auto& aspect : aspects) {
if (before) {
r = aspect->before(req, resp);
}
else {
r = aspect->after(req, resp);
}
if (!r) {
break;
}
}
return r;
}

auto handle_aspects(auto& req, auto& resp, std::string_view key,
bool before) {
decltype(aspects_.begin()) it;
if (it = aspects_.find(key); it != aspects_.end()) {
auto& aspects = it->second;
bool r = handle_aspects(req, resp, aspects, before);
if (!r) {
return std::make_pair(aspects_.end(), false);
}
}

return std::make_pair(it, true);
}

void handle_after() {}

private:
std::set<std::string> keys_;
std::unordered_map<
Expand Down Expand Up @@ -262,10 +277,5 @@ class coro_http_router {
std::regex, std::function<async_simple::coro::Lazy<void>(
coro_http_request& req, coro_http_response& resp)>>>
coro_regex_handles_;

std::unordered_map<std::string_view,
std::vector<std::shared_ptr<base_aspect>>>
aspects_;
bool has_aspects_ = false;
};
} // namespace cinatra
Loading

0 comments on commit 259b9f9

Please sign in to comment.