Commit 763d588e authored by Nico Caprioli's avatar Nico Caprioli

Master merge and tests

* Manually merged with master
* Fixed bugs
* invokeHandler modified in findRoute
* router_tests
parent 01eb1fbf
...@@ -68,9 +68,7 @@ struct Route { ...@@ -68,9 +68,7 @@ struct Route {
enum class Status { Match, NotFound }; enum class Status { Match, NotFound };
enum class Result { enum class Result { Ok, Failure };
Ok, Failure
};
typedef std::function<Result(const Request, Http::ResponseWriter)> Handler; typedef std::function<Result(const Request, Http::ResponseWriter)> Handler;
...@@ -84,8 +82,6 @@ struct Route { ...@@ -84,8 +82,6 @@ struct Route {
Handler handler_; Handler handler_;
}; };
namespace Private { namespace Private {
class RouterHandler; class RouterHandler;
} }
...@@ -112,8 +108,8 @@ private: ...@@ -112,8 +108,8 @@ private:
static FragmentType getFragmentType(const std::string_view &fragment); static FragmentType getFragmentType(const std::string_view &fragment);
Route::Status invokeRouteHandler(const std::string_view &path, std::tuple<std::shared_ptr<Route>, std::vector<TypedParam>, std::vector<TypedParam>>
const Http::Request& req, Http::ResponseWriter response, findRoute(const std::string_view &path,
std::vector<TypedParam> &params, std::vector<TypedParam> &params,
std::vector<TypedParam> &splats) const; std::vector<TypedParam> &splats) const;
...@@ -121,11 +117,20 @@ public: ...@@ -121,11 +117,20 @@ public:
FragmentTreeNode(); FragmentTreeNode();
explicit FragmentTreeNode(const std::shared_ptr<char> &resourceReference); explicit FragmentTreeNode(const std::shared_ptr<char> &resourceReference);
void addRoute(const std::string_view &path, Route::Handler &handler, std::shared_ptr<char> &resourceReference); void addRoute(const std::string_view &path, const Route::Handler &handler,
const std::shared_ptr<char> &resourceReference);
bool removeRoute(const std::string_view &path); bool removeRoute(const std::string_view &path);
Route::Status invokeRouteHandler(const Http::Request& req, Http::ResponseWriter response) const; /**
* Finds the correct route for the given path.
* \param[in] path Requested resource path (usually derived from request)
* \throws std::runtime_error An empty path was given
* \return Found route with its resolved parameters and splats (if no route is found, first element of the tuple
* is a null pointer).
*/
std::tuple<std::shared_ptr<Route>, std::vector<TypedParam>, std::vector<TypedParam>>
findRoute(const std::string_view &path) const;
}; };
class Router { class Router {
...@@ -152,8 +157,6 @@ public: ...@@ -152,8 +157,6 @@ public:
Route::Status route(const Http::Request& request, Http::ResponseWriter response); Route::Status route(const Http::Request& request, Http::ResponseWriter response);
Status route(const Http::Request& request, Http::ResponseWriter response);
private: private:
void addRoute(Http::Method method, std::string resource, Route::Handler handler); void addRoute(Http::Method method, std::string resource, Route::Handler handler);
std::unordered_map<Http::Method, FragmentTreeNode> routes; std::unordered_map<Http::Method, FragmentTreeNode> routes;
......
...@@ -46,7 +46,7 @@ namespace std { ...@@ -46,7 +46,7 @@ namespace std {
string_view string_view
substr(size_type pos = 0, size_type count = npos) const { substr(size_type pos, size_type count = npos) const {
if (pos > size_) { if (pos > size_) {
throw std::out_of_range("Out of range."); throw std::out_of_range("Out of range.");
} }
......
This diff is collapsed.
...@@ -12,23 +12,25 @@ ...@@ -12,23 +12,25 @@
using namespace Pistache; using namespace Pistache;
bool match(const Rest::Route& route, const std::string& req) { bool match(const Rest::FragmentTreeNode& routes, const std::string& req) {
return std::get<0>(route.match(req)); std::shared_ptr<Rest::Route> route;
std::tie(route, std::ignore, std::ignore) = routes.findRoute({req.data(), req.size()});
return route != nullptr;
} }
bool matchParams( bool matchParams(
const Rest::Route& route, const std::string& req, const Rest::FragmentTreeNode& routes, const std::string& req,
std::initializer_list<std::pair<std::string, std::string>> list) std::initializer_list<std::pair<std::string, std::string>> list)
{ {
bool ok; std::shared_ptr<Rest::Route> route;
std::vector<Rest::TypedParam> params; std::vector<Rest::TypedParam> params;
std::tie(ok, params, std::ignore) = route.match(req); std::tie(route, params, std::ignore) = routes.findRoute({req.data(), req.size()});
if (!ok) return false; if (route == nullptr) return false;
for (const auto& p: list) { for (const auto& p: list) {
auto it = std::find_if(params.begin(), params.end(), [&](const Rest::TypedParam& param) { auto it = std::find_if(params.begin(), params.end(), [&](const Rest::TypedParam& param) {
return param.name() == p.first; return param.name() == std::string_view(p.first.data(), p.first.size());
}); });
if (it == std::end(params)) { if (it == std::end(params)) {
std::cerr << "Did not find param '" << p.first << "'" << std::endl; std::cerr << "Did not find param '" << p.first << "'" << std::endl;
...@@ -45,14 +47,14 @@ bool matchParams( ...@@ -45,14 +47,14 @@ bool matchParams(
} }
bool matchSplat( bool matchSplat(
const Rest::Route& route, const std::string& req, const Rest::FragmentTreeNode& routes, const std::string& req,
std::initializer_list<std::string> list) std::initializer_list<std::string> list)
{ {
bool ok; std::shared_ptr<Rest::Route> route;
std::vector<Rest::TypedParam> splats; std::vector<Rest::TypedParam> splats;
std::tie(ok, std::ignore, splats) = route.match(req); std::tie(route, std::ignore, splats) = routes.findRoute({req.data(), req.size()});
if (!ok) return false; if (route == nullptr) return false;
if (list.size() != splats.size()) { if (list.size() != splats.size()) {
std::cerr << "Size mismatch (" << list.size() << " != " << splats.size() << ")" std::cerr << "Size mismatch (" << list.size() << " != " << splats.size() << ")"
...@@ -74,55 +76,57 @@ bool matchSplat( ...@@ -74,55 +76,57 @@ bool matchSplat(
return true; return true;
} }
Rest::Route
makeRoute(std::string value) {
auto noop = [](const Http::Request&, Http::Response) { return Rest::Route::Result::Ok; };
return Rest::Route(value, Http::Method::Get, noop);
}
TEST(router_test, test_fixed_routes) { TEST(router_test, test_fixed_routes) {
auto r1 = makeRoute("/v1/hello"); Rest::FragmentTreeNode routes;
ASSERT_TRUE(match(r1, "/v1/hello")); routes.addRoute(std::string_view("/v1/hello"), nullptr, nullptr);
ASSERT_FALSE(match(r1, "/v2/hello"));
ASSERT_FALSE(match(r1, "/v1/hell0")); ASSERT_TRUE(match(routes, "/v1/hello"));
ASSERT_FALSE(match(routes, "/v2/hello"));
ASSERT_FALSE(match(routes, "/v1/hell0"));
auto r2 = makeRoute("/a/b/c"); routes.addRoute(std::string_view("/a/b/c"), nullptr, nullptr);
ASSERT_TRUE(match(r2, "/a/b/c")); ASSERT_TRUE(match(routes, "/a/b/c"));
} }
TEST(router_test, test_parameters) { TEST(router_test, test_parameters) {
auto r1 = makeRoute("/v1/hello/:name"); Rest::FragmentTreeNode routes;
ASSERT_TRUE(matchParams(r1, "/v1/hello/joe", { routes.addRoute(std::string_view("/v1/hello/:name"), nullptr, nullptr);
ASSERT_TRUE(matchParams(routes, "/v1/hello/joe", {
{ ":name", "joe" } { ":name", "joe" }
})); }));
auto r2 = makeRoute("/greetings/:from/:to"); routes.addRoute(std::string_view("/greetings/:from/:to"), nullptr, nullptr);
ASSERT_TRUE(matchParams(r2, "/greetings/foo/bar", { ASSERT_TRUE(matchParams(routes, "/greetings/foo/bar", {
{ ":from", "foo" }, { ":from", "foo" },
{ ":to" , "bar" } { ":to" , "bar" }
})); }));
} }
TEST(router_test, test_optional) { TEST(router_test, test_optional) {
auto r1 = makeRoute("/get/:key?"); Rest::FragmentTreeNode routes;
ASSERT_TRUE(match(r1, "/get")); routes.addRoute(std::string_view("/get/:key?"), nullptr, nullptr);
ASSERT_TRUE(match(r1, "/get/"));
ASSERT_TRUE(matchParams(r1, "/get/foo", { ASSERT_TRUE(match(routes, "/get"));
ASSERT_TRUE(match(routes, "/get/"));
ASSERT_TRUE(matchParams(routes, "/get/foo", {
{ ":key", "foo" } { ":key", "foo" }
})); }));
ASSERT_TRUE(matchParams(r1, "/get/foo/", { ASSERT_TRUE(matchParams(routes, "/get/foo/", {
{ ":key", "foo" } { ":key", "foo" }
})); }));
ASSERT_FALSE(match(r1, "/get/foo/bar")); ASSERT_FALSE(match(routes, "/get/foo/bar"));
} }
TEST(router_test, test_splat) { TEST(router_test, test_splat) {
auto r1 = makeRoute("/say/*/to/*"); Rest::FragmentTreeNode routes;
ASSERT_TRUE(match(r1, "/say/hello/to/user")); routes.addRoute(std::string_view("/say/*/to/*"), nullptr, nullptr);
ASSERT_FALSE(match(r1, "/say/hello/to"));
ASSERT_FALSE(match(r1, "/say/hello/to/user/please")); ASSERT_TRUE(match(routes, "/say/hello/to/user"));
ASSERT_FALSE(match(routes, "/say/hello/to"));
ASSERT_FALSE(match(routes, "/say/hello/to/user/please"));
ASSERT_TRUE(matchSplat(r1, "/say/hello/to/user", { "hello", "user" })); ASSERT_TRUE(matchSplat(routes, "/say/hello/to/user", { "hello", "user" }));
ASSERT_TRUE(matchSplat(r1, "/say/hello/to/user/", { "hello", "user" })); ASSERT_TRUE(matchSplat(routes, "/say/hello/to/user/", { "hello", "user" }));
} }
\ No newline at end of file
...@@ -5,7 +5,7 @@ TEST(string_view_test, substr_test) { ...@@ -5,7 +5,7 @@ TEST(string_view_test, substr_test) {
std::string_view orig ("test"); std::string_view orig ("test");
std::string_view targ ("est"); std::string_view targ ("est");
sub = orig.substr(1); std::string_view sub = orig.substr(1);
ASSERT_EQ(sub, targ); ASSERT_EQ(sub, targ);
sub = orig.substr(1, 10); sub = orig.substr(1, 10);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment