From 583ea80d09560bb39bbc51cf6d1f8a025e2e1a48 Mon Sep 17 00:00:00 2001 From: Christian Hofstaedtler Date: Mon, 9 Jun 2014 16:35:26 +0200 Subject: [PATCH] Webserver: convert to new yahttp api and router --- pdns/webserver.cc | 120 ++++++++++++-------------------------------- pdns/webserver.hh | 10 ---- pdns/ws-api.cc | 2 +- pdns/ws-auth.cc | 48 +++++++++--------- pdns/ws-recursor.cc | 20 ++++---- 5 files changed, 67 insertions(+), 133 deletions(-) diff --git a/pdns/webserver.cc b/pdns/webserver.cc index 579b3f8bf6..0e56af580a 100644 --- a/pdns/webserver.cc +++ b/pdns/webserver.cc @@ -28,6 +28,7 @@ #include "dns.hh" #include "base64.hh" #include "json.hh" +#include struct connectionThreadData { WebServer* webServer; @@ -36,7 +37,12 @@ struct connectionThreadData { void HttpRequest::json(rapidjson::Document& document) { + if(this->body.empty()) { + L<(this->body.c_str()).HasParseError()) { + L<; such variables -// are parsed out during routing and are put into the "pathArgs" map. -// route() makes no assumptions about the contents of variables except -// that the following URL segment can't be part of the variable. -// -// Note: ORDER of registration MATTERS: -// URLs that do a more specific match should come FIRST. -// -// Examples: -// registerHandler("/foo//", &foobarbaz); -// registerHandler("/foo/", &foobar); -// registerHandler("/foo", &foo); -// registerHandler("/", &index); -void WebServer::registerHandler(const string& url, HandlerFunction handler) +static void handlerWrapper(WebServer::HandlerFunction handler, YaHTTP::Request* req, YaHTTP::Response* resp) { - std::size_t pos = 0, lastpos = 0; - - HandlerRegistration reg; - while ((pos = url.find('<', lastpos)) != std::string::npos) { - std::string part = url.substr(lastpos, pos-lastpos); - lastpos = pos; - pos = url.find('>', pos); - - if (pos == std::string::npos) { - throw std::logic_error("invalid url given"); - } - - std::string paramName = url.substr(lastpos+1, pos-lastpos-1); - lastpos = pos+1; + // wrapper to convert from YaHTTP::* to our subclasses + handler(static_cast(req), static_cast(resp)); +} - reg.urlParts.push_back(part); - reg.paramNames.push_back(paramName); - } - std::string remainder = url.substr(lastpos); - if (!remainder.empty()) { - reg.urlParts.push_back(remainder); - reg.paramNames.push_back(""); - } - reg.handler = handler; - d_handlers.push_back(reg); +void WebServer::registerHandler(const string& url, HandlerFunction handler) +{ + YaHTTP::THandlerFunction f = boost::bind(&handlerWrapper, handler, _1, _2); + YaHTTP::Router::Any(url, f); } -static void apiWrapper(boost::function handler, HttpRequest* req, HttpResponse* resp) { +static void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) { resp->headers["Access-Control-Allow-Origin"] = "*"; resp->headers["Content-Type"] = "application/json"; string callback; - if(req->parameters.count("callback")) { - callback=req->parameters["callback"]; - req->parameters.erase("callback"); + if(req->getvars.count("callback")) { + callback=req->getvars["callback"]; + req->getvars.erase("callback"); } - req->parameters.erase("_"); // jQuery cache buster + req->getvars.erase("_"); // jQuery cache buster try { + resp->status = 200; handler(req, resp); } catch (ApiException &e) { resp->body = returnJsonError(e.what()); @@ -134,46 +110,6 @@ void WebServer::registerApiHandler(const string& url, HandlerFunction handler) { registerHandler(url, f); } -bool WebServer::route(const std::string& url, std::map& pathArgs, HandlerFunction** handler) -{ - for (std::list::iterator reg=d_handlers.begin(); reg != d_handlers.end(); ++reg) { - bool matches = true; - size_t lastpos = 0, pos = 0; - string lastParam; - pathArgs.clear(); - for (std::list::iterator urlPart = reg->urlParts.begin(), param = reg->paramNames.begin(); - urlPart != reg->urlParts.end() && param != reg->paramNames.end(); - urlPart++, param++) { - if (!urlPart->empty()) { - pos = url.find(*urlPart, lastpos); - if (pos == std::string::npos) { - matches = false; - break; - } - if (!lastParam.empty()) { - // store - pathArgs[lastParam] = url.substr(lastpos, pos-lastpos); - } - lastpos = pos + urlPart->size(); - lastParam = *param; - } - } - if (matches) { - if (!lastParam.empty()) { - // store trailing parameter - pathArgs[lastParam] = url.substr(lastpos, pos-lastpos); - } else if (lastpos != url.size()) { - matches = false; - continue; - } - - *handler = ®->handler; - return true; - } - } - return false; -} - static void *WebServerConnectionThreadStart(void *p) { connectionThreadData* data = static_cast(p); pthread_detach(pthread_self()); @@ -187,13 +123,14 @@ static void *WebServerConnectionThreadStart(void *p) { HttpResponse WebServer::handleRequest(HttpRequest req) { - HttpResponse resp(req); + HttpResponse resp; // set default headers resp.headers["Content-Type"] = "text/html; charset=utf-8"; try { if (!req.complete) { + L<setNonBlocking(); @@ -298,6 +239,7 @@ try { break; } } + yarl.finalize(); } catch (YaHTTP::ParseError &e) { // request stays incomplete } diff --git a/pdns/webserver.hh b/pdns/webserver.hh index e9fabd2825..5b3376748d 100644 --- a/pdns/webserver.hh +++ b/pdns/webserver.hh @@ -36,7 +36,6 @@ class HttpRequest : public YaHTTP::Request { public: HttpRequest() : YaHTTP::Request(), accept_json(false), accept_html(false), complete(false) { }; - map path_parameters; bool accept_json; bool accept_html; bool complete; @@ -46,7 +45,6 @@ public: class HttpResponse: public YaHTTP::Response { public: HttpResponse() : YaHTTP::Response() { }; - HttpResponse(const YaHTTP::Request &req) : YaHTTP::Response(req) { }; HttpResponse(const YaHTTP::Response &resp) : YaHTTP::Response(resp) { }; void setBody(rapidjson::Document& document); @@ -135,19 +133,12 @@ public: HttpResponse handleRequest(HttpRequest request); typedef boost::function HandlerFunction; - struct HandlerRegistration { - std::list urlParts; - std::list paramNames; - HandlerFunction handler; - }; - void registerHandler(const string& url, HandlerFunction handler); void registerApiHandler(const string& url, HandlerFunction handler); protected: static char B64Decode1(char cInChar); static int B64Decode(const std::string& strInput, std::string& strOutput); - bool route(const std::string& url, std::map& urlArgs, HandlerFunction** handler); virtual Server* createServer() { return new Server(d_listenaddress, d_port); @@ -155,7 +146,6 @@ protected: string d_listenaddress; int d_port; - std::list d_handlers; string d_password; Server* d_server; }; diff --git a/pdns/ws-api.cc b/pdns/ws-api.cc index 98d63c8672..44af01b383 100644 --- a/pdns/ws-api.cc +++ b/pdns/ws-api.cc @@ -188,7 +188,7 @@ void apiServerSearchLog(HttpRequest* req, HttpResponse* resp) { throw HttpMethodNotAllowedException(); string prefix = " " + s_programname + "["; - resp->body = logGrep(req->parameters["q"], ::arg()["experimental-logfile"], prefix); + resp->body = logGrep(req->getvars["q"], ::arg()["experimental-logfile"], prefix); } void apiServerStatistics(HttpRequest* req, HttpResponse* resp) { diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 70238530cb..7480a318f9 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -197,17 +197,17 @@ string AuthWebServer::makePercentage(const double& val) void AuthWebServer::indexfunction(HttpRequest* req, HttpResponse* resp) { - if(!req->parameters["resetring"].empty()) { - if (S.ringExists(req->parameters["resetring"])) - S.resetRing(req->parameters["resetring"]); + if(!req->getvars["resetring"].empty()) { + if (S.ringExists(req->getvars["resetring"])) + S.resetRing(req->getvars["resetring"]); resp->status = 301; resp->headers["Location"] = "/"; return; } - if(!req->parameters["resizering"].empty()){ - int size=atoi(req->parameters["size"].c_str()); - if (S.ringExists(req->parameters["resizering"]) && size > 0 && size <= 500000) - S.resizeRing(req->parameters["resizering"], atoi(req->parameters["size"].c_str())); + if(!req->getvars["resizering"].empty()){ + int size=atoi(req->getvars["size"].c_str()); + if (S.ringExists(req->getvars["resizering"]) && size > 0 && size <= 500000) + S.resizeRing(req->getvars["resizering"], atoi(req->getvars["size"].c_str())); resp->status = 301; resp->headers["Location"] = "/"; return; @@ -264,7 +264,7 @@ void AuthWebServer::indexfunction(HttpRequest* req, HttpResponse* resp) "
"<
"<parameters["ring"].empty()) { + if(req->getvars["ring"].empty()) { vectorentries=S.listRings(); for(vector::const_iterator i=entries.begin();i!=entries.end();++i) printtable(ret,*i,S.getRingTitle(*i)); @@ -274,7 +274,7 @@ void AuthWebServer::indexfunction(HttpRequest* req, HttpResponse* resp) printargs(ret); } else - printtable(ret,req->parameters["ring"],S.getRingTitle(req->parameters["ring"]),100); + printtable(ret,req->getvars["ring"],S.getRingTitle(req->getvars["ring"]),100); ret<<""<"<© 2013 - 2014 PowerDNS.COM BV."<method != "GET") throw ApiException("Only GET is implemented"); - string zonename = apiZoneIdToName(req->path_parameters["id"]); + string zonename = apiZoneIdToName(req->parameters["id"]); UeberBackend B; DomainInfo di; @@ -499,8 +499,8 @@ static void apiZoneCryptokeys(HttpRequest* req, HttpResponse* resp) { doc.SetArray(); BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) { - if (req->path_parameters.count("key_id")) { - int keyid = lexical_cast(req->path_parameters["key_id"]); + if (req->parameters.count("key_id")) { + int keyid = lexical_cast(req->parameters["key_id"]); int curid = lexical_cast(value.second.id); if (keyid != curid) continue; @@ -513,8 +513,8 @@ static void apiZoneCryptokeys(HttpRequest* req, HttpResponse* resp) { key.AddMember("keytype", (value.second.keyOrZone ? "ksk" : "zsk"), doc.GetAllocator()); Value dnskey(value.first.getDNSKEY().getZoneRepresentation().c_str(), doc.GetAllocator()); key.AddMember("dnskey", dnskey, doc.GetAllocator()); - if (req->path_parameters.count("key_id")) { - DNSSECPrivateKey dpk=dk.getKeyById(zonename, lexical_cast(req->path_parameters["key_id"])); + if (req->parameters.count("key_id")) { + DNSSECPrivateKey dpk=dk.getKeyById(zonename, lexical_cast(req->parameters["key_id"])); Value content(dpk.getKey()->convertToISC().c_str(), doc.GetAllocator()); key.AddMember("content", content, doc.GetAllocator()); } @@ -708,7 +708,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) { } static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { - string zonename = apiZoneIdToName(req->path_parameters["id"]); + string zonename = apiZoneIdToName(req->parameters["id"]); if(req->method == "PUT" && !::arg().mustDo("experimental-api-readonly")) { // update domain settings @@ -761,7 +761,7 @@ static string makeDotted(string in) { } static void apiServerZoneExport(HttpRequest* req, HttpResponse* resp) { - string zonename = apiZoneIdToName(req->path_parameters["id"]); + string zonename = apiZoneIdToName(req->parameters["id"]); if(req->method != "GET") throw HttpMethodNotAllowedException(); @@ -863,7 +863,7 @@ static void makePtr(const DNSResourceRecord& rr, DNSResourceRecord* ptr) { static void patchZone(HttpRequest* req, HttpResponse* resp) { UeberBackend B; DomainInfo di; - string zonename = apiZoneIdToName(req->path_parameters["id"]); + string zonename = apiZoneIdToName(req->parameters["id"]); if (!B.getDomainInfo(zonename, di)) throw ApiException("Could not find domain '"+zonename+"'"); @@ -1003,7 +1003,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) { if(req->method != "GET") throw HttpMethodNotAllowedException(); - string q = req->parameters["q"]; + string q = req->getvars["q"]; if (q.empty()) throw ApiException("Query q can't be blank"); @@ -1088,18 +1088,18 @@ void AuthWebServer::jsonstat(HttpRequest* req, HttpResponse* resp) { string command; - if(req->parameters.count("command")) { - command = req->parameters["command"]; - req->parameters.erase("command"); + if(req->getvars.count("command")) { + command = req->getvars["command"]; + req->getvars.erase("command"); } if(command == "flush-cache") { extern PacketCache PC; int number; - if(req->parameters["domain"].empty()) + if(req->getvars["domain"].empty()) number = PC.purge(); else - number = PC.purge(req->parameters["domain"]); + number = PC.purge(req->getvars["domain"]); map object; object["number"]=lexical_cast(number); @@ -1133,7 +1133,7 @@ void AuthWebServer::jsonstat(HttpRequest* req, HttpResponse* resp) } else if(command=="log-grep") { // legacy parameter name hack - req->parameters["q"] = req->parameters["needle"]; + req->getvars["q"] = req->getvars["needle"]; apiServerSearchLog(req, resp); return; } diff --git a/pdns/ws-recursor.cc b/pdns/ws-recursor.cc index 9da97273f3..34afb3376d 100644 --- a/pdns/ws-recursor.cc +++ b/pdns/ws-recursor.cc @@ -331,7 +331,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) { - string zonename = apiZoneIdToName(req->path_parameters["id"]); + string zonename = apiZoneIdToName(req->parameters["id"]); zonename += "."; SyncRes::domainmap_t::const_iterator iter = t_sstorage->domainmap->find(zonename); @@ -367,7 +367,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) { if(req->method != "GET") throw HttpMethodNotAllowedException(); - string q = req->parameters["q"]; + string q = req->getvars["q"]; if (q.empty()) throw ApiException("Query q can't be blank"); @@ -442,9 +442,9 @@ void RecursorWebServer::jsonstat(HttpRequest* req, HttpResponse *resp) { string command; - if(req->parameters.count("command")) { - command = req->parameters["command"]; - req->parameters.erase("command"); + if(req->getvars.count("command")) { + command = req->getvars["command"]; + req->getvars.erase("command"); } map stats; @@ -476,7 +476,7 @@ void RecursorWebServer::jsonstat(HttpRequest* req, HttpResponse *resp) return; } else if(command == "zone") { - string arg_zone = req->parameters["zone"]; + string arg_zone = req->getvars["zone"]; SyncRes::domainmap_t::const_iterator ret = t_sstorage->domainmap->find(arg_zone); if (ret != t_sstorage->domainmap->end()) { Document doc; @@ -525,7 +525,7 @@ void RecursorWebServer::jsonstat(HttpRequest* req, HttpResponse *resp) } } else if(command == "flush-cache") { - string canon=toCanonic("", req->parameters["domain"]); + string canon=toCanonic("", req->getvars["domain"]); int count = broadcastAccFunction(boost::bind(pleaseWipeCache, canon)); count+=broadcastAccFunction(boost::bind(pleaseWipeAndCountNegCache, canon)); stats["number"]=lexical_cast(count); @@ -542,7 +542,7 @@ void RecursorWebServer::jsonstat(HttpRequest* req, HttpResponse *resp) } else if(command == "log-grep") { // legacy parameter name hack - req->parameters["q"] = req->parameters["needle"]; + req->getvars["q"] = req->getvars["needle"]; apiServerSearchLog(req, resp); return; } @@ -584,7 +584,8 @@ void AsyncServer::newConnection() void AsyncWebServer::serveConnection(Socket *client) { HttpRequest req; - YaHTTP::AsyncRequestLoader yarl(&req); + YaHTTP::AsyncRequestLoader yarl; + yarl.initialize(&req); client->setNonBlocking(); string data; @@ -599,6 +600,7 @@ void AsyncWebServer::serveConnection(Socket *client) break; } } + yarl.finalize(); } catch (YaHTTP::ParseError &e) { // request stays incomplete } -- 2.39.5