diff options
author | randomdan <randomdan@localhost> | 2011-02-25 14:53:40 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2011-02-25 14:53:40 +0000 |
commit | b5e7fca95a6a29b84d987f61414e0c27cf2a0ddf (patch) | |
tree | d79f16f16ecb190edf1cac3d8d2f554e403632f3 /project2/cgi | |
parent | Finally convert ytfs to use boost stuff (untested, but it does build) (diff) | |
download | project2-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.cpp | 238 | ||||
-rw-r--r-- | project2/cgi/cgiAppEngine.h | 83 | ||||
-rw-r--r-- | project2/cgi/cgiCommon.cpp | 17 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.cpp | 8 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.h | 4 |
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 |