summaryrefslogtreecommitdiff
path: root/project2/cgi
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2013-03-13 21:23:34 +0000
committerrandomdan <randomdan@localhost>2013-03-13 21:23:34 +0000
commitc9a1a31f48f715909bb3440a36442383acf46b49 (patch)
tree9fe49955bcd2cd28a490d1ec971663eedf79ce9b /project2/cgi
parentMinor fixes to work with GCC 4.7 (diff)
downloadproject2-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.cpp6
-rw-r--r--project2/cgi/cgiEnvironment.cpp29
-rw-r--r--project2/cgi/cgiEnvironment.h8
-rw-r--r--project2/cgi/cgiProgRouter.cpp186
-rw-r--r--project2/cgi/cgiRouter.h20
-rw-r--r--project2/cgi/cgiSimpleRouter.cpp55
-rw-r--r--project2/cgi/cgiStageInitial.cpp9
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)));
}
}