#include "dns.hh"
#include "base64.hh"
#include "json.hh"
+#include <yahttp/router.hpp>
struct connectionThreadData {
WebServer* webServer;
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();
}
}
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());
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 = ®->handler;
- return true;
- }
- }
- return false;
-}
-
static void *WebServerConnectionThreadStart(void *p) {
connectionThreadData* data = static_cast<connectionThreadData*>(p);
pthread_detach(pthread_self());
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();
}
}
}
- 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();
void WebServer::serveConnection(Socket *client)
try {
HttpRequest req;
- YaHTTP::AsyncRequestLoader yarl(&req);
+ YaHTTP::AsyncRequestLoader yarl;
+ yarl.initialize(&req);
int timeout = 5;
client->setNonBlocking();
break;
}
}
+ yarl.finalize();
} catch (YaHTTP::ParseError &e) {
// request stays incomplete
}