Skip to content

Commit

Permalink
Merge pull request #473 from helintongh/feat_restful_router_add
Browse files Browse the repository at this point in the history
feat: add radix tree router and regex router
  • Loading branch information
helintongh authored Jan 2, 2024
2 parents 2e132a3 + 5ddab2b commit 2432847
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 12 deletions.
54 changes: 52 additions & 2 deletions include/cinatra/coro_http_connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,58 @@ class coro_http_connection
co_await router_.route_coro(coro_handler, request_, response_);
}
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;
params_t params = {};
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_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
13 changes: 13 additions & 0 deletions include/cinatra/coro_http_request.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
#pragma once
#include <regex>

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

namespace cinatra {

typedef std::pair<std::string, std::string> paramters_t;

struct params_t {
std::vector<paramters_t> parameters;
int size;
};

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

params_t params_;
std::smatch matches_;

private:
http_parser& parser_;
std::string_view body_;
Expand Down
77 changes: 67 additions & 10 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 All @@ -28,6 +30,7 @@ constexpr inline bool is_lazy_v =

class coro_http_router {
public:
~coro_http_router() { delete router_tree_; }
// eg: "GET hello/" as a key
template <http_method method, typename Func>
void set_http_handler(std::string key, Func handler) {
Expand All @@ -39,20 +42,56 @@ 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 ||
whole_str.find(")") != std::string::npos) {
std::string pattern = whole_str;
std::unordered_map<std::string, int> params;
params.clear();

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));
}
coro_handles_.emplace(*it, std::move(handler));
}
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;
std::unordered_map<std::string, int> params;
params.clear();

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

regex_handles_.emplace_back(std::regex(pattern), std::move(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));
}
map_handles_.emplace(*it, std::move(handler));
}
}

Expand Down Expand Up @@ -104,6 +143,12 @@ class coro_http_router {

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

radix_tree* get_router_tree() { return router_tree_; }

const auto& get_coro_regex_handlers() { return coro_regex_handles_; }

const auto& get_regex_handlers() { return regex_handles_; }

private:
std::set<std::string> keys_;
std::unordered_map<
Expand All @@ -116,5 +161,17 @@ class coro_http_router {
std::function<async_simple::coro::Lazy<void>(
coro_http_request& req, coro_http_response& resp)>>
coro_handles_;

radix_tree* router_tree_ = new 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_;
};
} // namespace cinatra
Loading

0 comments on commit 2432847

Please sign in to comment.