summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2016-06-19 17:53:48 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2016-06-19 17:53:48 +0100
commitfdc1701cc8e25766f24c846feb3d22158ed272b8 (patch)
treea4869c9d48e6eb8001f5de915ae84606c85fae63
parentAdd all supported method types (diff)
downloadicespider-fdc1701cc8e25766f24c846feb3d22158ed272b8.tar.bz2
icespider-fdc1701cc8e25766f24c846feb3d22158ed272b8.tar.xz
icespider-fdc1701cc8e25766f24c846feb3d22158ed272b8.zip
Add and test a request router
-rw-r--r--icespider/compile/routeCompiler.cpp2
-rw-r--r--icespider/core/core.cpp69
-rw-r--r--icespider/core/core.h28
-rw-r--r--icespider/core/ihttpRequest.cpp5
-rw-r--r--icespider/core/ihttpRequest.h10
-rw-r--r--icespider/core/irouteHandler.cpp9
-rw-r--r--icespider/core/irouteHandler.h10
-rw-r--r--icespider/core/paths.cpp47
-rw-r--r--icespider/core/paths.h45
-rw-r--r--icespider/unittests/testApp.cpp78
-rw-r--r--icespider/unittests/testCompile.cpp9
-rw-r--r--icespider/unittests/testRoutes.json2
12 files changed, 295 insertions, 19 deletions
diff --git a/icespider/compile/routeCompiler.cpp b/icespider/compile/routeCompiler.cpp
index 1bf61a7..4881129 100644
--- a/icespider/compile/routeCompiler.cpp
+++ b/icespider/compile/routeCompiler.cpp
@@ -221,7 +221,7 @@ namespace IceSpider {
fprintbf(4, output, "auto _p_%s(request->get%sParam<%s>(_pn_%s));\n",
p->name, getEnumString(p->source), Slice::typeToString(ip->type()), p->name);
}
- fprintbf(4, output, "auto prx = getProxy<%s>();\n", proxyName);
+ fprintbf(4, output, "auto prx = getProxy<%s>(request);\n", proxyName);
if (o->returnsData()) {
fprintbf(4, output, "request->response(prx->%s(", operation);
}
diff --git a/icespider/core/core.cpp b/icespider/core/core.cpp
new file mode 100644
index 0000000..7c501c5
--- /dev/null
+++ b/icespider/core/core.cpp
@@ -0,0 +1,69 @@
+#include "core.h"
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+
+namespace ba = boost::algorithm;
+
+namespace IceSpider {
+ Core::Core()
+ {
+ // Big enough to map all the request methods
+ routes.resize(UserIceSpider::HttpMethod::OPTIONS + 1);
+ // Initialize routes
+ for (const auto & rp : AdHoc::PluginManager::getDefault()->getAll<IRouteHandler>()) {
+ auto r = rp->implementation();
+ auto & mroutes = routes[r->method];
+ if (mroutes.size() <= r->pathElementCount()) {
+ mroutes.resize(r->pathElementCount() + 1);
+ }
+ mroutes[r->pathElementCount()].push_back(r);
+ }
+ }
+
+ Core::~Core()
+ {
+ }
+
+ void
+ Core::process(IHttpRequest * request) const
+ {
+ auto routeHandler = findRoute(request);
+ routeHandler->execute(request);
+ }
+
+ const IRouteHandler *
+ Core::findRoute(const IHttpRequest * request) const
+ {
+ auto path = request->getRequestPath().substr(1);
+ const auto & mroutes = routes[request->getRequestMethod()];
+ std::vector<std::string> pathparts;
+ if (!path.empty()) {
+ ba::split(pathparts, path, ba::is_any_of("/"), ba::token_compress_off);
+ }
+ if (pathparts.size() > mroutes.size()) {
+ // Not found error
+ return NULL;
+ }
+ const auto & routeSet = mroutes[pathparts.size()];
+ auto ri = std::find_if(routeSet.begin(), routeSet.end(), [&pathparts](const auto & r) {
+ auto rpi = r->parts.begin();
+ for (auto ppi = pathparts.begin(); ppi != pathparts.end(); ++ppi, ++rpi) {
+ if (!(*rpi)->matches(*ppi)) return false;
+ }
+ return true;
+ });
+ if (ri == routeSet.end()) {
+ // Not found error
+ return NULL;
+ }
+ return (*ri);
+ }
+
+ Ice::ObjectPrx
+ Core::getProxy(const char * type) const
+ {
+ fprintf(stderr, "request for proxy type %s\n", type);
+ return NULL;
+ }
+}
+
diff --git a/icespider/core/core.h b/icespider/core/core.h
new file mode 100644
index 0000000..afca336
--- /dev/null
+++ b/icespider/core/core.h
@@ -0,0 +1,28 @@
+#ifndef ICESPIDER_CORE_CORE_H
+#define ICESPIDER_CORE_CORE_H
+
+#include <visibility.h>
+#include <vector>
+#include "irouteHandler.h"
+
+namespace IceSpider {
+ class DLL_PUBLIC Core {
+ public:
+ typedef std::vector<const IRouteHandler *> Routes;
+ typedef std::vector<Routes> LengthRoutes;
+ typedef std::vector<LengthRoutes> MethodRoutes;
+
+ Core();
+ ~Core();
+
+ void process(IHttpRequest *) const;
+ const IRouteHandler * findRoute(const IHttpRequest *) const;
+
+ Ice::ObjectPrx getProxy(const char * type) const;
+
+ MethodRoutes routes;
+ };
+}
+
+#endif
+
diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp
index 423e439..0e14957 100644
--- a/icespider/core/ihttpRequest.cpp
+++ b/icespider/core/ihttpRequest.cpp
@@ -1,6 +1,11 @@
#include "ihttpRequest.h"
namespace IceSpider {
+ IHttpRequest::IHttpRequest(const Core * c) :
+ core(c)
+ {
+ }
+
Ice::Context
IHttpRequest::getContext() const
{
diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h
index 3ea877e..2193a67 100644
--- a/icespider/core/ihttpRequest.h
+++ b/icespider/core/ihttpRequest.h
@@ -5,11 +5,19 @@
#include <IceUtil/Optional.h>
#include <Ice/Current.h>
#include <visibility.h>
+#include <routes.h>
namespace IceSpider {
+ class Core;
+
class DLL_PUBLIC IHttpRequest {
public:
+ IHttpRequest(const Core *);
+
Ice::Context getContext() const;
+ virtual std::string getRequestPath() const = 0;
+ virtual UserIceSpider::HttpMethod getRequestMethod() const = 0;
+
template<typename T>
T getURLParam(const std::string & key) const { (void)key; return T(); }
template<typename T>
@@ -20,6 +28,8 @@ namespace IceSpider {
T getHeaderParam(const std::string & key) const { (void)key; return T(); }
template<typename T>
void response(const T &) const { }
+
+ const Core * core;
};
}
diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp
index 2107ff7..3007772 100644
--- a/icespider/core/irouteHandler.cpp
+++ b/icespider/core/irouteHandler.cpp
@@ -1,19 +1,20 @@
#include "irouteHandler.h"
+#include "core.h"
#include <plugins.impl.h>
INSTANTIATEPLUGINOF(IceSpider::IRouteHandler);
namespace IceSpider {
IRouteHandler::IRouteHandler(UserIceSpider::HttpMethod m, const std::string & p) :
- method(m),
- path(p)
+ Path(p),
+ method(m)
{
}
Ice::ObjectPrx
- IRouteHandler::getProxy(const char *) const
+ IRouteHandler::getProxy(IHttpRequest * request, const char * type) const
{
- return NULL;
+ return request->core->getProxy(type);
}
}
diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h
index e02d0a3..416048e 100644
--- a/icespider/core/irouteHandler.h
+++ b/icespider/core/irouteHandler.h
@@ -2,26 +2,26 @@
#define ICESPIDER_IROUTEHANDLER_H
#include "ihttpRequest.h"
+#include "paths.h"
#include <routes.h>
#include <plugins.h>
#include <visibility.h>
namespace IceSpider {
- class DLL_PUBLIC IRouteHandler : public AdHoc::AbstractPluginImplementation {
+ class DLL_PUBLIC IRouteHandler : public AdHoc::AbstractPluginImplementation, public Path {
public:
IRouteHandler(UserIceSpider::HttpMethod, const std::string & path);
virtual void execute(IHttpRequest * request) const = 0;
const UserIceSpider::HttpMethod method;
- const std::string path;
protected:
- Ice::ObjectPrx getProxy(const char *) const;
+ Ice::ObjectPrx getProxy(IHttpRequest *, const char *) const;
template<typename Interface>
- typename Interface::ProxyType getProxy() const
+ typename Interface::ProxyType getProxy(IHttpRequest * request) const
{
- return Interface::ProxyType::uncheckedCast(getProxy(typeid(Interface).name()));
+ return Interface::ProxyType::uncheckedCast(getProxy(request, typeid(Interface).name()));
}
};
typedef AdHoc::PluginOf<IRouteHandler> RouteHandlers;
diff --git a/icespider/core/paths.cpp b/icespider/core/paths.cpp
new file mode 100644
index 0000000..f4bad0e
--- /dev/null
+++ b/icespider/core/paths.cpp
@@ -0,0 +1,47 @@
+#include "paths.h"
+#include <boost/algorithm/string/split.hpp>
+
+namespace ba = boost::algorithm;
+
+namespace IceSpider {
+ Path::Path(const std::string & p) :
+ path(p)
+ {
+ auto relp = p.substr(1);
+ if (relp.empty()) return;
+ for (auto pi = ba::make_split_iterator(relp, ba::first_finder("/", ba::is_equal())); pi != decltype(pi)(); ++pi) {
+ std::string pp(pi->begin(), pi->end());
+ if (pp.front() == '{' && pp.back() == '}') {
+ parts.push_back(PathPartPtr(new PathParameter()));
+ }
+ else {
+ parts.push_back(PathPartPtr(new PathLiteral(pp)));
+ }
+ }
+ }
+
+ unsigned int
+ Path::pathElementCount() const
+ {
+ return parts.size();
+ }
+
+ PathLiteral::PathLiteral(const std::string & p) :
+ value(p)
+ {
+
+ }
+
+ bool
+ PathLiteral::matches(const std::string & v) const
+ {
+ return value == v;
+ }
+
+ bool
+ PathParameter::matches(const std::string &) const
+ {
+ return true;
+ }
+}
+
diff --git a/icespider/core/paths.h b/icespider/core/paths.h
new file mode 100644
index 0000000..0999593
--- /dev/null
+++ b/icespider/core/paths.h
@@ -0,0 +1,45 @@
+#ifndef ICESPIDER_CORE_PATHS_H
+#define ICESPIDER_CORE_PATHS_H
+
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace IceSpider {
+
+ class PathPart {
+ public:
+ virtual bool matches(const std::string &) const = 0;
+ };
+ typedef std::shared_ptr<PathPart> PathPartPtr;
+
+ class PathLiteral : public PathPart {
+ public:
+ PathLiteral(const std::string & v);
+
+ bool matches(const std::string &) const;
+
+ private:
+ const std::string value;
+ };
+
+ class PathParameter : public PathPart {
+ public:
+ bool matches(const std::string &) const;
+ };
+ class Path {
+ public:
+ typedef std::vector<PathPartPtr> PathParts;
+
+ Path(const std::string &);
+
+ const std::string path;
+
+ unsigned int pathElementCount() const;
+
+ PathParts parts;
+ };
+}
+
+#endif
+
diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp
index 01249b7..2c4e1b2 100644
--- a/icespider/unittests/testApp.cpp
+++ b/icespider/unittests/testApp.cpp
@@ -3,9 +3,87 @@
#include <plugins.h>
#include <irouteHandler.h>
+#include <core.h>
+
+using namespace UserIceSpider;
BOOST_AUTO_TEST_CASE( testLoadConfiguration )
{
BOOST_REQUIRE_EQUAL(4, AdHoc::PluginManager::getDefault()->getAll<IceSpider::IRouteHandler>().size());
}
+BOOST_FIXTURE_TEST_SUITE(c, IceSpider::Core);
+
+BOOST_AUTO_TEST_CASE( testCoreSettings )
+{
+ BOOST_REQUIRE_EQUAL(6, routes.size());
+ BOOST_REQUIRE_EQUAL(4, routes[HttpMethod::GET].size());
+ BOOST_REQUIRE_EQUAL(1, routes[HttpMethod::GET][0].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::GET][1].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::GET][2].size());
+ BOOST_REQUIRE_EQUAL(1, routes[HttpMethod::GET][3].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::HEAD].size());
+ BOOST_REQUIRE_EQUAL(2, routes[HttpMethod::POST].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::POST][0].size());
+ BOOST_REQUIRE_EQUAL(1, routes[HttpMethod::POST][1].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::PUT].size());
+ BOOST_REQUIRE_EQUAL(2, routes[HttpMethod::DELETE].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::DELETE][0].size());
+ BOOST_REQUIRE_EQUAL(1, routes[HttpMethod::DELETE][1].size());
+ BOOST_REQUIRE_EQUAL(0, routes[HttpMethod::OPTIONS].size());
+}
+
+class TestRequest : public IceSpider::IHttpRequest {
+ public:
+ TestRequest(const IceSpider::Core * c, HttpMethod m, const std::string & p) :
+ IHttpRequest(c),
+ method(m),
+ path(p)
+ {
+ }
+
+ std::string getRequestPath() const override
+ {
+ return path;
+ }
+
+ HttpMethod getRequestMethod() const override
+ {
+ return method;
+ }
+
+ const HttpMethod method;
+ const std::string path;
+};
+
+BOOST_AUTO_TEST_CASE( testFindRoutes )
+{
+ TestRequest requestGetIndex(this, HttpMethod::GET, "/");
+ BOOST_REQUIRE(findRoute(&requestGetIndex));
+
+ TestRequest requestPostIndex(this, HttpMethod::POST, "/");
+ BOOST_REQUIRE(!findRoute(&requestPostIndex));
+
+ TestRequest requestPostUpdate(this, HttpMethod::POST, "/something");
+ BOOST_REQUIRE(findRoute(&requestPostUpdate));
+
+ TestRequest requestGetUpdate(this, HttpMethod::GET, "/something");
+ BOOST_REQUIRE(!findRoute(&requestGetUpdate));
+
+ TestRequest requestGetItem(this, HttpMethod::GET, "/view/something/something");
+ BOOST_REQUIRE(findRoute(&requestGetItem));
+
+ TestRequest requestGetItemLong(this, HttpMethod::GET, "/view/something/something/extra");
+ BOOST_REQUIRE(!findRoute(&requestGetItemLong));
+
+ TestRequest requestGetItemShort(this, HttpMethod::GET, "/view/missingSomething");
+ BOOST_REQUIRE(!findRoute(&requestGetItemShort));
+
+ TestRequest requestGetNothing(this, HttpMethod::GET, "/badview/something/something");
+ BOOST_REQUIRE(!findRoute(&requestGetNothing));
+
+ TestRequest requestDeleteThing(this, HttpMethod::DELETE, "/something");
+ BOOST_REQUIRE(findRoute(&requestDeleteThing));
+}
+
+BOOST_AUTO_TEST_SUITE_END();
diff --git a/icespider/unittests/testCompile.cpp b/icespider/unittests/testCompile.cpp
index d46a33d..b7ce0f5 100644
--- a/icespider/unittests/testCompile.cpp
+++ b/icespider/unittests/testCompile.cpp
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE( testLoadConfiguration )
BOOST_REQUIRE_EQUAL(0, cfg->routes[0]->params.size());
BOOST_REQUIRE_EQUAL("item", cfg->routes[1]->name);
- BOOST_REQUIRE_EQUAL("/{s}/{i}", cfg->routes[1]->path);
+ BOOST_REQUIRE_EQUAL("/view/{s}/{i}", cfg->routes[1]->path);
BOOST_REQUIRE_EQUAL(2, cfg->routes[1]->params.size());
BOOST_REQUIRE_EQUAL("del", cfg->routes[2]->name);
@@ -77,10 +77,6 @@ BOOST_AUTO_TEST_CASE( testCompile )
BOOST_REQUIRE_EQUAL(0, compileResult);
}
-class DuffRequest : public IceSpider::IHttpRequest {
-
-};
-
BOOST_AUTO_TEST_CASE( testLoad )
{
auto outputso = binDir / "testRoutes.so";
@@ -93,9 +89,6 @@ BOOST_AUTO_TEST_CASE( testLoad )
{
auto route = AdHoc::PluginManager::getDefault()->get<IceSpider::IRouteHandler>("common::index");
BOOST_REQUIRE(route);
- BOOST_REQUIRE_THROW({
- route->implementation()->execute(new DuffRequest());
- }, IceUtil::NullHandleException);
}
BOOST_REQUIRE_EQUAL(0, dlclose(lib));
diff --git a/icespider/unittests/testRoutes.json b/icespider/unittests/testRoutes.json
index b4eb54f..2b7b48d 100644
--- a/icespider/unittests/testRoutes.json
+++ b/icespider/unittests/testRoutes.json
@@ -9,7 +9,7 @@
},
{
"name": "item",
- "path": "/{s}/{i}",
+ "path": "/view/{s}/{i}",
"method": "GET",
"operation": "TestIceSpider.TestApi.withParams"
},