From 016e4ba553044edee32c2a20ee34463ad82106e8 Mon Sep 17 00:00:00 2001
From: deva <deva>
Date: Fri, 7 May 2010 12:36:13 +0000
Subject: Use connection object instead of struct and make xml parsing
 on-the-fly instead of collecting all data first.

---
 server/src/Makefile.am           |   2 +
 server/src/connection.cc         | 129 ++++++++++++++++++++++++++++++
 server/src/connection.h          |  57 +++++++++++++
 server/src/resumeparser.cc       |   3 +-
 server/src/server.cc             | 169 ++++++---------------------------------
 server/src/transactionhandler.cc |   5 +-
 server/src/transactionparser.cc  |   8 +-
 server/src/widgetgenerator.cc    |   8 +-
 8 files changed, 228 insertions(+), 153 deletions(-)
 create mode 100644 server/src/connection.cc
 create mode 100644 server/src/connection.h

(limited to 'server/src')

diff --git a/server/src/Makefile.am b/server/src/Makefile.am
index afc72f9..0a3569f 100644
--- a/server/src/Makefile.am
+++ b/server/src/Makefile.am
@@ -16,6 +16,7 @@ pracrod_SOURCES = \
 	database.cc \
 	configuration.cc \
 	configurationparser.cc \
+	connection.cc \
 	connectionpool.cc \
 	debug.cc \
 	entitylist.cc \
@@ -89,6 +90,7 @@ EXTRA_DIST = \
 	artefact.h \
 	configuration.h \
 	configurationparser.h \
+	connection.h \
 	connectionpool.h \
 	daemon.h \
 	database.h \
diff --git a/server/src/connection.cc b/server/src/connection.cc
new file mode 100644
index 0000000..36c030e
--- /dev/null
+++ b/server/src/connection.cc
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ *            connection.cc
+ *
+ *  Fri May  7 11:35:44 CEST 2010
+ *  Copyright 2010 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#include "connection.h"
+
+#include "transactionhandler.h"
+#include "xml_encode_decode.h"
+
+static std::string error_box(std::string message)
+{
+  std::string errorbox =
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+    "<pracro version=\"1.0\">\n"
+    "  <error>" + message + "</error>\n"
+    "</pracro>\n";
+  return errorbox;
+}
+
+Connection::Connection(Environment &e, std::string sid, bool c)
+  : env(e), parser(&transaction)
+{
+  PRACRO_DEBUG(connection, "[%p] CREATE\n", this);
+
+  sessionid = sid;
+  commit = c;
+}
+
+Connection::~Connection()
+{
+  PRACRO_DEBUG(connection, "[%p] DESTROY\n", this);
+}
+
+bool Connection::handle(const char *data, size_t size)
+{
+  Session *session = NULL;
+  if(sessionid == "") {
+    // Create new session
+    session = env.sessions.newSession();
+  } else {
+    // Attach to old session
+    session = env.sessions.session(sessionid);
+
+    // Session didn't exist - create a new one anyway.
+    if(session == NULL) session = env.sessions.newSession();
+  }
+  
+  if(session == NULL) {
+    PRACRO_ERR(connection, "New session could not be created.");
+    response = error_box(xml_encode("New session could not be created."));
+    return true;
+  }
+  
+  sessionid = session->id();
+
+  if(!data || !size) return true;
+
+  try {
+    if(parser.parse(data, size)) {
+      {
+        SessionAutolock lock(*session);
+        response = handleTransaction(transaction, env, *session);
+      }
+
+      if(commit) {
+        session->commit();
+        env.sessions.deleteSession(session->id());
+      }
+
+      return true;
+    }
+  } catch(...) {
+    PRACRO_ERR(server, "Failed to parse data!\n");
+    response = error_box(xml_encode("XML Parse error."));
+    return true;
+  }
+
+  return false;
+}
+
+std::string Connection::getResponse()
+{
+  return response;
+}
+
+std::string Connection::getSessionID()
+{
+  return sessionid;
+}
+
+#ifdef TEST_CONNECTION
+//Additional dependency files
+//deps:
+//Required cflags (autoconf vars may be used)
+//cflags:
+//Required link options (autoconf vars may be used)
+//libs:
+#include "test.h"
+
+TEST_BEGIN;
+
+// TODO: Put some testcode here (see test.h for usable macros).
+
+TEST_END;
+
+#endif/*TEST_CONNECTION*/
diff --git a/server/src/connection.h b/server/src/connection.h
new file mode 100644
index 0000000..f91ddc1
--- /dev/null
+++ b/server/src/connection.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ *            connection.h
+ *
+ *  Fri May  7 11:35:43 CEST 2010
+ *  Copyright 2010 Bent Bisballe Nyeng
+ *  deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ *  This file is part of Pracro.
+ *
+ *  Pracro is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Pracro is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Pracro; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#ifndef __PRACRO_CONNECTION_H__
+#define __PRACRO_CONNECTION_H__
+
+#include <string>
+#include "environment.h"
+#include "transaction.h"
+#include "transactionparser.h"
+
+class Connection {
+public:
+  Connection(Environment &e, std::string sessionid, bool commit);
+  ~Connection();
+
+  bool handle(const char *data, size_t size);
+
+  std::string getResponse();
+  std::string getSessionID();
+
+private:
+  std::string sessionid;
+  bool commit;
+  Environment &env;
+
+  Transaction transaction;
+  TransactionParser parser;
+
+  std::string response;
+};
+
+#endif/*__PRACRO_CONNECTION_H__*/
diff --git a/server/src/resumeparser.cc b/server/src/resumeparser.cc
index bf3483e..4d514ac 100644
--- a/server/src/resumeparser.cc
+++ b/server/src/resumeparser.cc
@@ -26,9 +26,10 @@
  */
 #include "resumeparser.h"
 
-#include "luaresume.h"
 #include <string.h>
 
+#include "luaresume.h"
+
 static std::string resume_parser_format(Resume &r, Commit &commit)
 {
   const char* format = r.attributes["format"].c_str();
diff --git a/server/src/server.cc b/server/src/server.cc
index 4283a11..118db85 100644
--- a/server/src/server.cc
+++ b/server/src/server.cc
@@ -41,84 +41,8 @@
 #include <microhttpd.h>
 
 #include "configuration.h"
-#include "transaction.h"
-#include "transactionparser.h"
-#include "database.h"
+#include "connection.h"
 #include "log.h"
-#include "environment.h"
-#include "transactionhandler.h"
-#include "connectionpool.h"
-#include "session.h"
-#include "xml_encode_decode.h"
-
-static std::string error_box(std::string message)
-{
-  std::string errorbox =
-    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-    "<pracro version=\"1.0\">\n"
-    "  <error>" + message + "</error>\n"
-    "</pracro>\n";
-  return errorbox;
-}
-
-static std::string handleConnection(const std::string &data,
-                                    Environment &env,
-                                    std::string &sessionid,
-                                    bool sessioncommit)
-{
-  std::string res;
-
-  Session *session = NULL;
-  if(sessionid == "") {
-    session = env.sessions.newSession();
-  } else {
-    session = env.sessions.session(sessionid);
-    if(session == NULL) session = env.sessions.newSession();
-  }
-
-  if(session == NULL) {
-    PRACRO_ERR(server, "New session could not be created.");
-    return error_box(xml_encode("New session could not be created."));
-  }
-
-  sessionid = session->id();
-
-  {
-    SessionAutolock lock(*session);
-
-    if(data.length()) {
-      Transaction transaction;
-      TransactionParser parser(&transaction);
-      
-      if(!parser.parse(data.c_str(), data.length())) {
-        PRACRO_ERR(server, "Failed to parse data!\n");
-        PRACRO_ERR(server, "DATA:[[%s]]\n", data.c_str());
-        res = error_box(xml_encode("XML Parse error."));
-      } else {
-        res = handleTransaction(transaction, env, *session);
-      }
-    }
-
-  }
-
-  if(sessioncommit) {
-    session->commit();
-    env.sessions.deleteSession(session->id());
-  }
-
-  return res;
-}
-
-struct condata {
-  std::map<std::string, std::string> headers;
-  std::string data;
-  size_t data_size;
-  std::string url;
-  std::string method;
-  std::string version;
-};
-
-//static std::map<struct MHD_Connection *, struct condata> condata;
 
 static int handle_request_callback(void *cls,
                                    struct MHD_Connection *con,
@@ -129,92 +53,51 @@ static int handle_request_callback(void *cls,
                                    unsigned int *data_size,
                                    void **con_cls)
 {
+  int ret = MHD_YES;
+
+  Connection *connection = (Connection*)*con_cls;
+
   PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n",
                con, *con_cls);
 
   // Test for new connection
-  if(*con_cls == NULL) {
-    PRACRO_DEBUG(httpd,
-                 "handle_request(url=\"%s\", method=\"%s\","
-                 " version=\"%s\", data_size=\"%d\")\n",
-                 url, method, version, *data_size);
-
-    struct condata *cd = new struct condata;
-    cd->url = url;
-    cd->method = method;
-    cd->version = version;
-    cd->data_size = 0;
-    cd->data = "";
-
+  if(connection == NULL) {
+    std::string sessionid;
     const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
                                                   "SessionID");
-    if(sid) cd->headers["SessionID"] = sid;
+    if(sid) sessionid = sid;
 
     const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
                                                   "SessionCommit");
-    if(scm) cd->headers["SessionCommit"] = scm;
-
-    const char *csz = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
-                                                  "Content-Length");
-    if(csz) {
-      cd->headers["Content-Length"] = csz;
-      cd->data_size = atol(csz);
-      PRACRO_DEBUG(httpd, "Content-Length: %s (%d)\n", csz, cd->data_size);
-    }
-
-    *con_cls = cd;
+    Environment *env = (Environment *)cls;
+    connection = new Connection(*env, sessionid, scm != NULL);
+    *con_cls = connection;
   }
 
-  struct condata *cd = (struct condata*)*con_cls;
-  cd->data.append(data, *data_size);
-
-  int ret = MHD_YES;
+  if(!connection) return MHD_NO;
 
-  PRACRO_DEBUG(httpd, "Waiting for %d bytes of data. Got %d (total %d)\n",
-               cd->data_size, *data_size, cd->data.length());
-
-  if(cd->data.length() >= cd->data_size) {
-
-    Environment *env = (Environment *)cls;
-
-    const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
-                                                  "SessionID");
-    std::string sessionid;
-    if(sid) sessionid = sid;
-    
-    const char *sessioncommit =
-      MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionCommit");
-    
-    PRACRO_DEBUG(httpd, "Starting to handle SessionID: %s%s\n",
-                 sessionid.c_str(),
-                 sessioncommit != NULL?" COMMIT":"");
-    
-    PRACRO_DEBUG(httpd, "Data: [%s]\n", cd->data.c_str());
-    
-    std::string reply =
-      handleConnection(cd->data, *env, sessionid, sessioncommit != NULL);
+  if(connection->handle(data, *data_size)) {
+    std::string response = connection->getResponse();
     
+    PRACRO_DEBUG(httpd, "Sending response: [[%s]]\n", response.c_str());
+
     struct MHD_Response *rsp =
-      MHD_create_response_from_data(reply.length(), (char*)reply.c_str(),
-                                    MHD_NO, MHD_YES);
+      MHD_create_response_from_data(response.size(),
+                                    (void*)response.data(),
+                                    MHD_NO,   // must free
+                                    MHD_YES); // must copy
+    
     MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE,
                             "text/plain; charset=UTF-8");
     
-    if(sessionid != "") {
-      MHD_add_response_header(rsp, "SessionID", sessionid.c_str());
-    }
+    MHD_add_response_header(rsp, "SessionID",
+                            connection->getSessionID().c_str());
     
     ret = MHD_queue_response(con, MHD_HTTP_OK, rsp);
     MHD_destroy_response(rsp);
     
-    PRACRO_DEBUG(httpd, "Finished handling SessionID: %s%s\n",
-                 sessionid.c_str(),
-                 sessioncommit != NULL?" COMMIT":"");
-
-
-    delete cd;
+    delete connection;
     *con_cls = NULL;
-
   }
 
   *data_size = 0;
@@ -231,8 +114,8 @@ void requestCompletedCallback(void *cls,
 
   // If connection was interrupted prematurely delete the content data here.
   if(*con_cls) {
-    struct condata *cd = (struct condata*)*con_cls;
-    delete cd;
+    Connection *connection = (Connection*)*con_cls;
+    delete connection;
     *con_cls = NULL;
   }
 }
diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc
index 7a45800..662896b 100644
--- a/server/src/transactionhandler.cc
+++ b/server/src/transactionhandler.cc
@@ -219,8 +219,9 @@ static std::string handleRequest(Transaction &transaction, Environment &env,
 
         if(completed) {
           answer += "      <resume>";
-          answer += db->getResume(transaction.cpr, macro, time(NULL) -
-                                  Conf::db_max_ttl);
+          answer += xml_encode(db->getResume(transaction.cpr,
+                                             macro,
+                                             time(NULL) - Conf::db_max_ttl));
           answer += "</resume>\n";
         }
 
diff --git a/server/src/transactionparser.cc b/server/src/transactionparser.cc
index 7422a13..7595bb2 100644
--- a/server/src/transactionparser.cc
+++ b/server/src/transactionparser.cc
@@ -72,17 +72,17 @@ void TransactionParser::startTag(std::string name,
   if(name == "field") {
     if(!transaction->commits.size()) {
       PRACRO_ERR(transactionparser, "Field without a commit tag!");
-      return;
+      throw std::exception();
     }
 
     if(attributes.find("name") == attributes.end()) {
       PRACRO_ERR(transactionparser, "Field is missing 'name' attribute");
-      return;
+      throw std::exception();
     }
 
     if(attributes.find("value") == attributes.end()) {
       PRACRO_ERR(transactionparser, "Field is missing 'value' attribute");
-      return;
+      throw std::exception();
     }
 
     transaction->commits.back().fields[attributes["name"]] =
@@ -101,6 +101,8 @@ void TransactionParser::parseError(const char *buf, size_t len,
 
   PRACRO_ERR(transactionparser, "\tBuffer %u bytes: [%s]\n",
              len, xml.c_str());
+
+  throw std::exception();
 }
 
 #ifdef TEST_TRANSACTIONPARSER
diff --git a/server/src/widgetgenerator.cc b/server/src/widgetgenerator.cc
index 1e60479..425c71e 100644
--- a/server/src/widgetgenerator.cc
+++ b/server/src/widgetgenerator.cc
@@ -109,9 +109,9 @@ static std::string send_macro_widget(Macro &macro,
     if(luamap != "") {
       Value value = mapper.map(luamap);
       if(value.timestamp > now - Conf::pentominos_max_ttl) {
-        widget.attributes["value"] = xml_encode(value.value);
+        widget.attributes["value"] = value.value;
         timestamp = value.timestamp;
-        prefilled = xml_encode(value.source);
+        prefilled = value.source;
       }
 
       PRACRO_DEBUG(prefill, "map: (%s, %d)\n",
@@ -137,7 +137,7 @@ static std::string send_macro_widget(Macro &macro,
     
     if(values[widget.attributes["name"]].timestamp > timestamp) {
       if(values[widget.attributes["name"]].timestamp > now - Conf::db_max_ttl) {
-        widget.attributes["value"] = xml_encode(values[widget.attributes["name"]].value);
+        widget.attributes["value"] = values[widget.attributes["name"]].value;
         timestamp = values[widget.attributes["name"]].timestamp;
         prefilled = "pracro";
       }
@@ -152,7 +152,7 @@ static std::string send_macro_widget(Macro &macro,
   while(p != widget.attributes.end()) {
     if(p->first != "tagname" && p->first != "map") {
       if( ! (p->first == "name" && p->second == "") )
-        result += " " + p->first + "=\"" + p->second + "\"";
+        result += " " + p->first + "=\"" + xml_encode(p->second) + "\"";
     }
     p++;
   }
-- 
cgit v1.2.3