From f8101747dbcf6f0a14337a43f5b7f6601658d576 Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Fri, 18 Aug 2023 13:52:21 +0300 Subject: [PATCH] Report method mismatch separately from route not found --- tests/test-router.cpp | 45 +++++++++++++++++++++++++++++++++++++++---- yahttp/router.cpp | 26 +++++++++++++++++++------ yahttp/router.hpp | 10 ++++++++-- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/tests/test-router.cpp b/tests/test-router.cpp index 387d20b..0d2b270 100644 --- a/tests/test-router.cpp +++ b/tests/test-router.cpp @@ -41,6 +41,8 @@ struct RouterFixture { YaHTTP::Router::Get("/test//.", rth.ObjectHandler, "object_attribute_format_get"); YaHTTP::Router::Patch("/test//.", rth.Handler, "object_attribute_format_update"); YaHTTP::Router::Get("/test", rth.Handler, "test_index"); + YaHTTP::Router::Put("/test", rth.Handler, "test_put"); + YaHTTP::Router::Post("/test", rth.Handler, "test_post"); YaHTTP::Router::Get("/", rth.Handler, "root_path"); // reset routes to false @@ -74,7 +76,7 @@ BOOST_AUTO_TEST_CASE( test_router_basic ) { YaHTTP::THandlerFunction func = rth.NonHandler; req.setup("get", "http://test.org/"); - BOOST_CHECK(YaHTTP::Router::Route(&req, func)); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); func(&req, &resp); // check if it was hit @@ -88,7 +90,7 @@ BOOST_AUTO_TEST_CASE( test_router_object ) { YaHTTP::THandlerFunction func = rth.NonHandler; req.setup("get", "http://test.org/test/1234/name.json"); - BOOST_CHECK(YaHTTP::Router::Route(&req, func)); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); func(&req, &resp); // check if it was hit @@ -101,13 +103,37 @@ BOOST_AUTO_TEST_CASE( test_router_glob ) { YaHTTP::THandlerFunction func = rth.NonHandler; req.setup("get", "http://test.org/glob/truly and really/everything/there/is.json"); - BOOST_CHECK(YaHTTP::Router::Route(&req, func)); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); func(&req, &resp); // check if it was hit BOOST_CHECK(rth.routes["glob_get"]); }; +BOOST_AUTO_TEST_CASE( test_router_method ) { + YaHTTP::Request req; + YaHTTP::Response resp; + YaHTTP::THandlerFunction func = rth.NonHandler; + + req.setup("get", "http://test.org/test"); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); + func(&req, &resp); + BOOST_CHECK(rth.routes["test_index"]); + + req.setup("post", "http://test.org/test"); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); + func(&req, &resp); + BOOST_CHECK(rth.routes["test_post"]); + + req.setup("put", "http://test.org/test"); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteFound); + func(&req, &resp); + BOOST_CHECK(rth.routes["test_put"]); + + req.setup("delete", "http://test.org/test"); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteNoMethod); +}; + BOOST_AUTO_TEST_CASE( test_router_urlFor ) { YaHTTP::strstr_map_t params; @@ -134,7 +160,16 @@ BOOST_AUTO_TEST_CASE( test_router_missing_route ) { YaHTTP::THandlerFunction func = rth.NonHandler; req.setup("get", "http://test.org/missing/route"); - BOOST_CHECK(!YaHTTP::Router::Route(&req, func)); + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteNotFound); +} + +BOOST_AUTO_TEST_CASE( test_router_method_mismatch ) { + YaHTTP::Request req; + YaHTTP::Response resp; + YaHTTP::THandlerFunction func = rth.NonHandler; + req.setup("post", "http://test.org/"); + + BOOST_CHECK(YaHTTP::Router::Route(&req, func) == YaHTTP::RouteNoMethod); } BOOST_AUTO_TEST_CASE( test_router_missing_urlFor ) { @@ -152,6 +187,8 @@ BOOST_AUTO_TEST_CASE( test_router_print_routes ) { GET/test//.object_attribute_format_get\n\ PATCH/test//.object_attribute_format_update\n\ GET/testtest_index\n\ +PUT/testtest_put\n\ +POST/testtest_post\n\ GET/root_path\n\ "); }; diff --git a/yahttp/router.cpp b/yahttp/router.cpp index a92be62..e484109 100644 --- a/yahttp/router.cpp +++ b/yahttp/router.cpp @@ -24,10 +24,11 @@ namespace YaHTTP { routes.push_back(funcptr::make_tuple(method2, url, handler, name)); }; - bool Router::route(Request *req, THandlerFunction& handler) { + RoutingResult Router::route(Request *req, THandlerFunction& handler) { std::map params; int pos1,pos2; bool matched = false; + bool seen = false; std::string rname; // iterate routes @@ -36,8 +37,8 @@ namespace YaHTTP { std::string pname; std::string method, url; funcptr::tie(method, url, handler, rname) = *i; - - if (method.empty() == false && req->method != method) continue; // no match on method + matched = false; + // see if we can't match the url params.clear(); // simple matcher func @@ -79,10 +80,23 @@ namespace YaHTTP { matched = false; else matched = true; + + if (matched && method.empty() == false && req->method != method) { + // method did not match, record it though so we can return correct result + matched = false; + seen = true; + continue; + } + } + + if (!matched) { + if (seen) + return RouteNoMethod; + // no route + return RouteNotFound; } - if (!matched) { return false; } // no route - req->parameters.clear(); + req->parameters.clear(); for(std::map::iterator i = params.begin(); i != params.end(); i++) { int p1,p2; @@ -94,7 +108,7 @@ namespace YaHTTP { req->routeName = rname; - return true; + return RouteFound; }; void Router::printRoutes(std::ostream &os) { diff --git a/yahttp/router.hpp b/yahttp/router.hpp index a0dbd13..c7f5882 100644 --- a/yahttp/router.hpp +++ b/yahttp/router.hpp @@ -25,6 +25,12 @@ namespace funcptr = boost; #include namespace YaHTTP { + enum RoutingResult { + RouteFound = 1, + RouteNotFound = 0, + RouteNoMethod = -1, + }; + typedef funcptr::function THandlerFunction; //!< Handler function pointer typedef funcptr::tuple TRoute; //!< Route tuple (method, urlmask, handler, name) typedef std::vector TRouteList; //!< List of routes in order of evaluation @@ -44,7 +50,7 @@ is consumed but not stored. Note that only path is matched, scheme, host and url static Router router; // urlFor(const std::string &name, const strstr_map_t& arguments); //url.path + static RoutingResult Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //url.path, returns RouteFound if route is found and method matches, RouteNoMethod if route is seen but method did match, and RouteNotFound if not found. static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; // URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //