summaryrefslogtreecommitdiff
path: root/project2/cgi
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2011-02-25 14:53:40 +0000
committerrandomdan <randomdan@localhost>2011-02-25 14:53:40 +0000
commitb5e7fca95a6a29b84d987f61414e0c27cf2a0ddf (patch)
treed79f16f16ecb190edf1cac3d8d2f554e403632f3 /project2/cgi
parentFinally convert ytfs to use boost stuff (untested, but it does build) (diff)
downloadproject2-b5e7fca95a6a29b84d987f61414e0c27cf2a0ddf.tar.bz2
project2-b5e7fca95a6a29b84d987f61414e0c27cf2a0ddf.tar.xz
project2-b5e7fca95a6a29b84d987f61414e0c27cf2a0ddf.zip
Build a common base for loading p2 xml scripts with proper error checking
Extend cgiAppEngine to be able to return data documents specifying an error (requires Apache 2.2.16 and to have filter-errordocs set) Allow cgi requests to return a default document, suitable for 'yes, I did that' type responses Updates to GentooBrowse to use these features
Diffstat (limited to 'project2/cgi')
-rw-r--r--project2/cgi/cgiAppEngine.cpp238
-rw-r--r--project2/cgi/cgiAppEngine.h83
-rw-r--r--project2/cgi/cgiCommon.cpp17
-rw-r--r--project2/cgi/cgiEnvironment.cpp8
-rw-r--r--project2/cgi/cgiEnvironment.h4
5 files changed, 273 insertions, 77 deletions
diff --git a/project2/cgi/cgiAppEngine.cpp b/project2/cgi/cgiAppEngine.cpp
index 40d1431..043da76 100644
--- a/project2/cgi/cgiAppEngine.cpp
+++ b/project2/cgi/cgiAppEngine.cpp
@@ -1,14 +1,15 @@
#include "cgiAppEngine.h"
#include <cgicc/Cgicc.h>
-#include <cgicc/HTTPContentHeader.h>
+#include <cgicc/HTTPStatusHeader.h>
#include "cgiEnvironment.h"
-#include <libxml/xinclude.h>
#include "../xmlObjectLoader.h"
#include "../iterate.h"
+#include "../logger.h"
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <boost/foreach.hpp>
#include "../sessionXml.h"
+#include <cxxabi.h>
const std::string SESSIONID = "sessionID";
typedef UUID SIDKey;
@@ -18,27 +19,64 @@ SessionContainer * sessionsContainer = new SessionContainerXml();
SimpleMessageException(UnknownDomain);
+class Project2HttpHeader : public cgicc::HTTPStatusHeader {
+ public:
+ typedef std::map<std::string, const Glib::ustring> Headers;
+ Project2HttpHeader(int c, const std::string & m, const std::string & t) :
+ cgicc::HTTPStatusHeader(c, m)
+ {
+ addHeader("Content-Type", t);
+ }
+ void addHeader(const std::string & name, const Glib::ustring & value) {
+ headers.erase(name);
+ headers.insert(Headers::value_type(name, value));
+ }
+ void render(std::ostream & out) const {
+ BOOST_FOREACH(const Headers::value_type & h, headers) {
+ out << h.first << ": " << h.second << std::endl;
+ }
+ cgicc::HTTPStatusHeader::render(out);
+ }
+ private:
+ Headers headers;
+};
+
+static
+int
+xmlWrite(void * _out, const char * buf, int len)
+{
+ std::ostream * IO = static_cast<std::ostream*>(_out);
+ IO->write(buf, len);
+ return len;
+}
+
CgiApplicationEngine::CgiApplicationEngine(const CgiEnvironment * e) :
ApplicationEngine("web/host"),
- _env(e),
- header(NULL)
+ _env(e)
{
BOOST_FOREACH(const cgicc::HTTPCookie c, e->getCookieList()) {
if (c.getName() == SESSIONID) {
sessionID = c.getValue();
}
}
- if (_env->getRequestMethod() == "POST") {
- currentStage = new RequestStage(this, e->elems[0]);
+ try {
+ if (_env->getRequestMethod() == "POST") {
+ currentStage = new RequestStage(e->elems[0]);
+ }
+ else {
+ currentStage = new PresentStage(e->elems.size() > 0 ? e->elems[0] : "index");
+ }
+ }
+ catch (const XmlScriptParser::NotFound & nf) {
+ currentStage = new NotFoundStage(_env, nf);
}
- else {
- currentStage = new PresentStage(this, e->elems.size() > 0 ? e->elems[0] : "index");
+ catch (const std::exception & ex) {
+ currentStage = new ErrorStage(_env, ex);
}
}
CgiApplicationEngine::~CgiApplicationEngine()
{
- delete header;
}
const Environment *
@@ -48,45 +86,50 @@ CgiApplicationEngine::env() const
}
void
-CgiApplicationEngine::write(const XmlWriter & w) const
+CgiApplicationEngine::write(std::ostream & IO) const
{
- w(doc->cobj());
-}
-
-void
-CgiApplicationEngine::process() const
-{
- while (!doc && currentStage) {
- currentStage = currentStage->run();
- }
+ HttpHeaderPtr header = currentStage->getHeader();
if (!sessionID.is_nil()) {
header->setCookie(cgicc::HTTPCookie(SESSIONID, sessionID.str(), "Session ID",
_env->getServerName().substr(_env->getServerName().find(".")), 3600, "/", false));
}
+ header->render(IO);
+ xmlOutputBufferPtr out = xmlOutputBufferCreateIO(
+ xmlWrite, NULL, &IO, xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8));
+ xmlSaveFileTo(out, currentStage->getDataDocument()->cobj(), "utf-8");
#ifndef NDEBUG
if (!_env->dumpdatadoc.empty()) {
- doc->write_to_file_formatted(_env->dumpdatadoc);
+ currentStage->getDataDocument()->write_to_file_formatted(_env->dumpdatadoc);
}
#endif
}
-CgiApplicationEngine::Stage::Stage(const CgiApplicationEngine * e) : appEngine(e)
+void
+CgiApplicationEngine::process() const
{
+ try {
+ for (StagePtr nextStage; (nextStage = currentStage->run()); ) {
+ currentStage = nextStage;
+ }
+ }
+ catch (const XmlScriptParser::NotFound & nf) {
+ currentStage = new NotFoundStage(_env, nf);
+ }
+ catch (const std::exception & ex) {
+ currentStage = new ErrorStage(_env, ex);
+ }
}
-CgiApplicationEngine::Stage::~Stage()
+CgiApplicationEngine::Stage::Stage()
{
}
-CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & id) :
- CgiApplicationEngine::Stage(e),
- XmlPresenter("present", id)
+CgiApplicationEngine::Stage::~Stage()
{
}
-CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & group, const std::string & id) :
- CgiApplicationEngine::Stage(e),
- XmlPresenter(group, id)
+CgiApplicationEngine::PresentStage::PresentStage(const std::string & id) :
+ XmlProcessPresenter("present", id, false)
{
}
@@ -99,16 +142,28 @@ CgiApplicationEngine::PresentStage::run()
{
BOOST_FOREACH(ParamCheckers::value_type pc, parameterChecks.get<bySOOrder>()) {
if (!pc->performCheck()) {
- return new PresentStage(appEngine, pc->present);
+ return new PresentStage(pc->present);
}
}
execute();
- appEngine->doc = getDataDocument();
- appEngine->header = new cgicc::HTTPContentHeader(contentType);
sessionsContainer->CleanUp();
return NULL;
}
+CgiApplicationEngine::HttpHeaderPtr
+CgiApplicationEngine::PresentStage::getHeader() const
+{
+ Project2HttpHeader * header = new Project2HttpHeader(200, "OK", contentType);
+ header->addHeader("Cache-control", "no-cache");
+ return HttpHeaderPtr(header);
+}
+
+CgiApplicationEngine::XmlDocPtr
+CgiApplicationEngine::PresentStage::getDataDocument() const
+{
+ return XmlProcessPresenter::getDataDocument();
+}
+
void
CgiApplicationEngine::addAppData(const Presenter * p) const
{
@@ -144,11 +199,9 @@ CgiApplicationEngine::addAppData(const Presenter * p) const
}
}
-CgiApplicationEngine::RequestStage::RequestStage(const CgiApplicationEngine * e, const std::string & id) :
- CgiApplicationEngine::Stage(e)
+CgiApplicationEngine::RequestStage::RequestStage(const std::string & id)
{
- xmlpp::DomParser request("request/" + id + ".xml");
- while (xmlXIncludeProcessFlags(request.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0);
+ XmlScriptParser request("request", id, false);
xmlpp::Element * requestRoot = request.get_document()->get_root_node();
present = requestRoot->get_attribute_value("present");
@@ -166,7 +219,7 @@ CgiApplicationEngine::RequestStage::run()
{
BOOST_FOREACH(const ParamCheckers::value_type & pc, parameterChecks.get<bySOOrder>()) {
if (!pc->performCheck()) {
- return new PresentStage(appEngine, pc->present);
+ return new PresentStage(pc->present);
}
}
try {
@@ -177,7 +230,7 @@ CgiApplicationEngine::RequestStage::run()
BOOST_FOREACH(const DataSources::value_type & ds, datasources) {
ds->commit();
}
- return new PresentStage(appEngine, present);
+ return present.empty() ? NULL : new PresentStage(present);
}
catch (...) {
// Do something about the error
@@ -188,6 +241,31 @@ CgiApplicationEngine::RequestStage::run()
}
}
+CgiApplicationEngine::HttpHeaderPtr
+CgiApplicationEngine::RequestStage::getHeader() const
+{
+ return HttpHeaderPtr(new Project2HttpHeader(200, "OK", "text/xml"));
+}
+
+CgiApplicationEngine::XmlDocPtr
+CgiApplicationEngine::RequestStage::getDataDocument() const
+{
+ return XmlPresenter::getDataDocument();
+}
+
+const Glib::ustring CgiApplicationEngine::RequestStage::resp("request");
+const Glib::ustring CgiApplicationEngine::RequestStage::style;
+const Glib::ustring &
+CgiApplicationEngine::RequestStage::getResponseRootNodeName() const
+{
+ return resp;
+}
+const Glib::ustring &
+CgiApplicationEngine::RequestStage::getResponseStyle() const
+{
+ return style;
+}
+
SessionPtr
CgiApplicationEngine::session() const
{
@@ -217,3 +295,89 @@ CgiApplicationEngine::loadEngineSection(const xmlpp::Element * e) const
domplat.push_back(DomainPlatforms::value_type(e->get_attribute_value("name"), e->get_attribute_value("platform")));
}
+CgiApplicationEngine::FailStage::FailStage(const CgiEnvironment * e) :
+ env(e)
+{
+}
+
+CgiApplicationEngine::FailStage::~FailStage()
+{
+}
+
+const Glib::ustring &
+CgiApplicationEngine::FailStage::getResponseStyle() const
+{
+ return env->errorTransformStyle;
+}
+
+CgiApplicationEngine::StagePtr
+CgiApplicationEngine::FailStage::run()
+{
+ return NULL;
+}
+
+CgiApplicationEngine::NotFoundStage::NotFoundStage(const CgiEnvironment * e, const XmlScriptParser::NotFound & nf) :
+ CgiApplicationEngine::FailStage(e)
+{
+ initDoc();
+ responseDoc->get_root_node()->add_child("resource")->set_child_text(nf.what());
+}
+
+CgiApplicationEngine::NotFoundStage::~NotFoundStage()
+{
+}
+
+CgiApplicationEngine::HttpHeaderPtr
+CgiApplicationEngine::NotFoundStage::getHeader() const
+{
+ return HttpHeaderPtr(new Project2HttpHeader(404, "Not found", env->errorContentType));
+}
+
+const Glib::ustring CgiApplicationEngine::NotFoundStage::resp("notfound");
+const Glib::ustring &
+CgiApplicationEngine::NotFoundStage::getResponseRootNodeName() const
+{
+ return resp;
+}
+
+CgiApplicationEngine::XmlDocPtr
+CgiApplicationEngine::NotFoundStage::getDataDocument() const
+{
+ return XmlPresenter::getDataDocument();
+}
+
+CgiApplicationEngine::ErrorStage::ErrorStage(const CgiEnvironment * e, const std::exception & ex) :
+ CgiApplicationEngine::FailStage(e)
+{
+ initDoc();
+ char * buf = __cxxabiv1::__cxa_demangle(typeid(ex).name(), NULL, NULL, NULL);
+ Logger()->messagef(LOG_ERR, "%s: Request errored: %s: %s", __FUNCTION__, buf, ex.what());
+ responseDoc->get_root_node()->add_child("type")->set_child_text(buf);
+ responseDoc->get_root_node()->add_child("what")->set_child_text(ex.what());
+ free(buf);
+}
+
+CgiApplicationEngine::ErrorStage::~ErrorStage()
+{
+}
+
+CgiApplicationEngine::HttpHeaderPtr
+CgiApplicationEngine::ErrorStage::getHeader() const
+{
+ return HttpHeaderPtr(new Project2HttpHeader(500, "Internal Server Error", env->errorContentType));
+}
+
+const Glib::ustring CgiApplicationEngine::ErrorStage::resp("error");
+const Glib::ustring &
+CgiApplicationEngine::ErrorStage::getResponseRootNodeName() const
+{
+ return resp;
+}
+
+CgiApplicationEngine::XmlDocPtr
+CgiApplicationEngine::ErrorStage::getDataDocument() const
+{
+ return XmlPresenter::getDataDocument();
+}
+
+
diff --git a/project2/cgi/cgiAppEngine.h b/project2/cgi/cgiAppEngine.h
index 1425207..79638f4 100644
--- a/project2/cgi/cgiAppEngine.h
+++ b/project2/cgi/cgiAppEngine.h
@@ -8,36 +8,32 @@
#include "../commonObjects.h"
#include "../uuid.h"
#include <boost/intrusive_ptr.hpp>
-#include <boost/function.hpp>
-#include <libxml++/document.h>
-#include <libxml++/parsers/domparser.h>
class CgiEnvironment;
class Session;
namespace cgicc {
- class HTTPContentHeader;
+ class HTTPHeader;
+}
+namespace xmlpp {
+ class Document;
}
class CgiApplicationEngine : public ApplicationEngine {
public:
typedef boost::shared_ptr<xmlpp::Document> XmlDocPtr;
- typedef boost::function1<void, xmlDocPtr> XmlWriter;
+ typedef boost::shared_ptr<cgicc::HTTPHeader> HttpHeaderPtr;
CgiApplicationEngine(const CgiEnvironment *);
virtual ~CgiApplicationEngine();
void process() const;
- const cgicc::HTTPContentHeader * getHeader() const { return header; }
- void write(const XmlWriter & w) const;
+ void write(std::ostream &) const;
const Environment * env() const;
SessionPtr session() const;
virtual Glib::ustring resolveCurrentConfig() const;
void addAppData(const Presenter * p) const;
const CgiEnvironment * _env;
- protected:
- mutable cgicc::HTTPContentHeader * header;
-
private:
typedef std::pair<Glib::ustring, Glib::ustring> DomainPlatform;
typedef std::list<DomainPlatform> DomainPlatforms;
@@ -45,39 +41,84 @@ class CgiApplicationEngine : public ApplicationEngine {
bool checkDomain(const DomainPlatforms::value_type & i) const;
void loadEngineSection(const xmlpp::Element *) const;
- mutable XmlDocPtr doc;
-
class Stage;
typedef boost::intrusive_ptr<Stage> StagePtr;
class Stage : public virtual CommonObjects {
public:
- Stage(const CgiApplicationEngine *);
+ Stage();
virtual ~Stage() = 0;
virtual StagePtr run() = 0;
- protected:
- const CgiApplicationEngine * appEngine;
+ virtual XmlDocPtr getDataDocument() const = 0;
+ virtual HttpHeaderPtr getHeader() const = 0;
};
- class RequestStage : public Stage {
+ class RequestStage : public Stage, public XmlPresenter {
public:
- RequestStage(const CgiApplicationEngine *, const std::string & id);
+ RequestStage(const std::string & id);
virtual ~RequestStage();
+
virtual StagePtr run();
- std::string present;
+ virtual XmlDocPtr getDataDocument() const;
+ virtual HttpHeaderPtr getHeader() const;
+ virtual const Glib::ustring & getResponseRootNodeName() const;
+ virtual const Glib::ustring & getResponseStyle() const;
+
protected:
+ std::string present;
typedef Storage<ParamChecker>::Objects ParamCheckers;
ParamCheckers parameterChecks;
typedef Storage<NoOutputExecute>::Objects Tasks;
Tasks tasks;
+
+ private:
+ static const Glib::ustring resp;
+ static const Glib::ustring style;
};
- class PresentStage : public Stage, public XmlPresenter {
+ class PresentStage : public Stage, public XmlProcessPresenter {
public:
- PresentStage(const CgiApplicationEngine *, const std::string & id);
- PresentStage(const CgiApplicationEngine *, const std::string & group, const std::string & id);
+ PresentStage(const std::string & id);
virtual ~PresentStage();
+
+ virtual StagePtr run();
+ virtual XmlDocPtr getDataDocument() const;
+ virtual HttpHeaderPtr getHeader() const;
+ };
+
+ class FailStage : public Stage, public XmlPresenter {
+ public:
+ FailStage(const CgiEnvironment *);
+ virtual ~FailStage();
+
+ virtual const Glib::ustring & getResponseStyle() const;
virtual StagePtr run();
+
+ protected:
+ const CgiEnvironment * env;
+ };
+
+ class NotFoundStage : public FailStage {
+ public:
+ NotFoundStage(const CgiEnvironment *, const XmlScriptParser::NotFound &);
+ virtual ~NotFoundStage();
+
+ virtual XmlDocPtr getDataDocument() const;
+ virtual HttpHeaderPtr getHeader() const;
+ virtual const Glib::ustring & getResponseRootNodeName() const;
+ private:
+ static const Glib::ustring resp;
+ };
+
+ class ErrorStage : public FailStage {
+ public:
+ ErrorStage(const CgiEnvironment *, const std::exception &);
+ virtual ~ErrorStage();
+ virtual XmlDocPtr getDataDocument() const;
+ virtual HttpHeaderPtr getHeader() const;
+ virtual const Glib::ustring & getResponseRootNodeName() const;
+ private:
+ static const Glib::ustring resp;
};
mutable StagePtr currentStage;
mutable UUID sessionID;
diff --git a/project2/cgi/cgiCommon.cpp b/project2/cgi/cgiCommon.cpp
index 322bdf7..fa40d2b 100644
--- a/project2/cgi/cgiCommon.cpp
+++ b/project2/cgi/cgiCommon.cpp
@@ -10,15 +10,6 @@
#include <boost/bind.hpp>
#include <cxxabi.h>
-static
-int
-xmlWrite(void * _out, const char * buf, int len)
-{
- std::ostream * IO = static_cast<std::ostream*>(_out);
- IO->write(buf, len);
- return len;
-}
-
// These are templates because some people don't inherit their
// exceptions from std::exception like normal people (Glib)
const char *
@@ -57,14 +48,8 @@ cgiServe(cgicc::CgiInput * i, std::ostream & IO)
Logger()->messagef(LOG_DEBUG, "%s: Processing request", __FUNCTION__);
app.process();
-
Logger()->messagef(LOG_DEBUG, "%s: Sending request result", __FUNCTION__);
- IO << "Cache-control: no-cache" << std::endl;
- app.getHeader()->render(IO);
- xmlOutputBufferPtr out = xmlOutputBufferCreateIO(
- xmlWrite, NULL, &IO, xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8));
- app.write(boost::bind(xmlSaveFileTo, out, _1, "utf-8"));
-
+ app.write(IO);
Logger()->messagef(LOG_DEBUG, "%s: Completed request", __FUNCTION__);
}
catch (const std::exception & e) {
diff --git a/project2/cgi/cgiEnvironment.cpp b/project2/cgi/cgiEnvironment.cpp
index f858262..3fdb158 100644
--- a/project2/cgi/cgiEnvironment.cpp
+++ b/project2/cgi/cgiEnvironment.cpp
@@ -27,12 +27,16 @@ boost::program_options::options_description
CgiEnvironment::addOptions(boost::program_options::positional_options_description &)
{
boost::program_options::options_description cgi("Project2 CGI options");
-#ifndef NDEBUG
cgi.add_options()
+ ("errorcontenttype", boost::program_options::value<Glib::ustring>(&errorContentType)->default_value("text/xml"),
+ "The Content-Type to use in HTTP headers in event of an error")
+ ("errortransformstyle", boost::program_options::value<Glib::ustring>(&errorTransformStyle),
+ "The xml-stylesheet to specify in the data document in event of an error")
+#ifndef NDEBUG
("dumpdatadoc", boost::program_options::value<std::string>(&dumpdatadoc),
"Write a copy of the data document before sending it to the web server")
- ;
#endif
+ ;
return cgi;
}
diff --git a/project2/cgi/cgiEnvironment.h b/project2/cgi/cgiEnvironment.h
index 1774417..c9b33fa 100644
--- a/project2/cgi/cgiEnvironment.h
+++ b/project2/cgi/cgiEnvironment.h
@@ -26,10 +26,12 @@ class CgiEnvironment : public Environment, public cgicc::CgiEnvironment {
private:
boost::program_options::options_description addOptions(boost::program_options::positional_options_description &);
void postinit(const boost::program_options::options_description &, const boost::program_options::variables_map &);
-#ifndef NDEBUG
public:
+#ifndef NDEBUG
std::string dumpdatadoc;
#endif
+ Glib::ustring errorContentType;
+ Glib::ustring errorTransformStyle;
};
#endif