summaryrefslogtreecommitdiff
path: root/server/src/server.cc
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/server.cc')
-rw-r--r--server/src/server.cc462
1 files changed, 115 insertions, 347 deletions
diff --git a/server/src/server.cc b/server/src/server.cc
index 727ac28..1f85479 100644
--- a/server/src/server.cc
+++ b/server/src/server.cc
@@ -26,6 +26,8 @@
*/
#include "server.h"
+#include <config.h>
+
#include "tcpsocket.h"
#include <errno.h>
@@ -36,388 +38,153 @@
#include <unistd.h>
#include <string.h>
+#include <microhttpd.h>
+
#include "configuration.h"
-#include "transaction.h"
-#include "transactionparser.h"
-#include "templateparser.h"
-#include "macroparser.h"
-
-#include "queryhandler.h"
-#include "queryhandlerpracro.h"
-#include "queryhandlerpentominos.h"
-
-#include "luaquerymapper.h"
-#include "database.h"
-#include "widgetgenerator.h"
-#include "resumeparser.h"
-#include "journal_commit.h"
-#include "xml_encode_decode.h"
-
-#include "macrolist.h"
-#include "templatelist.h"
-#include "versionstr.h"
-
-static std::string error_box(std::string message)
+#include "connection.h"
+#include "log.h"
+
+static int handle_request_callback(void *cls,
+ struct MHD_Connection *con,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *data,
+ unsigned int *data_size,
+ void **con_cls)
{
- std::string errorbox =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<pracro version=\"1.0\">\n"
- " <template name=\"error\">\n"
- " <macro name=\"error\" static=\"true\">\n"
- " <widgets caption=\"ERROR!\" layout=\"vbox\" name=\"error\">\n"
- " <textedit name=\"errorlabel\" value=\"" + message + "\"/>\n"
- " </widgets>\n"
- " </macro>\n"
- " </template>\n"
- "</pracro>\n";
- return errorbox;
-}
+ int ret = MHD_YES;
-class NotFoundException : public Exception {
-public:
- NotFoundException(Request &r)
- : Exception("Macro " + r.macro + " not found in template " + r.templ) {}
-};
+ Connection *connection = (Connection*)*con_cls;
+ PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n",
+ con, *con_cls);
-static std::string handleCommits(Transaction *transaction, Database &db,
- JournalWriter &journalwriter, MacroList &macrolist,
- TemplateList &templatelist)
-{
- std::string answer;
+ // Test for new connection
+ if(connection == NULL) {
+ std::string sessionid;
+ const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
+ "SessionID");
+ if(sid) sessionid = sid;
- Commits::iterator i = transaction->commits.begin();
- while(i != transaction->commits.end()) {
- Commit &commit = *i;
-
- MacroParser mp(macrolist.getLatestVersion(commit.macro));
- mp.parse();
- Macro *macro = mp.getMacro();
-
- std::string resume = resume_parser(macro->resume, commit);
- commit.fields["journal.resume"] = resume;
- db.commitTransaction(transaction->user, transaction->cpr, *macro, commit.fields);
-
- if(resume != "") {
+ const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
+ "SessionCommit");
- TemplateParser tp(templatelist.getLatestVersion(commit.templ));
- tp.parse();
- Template *templ = tp.getTemplate();
+ const char *sdc = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
+ "SessionDiscard");
- journalwriter.addEntry(*transaction, commit, resume, templ);
- }
-
- i++;
+ Environment *env = (Environment *)cls;
+ connection = new Connection(*env, sessionid, scm != NULL, sdc != NULL);
+ *con_cls = connection;
}
- return answer;
-}
-
-
-static std::string handleRequest(Transaction *transaction,
- Database &db,
- JournalWriter &journalwriter,
- MacroList &macrolist,
- TemplateList &templatelist)
-{
- std::string answer;
-
- // Reuse connection throughout entire template.
- QueryHandlerPentominos qh(transaction->cpr);
+ if(!connection) return MHD_NO;
- Requests::iterator i = transaction->requests.begin();
- while(i != transaction->requests.end()) {
- Request &request = *i;
+ if(connection->handle(data, *data_size)) {
+ std::string response = connection->getResponse();
- PRACRO_DEBUG(server, "Handling request - macro: %s, template: %s\n",
- request.macro.c_str(), request.templ.c_str());
-
- // Read and parse the template file.
- TemplateParser tp(templatelist.getLatestVersion(request.templ));
- tp.parse();
+ PRACRO_DEBUG(httpd, "Sending response: [[%s]]\n", response.c_str());
+
+ struct MHD_Response *rsp =
+ MHD_create_response_from_data(response.size(),
+ (void*)response.data(),
+ MHD_NO, // must free
+ MHD_YES); // must copy
- Template *templ = tp.getTemplate();
+ MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE,
+ "text/plain; charset=UTF-8");
- answer += " <template name=\"";
- answer += templ->attributes["name"];
- answer += "\" title=\"";
- answer += templ->attributes["title"];
- answer += "\">\n";
+ MHD_add_response_header(rsp, "SessionID",
+ connection->getSessionID().c_str());
- bool foundmacro = false;
+ ret = MHD_queue_response(con, MHD_HTTP_OK, rsp);
+ MHD_destroy_response(rsp);
- // Generate the macro and return it to the client
- std::vector< Macro >::iterator mi2 = templ->macros.begin();
- while(mi2 != templ->macros.end()) {
- Macro &macro = (*mi2);
-
- if(macro.isHeader) {
- answer += " <header caption=\"" + macro.attributes["caption"] + "\"/>\n";
- mi2++;
- continue;
- }
-
- bool completed = db.checkMacro(transaction->cpr,
- macro.attributes["name"],
- time(NULL)-Conf::db_max_ttl);
-
- answer += " <macro completed=";
- if(completed) answer += "\"true\"";
- else answer += "\"false\"";
-
- std::map< std::string, std::string >::iterator ai = macro.attributes.begin();
- while(ai != macro.attributes.end()) {
- std::string name = ai->first;
- std::string value = ai->second;
- answer += " "+name+"=\"" + value + "\"";
- ai++;
- }
-
- if(macro.attributes["name"] == request.macro ||
- (macro.attributes.find("static") != macro.attributes.end() &&
- macro.attributes["static"] == "true")
- ) {
- foundmacro = true;
-
- MacroParser mp(macrolist.getLatestVersion(macro.attributes["name"]));
- mp.parse();
- Macro *m = mp.getMacro();
- answer += " caption=\"" + m->widgets.attributes["caption"] + "\"";
- answer += ">\n";
-
- LUAQueryMapper lqm;
-
- ////////////////////////
- std::vector< Query >::iterator qi = m->queries.begin();
- while(qi != m->queries.end()) {
-
- Query &query = *qi;
- std::string service = query.attributes["service"];
-
- if(service == "pentominos") {
- // Send the queries to Pentominos (if any)
- QueryResult queryresult = qh.exec(*qi);
- lqm.addQueryResult(queryresult);
- }
-
- if(service == "pracro") {
- // Send the queries to Pracro (if any)
- QueryHandlerPracro qh(db, transaction->cpr);
-
- QueryResult queryresult = qh.exec(*qi);
- lqm.addQueryResult(queryresult);
- }
-
- qi++;
- }
-
- // Handle scripts
- if(m->scripts.size()) {
- answer += " <scripts>\n";
-
- std::vector< Script >::iterator spi = m->scripts.begin();
- while(spi != m->scripts.end()) {
- answer += " <script language=\"" + spi->attributes["language"]
- + "\" name=\"" + spi->attributes["name"] + "\">\n";
- answer += xml_encode(spi->attributes["code"]);
- answer += "\n </script>\n";
- spi++;
- }
- answer += " </scripts>\n";
- }
-
- answer += widgetgenerator(transaction->cpr, *m, lqm, db);
- } else {
- // only find macro title
- MacroParser mp(macrolist.getLatestVersion(macro.attributes["name"]));
- mp.parse();
- Macro *m = mp.getMacro();
- answer += " caption=\"" + m->widgets.attributes["caption"] + "\"";
- answer += ">\n";
-
- }
-
- if(completed) {
- answer += " <resume>";
- answer += xml_encode(db.getResume(transaction->cpr, macro, time(NULL) - Conf::db_max_ttl));
- answer += "</resume>\n";
- }
-
- answer += " </macro>\n";
- mi2++;
-
- }
-
- if(foundmacro == false && request.macro != "")
- throw NotFoundException(request);
-
- answer += " </template>\n";
-
- i++;
+ delete connection;
+ *con_cls = NULL;
}
- return answer;
+ *data_size = 0;
+
+ return ret;
}
-static std::string handleTransaction(Transaction *transaction,
- Database &db,
- JournalWriter &journalwriter,
- MacroList &macrolist,
- TemplateList &templatelist)
+void requestCompletedCallback(void *cls,
+ struct MHD_Connection *con,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
{
- std::string answer;
- answer += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- answer += "<pracro version=\"1.0\">\n";
-
- try {
- answer += handleCommits(transaction, db, journalwriter, macrolist, templatelist);
- } catch( std::exception &e ) {
- PRACRO_ERR(server, "Commit error: %s\n", e.what());
- return error_box(xml_encode(e.what()));
- }
+ PRACRO_DEBUG(httpd, "requestCompletedCallback %p\n", con);
- try {
- answer += handleRequest(transaction, db, journalwriter,
- macrolist, templatelist);
- } catch( std::exception &e ) {
- PRACRO_ERR(server, "Request error: %s\n", e.what());
- return error_box(xml_encode(e.what()));
+ // If connection was interrupted prematurely delete the content data here.
+ if(*con_cls) {
+ Connection *connection = (Connection*)*con_cls;
+ delete connection;
+ *con_cls = NULL;
}
-
- answer += "</pracro>\n";
-
- PRACRO_DEBUG(server, "Done handling transaction\n");
- PRACRO_DEBUG(serverxml, "%s\n", answer.c_str());
- return answer;
}
-
-static void handleConnection(TCPSocket *socket)
+static void httpderr(void *arg, const char *fmt, va_list ap)
{
- Database db(Conf::database_backend, Conf::database_addr, "", Conf::database_user, Conf::database_passwd, "");
-
- JournalWriter journalwriter(Conf::journal_commit_addr.c_str(), Conf::journal_commit_port);
-
- MacroList macrolist(Conf::xml_basedir + "/macros");
- TemplateList templatelist(Conf::xml_basedir + "/templates");
-
- ssize_t size;
- char buf[4096];
-
- Transaction *transaction = NULL;
- TransactionParser *parser = NULL;
-
- // while( (size = socket->read(buf, sizeof(buf))) != -1) {
- while( (size = socket->read(buf, sizeof(buf))) > 0) {
-
- PRACRO_DEBUG(server, "Read %d bytes from network\n", size);
-
- while(size) {
-
- if(transaction == NULL) {
- transaction = new Transaction();
- }
-
- if(parser == NULL) {
- parser = new TransactionParser(transaction);
- }
-
- PRACRO_DEBUG(server, "Got %d bytes in read loop\n", size);
- if(parser->parse(buf, size)) {
- PRACRO_DEBUG(server, "Got complete XML document %d bytes used, %d bytes in current buffer.\n", parser->usedBytes(), size);
-
- socket->write(handleTransaction(transaction, db, journalwriter,
- macrolist, templatelist));
- size = size - parser->usedBytes();
- if(size) {
- strcpy(buf, buf + parser->usedBytes());
- PRACRO_DEBUG(server, "Replaying %d bytes.\n", size);
- }
-
- delete transaction; transaction = NULL;
- delete parser; parser = NULL;
- } else {
- size = 0;
- memset(buf, 0, sizeof(buf));
- }
- }
- }
-
- if(transaction) {
- delete transaction;
- transaction = NULL;
- }
-
- if(parser) {
- delete parser;
- parser = NULL;
- }
-
- journalwriter.commit();
-
- PRACRO_DEBUG(server, "Out of read loop!\n");
+ PRACRO_ERR_VA(server, fmt, ap);
}
-//#define NON_FORKING
-#include <sys/socket.h>
extern bool pracro_is_running;
void server()
{
+ srand(time(NULL));
+
+ // bool forceshutdown = false;
port_t port = Conf::server_port;
- TCPSocket *socket = NULL;
-
- try {
- socket = new TCPSocket("Listen socket");
- socket->listen(port);
- } catch (Exception &e) {
- PRACRO_ERR_LOG(server, "Error in listen:\n%s\n", e.what());
- delete socket;
- socket = NULL;
+
+ int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY;
+ // | MHD_USE_PEDANTIC_CHECKS
+#ifndef WITHOUT_SSL
+ if(Conf::use_ssl) flags |= MHD_USE_SSL;
+#endif
+
+ PRACRO_DEBUG(server, "Server running on port %d.\n", port);
+
+ Environment env;
+
+ struct MHD_Daemon *d;
+ d = MHD_start_daemon(flags, port, NULL, NULL,
+ handle_request_callback, &env,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ requestCompletedCallback, NULL,
+ MHD_OPTION_CONNECTION_LIMIT, Conf::connection_limit,
+#ifndef WITHOUT_SSL
+ MHD_OPTION_HTTPS_MEM_KEY, Conf::ssl_key.c_str(),
+ MHD_OPTION_HTTPS_MEM_CERT, Conf::ssl_cert.c_str(),
+#endif
+ MHD_OPTION_CONNECTION_TIMEOUT, Conf::connection_timeout,
+ MHD_OPTION_EXTERNAL_LOGGER, httpderr, NULL,
+ MHD_OPTION_END);
+
+ if(!d) {
+ PRACRO_ERR(server, "Failed to initialise MHD_start_daemon!\n");
return;
}
-
- while(pracro_is_running && socket->connected()) {
-
- { // Reload if new port is assigned.
- int old_port = port;
- port = Conf::server_port;
-
- if(port != old_port) {
- // Start listening on the new port
- delete socket;
- socket = new TCPSocket("Listen socket (reloaded)");
- socket->listen(port);
- }
- }
-
- TCPSocket *child = socket->accept();
- if(child) {
-
-#ifndef NON_FORKING
- switch(fork()) {
- case -1: // error
- PRACRO_ERR_LOG(server, "Could not fork: %s\n", strerror(errno));
- break;
-
- case 0: // child
- delete socket;
-#endif/*NON_FORKING*/
- handleConnection(child);
- delete child;
-#ifndef NON_FORKING
- return;
-
- default: // parent
- delete child;
- break;
- }
-#endif/*NON_FORKING*/
-
+ // again:
+ while(pracro_is_running) sleep(1);
+ /*
+ if(!forceshutdown && env.sessions.size() != 0) {
+ char *errbuf;
+ if(asprintf(&errbuf, "There are %d live sessions."
+ " Kill again to force shutdown.\n",
+ env.sessions.size()) != -1) {
+ PRACRO_ERR_LOG(server, "%s", errbuf);
+ log(errbuf);
+ free(errbuf);
}
+ pracro_is_running = true;
+ forceshutdown = true;
+ goto again;
}
+ */
+ env.sessions.store();
- //socket->shutdown();
- delete socket;
+ MHD_stop_daemon(d);
PRACRO_DEBUG(server, "Server gracefully shut down.\n");
}
@@ -439,10 +206,11 @@ char request[] =
int main()
{
Conf::xml_basedir = "../xml/";
- Conf::server_port = 32100; // Make sure wo don't interrupt an already running server.
+ // Make sure wo don't interrupt an already running server.
+ Conf::server_port = 32100;
Conf::database_backend = "testdb";
pid_t pid = fork();
-
+
switch(pid) {
case -1: // error
perror("fork() failed!\n");