]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/webserver.cc
Webserver: convert to new yahttp api and router
[thirdparty/pdns.git] / pdns / webserver.cc
index 579b3f8bf670014f7eadfbfd2d508e9798a6ea0e..0e56af580aa90e2c2cdb6b4aa801d6296b762006 100644 (file)
@@ -28,6 +28,7 @@
 #include "dns.hh"
 #include "base64.hh"
 #include "json.hh"
+#include <yahttp/router.hpp>
 
 struct connectionThreadData {
   WebServer* webServer;
@@ -36,7 +37,12 @@ struct connectionThreadData {
 
 void HttpRequest::json(rapidjson::Document& document)
 {
+  if(this->body.empty()) {
+    L<<Logger::Debug<<"HTTP: JSON document expected in request body, but body was empty" << endl;
+    throw HttpBadRequestException();
+  }
   if(document.Parse<0>(this->body.c_str()).HasParseError()) {
+    L<<Logger::Debug<<"HTTP: parsing of JSON document failed" << endl;
     throw HttpBadRequestException();
   }
 }
@@ -51,63 +57,33 @@ int WebServer::B64Decode(const std::string& strInput, std::string& strOutput)
   return ::B64Decode(strInput, 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 "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/<bar>/<baz>", &foobarbaz);
-//   registerHandler("/foo/<bar>", &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<HttpRequest*>(req), static_cast<HttpResponse*>(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<void(HttpRequest*,HttpResponse*)> 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<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;
-    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++) {
-      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 = &reg->handler;
-      return true;
-    }
-  }
-  return false;
-}
-
 static void *WebServerConnectionThreadStart(void *p) {
   connectionThreadData* data = static_cast<connectionThreadData*>(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<<Logger::Debug<<"HTTP: Incomplete request" << endl;
       throw HttpBadRequestException();
     }
 
@@ -232,16 +169,19 @@ HttpResponse WebServer::handleRequest(HttpRequest req)
       }
     }
 
-    HandlerFunction *handler;
-    if (!route(req.url.path, req.path_parameters, &handler)) {
+    YaHTTP::THandlerFunction handler;
+    if (!YaHTTP::Router::Route(&req, handler)) {
       L<<Logger::Debug<<"HTTP: No route found for \"" << req.url.path << "\"" << endl;
       throw HttpNotFoundException();
     }
 
     try {
-      (*handler)(&req, &resp);
+      handler(&req, &resp);
       L<<Logger::Debug<<"HTTP: Result for \"" << req.url.path << "\": " << resp.status << ", body length: " << resp.body.size() << endl;
     }
+    catch(HttpException) {
+      throw;
+    }
     catch(PDNSException &e) {
       L<<Logger::Error<<"HTTP ISE for \""<< req.url.path << "\": Exception: " << e.reason << endl;
       throw HttpInternalServerErrorException();
@@ -281,7 +221,8 @@ HttpResponse WebServer::handleRequest(HttpRequest req)
 void WebServer::serveConnection(Socket *client)
 try {
   HttpRequest req;
-  YaHTTP::AsyncRequestLoader yarl(&req);
+  YaHTTP::AsyncRequestLoader yarl;
+  yarl.initialize(&req);
   int timeout = 5;
   client->setNonBlocking();
 
@@ -298,6 +239,7 @@ try {
         break;
       }
     }
+    yarl.finalize();
   } catch (YaHTTP::ParseError &e) {
     // request stays incomplete
   }