SYSCONFDIR=/etc/powerdns/
LOCALSTATEDIR=/var/run/
OPTFLAGS?=-O3
-CXXFLAGS:= $(CXXFLAGS) -Iext/rapidjson/include -I$(CURDIR)/ext/polarssl-1.3.2/include -Wall $(OPTFLAGS) $(PROFILEFLAGS) $(ARCHFLAGS) -pthread
+CXXFLAGS:= $(CXXFLAGS) -Iext/rapidjson/include -I$(CURDIR)/ext/polarssl-1.3.2/include -Wall $(OPTFLAGS) $(PROFILEFLAGS) $(ARCHFLAGS) -pthread -Iext/yahttp
CFLAGS:=$(CFLAGS) -Wall $(OPTFLAGS) $(PROFILEFLAGS) $(ARCHFLAGS) -I$(CURDIR)/ext/polarssl-1.3.2/include -pthread
LDFLAGS:=$(LDFLAGS) $(ARCHFLAGS) -pthread
rec_channel.o rec_channel_rec.o selectmplexer.o sillyrecords.o \
dns_random.o ext/polarssl-1.3.2/library/aes.o ext/polarssl-1.3.2/library/padlock.o dnslabeltext.o \
lua-pdns.o lua-recursor.o randomhelper.o recpacketcache.o dns.o \
-reczones.o base32.o nsecrecords.o json.o json_ws.o version.o responsestats.o
+reczones.o base32.o nsecrecords.o json.o json_ws.o version.o responsestats.o \
+session.o webserver.o ext/yahttp/yahttp/reqresp.o
REC_CONTROL_OBJECTS=rec_channel.o rec_control.o arguments.o misc.o \
unix_utility.o logger.o qtype.o
-rm -f dep *~ *.gcda *.gcno optional/*.gcda optional/*.gcno
binclean:
- -rm -f *.o pdns_recursor rec_control optional/*.o build-stamp ext/polarssl-1.3.2/library/*.o
+ -rm -f *.o pdns_recursor rec_control optional/*.o build-stamp ext/polarssl-1.3.2/library/*.o ext/yahttp/yahttp/*.o
dep:
$(CXX) $(CXXFLAGS) -MM -MG *.cc *.hh > $@
AM_CXXFLAGS=-DSYSCONFDIR=\"@sysconfdir@\" -DLIBDIR=\"@libdir@\" -DLOCALSTATEDIR=\"@socketdir@\" @THREADFLAGS@ $(LUA_CFLAGS) $(SQLITE3_CFLAGS) $(POLARSSL_CFLAGS) -Iext/rapidjson/include -Iext/yahttp
+YAHTTP_LIBS = -Lext/yahttp/yahttp -lyahttp
+
AM_LFLAGS = -i
AM_YFLAGS = -d --verbose --debug
pdns_server_LDFLAGS=@moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@ $(BOOST_SERIALIZATION_LDFLAGS) -rdynamic
-pdns_server_LDADD= $(POLARSSL_LIBS) $(BOOST_SERIALIZATION_LIBS) $(LUA_LIBS) $(SQLITE3_LIBS) -Lext/yahttp/yahttp -lyahttp
+pdns_server_LDADD= $(POLARSSL_LIBS) $(BOOST_SERIALIZATION_LIBS) $(LUA_LIBS) $(SQLITE3_LIBS) $(YAHTTP_LIBS)
if BOTAN110
pdns_server_SOURCES += botan110signers.cc botansigners.cc
dns_random.cc \
lua-pdns.cc lua-pdns.hh lua-recursor.cc lua-recursor.hh randomhelper.cc \
recpacketcache.cc recpacketcache.hh dns.cc nsecrecords.cc base32.cc cachecleaner.hh json_ws.cc json_ws.hh \
-json.cc json.hh version.hh version.cc responsestats.cc
+json.cc json.hh version.hh version.cc responsestats.cc webserver.cc webserver.hh session.cc session.hh
pdns_recursor_LDFLAGS= $(LUA_LIBS)
-pdns_recursor_LDADD= $(POLARSSL_LIBS)
+pdns_recursor_LDADD= $(POLARSSL_LIBS) $(YAHTTP_LIBS)
pdns_control_SOURCES=dynloader.cc dynmessenger.cc arguments.cc logger.cc statbag.cc \
misc.cc unix_utility.cc qtype.cc
mplexer.hh \
dns_random.hh lua-pdns.hh lua-recursor.hh namespaces.hh \
recpacketcache.hh base32.hh cachecleaner.hh json.hh version.hh \
-responsestats.hh"
+responsestats.hh webserver.hh session.hh"
CFILES="syncres.cc misc.cc unix_utility.cc qtype.cc \
logger.cc arguments.cc lwres.cc pdns_recursor.cc \
sillyrecords.cc lua-pdns.cc lua-recursor.cc randomhelper.cc \
devpollmplexer.cc recpacketcache.cc dns.cc reczones.cc base32.cc nsecrecords.cc \
dnslabeltext.cc json.cc json_ws.cc json_ws.hh version.cc dns_random.cc \
-responsestats.cc"
+responsestats.cc webserver.cc session.cc"
cd docs
make pdns_recursor.1 rec_control.1
cp -a ext/polarssl-1.3.2/include/polarssl/config.h ext/polarssl-1.3.2/include/polarssl/aes.h ext/polarssl-1.3.2/include/polarssl/padlock.h $DIRNAME/ext/polarssl-1.3.2/include/polarssl
mkdir -p $DIRNAME/ext/polarssl-1.3.2/library
cp -a ext/polarssl-1.3.2/library/aes.c ext/polarssl-1.3.2/library/padlock.c $DIRNAME/ext/polarssl-1.3.2/library
+cp -a ext/yahttp/ $DIRNAME/ext/
mkdir $DIRNAME/rrd
cp tools/rrd/{create,update,makegraphs,index.html} $DIRNAME/rrd
cp pdns-recursor.init.d $DIRNAME
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
+#include "webserver.hh"
using namespace rapidjson;
-JWebserver::JWebserver(FDMultiplexer* fdm) : d_fdm(fdm)
+JWebserver::JWebserver(FDMultiplexer* fdm)
{
RecursorControlParser rcp; // inits
- d_socket = socket(AF_INET6, SOCK_STREAM, 0);
- if(d_socket<0) {
- throw PDNSException("Making webserver socket: "+stringerror());
- }
- setSocketReusable(d_socket);
- ComboAddress local("::", 8082);
- if(bind(d_socket, (struct sockaddr*)&local, local.getSocklen())<0) {
- throw PDNSException("Binding webserver socket: "+stringerror());
- }
- listen(d_socket, 5);
-
- d_fdm->addReadFD(d_socket, boost::bind(&JWebserver::newConnection, this));
-}
-void JWebserver::readRequest(int fd)
-{
- char buffer[16384];
- int res = read(fd, buffer, sizeof(buffer)-1);
- if(res <= 0) {
- d_fdm->removeReadFD(fd);
- close(fd);
+ if(!arg().mustDo("webserver"))
return;
- }
- buffer[res]=0;
-
- // Note: this code makes it impossible to read the request body.
- // We'll at least need to wait for two \r\n sets to arrive, parse the
- // headers, and then read the body (using the supplied Content-Length).
- char *p = strchr(buffer, '\r');
- if(p) *p = 0;
-
- vector<string> parts;
- string method, uri;
- if(strlen(buffer) < 2048) {
- stringtok(parts, buffer);
- if(parts.size()>1) {
- method=parts[0];
- uri=parts[1];
- }
- }
- string content;
+ d_ws = new AsyncWebServer(fdm, arg()["webserver-address"], arg().asNum("webserver-port"), arg()["webserver-password"]);
- string status = "200 OK";
- string headers = "Date: Wed, 30 Nov 2012 22:01:15 GMT\r\n"
- "Server: PowerDNS Recursor/"VERSION"\r\n"
- "Connection: keep-alive\r\n";
+ // legacy dispatch
+ d_ws->registerApiHandler("/jsonstat", boost::bind(&JWebserver::jsonstat, this, _1, _2));
- if (method != "GET") {
- status = "400 Bad Request";
- content = "Your client sent a request this server could not understand.\n";
- } else {
- parts.clear();
- stringtok(parts, uri, "?");
- map<string, string> varmap;
- if(parts.size()>1) {
- vector<string> variables;
- stringtok(variables, parts[1], "&");
- BOOST_FOREACH(const string& var, variables) {
- varmap.insert(splitField(var, '='));
- }
- }
-
- content = handleRequest(method, uri, varmap, headers);
- }
-
- const char *headers_append = "Content-Length: %d\r\n\r\n";
- string reply = "HTTP/1.1 " + status + "\r\n" + headers +
- (boost::format(headers_append) % content.length()).str() +
- content;
-
- Utility::setBlocking(fd);
- writen2(fd, reply.c_str(), reply.length());
- Utility::setNonBlocking(fd);
+ d_ws->go();
}
-string JWebserver::handleRequest(const string &method, const string &uri, const map<string,string> &rovarmap, string &headers)
+void JWebserver::jsonstat(HttpRequest* req, HttpResponse *resp)
{
- map<string,string> varmap = rovarmap;
- string callback;
- if (varmap.count("callback")) {
- callback = varmap["callback"];
- varmap.erase("callback");
- }
string command;
- if (varmap.count("command")) {
- command = varmap["command"];
- varmap.erase("command");
- }
-
- headers += "Access-Control-Allow-Origin: *\r\n";
- headers += "Content-Type: application/json\r\n";
- string content;
- if(!callback.empty())
- content=callback+"(";
+ if(req->parameters.count("command")) {
+ command = req->parameters["command"];
+ req->parameters.erase("command");
+ }
map<string, string> stats;
if(command == "domains") {
doc.PushBack(jzone, doc.GetAllocator());
}
- content += makeStringFromDocument(doc);
+ resp->body = makeStringFromDocument(doc);
+ return;
}
else if(command == "zone") {
- SyncRes::domainmap_t::const_iterator ret = t_sstorage->domainmap->find(varmap["zone"]);
+ string arg_zone = req->parameters["zone"];
+ SyncRes::domainmap_t::const_iterator ret = t_sstorage->domainmap->find(arg_zone);
if (ret != t_sstorage->domainmap->end()) {
Document doc;
doc.SetObject();
root.AddMember("records", records, doc.GetAllocator());
doc.AddMember("zone", root, doc.GetAllocator());
- content += makeStringFromDocument(doc);
+ resp->body = makeStringFromDocument(doc);
+ return;
} else {
- content += returnJSONError("Could not find domain '"+varmap["zone"]+"'");
+ resp->body = returnJSONError("Could not find domain '"+arg_zone+"'");
+ return;
}
}
else if(command == "flush-cache") {
- string canon=toCanonic("", varmap["domain"]);
+ string canon=toCanonic("", req->parameters["domain"]);
int count = broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, canon));
count+=broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, canon));
stats["number"]=lexical_cast<string>(count);
- content += returnJSONObject(stats);
+ resp->body = returnJSONObject(stats);
+ return;
}
else if(command == "config") {
vector<string> items = ::arg().list();
BOOST_FOREACH(const string& var, items) {
stats[var] = ::arg()[var];
}
- content += returnJSONObject(stats);
+ resp->body = returnJSONObject(stats);
+ return;
}
else if(command == "log-grep") {
- content += makeLogGrepJSON(varmap["needle"], ::arg()["experimental-logfile"], " pdns_recursor[");
+ resp->body = makeLogGrepJSON(req->parameters["needle"], ::arg()["experimental-logfile"], " pdns_recursor[");
+ return;
}
- else { // if(command == "stats") {
+ else if(command == "stats") {
stats = getAllStatsMap();
- content += returnJSONObject(stats);
- }
-
- if(!callback.empty())
- content += ");";
-
- return content;
-}
-
-void JWebserver::newConnection()
-{
- ComboAddress remote;
- remote.sin4.sin_family=AF_INET6;
- socklen_t remlen = remote.getSocklen();
- int sock = accept(d_socket, (struct sockaddr*) &remote, &remlen);
- if(sock < 0)
+ resp->body = returnJSONObject(stats);
return;
-
- Utility::setNonBlocking(sock);
- d_fdm->addReadFD(sock, boost::bind(&JWebserver::readRequest, this, _1));
+ } else {
+ resp->status = 404;
+ resp->body = returnJSONError("Not found");
+ }
}
#include <boost/utility.hpp>
#include "namespaces.hh"
#include "mplexer.hh"
+#include "webserver.hh"
class JWebserver : public boost::noncopyable
{
- public:
- explicit JWebserver(FDMultiplexer* fdm);
- void newConnection();
- void readRequest(int fd);
- string handleRequest(const string &method, const string &uri, const map<string,string> &varmap, string &headers);
- private:
- FDMultiplexer* d_fdm;
- int d_socket;
+public:
+ explicit JWebserver(FDMultiplexer* fdm);
+ void jsonstat(HttpRequest* req, HttpResponse *resp);
+
+private:
+ AsyncWebServer* d_ws;
};
string returnJSONStats(const map<string, string>& items);
return ret;
}
-inline void setSocketReusable(int fd)
-{
- int tmp=1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp));
-}
-
string stripDot(const string& dom);
void seedRandom(const string& source);
string makeRelative(const std::string& fqdn, const std::string& zone);
::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
::arg().set( "experimental-logfile", "Filename of the log file for JSON parser" )= "/var/log/pdns.log";
::arg().setSwitch( "experimental-json-interface", "If we should run a JSON webserver") = "no";
+ ::arg().setSwitch("webserver", "Start a webserver for monitoring") = "no";
+ ::arg().set("webserver-address", "IP Address of webserver to listen on") = "127.0.0.1";
+ ::arg().set("webserver-port", "Port of webserver to listen on") = "8082";
+ ::arg().set("webserver-password", "Password required for accessing the webserver") = "";
::arg().set("quiet","Suppress logging of questions and answers")="";
::arg().set("logging-facility","Facility to log messages as. 0 corresponds to local0")="";
::arg().set("config-dir","Location of configuration directory (recursor.conf)")=SYSCONFDIR;
#include "misc.hh"
#include "iputils.hh"
-void Session::init()
+Session::Session(int s, ComboAddress r) : d_good(true)
{
- d_good = true;
+ d_remote=r;
+ d_socket=s;
}
-Session::Session(int s, struct sockaddr_in r)
+Session::Session() : d_good(false)
{
- init();
- d_remote=r;
- d_socket=s;
}
int Session::close()
//! This function makes a deep copy of Session
Session::Session(const Session &s)
{
- init();
-
d_socket=s.d_socket;
d_remote=s.d_remote;
+ d_good=s.d_good;
+ d_timeout=s.d_timeout;
}
void Session::setTimeout(unsigned int seconds)
d_timeout=seconds;
}
-
bool Session::put(const string &s)
{
int length=s.length();
return d_socket;
}
-Session *Server::accept()
+Session Server::accept()
{
- struct sockaddr_in d_remote;
- Utility::socklen_t len=sizeof(d_remote);
+ ComboAddress remote;
+ remote.sin4.sin_family = AF_INET6;
+ socklen_t remlen = remote.getSocklen();
- int d_socket=-1;
+ int socket=-1;
-
- while((d_socket=::accept(s,(struct sockaddr *)(&d_remote),&len))==-1) // repeat until we have a successful connect
+ while((socket=::accept(s, (struct sockaddr *)&remote, &remlen))==-1) // repeat until we have a successful connect
{
// L<<Logger::Error<<"accept() returned: "<<strerror(errno)<<endl;
if(errno==EMFILE) {
}
- return new Session(d_socket, d_remote);
+ Session session(socket, remote);
+ return session;
+}
+
+void Server::asyncNewConnection()
+{
+ try {
+ d_asyncNewConnectionCallback(accept());
+ } catch (SessionException &e) {
+ // we're running in a shared process/thread, so can't just terminate/abort.
+ return;
+ }
+}
+
+void Server::asyncWaitForConnections(FDMultiplexer* fdm, const newconnectioncb_t& callback)
+{
+ d_asyncNewConnectionCallback = callback;
+ fdm->addReadFD(s, boost::bind(&Server::asyncNewConnection, this));
}
Server::Server(const string &localaddress, int port)
throw SessionException(string("socket: ")+strerror(errno));
Utility::setCloseOnExec(s);
-
+
int tmp=1;
- if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
throw SessionException(string("Setsockopt failed: ")+strerror(errno));
if(bind(s, (sockaddr*)&d_local, d_local.getSocklen())<0)
#include "iputils.hh"
#include "pdnsexception.hh"
+#include "mplexer.hh"
class SessionException: public PDNSException
{
bool good();
size_t read(char* buf, size_t len);
- Session(int s, struct sockaddr_in r); //!< Start a session on an existing socket, and inform this class of the remotes name
+ Session(int s, ComboAddress r); //!< Start a session on an existing socket, and inform this class of the remotes name
/** Create a session to a remote host and port. This function reads a timeout value from the ArgvMap class
and does a nonblocking connect to support this timeout. It should be noted that nonblocking connects
Session(const string &remote, int port, int timeout=0);
Session(const Session &s);
+ Session();
~Session();
int getSocket(); //!< return the filedescriptor for layering violations
void setTimeout(unsigned int seconds);
private:
int d_socket;
- struct sockaddr_in d_remote;
- void init();
+ ComboAddress d_remote;
int d_timeout;
bool d_good;
};
{
public:
Server(const string &localaddress, int port);
- Session* accept(); //!< Call accept() in an endless loop to accept new connections
ComboAddress d_local;
+ Session accept(); //!< Call accept() in an endless loop to accept new connections
+
+ typedef boost::function< void(Session) > newconnectioncb_t;
+ void asyncWaitForConnections(FDMultiplexer* fdm, const newconnectioncb_t& callback);
+
private:
int s;
+ void asyncNewConnection();
+ newconnectioncb_t d_asyncNewConnectionCallback;
};
#endif /* SESSION_HH */
#include "dns.hh"
#include "base64.hh"
#include "json.hh"
+#include "mplexer.hh"
+
+const char* INVALID_REQUEST_RESPONSE = "HTTP/1.0 400 Bad Request\r\nConnection: close\r\n\r\nYour Browser sent a request that this server failed to understand.\r\n";
struct connectionThreadData {
WebServer* webServer;
- Session* client;
+ Session client;
};
int WebServer::B64Decode(const std::string& strInput, std::string& strOutput)
d_handlers.push_back(reg);
}
+static void apiWrapper(boost::function<void(HttpRequest*,HttpResponse*)> 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");
+ }
+
+ req->parameters.erase("_"); // jQuery cache buster
+
+ try {
+ handler(req, resp);
+ } catch (ApiException &e) {
+ string what = e.what();
+ resp->body = returnJSONError(what);
+ resp->status = 422;
+ return;
+ }
+
+ if(!callback.empty()) {
+ resp->body = callback + "(" + resp->body + ");";
+ }
+}
+
+void WebServer::registerApiHandler(const string& url, HandlerFunction handler) {
+ HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2);
+ 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) {
pthread_detach(pthread_self());
data->webServer->serveConnection(data->client);
- data->client->close();
- delete data->client;
+ data->client.close();
delete data;
return resp;
}
-void WebServer::serveConnection(Session* client)
+void WebServer::serveConnection(Session client)
try {
HttpRequest req;
YaHTTP::AsyncRequestLoader yarl(&req);
- client->setTimeout(5);
+ client.setTimeout(5);
bool complete = false;
try {
- while(client->good()) {
+ while(client.good()) {
int bytes;
char buf[1024];
- bytes = client->read(buf, sizeof(buf));
+ bytes = client.read(buf, sizeof(buf));
if (bytes) {
string data = string(buf, bytes);
if (yarl.feed(data)) {
}
if (!complete) {
- client->put("HTTP/1.0 400 Bad Request\r\nConnection: close\r\n\r\nYour Browser sent a request that this server failed to understand.\r\n");
+ client.put(INVALID_REQUEST_RESPONSE);
return;
}
HttpResponse resp = WebServer::handleRequest(req);
ostringstream ss;
resp.write(ss);
- client->put(ss.str());
+ client.put(ss.str());
}
catch(SessionTimeoutException &e) {
// L<<Logger::Error<<"Timeout in webserver"<<endl;
if(!d_server)
return;
try {
- Session *client;
pthread_t tid;
L<<Logger::Error<<"Launched webserver on " << d_server->d_local.toStringWithPort() <<endl;
- while((client=d_server->accept())) {
+ while(true) {
// will be freed by thread
connectionThreadData *data = new connectionThreadData;
data->webServer = this;
- data->client = client;
+ data->client = d_server->accept();
pthread_create(&tid, 0, &WebServerConnectionThreadStart, (void *)data);
}
}
exit(1);
}
+
+void AsyncWebServer::go()
+{
+ if (!d_server)
+ return;
+
+ d_server->asyncWaitForConnections(d_fdm, boost::bind(&AsyncWebServer::newConnection, this, _1));
+}
+
+void AsyncWebServer::newConnection(Session session)
+{
+ int fd = session.getSocket();
+ Utility::setNonBlocking(fd);
+ d_fdm->addReadFD(fd, boost::bind(&AsyncWebServer::serveConnection, this, session));
+}
+
+void AsyncWebServer::serveConnection(Session session)
+{
+ int fd = session.getSocket();
+ d_fdm->removeReadFD(fd);
+
+ try {
+ char buffer[16384];
+ int res = read(fd, buffer, sizeof(buffer)-1);
+ if (res <= 0) {
+ throw PDNSException("Reading from client failed");
+ return;
+ }
+ buffer[res]=0;
+
+ HttpRequest req;
+ YaHTTP::AsyncRequestLoader yarl(&req);
+
+ bool complete = false;
+ string reply;
+
+ try {
+ if (yarl.feed(buffer)) {
+ complete = true;
+ }
+ } catch (YaHTTP::ParseError &e) {
+ complete = false;
+ }
+
+ if (complete) {
+ HttpResponse resp = handleRequest(req);
+ ostringstream ss;
+ resp.write(ss);
+ reply = ss.str();
+ } else {
+ reply = INVALID_REQUEST_RESPONSE;
+ }
+
+ Utility::setBlocking(fd);
+ writen2(fd, reply.c_str(), reply.length());
+ Utility::setNonBlocking(fd);
+ }
+ catch(PDNSException &e) {
+ L<<Logger::Error<<"Exception in webserver: "<<e.reason<<endl;
+ }
+ catch(std::exception &e) {
+ L<<Logger::Error<<"STL Exception in webserver: "<<e.what()<<endl;
+ }
+ catch(...) {
+ L<<Logger::Error<<"Unknown exception in webserver"<<endl;
+ }
+
+ close(fd);
+}
#include <map>
#include <string>
#include <list>
-#include "yahttp/yahttp.hpp"
+#include <boost/utility.hpp>
+#include <yahttp/yahttp.hpp>
#include "namespaces.hh"
class Server;
HttpMethodNotAllowedException() : HttpException(405) { };
};
+class ApiException : public runtime_error
+{
+public:
+ ApiException(const string& what) : runtime_error(what) {
+ }
+};
-class WebServer
+class WebServer : public boost::noncopyable
{
public:
WebServer(const string &listenaddress, int port, const string &password="");
void go();
- void serveConnection(Session* client);
+ void serveConnection(Session client);
HttpResponse handleRequest(HttpRequest request);
typedef boost::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
};
void registerHandler(const string& url, HandlerFunction handler);
+ void registerApiHandler(const string& url, HandlerFunction handler);
-private:
+protected:
static char B64Decode1(char cInChar);
static int B64Decode(const std::string& strInput, std::string& strOutput);
bool route(const std::string& url, std::map<std::string, std::string>& urlArgs, HandlerFunction** handler);
string d_password;
Server* d_server;
};
+
+class FDMultiplexer;
+
+class AsyncWebServer : public WebServer
+{
+public:
+ AsyncWebServer(FDMultiplexer* fdm, const string &listenaddress, int port, const string &password="") :
+ WebServer(listenaddress, port, password), d_fdm(fdm) { };
+ void go();
+
+private:
+ FDMultiplexer* d_fdm;
+
+ void newConnection(Session session);
+ void serveConnection(Session session);
+};
+
#endif /* WEBSERVER_HH */
typedef map<string,string> varmap_t;
-class ApiException : public runtime_error
-{
-public:
- ApiException(const string& what) : runtime_error(what) {
- }
-};
-
StatWebServer::StatWebServer()
{
d_start=time(0);
return;
}
-static void apiWrapper(boost::function<void(HttpRequest*,HttpResponse*)> 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");
- }
-
- req->parameters.erase("_"); // jQuery cache buster
-
- try {
- handler(req, resp);
- } catch (ApiException &e) {
- string what = e.what();
- resp->body = returnJSONError(what);
- resp->status = 422;
- return;
- }
-
- if(!callback.empty()) {
- resp->body = callback + "(" + resp->body + ");";
- }
-}
-
-void StatWebServer::registerApiHandler(const string& url, boost::function<void(HttpRequest*,HttpResponse*)> handler) {
- WebServer::HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2);
- d_ws->registerHandler(url, f);
-}
-
void StatWebServer::cssfunction(HttpRequest* req, HttpResponse* resp)
{
resp->headers["Cache-Control"] = "max-age=86400";
{
try {
if(::arg().mustDo("experimental-json-interface")) {
- registerApiHandler("/servers/localhost/config", &apiServerConfig);
- registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog);
- registerApiHandler("/servers/localhost/statistics", &apiServerStatistics);
- registerApiHandler("/servers/localhost/zones/<id>/rrset", &apiServerZoneRRset);
- registerApiHandler("/servers/localhost/zones/<id>", &apiServerZoneDetail);
- registerApiHandler("/servers/localhost/zones", &apiServerZones);
- registerApiHandler("/servers/localhost", &apiServerDetail);
- registerApiHandler("/servers", &apiServer);
+ d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig);
+ d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog);
+ d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics);
+ d_ws->registerApiHandler("/servers/localhost/zones/<id>/rrset", &apiServerZoneRRset);
+ d_ws->registerApiHandler("/servers/localhost/zones/<id>", &apiServerZoneDetail);
+ d_ws->registerApiHandler("/servers/localhost/zones", &apiServerZones);
+ d_ws->registerApiHandler("/servers/localhost", &apiServerDetail);
+ d_ws->registerApiHandler("/servers", &apiServer);
// legacy dispatch
- registerApiHandler("/jsonstat", boost::bind(&StatWebServer::jsonstat, this, _1, _2));
+ d_ws->registerApiHandler("/jsonstat", boost::bind(&StatWebServer::jsonstat, this, _1, _2));
}
d_ws->registerHandler("/style.css", boost::bind(&StatWebServer::cssfunction, this, _1, _2));
d_ws->registerHandler("/", boost::bind(&StatWebServer::indexfunction, this, _1, _2));