diff options
-rw-r--r-- | include/crow/app.h | 22 | ||||
-rw-r--r-- | include/crow/common.h | 15 | ||||
-rw-r--r-- | include/crow/http_parser_merged.h | 38 | ||||
-rw-r--r-- | include/crow/json.h | 135 | ||||
-rw-r--r-- | include/crow/routing.h | 294 | ||||
-rw-r--r-- | include/crow/settings.h | 2 | ||||
-rw-r--r-- | include/crow/socket_adaptors.h | 6 | ||||
-rw-r--r-- | include/crow/websocket.h | 9 | ||||
-rw-r--r-- | tests/unittest.cpp | 113 |
9 files changed, 429 insertions, 205 deletions
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 <cstdint> #include <type_traits> #include <thread> +#include <condition_variable> #include "crow/settings.h" #include "crow/logging.h" @@ -95,6 +96,13 @@ namespace crow router_.validate(); } + void notify_server_start() + { + std::unique_lock<std::mutex> 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<ssl_server_t>(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<server_t>(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<std::mutex> 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_t> ssl_server_; #endif std::unique_ptr<server_t> server_; + + bool server_started_{false}; + std::condition_variable cv_started_; + std::mutex start_mutex_; }; template <typename ... Middlewares> using App = Crow<Middlewares...>; diff --git a/include/crow/common.h b/include/crow/common.h index ac6e789..f38a3c7 100644 --- a/include/crow/common.h +++ b/include/crow/common.h @@ -19,7 +19,8 @@ namespace crow CONNECT, OPTIONS, TRACE, - PATCH = 24, + PATCH, + PURGE, #endif Delete = 0, @@ -30,7 +31,12 @@ namespace crow Connect, Options, Trace, - Patch = 24, + Patch, + Purge, + + + InternalMethodCount, + // should not add an item below this line: used for array count }; inline std::string method_name(HTTPMethod method) @@ -55,6 +61,10 @@ namespace crow return "TRACE"; case HTTPMethod::Patch: return "PATCH"; + case HTTPMethod::Purge: + return "PURGE"; + default: + return "invalid"; } return "invalid"; } @@ -137,6 +147,7 @@ constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch : + crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge : throw std::runtime_error("invalid http method"); } #endif diff --git a/include/crow/http_parser_merged.h b/include/crow/http_parser_merged.h index 2a10252..cf89b05 100644 --- a/include/crow/http_parser_merged.h +++ b/include/crow/http_parser_merged.h @@ -96,28 +96,28 @@ typedef int (*http_cb) (http_parser*); CROW_XX(5, CONNECT, CONNECT) \ CROW_XX(6, OPTIONS, OPTIONS) \ CROW_XX(7, TRACE, TRACE) \ + /* RFC-5789 */ \ + CROW_XX(8, PATCH, PATCH) \ + CROW_XX(9, PURGE, PURGE) \ /* webdav */ \ - CROW_XX(8, COPY, COPY) \ - CROW_XX(9, LOCK, LOCK) \ - CROW_XX(10, MKCOL, MKCOL) \ - CROW_XX(11, MOVE, MOVE) \ - CROW_XX(12, PROPFIND, PROPFIND) \ - CROW_XX(13, PROPPATCH, PROPPATCH) \ - CROW_XX(14, SEARCH, SEARCH) \ - CROW_XX(15, UNLOCK, UNLOCK) \ + CROW_XX(10, COPY, COPY) \ + CROW_XX(11, LOCK, LOCK) \ + CROW_XX(12, MKCOL, MKCOL) \ + CROW_XX(13, MOVE, MOVE) \ + CROW_XX(14, PROPFIND, PROPFIND) \ + CROW_XX(15, PROPPATCH, PROPPATCH) \ + CROW_XX(16, SEARCH, SEARCH) \ + CROW_XX(17, UNLOCK, UNLOCK) \ /* subversion */ \ - CROW_XX(16, REPORT, REPORT) \ - CROW_XX(17, MKACTIVITY, MKACTIVITY) \ - CROW_XX(18, CHECKOUT, CHECKOUT) \ - CROW_XX(19, MERGE, MERGE) \ + CROW_XX(18, REPORT, REPORT) \ + CROW_XX(19, MKACTIVITY, MKACTIVITY) \ + CROW_XX(20, CHECKOUT, CHECKOUT) \ + CROW_XX(21, MERGE, MERGE) \ /* upnp */ \ - CROW_XX(20, MSEARCH, M-SEARCH) \ - CROW_XX(21, NOTIFY, NOTIFY) \ - CROW_XX(22, SUBSCRIBE, SUBSCRIBE) \ - CROW_XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ - /* RFC-5789 */ \ - CROW_XX(24, PATCH, PATCH) \ - CROW_XX(25, PURGE, PURGE) \ + CROW_XX(22, MSEARCH, M-SEARCH) \ + CROW_XX(23, NOTIFY, NOTIFY) \ + CROW_XX(24, SUBSCRIBE, SUBSCRIBE) \ + CROW_XX(25, UNSUBSCRIBE, UNSUBSCRIBE) \ /* CalDAV */ \ CROW_XX(26, MKCALENDAR, MKCALENDAR) \ diff --git a/include/crow/json.h b/include/crow/json.h index d8dbace..05f68b9 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -96,6 +96,13 @@ namespace crow } } + enum class num_type : char { + Signed_integer, + Unsigned_integer, + Floating_point, + Null + }; + class rvalue; rvalue load(const char* data, size_t size); @@ -168,9 +175,10 @@ namespace crow return os; } private: - void force(char* s, uint32_t /*length*/) + void force(char* s, uint32_t length) { s_ = s; + e_ = s_ + length; owned_ = 1; } friend rvalue crow::json::load(const char* data, size_t size); @@ -216,13 +224,16 @@ namespace crow : start_{s}, end_{e}, t_{t} - {} + { + determine_num_type(); + } rvalue(const rvalue& r) : start_(r.start_), end_(r.end_), key_(r.key_), t_(r.t_), + nt_(r.nt_), option_(r.option_) { copy_l(r); @@ -238,9 +249,10 @@ namespace crow start_ = r.start_; end_ = r.end_; key_ = r.key_; - copy_l(r); t_ = r.t_; + nt_ = r.nt_; option_ = r.option_; + copy_l(r); return *this; } rvalue& operator = (rvalue&& r) noexcept @@ -252,6 +264,7 @@ namespace crow lsize_ = r.lsize_; lremain_ = r.lremain_; t_ = r.t_; + nt_ = r.nt_; option_ = r.option_; return *this; } @@ -287,6 +300,17 @@ namespace crow return t_; } + num_type nt() const + { +#ifndef CROW_JSON_NO_ERROR_CHECK + if (option_ & error_bit) + { + throw std::runtime_error("invalid json object"); + } +#endif + return nt_; + } + int64_t i() const { #ifndef CROW_JSON_NO_ERROR_CHECK @@ -591,6 +615,28 @@ namespace crow lremain_ --; } + // determines num_type from the string + void determine_num_type() + { + if (t_ != type::Number) + { + nt_ = num_type::Null; + return; + } + + const std::size_t len = end_ - start_; + const bool has_minus = std::memchr(start_, '-', len) != nullptr; + const bool has_e = std::memchr(start_, 'e', len) != nullptr + || std::memchr(start_, 'E', len) != nullptr; + const bool has_dec_sep = std::memchr(start_, '.', len) != nullptr; + if (has_dec_sep || has_e) + nt_ = num_type::Floating_point; + else if (has_minus) + nt_ = num_type::Signed_integer; + else + nt_ = num_type::Unsigned_integer; + } + mutable char* start_; mutable char* end_; detail::r_string key_; @@ -598,6 +644,7 @@ namespace crow uint32_t lsize_; uint16_t lremain_; type t_; + num_type nt_{num_type::Null}; mutable uint8_t option_{0}; friend rvalue load_nocopy_internal(char* data, size_t size); @@ -610,7 +657,17 @@ namespace crow case type::Null: os << "null"; break; case type::False: os << "false"; break; case type::True: os << "true"; break; - case type::Number: os << r.d(); break; + case type::Number: + { + switch (r.nt()) + { + case num_type::Floating_point: os << r.d(); break; + case num_type::Signed_integer: os << r.i(); break; + case num_type::Unsigned_integer: os << r.u(); break; + case num_type::Null: throw std::runtime_error("Number with num_type Null"); + } + } + break; case type::String: os << '"' << r.s() << '"'; break; case type::List: { @@ -1092,7 +1149,12 @@ namespace crow type t() const { return t_; } private: type t_{type::Null}; - double d {}; + num_type nt{num_type::Null}; + union { + double d; + int64_t si; + uint64_t ui {}; + } num; std::string s; std::unique_ptr<std::vector<wvalue>> l; std::unique_ptr<std::unordered_map<std::string, wvalue>> o; @@ -1110,7 +1172,13 @@ namespace crow case type::True: return; case type::Number: - d = r.d(); + nt = r.nt(); + if (nt == num_type::Floating_point) + num.d = r.d(); + else if (nt == num_type::Signed_integer) + num.si = r.i(); + else + num.ui = r.u(); return; case type::String: s = r.s(); @@ -1140,7 +1208,7 @@ namespace crow wvalue& operator = (wvalue&& r) { t_ = r.t_; - d = r.d; + num = r.num; s = std::move(r.s); l = std::move(r.l); o = std::move(r.o); @@ -1149,9 +1217,7 @@ namespace crow void clear() { - t_ = type::Null; - l.reset(); - o.reset(); + reset(); } void reset() @@ -1180,7 +1246,8 @@ namespace crow { reset(); t_ = type::Number; - d = value; + num.d = value; + nt = num_type::Floating_point; return *this; } @@ -1188,7 +1255,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1196,7 +1264,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1204,7 +1273,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1212,7 +1282,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1220,7 +1291,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.si = value; + nt = num_type::Signed_integer; return *this; } @@ -1228,7 +1300,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1236,7 +1309,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1244,7 +1318,8 @@ namespace crow { reset(); t_ = type::Number; - d = (double)value; + num.ui = value; + nt = num_type::Unsigned_integer; return *this; } @@ -1404,14 +1479,26 @@ namespace crow case type::True: out += "true"; break; case type::Number: { - char outbuf[128]; + if (v.nt == num_type::Floating_point) + { #ifdef _MSC_VER - sprintf_s(outbuf, 128, "%g", v.d); +#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf_s((BUFFER_PTR), 128, (FORMAT_PTR), (VALUE)) #else - sprintf(outbuf, "%g", v.d); +#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf((BUFFER_PTR), (FORMAT_PTR), (VALUE)) #endif - - out += outbuf; + char outbuf[128]; + MSC_COMPATIBLE_SPRINTF(outbuf, "%g", v.num.d); + out += outbuf; +#undef MSC_COMPATIBLE_SPRINTF + } + else if (v.nt == num_type::Signed_integer) + { + out += std::to_string(v.num.si); + } + else + { + out += std::to_string(v.num.ui); + } } break; case type::String: dump_string(v.s, out); break; 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<BaseRule> 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 <typename F> + 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<BaseRule> rule_to_upgrade_; + std::unique_ptr<BaseRule> rule_to_upgrade_; friend class Router; template <typename T> @@ -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<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); - } + { + new crow::websocket::Connection<SocketAdaptor>(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<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); - } + { + new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); + } #endif - template <typename Func> - self_t& onopen(Func f) - { - open_handler_ = f; - return *this; - } - - template <typename Func> - self_t& onmessage(Func f) - { - message_handler_ = f; - return *this; - } - - template <typename Func> - self_t& onclose(Func f) - { - close_handler_ = f; - return *this; - } - - template <typename Func> - self_t& onerror(Func f) - { - error_handler_ = f; - return *this; - } - - template <typename Func> - self_t& onaccept(Func f) - { - accept_handler_ = f; - return *this; - } - - protected: - std::function<void(crow::websocket::connection&)> open_handler_; - std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; - std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; - std::function<void(crow::websocket::connection&)> error_handler_; - std::function<bool(const crow::request&)> accept_handler_; - }; + template <typename Func> + self_t& onopen(Func f) + { + open_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onmessage(Func f) + { + message_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onclose(Func f) + { + close_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onerror(Func f) + { + error_handler_ = f; + return *this; + } + + template <typename Func> + self_t& onaccept(Func f) + { + accept_handler_ = f; + return *this; + } + + protected: + std::function<void(crow::websocket::connection&)> open_handler_; + std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; + std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; + std::function<void(crow::websocket::connection&)> error_handler_; + std::function<bool(const crow::request&)> accept_handler_; + }; template <typename T> 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<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule) { using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>; - 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 <typename Adaptor> - void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) - { - auto found = trie_.find(req.url); + template <typename Adaptor> + 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<std::unique_ptr<BaseRule>> rules_; - Trie trie_; + struct PerMethod + { + std::vector<BaseRule*> rules; + Trie trie; + + // rule index 0, 1 has special meaning; preallocate it to avoid duplication. + PerMethod() : rules(2) {} + }; + std::array<PerMethod, (int)HTTPMethod::InternalMethodCount> per_methods_; + std::vector<std::unique_ptr<BaseRule>> 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 diff --git a/include/crow/socket_adaptors.h b/include/crow/socket_adaptors.h index eebd50f..901117f 100644 --- a/include/crow/socket_adaptors.h +++ b/include/crow/socket_adaptors.h @@ -44,7 +44,8 @@ namespace crow void close() { - socket_.close(); + boost::system::error_code ec; + socket_.close(ec); } template <typename F> @@ -89,7 +90,8 @@ namespace crow void close() { - raw_socket().close(); + boost::system::error_code ec; + raw_socket().close(ec); } boost::asio::io_service& get_io_service() diff --git a/include/crow/websocket.h b/include/crow/websocket.h index ee0885f..c2dc3f8 100644 --- a/include/crow/websocket.h +++ b/include/crow/websocket.h @@ -152,7 +152,7 @@ namespace crow else { buf[1] += 127; - *(uint64_t*)(buf+2) = ((1==htonl(1)) ? (uint64_t)size : ((uint64_t)htonl((size) & 0xFFFFFFFF) << 32) | htonl((size) >> 32)); + *reinterpret_cast<uint64_t*>(buf+2) = ((1==htonl(1)) ? static_cast<uint64_t>(size) : (static_cast<uint64_t>(htonl((size) & 0xFFFFFFFF)) << 32) | htonl(static_cast<uint64_t>(size) >> 32)); return {buf, buf+10}; } } @@ -191,7 +191,7 @@ namespace crow { is_reading = false; - mini_header_ = htons(mini_header_); + mini_header_ = ntohs(mini_header_); #ifdef CROW_ENABLE_DEBUG if (!ec && bytes_transferred != 2) @@ -231,9 +231,9 @@ namespace crow case WebSocketReadState::Len16: { remaining_length_ = 0; - uint16_t remaining_length16_ = 0; + remaining_length16_ = 0; boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2), - [this,&remaining_length16_](const boost::system::error_code& ec, std::size_t + [this](const boost::system::error_code& ec, std::size_t #ifdef CROW_ENABLE_DEBUG bytes_transferred #endif @@ -502,6 +502,7 @@ namespace crow std::string message_; std::string fragment_; WebSocketReadState state_{WebSocketReadState::MiniHeader}; + uint16_t remaining_length16_{0}; uint64_t remaining_length_{0}; bool close_connection_{false}; bool is_reading{false}; diff --git a/tests/unittest.cpp b/tests/unittest.cpp index bffdd60..7b0a349 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -1,11 +1,10 @@ //#define CROW_ENABLE_LOGGING +#define CROW_LOG_LEVEL 0 #define CROW_ENABLE_DEBUG #include <iostream> #include <sstream> #include <vector> #include "crow.h" -#undef CROW_LOG_LEVEL -#define CROW_LOG_LEVEL 0 using namespace std; using namespace crow; @@ -128,6 +127,8 @@ TEST(PathRouting) return "path"; }); + app.validate(); + { request req; response res; @@ -329,6 +330,14 @@ TEST(http_method) ([](const request& /*req*/){ return "patch"; }); + CROW_ROUTE(app, "/purge_only") + .methods("PURGE"_method) + ([](const request& /*req*/){ + return "purge"; + }); + + app.validate(); + app.debug_print(); // cannot have multiple handlers for the same url @@ -381,6 +390,17 @@ TEST(http_method) request req; response res; + req.url = "/purge_only"; + req.method = "PURGE"_method; + app.handle(req, res); + + ASSERT_EQUAL("purge", res.body); + } + + { + request req; + response res; + req.url = "/get_only"; req.method = "POST"_method; app.handle(req, res); @@ -395,8 +415,10 @@ TEST(server_handling_error_request) static char buf[2048]; SimpleApp app; CROW_ROUTE(app, "/")([]{return "A";}); - Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&]{server.run();}); + //Server<SimpleApp> server(&app, LOCALHOST_ADDRESS, 45451); + //auto _ = async(launch::async, [&]{server.run();}); + auto _ = async(launch::async, [&]{app.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + app.wait_for_server_start(); std::string sendmsg = "POX"; asio::io_service is; { @@ -416,7 +438,7 @@ TEST(server_handling_error_request) //std::cerr << e.what() << std::endl; } } - server.stop(); + app.stop(); } TEST(multi_server) @@ -426,11 +448,15 @@ TEST(multi_server) CROW_ROUTE(app1, "/").methods("GET"_method, "POST"_method)([]{return "A";}); CROW_ROUTE(app2, "/").methods("GET"_method, "POST"_method)([]{return "B";}); - Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451); - Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452); + //Server<SimpleApp> server1(&app1, LOCALHOST_ADDRESS, 45451); + //Server<SimpleApp> server2(&app2, LOCALHOST_ADDRESS, 45452); - auto _ = async(launch::async, [&]{server1.run();}); - auto _2 = async(launch::async, [&]{server2.run();}); + //auto _ = async(launch::async, [&]{server1.run();}); + //auto _2 = async(launch::async, [&]{server2.run();}); + auto _ = async(launch::async, [&]{app1.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + auto _2 = async(launch::async, [&]{app2.bindaddr(LOCALHOST_ADDRESS).port(45452).run();}); + app1.wait_for_server_start(); + app2.wait_for_server_start(); std::string sendmsg = "POST /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n"; asio::io_service is; @@ -458,8 +484,8 @@ TEST(multi_server) ASSERT_EQUAL('B', buf[recved-1]); } - server1.stop(); - server2.stop(); + app1.stop(); + app2.stop(); } TEST(json_read) @@ -501,7 +527,7 @@ TEST(json_read) //ASSERT_THROW(3 == x["message"]); ASSERT_EQUAL(12, x["message"].size()); - std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })"; + std::string s = R"({"int":3, "ints" :[1,2,3,4,5], "bigint":1234567890 })"; auto y = json::load(s); ASSERT_EQUAL(3, y["int"]); ASSERT_EQUAL(3.0, y["int"]); @@ -519,6 +545,7 @@ TEST(json_read) ASSERT_EQUAL(2, q); q = y["ints"][2].i(); ASSERT_EQUAL(3, q); + ASSERT_EQUAL(1234567890, y["bigint"]); std::string s2 = R"({"bools":[true, false], "doubles":[1.2, -3.4]})"; auto z = json::load(s2); @@ -532,6 +559,10 @@ TEST(json_read) std::string s3 = R"({"uint64": 18446744073709551615})"; auto z1 = json::load(s3); ASSERT_EQUAL(18446744073709551615ull, z1["uint64"].u()); + + std::ostringstream os; + os << z1["uint64"]; + ASSERT_EQUAL("18446744073709551615", os.str()); } TEST(json_read_real) @@ -596,6 +627,8 @@ TEST(json_write) ASSERT_TRUE(R"({"message":{"x":3,"y":5}})" == json::dump(x) || R"({"message":{"y":5,"x":3}})" == json::dump(x)); x["message"] = 5.5; ASSERT_EQUAL(R"({"message":5.5})", json::dump(x)); + x["message"] = 1234567890; + ASSERT_EQUAL(R"({"message":1234567890})", json::dump(x)); json::wvalue y; y["scores"][0] = 1; @@ -616,6 +649,30 @@ TEST(json_write) } +TEST(json_copy_r_to_w_to_r) +{ + json::rvalue r = json::load(R"({"smallint":2,"bigint":2147483647,"fp":23.43,"fpsc":2.343e1,"str":"a string","trueval":true,"falseval":false,"nullval":null,"listval":[1,2,"foo","bar"],"obj":{"member":23,"other":"baz"}})"); + json::wvalue w{r}; + json::rvalue x = json::load(json::dump(w)); // why no copy-ctor wvalue -> rvalue? + ASSERT_EQUAL(2, x["smallint"]); + ASSERT_EQUAL(2147483647, x["bigint"]); + ASSERT_EQUAL(23.43, x["fp"]); + ASSERT_EQUAL(23.43, x["fpsc"]); + ASSERT_EQUAL("a string", x["str"]); + ASSERT_TRUE(true == x["trueval"].b()); + ASSERT_TRUE(false == x["falseval"].b()); + ASSERT_TRUE(json::type::Null == x["nullval"].t()); + ASSERT_EQUAL(4u, x["listval"].size()); + ASSERT_EQUAL(1, x["listval"][0]); + ASSERT_EQUAL(2, x["listval"][1]); + ASSERT_EQUAL("foo", x["listval"][2]); + ASSERT_EQUAL("bar", x["listval"][3]); + ASSERT_EQUAL(23, x["obj"]["member"]); + ASSERT_EQUAL("member", x["obj"]["member"].key()); + ASSERT_EQUAL("baz", x["obj"]["other"]); + ASSERT_EQUAL("other", x["obj"]["other"].key()); +} + TEST(template_basic) { auto t = crow::mustache::compile(R"---(attack of {{name}})---"); @@ -817,8 +874,10 @@ TEST(middleware_context) return ""; }); - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&]{server.run();}); + //decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + //auto _ = async(launch::async, [&]{server.run();}); + auto _ = async(launch::async, [&]{app.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + app.wait_for_server_start(); std::string sendmsg = "GET /\r\n\r\n"; asio::io_service is; { @@ -861,7 +920,7 @@ TEST(middleware_context) ASSERT_EQUAL("2 after", out[2]); ASSERT_EQUAL("1 after", out[3]); } - server.stop(); + app.stop(); } TEST(middleware_cookieparser) @@ -887,8 +946,10 @@ TEST(middleware_cookieparser) return ""; }); - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&]{server.run();}); + //decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + //auto _ = async(launch::async, [&]{server.run();}); + auto _ = async(launch::async, [&]{app.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + app.wait_for_server_start(); std::string sendmsg = "GET /\r\nCookie: key1=value1; key2=\"val=ue2\"; key3=\"val\"ue3\"; key4=\"val\"ue4\"\r\n\r\n"; asio::io_service is; { @@ -906,7 +967,7 @@ TEST(middleware_cookieparser) ASSERT_EQUAL("val\"ue3", value3); ASSERT_EQUAL("val\"ue4", value4); } - server.stop(); + app.stop(); } TEST(bug_quick_repeated_request) @@ -919,8 +980,10 @@ TEST(bug_quick_repeated_request) return "hello"; }); - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&]{server.run();}); + //decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + //auto _ = async(launch::async, [&]{server.run();}); + auto _ = async(launch::async, [&]{app.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + app.wait_for_server_start(); std::string sendmsg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; asio::io_service is; { @@ -944,7 +1007,7 @@ TEST(bug_quick_repeated_request) })); } } - server.stop(); + app.stop(); } TEST(simple_url_params) @@ -963,8 +1026,10 @@ TEST(simple_url_params) ///params?h=1&foo=bar&lol&count[]=1&count[]=4&pew=5.2 - decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); - auto _ = async(launch::async, [&]{server.run();}); + //decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451); + //auto _ = async(launch::async, [&]{server.run();}); + auto _ = async(launch::async, [&]{app.bindaddr(LOCALHOST_ADDRESS).port(45451).run();}); + app.wait_for_server_start(); asio::io_service is; std::string sendmsg; @@ -1075,7 +1140,7 @@ TEST(simple_url_params) ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[1]), "donatello"); ASSERT_EQUAL(string(last_url_params.get_list("tmnt")[2]), "raphael"); } - server.stop(); + app.stop(); } TEST(route_dynamic) |