]> granicus.if.org Git - pdns/commitdiff
webserver: bundle all request data into class HttpRequest
authorChristian Hofstaedtler <christian@hofstaedtler.name>
Sun, 20 Oct 2013 16:01:40 +0000 (18:01 +0200)
committerChristian Hofstaedtler <christian@hofstaedtler.name>
Tue, 5 Nov 2013 09:53:15 +0000 (10:53 +0100)
Now, pathArgs could actually be used by handlers.

pdns/webserver.cc
pdns/webserver.hh
pdns/ws.cc
pdns/ws.hh

index 9d3ba539cf5df3e77d5d8a3b36f1be5cba12b507..af667ccc27df424703d6a5681fb3dded044f80b7 100644 (file)
@@ -42,7 +42,7 @@ int WebServer::B64Decode(const std::string& strInput, std::string& strOutput)
 
 // url is supposed to start with a slash.
 // url can contain variable names, marked as <variable>; such variables
-// are parsed out during routing and are put into the "urlArgs" map.
+// 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.
 //
@@ -79,13 +79,13 @@ void WebServer::registerHandler(const string& url, HandlerFunction handler)
   d_handlers.push_back(reg);
 }
 
-bool WebServer::route(const std::string& url, std::map<std::string, std::string>& urlArgs, HandlerFunction** handler)
+bool WebServer::route(const std::string& url, std::map<std::string, std::string>& pathArgs, HandlerFunction** handler)
 {
   for (std::list<HandlerRegistration>::iterator reg=d_handlers.begin(); reg != d_handlers.end(); ++reg) {
     bool matches = true;
     size_t lastpos = 0, pos = 0;
     string lastParam;
-    urlArgs.clear();
+    pathArgs.clear();
     for (std::list<string>::iterator urlPart = reg->urlParts.begin(), param = reg->paramNames.begin();
          urlPart != reg->urlParts.end() && param != reg->paramNames.end();
          urlPart++, param++) {
@@ -97,7 +97,7 @@ bool WebServer::route(const std::string& url, std::map<std::string, std::string>
         }
         if (!lastParam.empty()) {
           // store
-          urlArgs[lastParam] = url.substr(lastpos, pos-lastpos);
+          pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
         }
         lastpos = pos + urlPart->size();
         lastParam = *param;
@@ -106,7 +106,7 @@ bool WebServer::route(const std::string& url, std::map<std::string, std::string>
     if (matches) {
       if (!lastParam.empty()) {
         // store trailing parameter
-        urlArgs[lastParam] = url.substr(lastpos, pos-lastpos);
+        pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
       } else if (lastpos != url.size()) {
         matches = false;
         continue;
@@ -129,8 +129,7 @@ static void *WebServerConnectionThreadStart(void *p) {
 
 void WebServer::serveConnection(Session* client)
 try {
-  bool want_html=false;
-  bool want_json=false;
+  HttpRequest req;
 
   try {
     string line;
@@ -142,36 +141,31 @@ try {
     //    L<<"page: "<<line<<endl;
 
     vector<string> parts;
-    stringtok(parts,line);
-    
-    string method, uri;
+    stringtok(parts, line);
+
     if(parts.size()>1) {
-      method=parts[0];
-      uri=parts[1];
+      req.method = parts[0];
+      req.uri = parts[1];
     }
 
     parts.clear();
-    stringtok(parts,uri,"?");
-
-    string baseUrl=parts[0];
+    stringtok(parts,req.uri,"?");
+    req.path = parts[0];
 
-    vector<string>variables;
+    vector<string> variables;
     if(parts.size()>1) {
       stringtok(variables,parts[1],"&");
     }
 
-    map<string,string>varmap;
-
     for(vector<string>::const_iterator i=variables.begin();
         i!=variables.end();++i) {
 
       parts.clear();
       stringtok(parts,*i,"=");
       if(parts.size()>1)
-        varmap[parts[0]]=parts[1];
+        req.queryArgs[parts[0]]=parts[1];
       else
-        varmap[parts[0]]="";
-
+        req.queryArgs[parts[0]]="";
     }
 
     bool authOK=0;
@@ -203,16 +197,16 @@ try {
           authOK=1;
         }
       }
-      else if(header == "content-length" && method=="POST") {
+      else if(header == "content-length" && req.method=="POST") {
         postlen = atoi(value.c_str());
 //        cout<<"Got a post: "<<postlen<<" bytes"<<endl;
       }
       else if(header == "accept") {
         // json wins over html
         if(value.find("application/json")!=std::string::npos) {
-          want_json=true;
+          req.accept_json=true;
         } else if(value.find("text/html")!=std::string::npos) {
-          want_html=true;
+          req.accept_html=true;
         }
       }
       else
@@ -220,20 +214,16 @@ try {
       
     } while(true);
 
-    string post;
     if(postlen) 
-      post = client->get(postlen);
+      req.body = client->get(postlen);
   
- //   cout<<"Post: '"<<post<<"'"<<endl;
-
     if(!d_password.empty() && !authOK)
       throw HttpUnauthorizedException();
 
     HandlerFunction *handler;
-    map<string, string> urlArgs;
-    if (route(baseUrl, urlArgs, &handler)) {
+    if (route(req.path, req.pathArgs, &handler)) {
       bool custom=false;
-      string ret=(*handler)(method, post, varmap, &custom);
+      string ret=(*handler)(&req, &custom);
 
       if(!custom) {
         client->putLine("HTTP/1.1 200 OK\n");
@@ -250,10 +240,10 @@ try {
     client->putLine(e.statusLine());
     client->putLine("Connection: close\n");
     client->putLine(e.headers());
-    if(want_html) {
+    if(req.accept_html) {
       client->putLine("Content-Type: text/html; charset=utf-8\n\n");
       client->putLine("<!html><title>" + e.what() + "</title><h1>" + e.what() + "</h1>");
-    } else if (want_json) {
+    } else if (req.accept_json) {
       client->putLine("Content-Type: application/json\n\n");
       client->putLine(returnJSONError(e.what()));
     } else {
index 48fcda1cc89d72762a16608b151e457cb0d5c037..bdf49f2abf8f22af92de9f6ebea899f9e4faaae4 100644 (file)
@@ -78,6 +78,21 @@ public:
   HttpMethodNotAllowedException() : HttpException(405, "Method Not Allowed") { };
 };
 
+class HttpRequest {
+public:
+  HttpRequest() : accept_json(false), accept_html(false) { };
+
+  string method;
+  string post;
+  string uri;
+  string path;
+  string body;
+  map<string,string> pathArgs;
+  map<string,string> queryArgs;
+  bool accept_json;
+  bool accept_html;
+};
+
 class WebServer
 {
 public:
@@ -86,7 +101,7 @@ public:
 
   void serveConnection(Session* client);
 
-  typedef boost::function<string(const string& method, const string& post, const map<string,string>&varmap, bool *custom)> HandlerFunction;
+  typedef boost::function<string(HttpRequest* req, bool *custom)> HandlerFunction;
   struct HandlerRegistration {
     std::list<string> urlParts;
     std::list<string> paramNames;
index 711f21e340281207d12252b025fa47211d031bec..2758a7cbaa5a76f9a1c011be1ad3bb307f7c9f35 100644 (file)
@@ -186,17 +186,16 @@ string StatWebServer::makePercentage(const double& val)
   return (boost::format("%.01f%%") % val).str();
 }
 
-string StatWebServer::indexfunction(const string& method, const string& post, const map<string,string> &varmap, bool *custom)
+string StatWebServer::indexfunction(HttpRequest* req, bool *custom)
 {
-  map<string,string>rvarmap=varmap;
-  if(!rvarmap["resetring"].empty()){
+  if(!req->queryArgs["resetring"].empty()){
     *custom=true;
-    S.resetRing(rvarmap["resetring"]);
+    S.resetRing(req->queryArgs["resetring"]);
     return "HTTP/1.1 301 Moved Permanently\nLocation: /\nConnection: close\n\n";
   }
-  if(!rvarmap["resizering"].empty()){
+  if(!req->queryArgs["resizering"].empty()){
     *custom=true;
-    S.resizeRing(rvarmap["resizering"], atoi(rvarmap["size"].c_str()));
+    S.resizeRing(req->queryArgs["resizering"], atoi(req->queryArgs["size"].c_str()));
     return "HTTP/1.1 301 Moved Permanently\nLocation: /\nConnection: close\n\n";
   }
 
@@ -251,7 +250,7 @@ string StatWebServer::indexfunction(const string& method, const string& post, co
     "<br>"<<endl;
 
   ret<<"Total queries: "<<S.read("udp-queries")<<". Question/answer latency: "<<S.read("latency")/1000.0<<"ms</p><br>"<<endl;
-  if(rvarmap["ring"].empty()) {
+  if(req->queryArgs["ring"].empty()) {
     vector<string>entries=S.listRings();
     for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i)
       printtable(ret,*i,S.getRingTitle(*i));
@@ -261,7 +260,7 @@ string StatWebServer::indexfunction(const string& method, const string& post, co
       printargs(ret);
   }
   else
-    printtable(ret,rvarmap["ring"],S.getRingTitle(rvarmap["ring"]),100);
+    printtable(ret,req->queryArgs["ring"],S.getRingTitle(req->queryArgs["ring"]),100);
 
   ret<<"</div></div>"<<endl;
   ret<<"<footer class=\"row\">"<<fullVersionString()<<"<br>&copy; 2013 <a href=\"http://www.powerdns.com/\">PowerDNS.COM BV</a>.</footer>"<<endl;
@@ -367,22 +366,22 @@ static string createOrUpdateZone(const string& zonename, bool onlyCreate, varmap
   return getZone(zonename);
 }
 
-static string jsonDispatch(const string& method, const string& post, varmap_t& varmap, const string& command) {
+static string jsonDispatch(HttpRequest* req, const string& command) {
   if(command=="get") {
-    if(varmap.empty()) {
+    if(req->queryArgs.empty()) {
       vector<string> entries = S.getEntries();
       BOOST_FOREACH(string& ent, entries) {
-        varmap[ent];
+        req->queryArgs[ent];
       }
-      varmap["version"];
-      varmap["uptime"];
+      req->queryArgs["version"];
+      req->queryArgs["uptime"];
     }
 
     string variable, value;
     
     Document doc;
     doc.SetObject();
-    for(varmap_t::const_iterator iter = varmap.begin(); iter != varmap.end() ; ++iter) {
+    for(varmap_t::const_iterator iter = req->queryArgs.begin(); iter != req->queryArgs.end() ; ++iter) {
       variable = iter->first;
       if(variable == "version") {
         value = VERSION;
@@ -421,22 +420,22 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
   else if(command == "flush-cache") {
     extern PacketCache PC;
     int number; 
-    if(varmap["domain"].empty())
+    if(req->queryArgs["domain"].empty())
       number = PC.purge();
     else
-      number = PC.purge(varmap["domain"]);
+      number = PC.purge(req->queryArgs["domain"]);
       
     map<string, string> object;
     object["number"]=lexical_cast<string>(number);
-    //cerr<<"Flushed cache for '"<<varmap["domain"]<<"', cleaned "<<number<<" records"<<endl;
+    //cerr<<"Flushed cache for '"<<queryArgs["domain"]<<"', cleaned "<<number<<" records"<<endl;
     return returnJSONObject(object);
   }
   else if(command == "pdns-control") {
-    if(method!="POST")
+    if(req->method!="POST")
       throw HttpMethodNotAllowedException();
     // cout<<"post: "<<post<<endl;
     rapidjson::Document document;
-    if(document.Parse<0>(post.c_str()).HasParseError())
+    if(document.Parse<0>(req->body.c_str()).HasParseError())
       return returnJSONError("Unable to parse JSON");
     // cout<<"Parameters: '"<<document["parameters"].GetString()<<"'\n";
     vector<string> parameters;
@@ -456,7 +455,7 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
   }
   else if(command == "zone-rest") { // http://jsonstat?command=zone-rest&rest=/powerdns.nl/www.powerdns.nl/a
     vector<string> parts;
-    stringtok(parts, varmap["rest"], "/");
+    stringtok(parts, req->queryArgs["rest"], "/");
     if(parts.size() != 3) 
       return returnJSONError("Could not parse rest parameter");
     UeberBackend B;
@@ -472,7 +471,7 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
     PC.purge(qname);
     // cerr<<"domain id: "<<sd.domain_id<<", lookup name: '"<<parts[1]<<"', for type: '"<<qtype.getName()<<"'"<<endl;
     
-    if(method == "GET" ) {
+    if(req->method == "GET") {
       B.lookup(qtype, parts[1], 0, sd.domain_id);
       
       DNSResourceRecord rr;
@@ -494,13 +493,13 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
       ret+="]}";
       return ret;
     }
-    else if(method=="DELETE") {
+    else if(req->method=="DELETE") {
       sd.db->replaceRRSet(sd.domain_id, qname, qtype, vector<DNSResourceRecord>());
       
     }
-    else if(method=="POST") {
+    else if(req->method=="POST") {
       rapidjson::Document document;
-      if(document.Parse<0>(post.c_str()).HasParseError())
+      if(document.Parse<0>(req->body.c_str()).HasParseError())
         return returnJSONError("Unable to parse JSON");
       
       DNSResourceRecord rr;
@@ -534,24 +533,24 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
       sd.db->startTransaction(qname);
       sd.db->replaceRRSet(sd.domain_id, qname, qtype, rrset);
       sd.db->commitTransaction();
-      return post;
+      return req->body;
     }  
   }
   else if(command == "zone") {
-    string zonename = varmap["zone"];
+    string zonename = req->queryArgs["zone"];
     if (zonename.empty())
       return returnJSONError("Must give zone parameter");
 
-    if(method == "GET") {
+    if(req->method == "GET") {
       // get current zone
       return getZone(zonename);
-    } else if (method == "POST") {
+    } else if (req->method == "POST") {
       // create
-      return createOrUpdateZone(zonename, true, varmap);
-    } else if (method == "PUT") {
+      return createOrUpdateZone(zonename, true, req->queryArgs);
+    } else if (req->method == "PUT") {
       // update or create
-      return createOrUpdateZone(zonename, false, varmap);
-    } else if (method == "DELETE") {
+      return createOrUpdateZone(zonename, false, req->queryArgs);
+    } else if (req->method == "DELETE") {
       // delete
       UeberBackend B;
       DomainInfo di;
@@ -566,7 +565,7 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
     }
   }
   else if(command=="log-grep") {
-    return makeLogGrepJSON(varmap, ::arg()["experimental-logfile"], " pdns[");
+    return makeLogGrepJSON(req->queryArgs, ::arg()["experimental-logfile"], " pdns[");
   }
   else if(command=="domains") {
     UeberBackend B;
@@ -603,7 +602,7 @@ static string jsonDispatch(const string& method, const string& post, varmap_t& v
   return returnJSONError("No or unknown command given");
 }
 
-string StatWebServer::jsonstat(const string& method, const string& post, const map<string,string> &varmap, bool *custom)
+string StatWebServer::jsonstat(HttpRequest* req, bool *custom)
 {
   *custom=1; // indicates we build the response
   string ret="HTTP/1.1 200 OK\r\n"
@@ -613,25 +612,24 @@ string StatWebServer::jsonstat(const string& method, const string& post, const m
   "Content-Type: application/json\r\n"
   "\r\n" ;
 
-  varmap_t ourvarmap=varmap;
   string callback;
   string command;
 
-  if(ourvarmap.count("callback")) {
-    callback=ourvarmap["callback"];
-    ourvarmap.erase("callback");
+  if(req->queryArgs.count("callback")) {
+    callback=req->queryArgs["callback"];
+    req->queryArgs.erase("callback");
   }
   
-  if(ourvarmap.count("command")) {
-    command=ourvarmap["command"];
-    ourvarmap.erase("command");
+  if(req->queryArgs.count("command")) {
+    command=req->queryArgs["command"];
+    req->queryArgs.erase("command");
   }
 
-  ourvarmap.erase("_");
+  req->queryArgs.erase("_");
   if(!callback.empty())
       ret += callback+"(";
 
-  ret += jsonDispatch(method, post, ourvarmap, command);
+  ret += jsonDispatch(req, command);
 
   if(!callback.empty()) {
     ret += ");";
@@ -639,7 +637,7 @@ string StatWebServer::jsonstat(const string& method, const string& post, const m
   return ret;
 }
 
-string StatWebServer::cssfunction(const string& method, const string& post, const map<string,string> &varmap, bool *custom)
+string StatWebServer::cssfunction(HttpRequest* req, bool *custom)
 {
   *custom=1; // indicates we build the response
   ostringstream ret;
@@ -682,10 +680,10 @@ string StatWebServer::cssfunction(const string& method, const string& post, cons
 void StatWebServer::launch()
 {
   try {
-    d_ws->registerHandler("/", boost::bind(&StatWebServer::indexfunction, this, _1, _2, _3, _4));
-    d_ws->registerHandler("/style.css", boost::bind(&StatWebServer::cssfunction, this, _1, _2, _3, _4));
+    d_ws->registerHandler("/", boost::bind(&StatWebServer::indexfunction, this, _1, _2));
+    d_ws->registerHandler("/style.css", boost::bind(&StatWebServer::cssfunction, this, _1, _2));
     if(::arg().mustDo("experimental-json-interface"))
-      d_ws->registerHandler("/jsonstat", boost::bind(&StatWebServer::jsonstat, this, _1, _2, _3, _4));
+      d_ws->registerHandler("/jsonstat", boost::bind(&StatWebServer::jsonstat, this, _1, _2));
     d_ws->go();
   }
   catch(...) {
index 907ae5547a188e9af765e80a6cecc917841cad49..d01f965de86af0339efd7a711127d17abd0c9684 100644 (file)
@@ -76,6 +76,7 @@ private:
 };
 
 class WebServer;
+class HttpRequest;
 
 class StatWebServer
 {
@@ -86,9 +87,9 @@ public:
 private:
   static void *threadHelper(void *);
   static void *statThreadHelper(void *p);
-  string indexfunction(const string& method, const string& post, const map<string,string> &varmap, bool *custom);
-  string cssfunction(const string& method, const string& post, const map<string,string> &varmap, bool *custom);
-  string jsonstat(const string& method, const string& post, const map<string,string> &varmap, bool *custom);
+  string indexfunction(HttpRequest* req, bool *custom);
+  string cssfunction(HttpRequest* req, bool *custom);
+  string jsonstat(HttpRequest* req, bool *custom);
   void printvars(ostringstream &ret);
   void printargs(ostringstream &ret);
   void launch();