From b7360a2bacb426401636a1e76e5e713ed0c3b0fc Mon Sep 17 00:00:00 2001 From: ipknHama Date: Mon, 25 Dec 2017 00:40:39 +0900 Subject: Support multiple methods for same URL - fix broken unittests because of server initializing order change - add a function to wait until server is up --- include/crow/app.h | 22 ++++ include/crow/routing.h | 294 +++++++++++++++++++++++++++--------------------- include/crow/settings.h | 2 + 3 files changed, 188 insertions(+), 130 deletions(-) (limited to 'include') diff --git a/include/crow/app.h b/include/crow/app.h index b454bf0..a380a79 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "crow/settings.h" #include "crow/logging.h" @@ -95,6 +96,13 @@ namespace crow router_.validate(); } + void notify_server_start() + { + std::unique_lock lock(start_mutex_); + server_started_ = true; + cv_started_.notify_all(); + } + void run() { validate(); @@ -103,6 +111,7 @@ namespace crow { ssl_server_ = std::move(std::unique_ptr(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_))); ssl_server_->set_tick_function(tick_interval_, tick_function_); + notify_server_start(); ssl_server_->run(); } else @@ -110,6 +119,7 @@ namespace crow { server_ = std::move(std::unique_ptr(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr))); server_->set_tick_function(tick_interval_, tick_function_); + notify_server_start(); server_->run(); } } @@ -226,6 +236,14 @@ namespace crow return *this; } + void wait_for_server_start() + { + std::unique_lock lock(start_mutex_); + if (server_started_) + return; + cv_started_.wait(lock); + } + private: uint16_t port_ = 80; uint16_t concurrency_ = 1; @@ -241,6 +259,10 @@ namespace crow std::unique_ptr ssl_server_; #endif std::unique_ptr server_; + + bool server_started_{false}; + std::condition_variable cv_started_; + std::mutex start_mutex_; }; template using App = Crow; diff --git a/include/crow/routing.h b/include/crow/routing.h index cdfa480..83c43e8 100644 --- a/include/crow/routing.h +++ b/include/crow/routing.h @@ -31,24 +31,24 @@ namespace crow virtual void validate() = 0; std::unique_ptr upgrade() - { - if (rule_to_upgrade_) - return std::move(rule_to_upgrade_); - return {}; - } + { + if (rule_to_upgrade_) + return std::move(rule_to_upgrade_); + return {}; + } virtual void handle(const request&, response&, const routing_params&) = 0; virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&) - { - res = response(404); - res.end(); - } + { + res = response(404); + res.end(); + } #ifdef CROW_ENABLE_SSL virtual void handle_upgrade(const request&, response& res, SSLAdaptor&&) - { - res = response(404); - res.end(); - } + { + res = response(404); + res.end(); + } #endif uint32_t get_methods() @@ -56,13 +56,25 @@ namespace crow return methods_; } + template + void foreach_method(F f) + { + for(uint32_t method = 0, method_bit = 1; method < (uint32_t)HTTPMethod::InternalMethodCount; method++, method_bit<<=1) + { + if (methods_ & method_bit) + f(method); + } + } + + const std::string& rule() { return rule_; } + protected: uint32_t methods_{1<<(int)HTTPMethod::Get}; std::string rule_; std::string name_; - std::unique_ptr rule_to_upgrade_; + std::unique_ptr rule_to_upgrade_; friend class Router; template @@ -255,89 +267,89 @@ namespace crow } } - class WebSocketRule : public BaseRule - { + class WebSocketRule : public BaseRule + { using self_t = WebSocketRule; - public: + public: WebSocketRule(std::string rule) : BaseRule(std::move(rule)) { } - void validate() override - { - } + void validate() override + { + } - void handle(const request&, response& res, const routing_params&) override - { - res = response(404); - res.end(); - } + void handle(const request&, response& res, const routing_params&) override + { + res = response(404); + res.end(); + } void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override - { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); - } + { + new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); + } #ifdef CROW_ENABLE_SSL void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override - { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); - } + { + new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); + } #endif - template - self_t& onopen(Func f) - { - open_handler_ = f; - return *this; - } - - template - self_t& onmessage(Func f) - { - message_handler_ = f; - return *this; - } - - template - self_t& onclose(Func f) - { - close_handler_ = f; - return *this; - } - - template - self_t& onerror(Func f) - { - error_handler_ = f; - return *this; - } - - template - self_t& onaccept(Func f) - { - accept_handler_ = f; - return *this; - } - - protected: - std::function open_handler_; - std::function message_handler_; - std::function close_handler_; - std::function error_handler_; - std::function accept_handler_; - }; + template + self_t& onopen(Func f) + { + open_handler_ = f; + return *this; + } + + template + self_t& onmessage(Func f) + { + message_handler_ = f; + return *this; + } + + template + self_t& onclose(Func f) + { + close_handler_ = f; + return *this; + } + + template + self_t& onerror(Func f) + { + error_handler_ = f; + return *this; + } + + template + self_t& onaccept(Func f) + { + accept_handler_ = f; + return *this; + } + + protected: + std::function open_handler_; + std::function message_handler_; + std::function close_handler_; + std::function error_handler_; + std::function accept_handler_; + }; template struct RuleParameterTraits { using self_t = T; - WebSocketRule& websocket() - { - auto p =new WebSocketRule(((self_t*)this)->rule_); + WebSocketRule& websocket() + { + auto p =new WebSocketRule(((self_t*)this)->rule_); ((self_t*)this)->rule_to_upgrade_.reset(p); - return *p; - } + return *p; + } self_t& name(std::string name) noexcept { @@ -879,15 +891,14 @@ public: class Router { public: - Router() : rules_(2) + Router() { } DynamicRule& new_rule_dynamic(const std::string& rule) { auto ruleObject = new DynamicRule(rule); - - internal_add_rule_object(rule, ruleObject); + all_rules_.emplace_back(ruleObject); return *ruleObject; } @@ -896,57 +907,78 @@ public: typename black_magic::arguments::type::template rebind& new_rule_tagged(const std::string& rule) { using RuleT = typename black_magic::arguments::type::template rebind; - auto ruleObject = new RuleT(rule); - internal_add_rule_object(rule, ruleObject); + auto ruleObject = new RuleT(rule); + all_rules_.emplace_back(ruleObject); return *ruleObject; } void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject) { - rules_.emplace_back(ruleObject); - trie_.add(rule, rules_.size() - 1); - - // directory case: - // request to `/about' url matches `/about/' rule + bool has_trailing_slash = false; + std::string rule_without_trailing_slash; if (rule.size() > 1 && rule.back() == '/') { - std::string rule_without_trailing_slash = rule; + has_trailing_slash = true; + rule_without_trailing_slash = rule; rule_without_trailing_slash.pop_back(); - trie_.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH); } + + ruleObject->foreach_method([&](int method) + { + per_methods_[method].rules.emplace_back(ruleObject); + per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1); + + // directory case: + // request to `/about' url matches `/about/' rule + if (has_trailing_slash) + { + per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH); + } + }); + } void validate() { - trie_.validate(); - for(auto& rule:rules_) + for(auto& rule:all_rules_) { if (rule) - { - auto upgraded = rule->upgrade(); - if (upgraded) - rule = std::move(upgraded); + { + auto upgraded = rule->upgrade(); + if (upgraded) + rule = std::move(upgraded); rule->validate(); - } + internal_add_rule_object(rule->rule(), rule.get()); + } + } + for(auto& per_method:per_methods_) + { + per_method.trie.validate(); } } - template - void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) - { - auto found = trie_.find(req.url); + template + void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) + { + if (req.method >= HTTPMethod::InternalMethodCount) + return; + auto& per_method = per_methods_[(int)req.method]; + auto& trie = per_method.trie; + auto& rules = per_method.rules; + + auto found = trie.find(req.url); unsigned rule_index = found.first; if (!rule_index) { - CROW_LOG_DEBUG << "Cannot match rules " << req.url; + CROW_LOG_DEBUG << "Cannot match rules " << req.url << ' ' << method_name(req.method); res = response(404); res.end(); return; } - if (rule_index >= rules_.size()) + if (rule_index >= rules.size()) throw std::runtime_error("Trie internal structure corrupted!"); if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) @@ -967,20 +999,12 @@ public: return; } - if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0) - { - CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods(); - res = response(404); - res.end(); - return; - } - - CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods(); + CROW_LOG_DEBUG << "Matched rule (upgrade) '" << rules[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules[rule_index]->get_methods(); // any uncaught exceptions become 500s try { - rules_[rule_index]->handle_upgrade(req, res, std::move(adaptor)); + rules[rule_index]->handle_upgrade(req, res, std::move(adaptor)); } catch(std::exception& e) { @@ -996,23 +1020,29 @@ public: res.end(); return; } - } + } void handle(const request& req, response& res) { - auto found = trie_.find(req.url); + if (req.method >= HTTPMethod::InternalMethodCount) + return; + auto& per_method = per_methods_[(int)req.method]; + auto& trie = per_method.trie; + auto& rules = per_method.rules; + + auto found = trie.find(req.url); unsigned rule_index = found.first; if (!rule_index) { - CROW_LOG_DEBUG << "Cannot match rules " << req.url; + CROW_LOG_DEBUG << "Cannot match rules " << req.url << ' ' << method_name(req.method); res = response(404); res.end(); return; } - if (rule_index >= rules_.size()) + if (rule_index >= rules.size()) throw std::runtime_error("Trie internal structure corrupted!"); if (rule_index == RULE_SPECIAL_REDIRECT_SLASH) @@ -1033,20 +1063,12 @@ public: return; } - if ((rules_[rule_index]->get_methods() & (1<<(uint32_t)req.method)) == 0) - { - CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->get_methods(); - res = response(404); - res.end(); - return; - } - - CROW_LOG_DEBUG << "Matched rule '" << rules_[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules_[rule_index]->get_methods(); + CROW_LOG_DEBUG << "Matched rule '" << rules[rule_index]->rule_ << "' " << (uint32_t)req.method << " / " << rules[rule_index]->get_methods(); // any uncaught exceptions become 500s try { - rules_[rule_index]->handle(req, res, found.second); + rules[rule_index]->handle(req, res, found.second); } catch(std::exception& e) { @@ -1066,11 +1088,23 @@ public: void debug_print() { - trie_.debug_print(); + for(int i = 0; i < (int)HTTPMethod::InternalMethodCount; i ++) + { + CROW_LOG_DEBUG << method_name((HTTPMethod)i); + per_methods_[i].trie.debug_print(); + } } private: - std::vector> rules_; - Trie trie_; + struct PerMethod + { + std::vector rules; + Trie trie; + + // rule index 0, 1 has special meaning; preallocate it to avoid duplication. + PerMethod() : rules(2) {} + }; + std::array per_methods_; + std::vector> all_rules_; }; } diff --git a/include/crow/settings.h b/include/crow/settings.h index bd6259c..5958e35 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -21,7 +21,9 @@ default to INFO */ +#ifndef CROW_LOG_LEVEL #define CROW_LOG_LEVEL 1 +#endif // compiler flags -- cgit v1.2.3-54-g00ecf