aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/crow/app.h22
-rw-r--r--include/crow/common.h15
-rw-r--r--include/crow/http_parser_merged.h38
-rw-r--r--include/crow/json.h135
-rw-r--r--include/crow/routing.h294
-rw-r--r--include/crow/settings.h2
-rw-r--r--include/crow/socket_adaptors.h6
-rw-r--r--include/crow/websocket.h9
-rw-r--r--tests/unittest.cpp113
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)