summaryrefslogtreecommitdiff
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
commita3d7782fb271f889e47d5661a0a04ca54f25cb90 (patch)
tree9fe49955bcd2cd28a490d1ec971663eedf79ce9b
parentMinor fixes to work with GCC 4.7 (diff)
downloadproject2-a3d7782fb271f889e47d5661a0a04ca54f25cb90.tar.bz2
project2-a3d7782fb271f889e47d5661a0a04ca54f25cb90.tar.xz
project2-a3d7782fb271f889e47d5661a0a04ca54f25cb90.zip
Plugable CGI request routers
The simple (default) router The programable router
-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
-rw-r--r--project2/common/environment.h2
-rw-r--r--project2/common/options.h34
-rw-r--r--project2/common/typePointer.h17
-rw-r--r--project2/common/variables-moduri.cpp6
-rw-r--r--project2/console/consoleEnvironment.cpp5
-rw-r--r--project2/console/consoleEnvironment.h2
-rw-r--r--project2/xml/xmlPresenter.cpp2
14 files changed, 300 insertions, 81 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)));
}
}
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 <typename T> class TypedTarget;
enum TargetState { Default = 1, Global = 2, Platform = 3 };
class Target : public IntrusivePtrBase {
@@ -58,14 +57,19 @@ class Options {
mutable TargetState ts;
};
- template <typename T> class TypedTarget : public InstanceTarget {
+ template <typename T, typename D = T> 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<std::is_assignable<T, Glib::ustring>, dummy>::type = 0) const {
*target = value;
}
+ D defValue;
};
template <typename T> class TypedTarget<std::vector<T> > : public InstanceTarget {
@@ -100,20 +105,6 @@ class Options {
VofT * target;
};
- template <typename T, typename D> class DefaultTypedTarget : public TypedTarget<T> {
- public:
- DefaultTypedTarget(T * t, const D & d) :
- TypedTarget<T>(t),
- defValue(d) {
- }
- void reset() const {
- InstanceTarget::ts = Default;
- *TypedTarget<T>::target = defValue;
- }
- private:
- T defValue;
- };
-
class Option : public IntrusivePtrBase {
public:
virtual void reset() const = 0;
@@ -138,7 +129,12 @@ class Options {
template <typename T, typename D>
static
TargetPtr value(T * t, const D & d) {
- return new DefaultTypedTarget<T, D>(t, d);
+ return new TypedTarget<T>(t, d);
+ }
+ template <typename T, typename D>
+ static
+ TargetPtr valueConvert(T * t, const D & d) {
+ return new TypedTarget<T, D>(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 <typename T, typename P = typename boost::intrusive_ptr<T>>
-class TypePointer : public P {
- public:
- TypePointer() { }
- TypePointer(const std::string &) { }
-
- TypePointer & operator=(const std::string & value) {
- P::operator=(GenLoader<T>::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<int32_t>())
+ 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<xmlpp::Document>(e, os),
SourceOf<_xmlDoc>(e, os),
SourceOf<boost::shared_ptr<xmlpp::Document> >(e, os),