diff options
-rw-r--r-- | icespider/common/routes.ice | 9 | ||||
-rw-r--r-- | icespider/compile/routeCompiler.cpp | 34 | ||||
-rw-r--r-- | icespider/compile/routeCompiler.h | 2 | ||||
-rw-r--r-- | icespider/core/core.cpp | 1 | ||||
-rw-r--r-- | icespider/core/irouteHandler.cpp | 25 | ||||
-rw-r--r-- | icespider/core/irouteHandler.h | 5 | ||||
-rw-r--r-- | icespider/unittests/Jamfile.jam | 7 | ||||
-rw-r--r-- | icespider/unittests/testApp.cpp | 24 | ||||
-rw-r--r-- | icespider/unittests/testCompile.cpp | 15 | ||||
-rw-r--r-- | icespider/unittests/testRoutes.json | 7 | ||||
-rw-r--r-- | icespider/unittests/xslt/transform.xslt | 14 | ||||
-rw-r--r-- | icespider/xslt/Jamfile.jam | 22 | ||||
-rw-r--r-- | icespider/xslt/exslt.cpp | 17 | ||||
-rw-r--r-- | icespider/xslt/xsltStreamSerializer.cpp | 70 | ||||
-rw-r--r-- | icespider/xslt/xsltStreamSerializer.h | 34 |
15 files changed, 278 insertions, 8 deletions
diff --git a/icespider/common/routes.ice b/icespider/common/routes.ice index 65e7d67..bc2406a 100644 --- a/icespider/common/routes.ice +++ b/icespider/common/routes.ice @@ -20,12 +20,21 @@ module IceSpider { sequence<Parameter> Parameters; + class OutputSerializer { + string contentType; + string serializer; + StringSeq params; + }; + + sequence<OutputSerializer> OutputSerializers; + class Route { string name; string path; HttpMethod method = GET; string operation; Parameters params; + OutputSerializers outputSerializers; }; sequence<Route> Routes; diff --git a/icespider/compile/routeCompiler.cpp b/icespider/compile/routeCompiler.cpp index 65b3141..8c274d8 100644 --- a/icespider/compile/routeCompiler.cpp +++ b/icespider/compile/routeCompiler.cpp @@ -193,6 +193,34 @@ namespace IceSpider { } void + RouteCompiler::registerOutputSerializers(FILE * output, RoutePtr r) const + { + for (const auto & os : r->outputSerializers) { + auto bs = boost::algorithm::replace_all_copy(os->serializer, ".", "::"); + auto slash = os->contentType.find('/'); + fprintbf(4, output, "addRouteSerializer({ \"%s\", \"%s\" }, new %s::IceSpiderFactory(", + os->contentType.substr(0, slash), os->contentType.substr(slash + 1), bs); + for (auto p = os->params.begin(); p != os->params.end(); ++p) { + if (p != os->params.begin()) { + fprintf(output, ", "); + } + fputs(p->c_str(), output); + } + fprintf(output, "));\n"); + } + } + + void + RouteCompiler::releaseOutputSerializers(FILE * output, RoutePtr r) const + { + for (const auto & os : r->outputSerializers) { + auto slash = os->contentType.find('/'); + fprintbf(4, output, "removeRouteSerializer({ \"%s\", \"%s\" });\n", + os->contentType.substr(0, slash), os->contentType.substr(slash + 1)); + } + } + + void RouteCompiler::processConfiguration(FILE * output, RouteConfigurationPtr c, const Units & units) const { fprintf(output, "// This source files was generated by IceSpider.\n"); @@ -200,6 +228,7 @@ namespace IceSpider { fprintf(output, "// Standard headers.\n"); fprintf(output, "#include <irouteHandler.h>\n"); + fprintf(output, "#include <slicer/serializer.h>\n"); fprintf(output, "\n// Interface headers.\n"); for (const auto & s : c->slices) { @@ -257,6 +286,11 @@ namespace IceSpider { } fprintf(output, "\n"); fprintbf(3, output, "{\n"); + registerOutputSerializers(output, r); + fprintbf(3, output, "}\n\n"); + fprintbf(3, output, "~%s()\n", r->name); + fprintbf(3, output, "{\n"); + releaseOutputSerializers(output, r); fprintbf(3, output, "}\n\n"); fprintbf(3, output, "void execute(IceSpider::IHttpRequest * request) const\n"); fprintbf(3, output, "{\n"); diff --git a/icespider/compile/routeCompiler.h b/icespider/compile/routeCompiler.h index af5912e..93522fd 100644 --- a/icespider/compile/routeCompiler.h +++ b/icespider/compile/routeCompiler.h @@ -26,6 +26,8 @@ namespace IceSpider { private: void processConfiguration(FILE * output, RouteConfigurationPtr, const Units &) const; + void registerOutputSerializers(FILE * output, RoutePtr) const; + void releaseOutputSerializers(FILE * output, RoutePtr) const; static Slice::OperationPtr findOperation(RoutePtr, const Units &); static Slice::OperationPtr findOperation(RoutePtr, const Slice::ContainerPtr &, const Ice::StringSeq & = Ice::StringSeq()); }; diff --git a/icespider/core/core.cpp b/icespider/core/core.cpp index 85a9bfa..b21a805 100644 --- a/icespider/core/core.cpp +++ b/icespider/core/core.cpp @@ -12,7 +12,6 @@ namespace IceSpider { // Initialize routes for (const auto & rp : AdHoc::PluginManager::getDefault()->getAll<IRouteHandler>()) { auto r = rp->implementation(); - r->initialize(); auto & mroutes = routes[r->method]; if (mroutes.size() <= r->pathElementCount()) { mroutes.resize(r->pathElementCount() + 1); diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp index dcbab7d..a0d8fcd 100644 --- a/icespider/core/irouteHandler.cpp +++ b/icespider/core/irouteHandler.cpp @@ -9,11 +9,6 @@ namespace IceSpider { Path(p), method(m) { - } - - void - IRouteHandler::initialize() - { auto globalSerializers = AdHoc::PluginManager::getDefault()->getAll<Slicer::StreamSerializerFactory>(); for (const auto & gs : globalSerializers) { auto slash = gs->name.find('/'); @@ -21,6 +16,10 @@ namespace IceSpider { } } + IRouteHandler::~IRouteHandler() + { + } + Ice::ObjectPrx IRouteHandler::getProxy(IHttpRequest * request, const char * type) const { @@ -44,5 +43,21 @@ namespace IceSpider { return Slicer::StreamSerializerFactory::createNew( "application/json", strm); } + + void + IRouteHandler::addRouteSerializer(const ContentType & ct, StreamSerializerFactoryPtr ssfp) + { + routeSerializers.insert({ ct, ssfp }); + } + + void + IRouteHandler::removeRouteSerializer(const ContentType & ct) + { + auto i = routeSerializers.find(ct); + if (i != routeSerializers.end()) { + delete i->second; + routeSerializers.erase(i); + } + } } diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h index 6ee8160..1cfa5ee 100644 --- a/icespider/core/irouteHandler.h +++ b/icespider/core/irouteHandler.h @@ -13,9 +13,9 @@ namespace IceSpider { class DLL_PUBLIC IRouteHandler : public AdHoc::AbstractPluginImplementation, public Path { public: IRouteHandler(HttpMethod, const std::string & path); + virtual ~IRouteHandler(); virtual void execute(IHttpRequest * request) const = 0; - virtual void initialize(); virtual Slicer::SerializerPtr getSerializer(const char *, const char *, std::ostream &) const; virtual Slicer::SerializerPtr defaultSerializer(std::ostream &) const; @@ -41,6 +41,9 @@ namespace IceSpider { { return Interface::ProxyType::uncheckedCast(getProxy(request, typeid(Interface).name())); } + + void addRouteSerializer(const ContentType &, StreamSerializerFactoryPtr); + void removeRouteSerializer(const ContentType &); }; typedef AdHoc::PluginOf<IRouteHandler> RouteHandlers; } diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam index 234e715..05fecb4 100644 --- a/icespider/unittests/Jamfile.jam +++ b/icespider/unittests/Jamfile.jam @@ -46,6 +46,8 @@ run <library>test-api <library>dl <library>adhocutil + <library>slicer-xml + <library>../xslt//icespider-xslt <library>../core//icespider-core <library>../compile//icespider-compile <implicit-dependency>../compile//icespider-compile @@ -56,7 +58,9 @@ run run testApp.cpp testRoutes.json - : : : + : : + xslt/transform.xslt + : <define>BOOST_TEST_DYN_LINK <icespider>../compile//icespider <library>testCommon @@ -67,6 +71,7 @@ run <library>slicer-xml <library>../common//icespider-common <library>../core//icespider-core + <library>../xslt//icespider-xslt <implicit-dependency>../common//icespider-common <implicit-dependency>test-api <dependency>../compile diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp index bb7c3c6..fb79b0a 100644 --- a/icespider/unittests/testApp.cpp +++ b/icespider/unittests/testApp.cpp @@ -9,9 +9,17 @@ #include <Ice/ObjectAdapter.h> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> +#include <boost/filesystem/operations.hpp> +#include <definedDirs.h> using namespace IceSpider; +static void forceEarlyChangeDir() __attribute__((constructor(101))); +void forceEarlyChangeDir() +{ + boost::filesystem::current_path(XSTR(ROOT)); +} + BOOST_AUTO_TEST_CASE( testLoadConfiguration ) { BOOST_REQUIRE_EQUAL(6, AdHoc::PluginManager::getDefault()->getAll<IRouteHandler>().size()); @@ -253,6 +261,22 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptXml ) BOOST_REQUIRE_EQUAL(requestXml.output.str(), "Status: 200 OK\r\n\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<SomeModel><value>index</value></SomeModel>\n"); } +BOOST_AUTO_TEST_CASE( testCallIndexAcceptTextHtml ) +{ + TestRequest requestHtml(this, HttpMethod::GET, "/"); + requestHtml.hdr["Accept"] = "text/html"; + process(&requestHtml); + BOOST_REQUIRE_EQUAL(requestHtml.output.str(), "Status: 200 OK\r\n\r\n<html><head><title>Some Model</title></head><body><p><b>value</b>: index</p></body></html>\n"); +} + +BOOST_AUTO_TEST_CASE( testCallViewSomethingAcceptHtml ) +{ + TestRequest requestHtml(this, HttpMethod::GET, "/view/something/1234"); + requestHtml.hdr["Accept"] = "text/html"; + process(&requestHtml); + BOOST_REQUIRE_EQUAL(requestHtml.output.str(), "Status: 406 Unacceptable\r\n\r\n"); +} + BOOST_AUTO_TEST_CASE( testCallIndexAcceptNotSupported ) { TestRequest requestBadAccept(this, HttpMethod::GET, "/"); diff --git a/icespider/unittests/testCompile.cpp b/icespider/unittests/testCompile.cpp index 411f703..46175ef 100644 --- a/icespider/unittests/testCompile.cpp +++ b/icespider/unittests/testCompile.cpp @@ -10,6 +10,12 @@ using namespace IceSpider; +static void forceEarlyChangeDir() __attribute__((constructor(101))); +void forceEarlyChangeDir() +{ + boost::filesystem::current_path(XSTR(ROOT)); +} + class CoreFixture { protected: CoreFixture() : @@ -73,9 +79,18 @@ BOOST_AUTO_TEST_CASE( testCompile ) auto compileCommand = boost::algorithm::join<std::vector<std::string>>({ "gcc", "-c", "-std=c++1y", "-fPIC", "-fvisibility=hidden", "-O3", "-I", "/usr/include/adhocutil", + "-I", "/usr/include/glib-2.0", + "-I", "/usr/include/glibmm-2.4", + "-I", "/usr/include/libxml2", + "-I", "/usr/include/libxml++-2.6", + "-I", "/usr/include/libxslt", "-I", "/usr/include/slicer", + "-I", "/usr/lib/glib-2.0/include", + "-I", "/usr/lib/glibmm-2.4/include", + "-I", "/usr/lib/libxml++-2.6/include", "-I", (rootDir.parent_path() / "core").string(), "-I", (rootDir.parent_path() / "common").string(), + "-I", (rootDir.parent_path() / "xslt").string(), "-I", (rootDir.parent_path() / "common" / "bin" / modeDir / "allow-ice-yes").string(), "-I", libGenDir.string(), "-o", outputo.string(), diff --git a/icespider/unittests/testRoutes.json b/icespider/unittests/testRoutes.json index cf5d289..921d983 100644 --- a/icespider/unittests/testRoutes.json +++ b/icespider/unittests/testRoutes.json @@ -6,6 +6,13 @@ "name": "index", "path": "/", "method": "GET", + "outputSerializers": [ + { + "contentType": "text/html", + "serializer": "IceSpider.XsltStreamSerializer", + "params": [ "\"xslt/transform.xslt\"" ] + } + ], "operation": "TestIceSpider.TestApi.index" }, { diff --git a/icespider/unittests/xslt/transform.xslt b/icespider/unittests/xslt/transform.xslt new file mode 100644 index 0000000..b70c90c --- /dev/null +++ b/icespider/unittests/xslt/transform.xslt @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> + <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes"/> + <xsl:template match="/SomeModel"> + <html> + <head> + <title>Some Model</title> + </head> + <body> + <p><b>value</b>: <xsl:value-of select="value"/></p> + </body> + </html> + </xsl:template> +</xsl:stylesheet> diff --git a/icespider/xslt/Jamfile.jam b/icespider/xslt/Jamfile.jam new file mode 100644 index 0000000..1bf62a8 --- /dev/null +++ b/icespider/xslt/Jamfile.jam @@ -0,0 +1,22 @@ +lib adhocutil : : : : <include>/usr/include/adhocutil ; +lib slicer-xml : : : : <include>/usr/include/slicer ; +lib boost_system ; +lib IceUtil ; +lib xslt ; +lib exslt ; + +lib icespider-xslt : + [ glob-tree *.cpp : bin ] + : + <library>adhocutil + <library>slicer-xml + <library>boost_system + <library>IceUtil + <library>xslt + <library>exslt + <library>../..//libxmlpp + : : + <include>. + <library>../..//libxmlpp + ; + diff --git a/icespider/xslt/exslt.cpp b/icespider/xslt/exslt.cpp new file mode 100644 index 0000000..63bd0d1 --- /dev/null +++ b/icespider/xslt/exslt.cpp @@ -0,0 +1,17 @@ +#include <libexslt/exslt.h> +#include <libxslt/transform.h> + +static void initLibXml() __attribute__((constructor(102))); +void initLibXml() +{ + xmlInitParser(); + exsltRegisterAll(); +} + +static void cleanupLibXml() __attribute__((destructor(102))); +void cleanupLibXml() +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} + diff --git a/icespider/xslt/xsltStreamSerializer.cpp b/icespider/xslt/xsltStreamSerializer.cpp new file mode 100644 index 0000000..bbe3943 --- /dev/null +++ b/icespider/xslt/xsltStreamSerializer.cpp @@ -0,0 +1,70 @@ +#include "xsltStreamSerializer.h" +#include <libxslt/xsltInternals.h> +#include <libxml/HTMLtree.h> +#include <factory.impl.h> + +namespace IceSpider { + static int xmlstrmclosecallback(void * context) + { + ((std::ostream*)context)->flush(); + return 0; + } + + static int xmlstrmwritecallback(void * context, const char * buffer, int len) + { + ((std::ostream*)context)->write(buffer, len); + return len; + } + + XsltStreamSerializer::IceSpiderFactory::IceSpiderFactory(const char * path) : + stylesheet(xsltParseStylesheetFile(BAD_CAST path)) + { + if (!stylesheet) { + throw xmlpp::exception("Failed to load stylesheet"); + } + } + + XsltStreamSerializer::IceSpiderFactory::~IceSpiderFactory() + { + xsltFreeStylesheet(stylesheet); + } + + XsltStreamSerializer * + XsltStreamSerializer::IceSpiderFactory::create(std::ostream & strm) const + { + return new XsltStreamSerializer(strm, stylesheet); + } + + XsltStreamSerializer::XsltStreamSerializer(std::ostream & os, xsltStylesheet * ss) : + Slicer::XmlDocumentSerializer(doc), + strm(os), + doc(nullptr), + stylesheet(ss) + { + } + + XsltStreamSerializer::~XsltStreamSerializer() + { + delete doc; + } + + void + XsltStreamSerializer::Serialize(Slicer::ModelPartPtr mp) + { + Slicer::XmlDocumentSerializer::Serialize(mp); + auto result = xsltApplyStylesheet(stylesheet, doc->cobj(), nullptr); + if (!result) { + throw xmlpp::exception("Failed to apply XSL transform"); + } + xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, NULL); + if (xmlStrcmp(stylesheet->method, BAD_CAST "html") == 0) { + htmlDocContentDumpFormatOutput(buf, result, (const char *) stylesheet->encoding, 0); + } + else { + xmlNodeDumpOutput(buf, result, result->children, 0, 0, (const char *) stylesheet->encoding); + } + xmlOutputBufferClose(buf); + xmlFreeDoc(result); + } +} + diff --git a/icespider/xslt/xsltStreamSerializer.h b/icespider/xslt/xsltStreamSerializer.h new file mode 100644 index 0000000..d8000d1 --- /dev/null +++ b/icespider/xslt/xsltStreamSerializer.h @@ -0,0 +1,34 @@ +#ifndef ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H +#define ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H + +#include <slicer/xml/serializer.h> +#include <visibility.h> +#include <libxslt/transform.h> + +namespace IceSpider { + class DLL_PUBLIC XsltStreamSerializer : public Slicer::XmlDocumentSerializer { + public: + class IceSpiderFactory : public Slicer::StreamSerializerFactory { + public: + IceSpiderFactory(const char *); + ~IceSpiderFactory(); + + XsltStreamSerializer * create(std::ostream &) const override; + + xsltStylesheet * stylesheet; + }; + + XsltStreamSerializer(std::ostream &, xsltStylesheet *); + ~XsltStreamSerializer(); + + void Serialize(Slicer::ModelPartPtr mp) override; + + protected: + std::ostream & strm; + xmlpp::Document * doc; + xsltStylesheet * stylesheet; + }; +} + +#endif + |