From: Aki Tuomi Date: Fri, 18 Aug 2023 07:09:56 +0000 (+0300) Subject: webserver: Allow specifying supported method X-Git-Tag: auth-4.9.0-alpha1~42^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=478e1699844a2a642822e375991a5e5deb7cb785;p=thirdparty%2Fpdns.git webserver: Allow specifying supported method If method is not empty and it does not match the request, throw exception. --- diff --git a/ext/yahttp/yahttp/router.cpp b/ext/yahttp/yahttp/router.cpp index 90612ad8ec..83465e318a 100644 --- a/ext/yahttp/yahttp/router.cpp +++ b/ext/yahttp/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 @@ -53,6 +54,7 @@ namespace YaHTTP { pname = pname.substr(1); // this matches whatever comes after it, basically end of string pos2 = req->url.path.size(); + matched = true; if (pname != "") params[pname] = funcptr::tie(pos1,pos2); k1 = url.size(); @@ -78,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; @@ -93,7 +108,7 @@ namespace YaHTTP { req->routeName = std::move(rname); - return true; + return RouteFound; }; void Router::printRoutes(std::ostream &os) { diff --git a/ext/yahttp/yahttp/router.hpp b/ext/yahttp/yahttp/router.hpp index 205119c7d4..0261bd4cd5 100644 --- a/ext/yahttp/yahttp/router.hpp +++ b/ext/yahttp/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); }; //(req), static_cast(resp)); } -void WebServer::registerBareHandler(const string& url, const HandlerFunction& handler) +void WebServer::registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method) { YaHTTP::THandlerFunction f = [=](YaHTTP::Request* req, YaHTTP::Response* resp){return bareHandlerWrapper(handler, req, resp);}; - YaHTTP::Router::Any(url, std::move(f)); + YaHTTP::Router::Map(method, url, std::move(f)); } static bool optionsHandler(HttpRequest* req, HttpResponse* resp) { @@ -215,9 +215,9 @@ void WebServer::apiWrapper(const WebServer::HandlerFunction& handler, HttpReques } } -void WebServer::registerApiHandler(const string& url, const HandlerFunction& handler, bool allowPassword) { +void WebServer::registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method, bool allowPassword) { auto f = [=](HttpRequest *req, HttpResponse* resp){apiWrapper(handler, req, resp, allowPassword);}; - registerBareHandler(url, f); + registerBareHandler(url, f, method); } void WebServer::webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp) { @@ -233,9 +233,9 @@ void WebServer::webWrapper(const WebServer::HandlerFunction& handler, HttpReques handler(req, resp); } -void WebServer::registerWebHandler(const string& url, const HandlerFunction& handler) { +void WebServer::registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method) { auto f = [=](HttpRequest *req, HttpResponse *resp){webWrapper(handler, req, resp);}; - registerBareHandler(url, f); + registerBareHandler(url, f, method); } static void *WebServerConnectionThreadStart(const WebServer* webServer, std::shared_ptr client) { @@ -293,11 +293,16 @@ void WebServer::handleRequest(HttpRequest& req, HttpResponse& resp) const } YaHTTP::THandlerFunction handler; - if (!YaHTTP::Router::Route(&req, handler)) { + YaHTTP::RoutingResult res = YaHTTP::Router::Route(&req, handler); + + if (res == YaHTTP::RouteNotFound) { SLOG(g_log<info(Logr::Debug, "No route found")); throw HttpNotFoundException(); } + if (res == YaHTTP::RouteNoMethod) { + throw HttpMethodNotAllowedException(); + } const string msg = "HTTP ISE Exception"; try { diff --git a/pdns/webserver.hh b/pdns/webserver.hh index 6f9c59e849..28d901dbd5 100644 --- a/pdns/webserver.hh +++ b/pdns/webserver.hh @@ -225,8 +225,8 @@ public: void handleRequest(HttpRequest& request, HttpResponse& resp) const; typedef std::function HandlerFunction; - void registerApiHandler(const string& url, const HandlerFunction& handler, bool allowPassword=false); - void registerWebHandler(const string& url, const HandlerFunction& handler); + void registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method = "", bool allowPassword=false); + void registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method = ""); enum class LogLevel : uint8_t { None = 0, // No logs from requests at all @@ -266,7 +266,7 @@ public: #endif protected: - void registerBareHandler(const string& url, const HandlerFunction& handler); + static void registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method); void logRequest(const HttpRequest& req, const ComboAddress& remote) const; void logResponse(const HttpResponse& resp, const ComboAddress& remote, const string& logprefix) const;