From c9a1a31f48f715909bb3440a36442383acf46b49 Mon Sep 17 00:00:00 2001 From: randomdan Date: Wed, 13 Mar 2013 21:23:34 +0000 Subject: Plugable CGI request routers The simple (default) router The programable router --- project2/cgi/cgiAppEngine.cpp | 6 +- project2/cgi/cgiEnvironment.cpp | 29 ++--- project2/cgi/cgiEnvironment.h | 8 +- project2/cgi/cgiProgRouter.cpp | 186 ++++++++++++++++++++++++++++++++ project2/cgi/cgiRouter.h | 20 ++++ project2/cgi/cgiSimpleRouter.cpp | 55 ++++++++++ project2/cgi/cgiStageInitial.cpp | 9 +- project2/common/environment.h | 2 +- project2/common/options.h | 34 +++--- project2/common/typePointer.h | 17 --- project2/common/variables-moduri.cpp | 6 +- project2/console/consoleEnvironment.cpp | 5 +- project2/console/consoleEnvironment.h | 2 +- project2/xml/xmlPresenter.cpp | 2 +- 14 files changed, 300 insertions(+), 81 deletions(-) create mode 100644 project2/cgi/cgiProgRouter.cpp create mode 100644 project2/cgi/cgiRouter.h create mode 100644 project2/cgi/cgiSimpleRouter.cpp delete mode 100644 project2/common/typePointer.h diff --git a/project2/cgi/cgiAppEngine.cpp b/project2/cgi/cgiAppEngine.cpp index 28600b3..f2c7f86 100644 --- a/project2/cgi/cgiAppEngine.cpp +++ b/project2/cgi/cgiAppEngine.cpp @@ -178,11 +178,7 @@ CgiApplicationEngine::addEnvData(const MultiRowSetPresenter * p, OutputOptionsPt // URL elements p->addNewRowSet("uriElems", env()->scriptNamespacePrefix); p->addAttribute("full", _env->getRedirectURL()); - p->addNewArray("uriElem", true); - BOOST_FOREACH(std::string s, _env->elems) { - p->addNamedValue("uriElem", s); - } - p->finishArray(true); + _env->router->present(p); p->finishRowSet(); } diff --git a/project2/cgi/cgiEnvironment.cpp b/project2/cgi/cgiEnvironment.cpp index 439f6d8..292a52f 100644 --- a/project2/cgi/cgiEnvironment.cpp +++ b/project2/cgi/cgiEnvironment.cpp @@ -9,22 +9,6 @@ #include #include -std::vector -makeVector(const boost::filesystem::path & y) -{ - std::vector r; - boost::filesystem::path::iterator p = y.begin(); - p++; - while(p != y.end()) { -#if BOOST_VERSION >= 104500 - r.push_back((p++)->string()); -#else - r.push_back(*(p++)); -#endif - } - return r; -} - CgiEnvironment::CgiEnvironment() : cgi(NULL), cgienv(NULL), @@ -60,6 +44,8 @@ CgiEnvironment::CgiEnvironment() : "The module with which to implement session management") ("cgi.hostRegex", hpi, "Regular expression used to define a hostname -> platform association") + ("cgi.router", Options::value(&routerType, "simple"), + "Implemenation of router model to map request paths to scripts") ; } @@ -77,7 +63,7 @@ CgiEnvironment::setCGICC(const cgicc::Cgicc * c, const CgiEnvInput * e) { cgi = c; cgienv = e; - elems = makeVector(boost::filesystem::path(getRedirectURL())); + router = boost::bind(&GenLoader::createNew, boost::cref(routerType), getRedirectURL()); } std::string @@ -137,18 +123,15 @@ CgiEnvironment::getRequestModifiedSince() const } Glib::ustring -CgiEnvironment::getParamUri(unsigned int p) const +CgiEnvironment::getParamUri(VariableType p) const { - if (p >= elems.size()) { - throw UriElementOutOfRange(p); - } - return elems[p]; + return router->routeParameter(p); } unsigned int CgiEnvironment::getParamUriCount() const { - return elems.size(); + return router->parameterCount(); } Glib::ustring diff --git a/project2/cgi/cgiEnvironment.h b/project2/cgi/cgiEnvironment.h index 6797a24..dd89a75 100644 --- a/project2/cgi/cgiEnvironment.h +++ b/project2/cgi/cgiEnvironment.h @@ -5,7 +5,9 @@ #include #include "environment.h" #include "cgiEnvInput.h" +#include "cgiRouter.h" #include +#include SimpleMessageException(NoSuchCookie); @@ -33,7 +35,7 @@ class CgiEnvironment : public Environment { CgiEnvironment(); virtual ~CgiEnvironment(); - Glib::ustring getParamUri(unsigned int idx) const; + Glib::ustring getParamUri(VariableType idx) const; unsigned int getParamUriCount() const; Glib::ustring getParamQuery(const std::string & idx) const; std::string getServerName() const; @@ -47,7 +49,6 @@ class CgiEnvironment : public Environment { void setCGICC(const cgicc::Cgicc *, const CgiEnvInput * cgienv); const cgicc::Cgicc * cgi; const CgiEnvInput * cgienv; - std::vector elems; private: const Options & engineOptions() const; @@ -69,6 +70,9 @@ class CgiEnvironment : public Environment { std::string onErrorPresent; std::string defaultPresenter; std::string sessionModule; + std::string routerType; + typedef LazyPointer RouterPtr; + RouterPtr router; }; #endif diff --git a/project2/cgi/cgiProgRouter.cpp b/project2/cgi/cgiProgRouter.cpp new file mode 100644 index 0000000..9218c89 --- /dev/null +++ b/project2/cgi/cgiProgRouter.cpp @@ -0,0 +1,186 @@ +#include +#include +#include "cgiEnvironment.h" +#include "safeMapFind.h" +#include "exceptions.h" + +typedef std::map VarMap; + +class RoutingTable { + public: + RoutingTable & operator=(const std::string & routeFile) { + routes.clear(); + routeScriptPath = routeFile; + if (routeFile.empty()) { + routeScript.reset(); + } + else { + setRouteScript(); + } + return *this; + } + + void onBefore() + { + if (routeScript && !routeScript->isCurrent()) { + setRouteScript(); + } + } + void setRouteScript() + { + routeScript = Environment::getCurrent()->resolveScript(Environment::getCurrent()->datasourceRoot, routeScriptPath, true); + routeScript->loader.addLoadTarget(routeScript->root(), Storer::into(&routes)); + routeScript->load(NULL, true); + } + ScriptReaderPtr routeScript; + std::string routeScriptPath; + + const std::string & present(const std::string & path, VarMap & vars) const { + BOOST_FOREACH(const auto route, routes) { + vars.clear(); + if (route->matches(path, vars)) { + return route->present; + } + } + throw ScriptNotFound("routed", path); + } + + class RouteElem : public IntrusivePtrBase { + public: + virtual bool matches(const std::string &, VarMap & vars) const = 0; + }; + typedef boost::intrusive_ptr RouteElemPtr; + + class RouteLiteral : public RouteElem { + public: + RouteLiteral(const std::string & v) : value(v) { } + bool matches(const std::string & path, VarMap &) const { + return value == path; + } + const std::string value; + }; + + class RouteVar : public RouteElem { + public: + RouteVar(const std::string & v) : variable(v.substr(1, v.length() - 2)) { } + bool matches(const std::string & value, VarMap & vars) const { + vars[variable] = value; + return true; + } + const std::string variable; + }; + + class Route : public SourceObject { + public: + Route(ScriptNodePtr s) : + SourceObject(s), + present(s->value("present").as()) + { + boost::filesystem::path path = s->value("path").as(); + boost::filesystem::path::iterator p = path.begin(); + p++; + while(p != path.end() && p->string() != ".") { + switch (p->string().front()) { + case '{': + routeElems.push_back(new RouteVar(p->string())); + break; + default: + routeElems.push_back(new RouteLiteral(p->string())); + break; + } + p++; + } + } + bool matches(const boost::filesystem::path & path, VarMap & vars) const { + boost::filesystem::path::iterator p = path.begin(); + p++; + for (RouteElems::const_iterator re = routeElems.begin(); re != routeElems.end() || p != path.end(); re++) { + if (re == routeElems.end() || p == path.end() || !(*re)->matches(p->string(), vars)) { + return false; + } + while (++p != path.end() && p->string() == ".") ; + } + return true; + } + typedef std::list RouteElems; + RouteElems routeElems; + const std::string present; + }; + typedef boost::intrusive_ptr RoutePtr; + + std::list routes; +}; + +typedef RoutingTable::Route Route; +DECLARE_LOADER("route", Route); + +class ProgRouter; +class ProgRouterLoader : public RouterLoader::For { + public: + ProgRouterLoader() : + opts("CGI Prog Router options") + { + opts + ("cgi.progRouter.routes", Options::value(&routingTable), + "Script file defining web service routes") + ; + } + + const Options * options() const + { + return &opts; + } + + void onBefore() + { + routingTable.onBefore(); + } + + static RoutingTable routingTable; + + private: + Options opts; +}; + +RoutingTable ProgRouterLoader::routingTable; + +SimpleMessageException(UriElementNotFound); + +class ProgRouter : public Router { + public: + ProgRouter(const std::string & p) : + path(p) { + } + std::string route() const { + return ProgRouterLoader::routingTable.present(path, vars); + } + bool isDefault() const { + return false; + } + VariableType routeParameter(const VariableType & vp) const { + return safeMapLookup(vars, vp); + } + unsigned int parameterCount() const { + return vars.size(); + } + void present(const MultiRowSetPresenter * p) const { + p->addNewArray("uriElem", true); + boost::filesystem::path y(path); + boost::filesystem::path::iterator pathPart = y.begin(); + while(++pathPart != y.end()) { + p->addNamedValue("uriElem", pathPart->string()); + } + p->finishArray(true); + p->addNewArray("uriParam", true); + BOOST_FOREACH(const auto & v, vars) { + p->addNamedValue("uriParam", v.first); + } + p->finishArray(true); + } + + private: + const std::string path; + mutable VarMap vars; +}; + +DECLARE_CUSTOM_COMPONENT_LOADER("progRouter", ProgRouter, ProgRouterLoader, RouterLoader); diff --git a/project2/cgi/cgiRouter.h b/project2/cgi/cgiRouter.h new file mode 100644 index 0000000..dd9c0e6 --- /dev/null +++ b/project2/cgi/cgiRouter.h @@ -0,0 +1,20 @@ +#ifndef CGIROUTER_H +#define CGIROUTER_H + +#include +#include + +class MultiRowSetPresenter; + +class Router : public IntrusivePtrBase { + public: + virtual bool isDefault() const = 0; + virtual std::string route() const = 0; + virtual VariableType routeParameter(const VariableType & var) const = 0; + virtual unsigned int parameterCount() const = 0; + virtual void present(const MultiRowSetPresenter * p) const = 0; +}; +typedef GenLoader RouterLoader; + +#endif + diff --git a/project2/cgi/cgiSimpleRouter.cpp b/project2/cgi/cgiSimpleRouter.cpp new file mode 100644 index 0000000..a28cf1d --- /dev/null +++ b/project2/cgi/cgiSimpleRouter.cpp @@ -0,0 +1,55 @@ +#include "cgiEnvironment.h" +#include "scriptLoader.h" +#include "presenter.h" + +std::vector +makeVector(const boost::filesystem::path & y) +{ + std::vector r; + boost::filesystem::path::iterator p = y.begin(); + p++; + while(p != y.end()) { + r.push_back((p++)->string()); + } + return r; +} + +class SimpleRouter : public Router { + public: + SimpleRouter(const std::string & p) : + path(p), + elems(makeVector(p)) { + } + std::string route() const { + return path; + } + bool isDefault() const { + return elems.empty(); + } + VariableType routeParameter(const VariableType & vp) const { + unsigned int p = (int)vp; + if (p >= elems.size()) { + throw UriElementOutOfRange(p); + } + return elems[p]; + } + unsigned int parameterCount() const { + return elems.size(); + } + void present(const MultiRowSetPresenter * p) const { + p->addNewArray("uriElem", true); + boost::filesystem::path y(path); + boost::filesystem::path::iterator pathPart = y.begin(); + while(++pathPart != y.end()) { + p->addNamedValue("uriElem", pathPart->string()); + } + p->finishArray(true); + } + + private: + const std::string path; + std::vector elems; +}; + +DECLARE_GENERIC_LOADER("simple", RouterLoader, SimpleRouter); + diff --git a/project2/cgi/cgiStageInitial.cpp b/project2/cgi/cgiStageInitial.cpp index 5abbbfb..1fe4820 100644 --- a/project2/cgi/cgiStageInitial.cpp +++ b/project2/cgi/cgiStageInitial.cpp @@ -3,20 +3,15 @@ #include "cgiEnvironment.h" #include "exceptions.h" -StaticMessageException(EmptyRequestURL, "Request URL cannot be empty"); - CgiApplicationEngine::NextStage CgiApplicationEngine::InitialStage::run() { const CgiEnvironment * e = env(); if (e->getRequestMethod() == "POST") { - if (e->elems.empty()) { - throw EmptyRequestURL(); - } - return NextStage(new RequestStage(e->resolveScript(e->requestRoot, e->getRedirectURL(), false))); + return NextStage(new RequestStage(e->resolveScript(e->requestRoot, e->router->route(), false))); } else { - return NextStage(new PresentStage(e->resolveScript(e->presentRoot, e->elems.empty() ? e->defaultPresent : e->getRedirectURL(), false))); + return NextStage(new PresentStage(e->resolveScript(e->presentRoot, e->router->isDefault() ? e->defaultPresent : e->router->route(), false))); } } diff --git a/project2/common/environment.h b/project2/common/environment.h index accbb80..4daea74 100644 --- a/project2/common/environment.h +++ b/project2/common/environment.h @@ -21,7 +21,7 @@ class Environment { static const Environment * getCurrent(); - virtual Glib::ustring getParamUri(unsigned int idx) const = 0; + virtual Glib::ustring getParamUri(VariableType idx) const = 0; virtual unsigned int getParamUriCount() const = 0; virtual Glib::ustring getParamQuery(const std::string & idx) const = 0; diff --git a/project2/common/options.h b/project2/common/options.h index 62a38fb..a28b5f8 100644 --- a/project2/common/options.h +++ b/project2/common/options.h @@ -35,7 +35,6 @@ namespace std { class Options { public: class Target; - template class TypedTarget; enum TargetState { Default = 1, Global = 2, Platform = 3 }; class Target : public IntrusivePtrBase { @@ -58,14 +57,19 @@ class Options { mutable TargetState ts; }; - template class TypedTarget : public InstanceTarget { + template class TypedTarget : public InstanceTarget { public: + TypedTarget(T * t, const D & d) : + target(t), + defValue(d) { + } TypedTarget(T * t) : - target(t) { + target(t), + defValue(T()) { } void reset() const { ts = Default; - *target = T(); + *target = defValue; } void assign(const Glib::ustring & value) const { doAssign(value); @@ -81,6 +85,7 @@ class Options { void doAssign(const Glib::ustring & value, typename boost::enable_if, dummy>::type = 0) const { *target = value; } + D defValue; }; template class TypedTarget > : public InstanceTarget { @@ -100,20 +105,6 @@ class Options { VofT * target; }; - template class DefaultTypedTarget : public TypedTarget { - public: - DefaultTypedTarget(T * t, const D & d) : - TypedTarget(t), - defValue(d) { - } - void reset() const { - InstanceTarget::ts = Default; - *TypedTarget::target = defValue; - } - private: - T defValue; - }; - class Option : public IntrusivePtrBase { public: virtual void reset() const = 0; @@ -138,7 +129,12 @@ class Options { template static TargetPtr value(T * t, const D & d) { - return new DefaultTypedTarget(t, d); + return new TypedTarget(t, d); + } + template + static + TargetPtr valueConvert(T * t, const D & d) { + return new TypedTarget(t, d); } void reset() const; diff --git a/project2/common/typePointer.h b/project2/common/typePointer.h deleted file mode 100644 index 95bf28a..0000000 --- a/project2/common/typePointer.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TYPEPOINTER_H -#define TYPEPOINTER_H - -template > -class TypePointer : public P { - public: - TypePointer() { } - TypePointer(const std::string &) { } - - TypePointer & operator=(const std::string & value) { - P::operator=(GenLoader::createNew(value)); - return *this; - } -}; - -#endif - diff --git a/project2/common/variables-moduri.cpp b/project2/common/variables-moduri.cpp index 041141f..81b8698 100644 --- a/project2/common/variables-moduri.cpp +++ b/project2/common/variables-moduri.cpp @@ -9,13 +9,13 @@ class VariableUri : public VariableImplDyn { public: VariableUri(ScriptNodePtr e) : VariableImplDyn(e), - index(e->value("index").as()) + index(e, "index") { } VariableType value() const { try { - return ApplicationEngine::getCurrent()->env()->getParamUri(index); + return ApplicationEngine::getCurrent()->env()->getParamUri(index()); } catch (UriElementOutOfRange) { if (!defaultValue) { @@ -25,7 +25,7 @@ class VariableUri : public VariableImplDyn { } } private: - unsigned int index; + Variable index; }; DECLARE_COMPONENT_LOADER("uri", VariableUri, VariableLoader); diff --git a/project2/console/consoleEnvironment.cpp b/project2/console/consoleEnvironment.cpp index 8f4465c..69f17cd 100644 --- a/project2/console/consoleEnvironment.cpp +++ b/project2/console/consoleEnvironment.cpp @@ -63,9 +63,10 @@ ConsoleEnvironment::~ConsoleEnvironment() } Glib::ustring -ConsoleEnvironment::getParamUri(unsigned int idx) const +ConsoleEnvironment::getParamUri(VariableType _idx) const { - if (idx < uriParams.size()) { + int32_t idx = _idx; + if (idx < (int32_t)uriParams.size()) { return uriParams[idx]; } throw UriElementOutOfRange(idx); diff --git a/project2/console/consoleEnvironment.h b/project2/console/consoleEnvironment.h index 6d57d3c..6ce2d75 100644 --- a/project2/console/consoleEnvironment.h +++ b/project2/console/consoleEnvironment.h @@ -17,7 +17,7 @@ class ConsoleEnvironment : public Environment { virtual ~ConsoleEnvironment(); - Glib::ustring getParamUri(unsigned int idx) const; + Glib::ustring getParamUri(VariableType idx) const; unsigned int getParamUriCount() const; Glib::ustring getParamQuery(const std::string & idx) const; std::string getServerName() const; diff --git a/project2/xml/xmlPresenter.cpp b/project2/xml/xmlPresenter.cpp index 2d72561..45836a2 100644 --- a/project2/xml/xmlPresenter.cpp +++ b/project2/xml/xmlPresenter.cpp @@ -77,7 +77,7 @@ XmlPresenter::XmlPresenter(const Glib::ustring & responseRootNodeName, const Gli XmlPresenter::XmlPresenter(ScriptNodePtr e, ObjectSource os) : TransformSource(e, os), Presenter(os), - ContentPresenter(e->value("contenttype", "")), + ContentPresenter(e->value("contenttype", "text/xml")), SourceOf(e, os), SourceOf<_xmlDoc>(e, os), SourceOf >(e, os), -- cgit v1.2.3