Skip to content

Commit

Permalink
feat: add radix tree router and regex router for restful api (qicosmo…
Browse files Browse the repository at this point in the history
  • Loading branch information
helintongh authored Jan 10, 2024
1 parent b2b4acb commit 033f92d
Show file tree
Hide file tree
Showing 5 changed files with 711 additions and 18 deletions.
65 changes: 63 additions & 2 deletions include/cinatra/coro_http_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,69 @@ class coro_http_connection
co_await router_.route_coro(coro_handler, request_, response_, key);
}
else {
// not found
response_.set_status(status_type::not_found);
bool is_exist = false;
std::function<void(coro_http_request & req,
coro_http_response & resp)>
handler;
std::string method_str;
method_str.assign(parser_.method().data(), parser_.method().length());
std::string url_path;
url_path.assign(parser_.url().data(), parser_.url().length());
std::tie(is_exist, handler, request_.params_) =
router_.get_router_tree()->get(url_path, method_str);
if (is_exist) {
(handler)(request_, response_);
}
else {
bool is_coro_exist = false;
std::function<async_simple::coro::Lazy<void>(
coro_http_request & req, coro_http_response & resp)>
coro_handler;

std::tie(is_coro_exist, coro_handler, request_.params_) =
router_.get_coro_router_tree()->get_coro(url_path, method_str);

if (is_coro_exist) {
co_await (coro_handler)(request_, response_);
}
else {
bool is_matched_regex_router = false;
// coro regex router
auto coro_regex_handlers = router_.get_coro_regex_handlers();
if (coro_regex_handlers.size() != 0) {
for (auto &pair : coro_regex_handlers) {
std::string coro_regex_key;
coro_regex_key.assign(key.data(), key.size());

if (std::regex_match(coro_regex_key, request_.matches_,
std::get<0>(pair))) {
auto coro_handler = std::get<1>(pair);
co_await (coro_handler)(request_, response_);
is_matched_regex_router = true;
}
}
}
// regex router
if (!is_matched_regex_router) {
auto regex_handlers = router_.get_regex_handlers();
if (regex_handlers.size() != 0) {
for (auto &pair : regex_handlers) {
std::string regex_key;
regex_key.assign(key.data(), key.size());
if (std::regex_match(regex_key, request_.matches_,
std::get<0>(pair))) {
auto handler = std::get<1>(pair);
(handler)(request_, response_);
is_matched_regex_router = true;
}
}
}
}
// not found
if (!is_matched_regex_router)
response_.set_status(status_type::not_found);
}
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions include/cinatra/coro_http_request.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#pragma once

#include <charconv>
#include <regex>

#include "async_simple/coro/Lazy.h"
#include "define.h"
#include "http_parser.hpp"
#include "ws_define.h"

namespace cinatra {

inline std::vector<std::string_view> split_sv(std::string_view s,
std::string_view delimiter) {
size_t start = 0;
Expand Down Expand Up @@ -108,6 +111,7 @@ inline std::vector<std::pair<int, int>> parse_ranges(std::string_view range_str,
}
return vec;
}

class coro_http_connection;
class coro_http_request {
public:
Expand Down Expand Up @@ -221,6 +225,9 @@ class coro_http_request {
return true;
}

std::unordered_map<std::string, std::string> params_;
std::smatch matches_;

private:
http_parser& parser_;
std::string_view body_;
Expand Down
103 changes: 87 additions & 16 deletions include/cinatra/coro_http_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

#include "cinatra/cinatra_log_wrapper.hpp"
#include "cinatra/coro_http_request.hpp"
#include "cinatra/coro_radix_tree.hpp"
#include "cinatra/response_cv.hpp"
#include "cinatra/utils.hpp"
#include "coro_http_response.hpp"
#include "ylt/util/type_traits.h"

Expand Down Expand Up @@ -51,27 +53,69 @@ 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>) {
auto [it, ok] = coro_keys_.emplace(std::move(whole_str));
if (!ok) {
CINATRA_LOG_WARNING << key << " has already registered.";
return;
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_method_names);
}
coro_handles_.emplace(*it, std::move(handler));
if (!aspects.empty()) {
has_aspects_ = true;
aspects_.emplace(*it, std::move(aspects));
else {
if (whole_str.find("{") != std::string::npos ||
whole_str.find(")") != std::string::npos) {
std::string pattern = whole_str;

if (pattern.find("{}") != std::string::npos) {
replace_all(pattern, "{}", "([^/]+)");
}

coro_regex_handles_.emplace_back(std::regex(pattern),
std::move(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));
}
}
}
}
else {
auto [it, ok] = keys_.emplace(std::move(whole_str));
if (!ok) {
CINATRA_LOG_WARNING << key << " has already registered.";
return;
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(key, std::move(handler), method_names);
}
else if (whole_str.find("{") != std::string::npos ||
whole_str.find(")") != std::string::npos) {
std::string pattern = whole_str;

if (pattern.find("{}") != std::string::npos) {
replace_all(pattern, "{}", "([^/]+)");
}

regex_handles_.emplace_back(std::regex(pattern), std::move(handler));
}
map_handles_.emplace(*it, std::move(handler));
if (!aspects.empty()) {
has_aspects_ = true;
aspects_.emplace(*it, std::move(aspects));
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));
}
}
}
}
Expand Down Expand Up @@ -148,6 +192,16 @@ class coro_http_router {

const auto& get_coro_handlers() const { return coro_handles_; }

std::shared_ptr<radix_tree> get_router_tree() { return router_tree_; }

std::shared_ptr<radix_tree> get_coro_router_tree() {
return coro_router_tree_;
}

const auto& get_coro_regex_handlers() { return coro_regex_handles_; }

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) {
Expand Down Expand Up @@ -192,6 +246,23 @@ class coro_http_router {
std::function<async_simple::coro::Lazy<void>(
coro_http_request& req, coro_http_response& resp)>>
coro_handles_;

std::shared_ptr<radix_tree> router_tree_ =
std::make_shared<radix_tree>(radix_tree());

std::shared_ptr<radix_tree> coro_router_tree_ =
std::make_shared<radix_tree>(radix_tree());

std::vector<std::tuple<
std::regex,
std::function<void(coro_http_request& req, coro_http_response& resp)>>>
regex_handles_;

std::vector<std::tuple<
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_;
Expand Down
Loading

0 comments on commit 033f92d

Please sign in to comment.