diff options
Diffstat (limited to 'server/src/server.cc')
-rw-r--r-- | server/src/server.cc | 462 |
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 ¯olist, - 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 ¯olist, - 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 ¯o = (*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 ¯olist, - 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"); |