aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/crow/app.h22
-rw-r--r--include/crow/routing.h294
-rw-r--r--include/crow/settings.h2
-rw-r--r--tests/unittest.cpp64
4 files changed, 229 insertions, 153 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/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/tests/unittest.cpp b/tests/unittest.cpp
index e21af39..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;
@@ -335,6 +336,9 @@ TEST(http_method)
return "purge";
});
+ app.validate();
+ app.debug_print();
+
// cannot have multiple handlers for the same url
//CROW_ROUTE(app, "/")
@@ -411,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;
{
@@ -432,7 +438,7 @@ TEST(server_handling_error_request)
//std::cerr << e.what() << std::endl;
}
}
- server.stop();
+ app.stop();
}
TEST(multi_server)
@@ -442,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;
@@ -474,8 +484,8 @@ TEST(multi_server)
ASSERT_EQUAL('B', buf[recved-1]);
}
- server1.stop();
- server2.stop();
+ app1.stop();
+ app2.stop();
}
TEST(json_read)
@@ -864,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;
{
@@ -908,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)
@@ -934,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;
{
@@ -953,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)
@@ -966,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;
{
@@ -991,7 +1007,7 @@ TEST(bug_quick_repeated_request)
}));
}
}
- server.stop();
+ app.stop();
}
TEST(simple_url_params)
@@ -1010,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;
@@ -1122,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)