// 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.
//
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++) {
}
if (!lastParam.empty()) {
// store
- urlArgs[lastParam] = url.substr(lastpos, pos-lastpos);
+ pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
}
lastpos = pos + urlPart->size();
lastParam = *param;
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;
void WebServer::serveConnection(Session* client)
try {
- bool want_html=false;
- bool want_json=false;
+ HttpRequest req;
try {
string line;
// 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;
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
} 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");
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 {
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";
}
"<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));
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>© 2013 <a href=\"http://www.powerdns.com/\">PowerDNS.COM BV</a>.</footer>"<<endl;
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;
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;
}
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;
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;
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;
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;
}
}
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;
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"
"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 += ");";
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;
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(...) {