diff options
author | randomdan <randomdan@localhost> | 2013-03-13 21:23:34 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2013-03-13 21:23:34 +0000 |
commit | c9a1a31f48f715909bb3440a36442383acf46b49 (patch) | |
tree | 9fe49955bcd2cd28a490d1ec971663eedf79ce9b /project2/cgi | |
parent | Minor fixes to work with GCC 4.7 (diff) | |
download | project2-c9a1a31f48f715909bb3440a36442383acf46b49.tar.bz2 project2-c9a1a31f48f715909bb3440a36442383acf46b49.tar.xz project2-c9a1a31f48f715909bb3440a36442383acf46b49.zip |
Plugable CGI request routers
The simple (default) router
The programable router
Diffstat (limited to 'project2/cgi')
-rw-r--r-- | project2/cgi/cgiAppEngine.cpp | 6 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.cpp | 29 | ||||
-rw-r--r-- | project2/cgi/cgiEnvironment.h | 8 | ||||
-rw-r--r-- | project2/cgi/cgiProgRouter.cpp | 186 | ||||
-rw-r--r-- | project2/cgi/cgiRouter.h | 20 | ||||
-rw-r--r-- | project2/cgi/cgiSimpleRouter.cpp | 55 | ||||
-rw-r--r-- | project2/cgi/cgiStageInitial.cpp | 9 |
7 files changed, 276 insertions, 37 deletions
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 <glibmm/regex.h> #include <curl/curl.h> -std::vector<std::string> -makeVector(const boost::filesystem::path & y) -{ - std::vector<std::string> 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<Router, const std::string &>::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 <vector> #include "environment.h" #include "cgiEnvInput.h" +#include "cgiRouter.h" #include <cgicc/CgiEnvironment.h> +#include <lazyPointer.h> 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<std::string> 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<const Router> 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 <pch.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include "cgiEnvironment.h" +#include "safeMapFind.h" +#include "exceptions.h" + +typedef std::map<std::string, std::string> 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<ElementLoader>(&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<RouteElem> 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<std::string>()) + { + boost::filesystem::path path = s->value("path").as<std::string>(); + 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<RouteElemPtr> RouteElems; + RouteElems routeElems; + const std::string present; + }; + typedef boost::intrusive_ptr<Route> RoutePtr; + + std::list<RoutePtr> routes; +}; + +typedef RoutingTable::Route Route; +DECLARE_LOADER("route", Route); + +class ProgRouter; +class ProgRouterLoader : public RouterLoader::For<ProgRouter> { + 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<UriElementNotFound>(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 <variables.h> +#include <scriptLoader.h> + +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<Router, const std::string &> 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<std::string> +makeVector(const boost::filesystem::path & y) +{ + std::vector<std::string> 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<std::string> 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))); } } |