diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-08-21 18:18:12 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-08-21 18:18:12 +0100 |
commit | 94f655f3563ee428e8282cd7ba4ee7612cdcba8f (patch) | |
tree | 6d43509ff65bdefdcf02fed836bbc348d2cf8bcd | |
parent | Parse output stream in tests instead of big string assertion (diff) | |
download | icespider-94f655f3563ee428e8282cd7ba4ee7612cdcba8f.tar.bz2 icespider-94f655f3563ee428e8282cd7ba4ee7612cdcba8f.tar.xz icespider-94f655f3563ee428e8282cd7ba4ee7612cdcba8f.zip |
Bit of a refactor around Accept / Content-Type and include Content-Type in response headers
-rw-r--r-- | icespider/common/http.ice | 11 | ||||
-rw-r--r-- | icespider/core/acceptable.cpp | 12 | ||||
-rw-r--r-- | icespider/core/acceptable.h | 18 | ||||
-rw-r--r-- | icespider/core/ihttpRequest.cpp | 40 | ||||
-rw-r--r-- | icespider/core/ihttpRequest.h | 9 | ||||
-rw-r--r-- | icespider/core/irouteHandler.cpp | 22 | ||||
-rw-r--r-- | icespider/core/irouteHandler.h | 11 | ||||
-rw-r--r-- | icespider/unittests/testApp.cpp | 10 |
8 files changed, 60 insertions, 73 deletions
diff --git a/icespider/common/http.ice b/icespider/common/http.ice index 33c0543..cefb494 100644 --- a/icespider/common/http.ice +++ b/icespider/common/http.ice @@ -9,6 +9,17 @@ module IceSpider { enum ParameterSource { URL, Body, QueryString, Header }; + + struct MimeType { + string group; + string type; + }; + + class Accept { + optional(0) string group; + optional(1) string type; + float q = 1.0; + }; }; #endif diff --git a/icespider/core/acceptable.cpp b/icespider/core/acceptable.cpp deleted file mode 100644 index d7a2c58..0000000 --- a/icespider/core/acceptable.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "acceptable.h" -#include <stdlib.h> -#include <string.h> - -namespace IceSpider { - void - Acceptable::free() - { - ::free(grp); - ::free(type); - } -} diff --git a/icespider/core/acceptable.h b/icespider/core/acceptable.h deleted file mode 100644 index 20b6f09..0000000 --- a/icespider/core/acceptable.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef ICESPIDER_ACCEPTABLE_H -#define ICESPIDER_ACCEPTABLE_H - -#include <visibility.h> - -namespace IceSpider { - class DLL_PUBLIC Acceptable { - public: - void free(); - - char * grp; - char * type; - float pri; - }; -} - -#endif - diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp index a4e25b2..9b62507 100644 --- a/icespider/core/ihttpRequest.cpp +++ b/icespider/core/ihttpRequest.cpp @@ -24,48 +24,42 @@ namespace IceSpider { }, getInputStream()); } - Slicer::SerializerPtr + ContentTypeSerializer IHttpRequest::getSerializer(const IRouteHandler * handler) const { auto acceptHdr = getHeaderParam("Accept"); if (acceptHdr) { auto accept = acceptHdr->c_str(); - std::vector<Acceptable> accepts; + std::vector<AcceptPtr> accepts; accepts.reserve(5); - char * grp = NULL, * type = NULL; + char grp[BUFSIZ], type[BUFSIZ]; float pri = 0.0f; int chars, v; - while ((v = sscanf(accept, " %m[^ /] / %m[^ ;,] %n , %n", &grp, &type, &chars, &chars)) == 2) { + while ((v = sscanf(accept, " %[^ /] / %[^ ;,] %n , %n", grp, type, &chars, &chars)) == 2) { accept += chars; chars = 0; - if ((v = sscanf(accept, " ; q = %f %n , %n", &pri, &chars, &chars)) != 1) { - pri = 1.0; + auto a = new Accept(); + if ((v = sscanf(accept, " ; q = %f %n , %n", &pri, &chars, &chars)) == 1) { + a->q = pri; } - if (!strcmp(grp, "*")) { - free(grp); - grp = NULL; + if (strcmp(grp, "*")) { + a->group = grp; } - if (!strcmp(type, "*")) { - free(type); - type = NULL; + if (strcmp(type, "*")) { + a->type = type; } - accepts.push_back( { grp, type, pri } ); - grp = NULL; - type = NULL; accept += chars; + accepts.push_back(a); } - free(grp); - free(type); - std::stable_sort(accepts.begin(), accepts.end(), [](const auto & a, const auto & b) { return a.pri > b.pri; }); - Slicer::SerializerPtr serializer; + std::stable_sort(accepts.begin(), accepts.end(), [](const auto & a, const auto & b) { return a->q > b->q; }); auto & strm = getOutputStream(); for(auto & a : accepts) { - if (!serializer) { - serializer = handler->getSerializer(a.grp, a.type, strm); + ContentTypeSerializer serializer = handler->getSerializer(a, strm); + if (serializer.second) { + return serializer; } - a.free(); } - return serializer; + return ContentTypeSerializer(); } else { return handler->defaultSerializer(getOutputStream()); diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h index 45bcd11..9ce869f 100644 --- a/icespider/core/ihttpRequest.h +++ b/icespider/core/ihttpRequest.h @@ -8,7 +8,6 @@ #include <routes.h> #include <slicer/slicer.h> #include <IceUtil/Optional.h> -#include "acceptable.h" namespace IceSpider { class Core; @@ -16,6 +15,7 @@ namespace IceSpider { typedef std::vector<std::string> PathElements; typedef IceUtil::Optional<std::string> OptionalString; + typedef std::pair<MimeType, Slicer::SerializerPtr> ContentTypeSerializer; class DLL_PUBLIC IHttpRequest { public: @@ -29,7 +29,7 @@ namespace IceSpider { virtual OptionalString getQueryStringParam(const std::string &) const = 0; virtual OptionalString getHeaderParam(const std::string &) const = 0; virtual Slicer::DeserializerPtr getDeserializer() const; - virtual Slicer::SerializerPtr getSerializer(const IRouteHandler *) const; + virtual ContentTypeSerializer getSerializer(const IRouteHandler *) const; virtual std::istream & getInputStream() const = 0; virtual std::ostream & getOutputStream() const = 0; @@ -49,9 +49,10 @@ namespace IceSpider { void response(const IRouteHandler * route, const T & t) const { auto s = getSerializer(route); - if (s) { + if (s.second) { + getOutputStream() << "Content-Type: " << s.first.group << "/" << s.first.type << "\r\n"; response(200, "OK"); - Slicer::SerializeAnyWith<T>(t, s); + Slicer::SerializeAnyWith<T>(t, s.second); } else { response(406, "Unacceptable"); diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp index a0d8fcd..82c6df1 100644 --- a/icespider/core/irouteHandler.cpp +++ b/icespider/core/irouteHandler.cpp @@ -26,32 +26,34 @@ namespace IceSpider { return request->core->getProxy(type); } - Slicer::SerializerPtr - IRouteHandler::getSerializer(const char * grp, const char * type, std::ostream & strm) const + ContentTypeSerializer + IRouteHandler::getSerializer(const AcceptPtr & a, std::ostream & strm) const { for (const auto & rs : routeSerializers) { - if ((!grp || rs.first.first == grp) && (!type || rs.first.second == type)) { - return rs.second->create(strm); + if ((!a->group || rs.first.group == a->group) && (!a->type || rs.first.type == a->type)) { + return { rs.first, rs.second->create(strm) }; } } - return nullptr; + return ContentTypeSerializer(); } - Slicer::SerializerPtr + ContentTypeSerializer IRouteHandler::defaultSerializer(std::ostream & strm) const { - return Slicer::StreamSerializerFactory::createNew( - "application/json", strm); + return { + { "application", "json" }, + Slicer::StreamSerializerFactory::createNew("application/json", strm) + }; } void - IRouteHandler::addRouteSerializer(const ContentType & ct, StreamSerializerFactoryPtr ssfp) + IRouteHandler::addRouteSerializer(const MimeType & ct, StreamSerializerFactoryPtr ssfp) { routeSerializers.insert({ ct, ssfp }); } void - IRouteHandler::removeRouteSerializer(const ContentType & ct) + IRouteHandler::removeRouteSerializer(const MimeType & ct) { auto i = routeSerializers.find(ct); if (i != routeSerializers.end()) { diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h index 1cfa5ee..bbd9ab3 100644 --- a/icespider/core/irouteHandler.h +++ b/icespider/core/irouteHandler.h @@ -16,15 +16,14 @@ namespace IceSpider { virtual ~IRouteHandler(); virtual void execute(IHttpRequest * request) const = 0; - virtual Slicer::SerializerPtr getSerializer(const char *, const char *, std::ostream &) const; - virtual Slicer::SerializerPtr defaultSerializer(std::ostream &) const; + virtual ContentTypeSerializer getSerializer(const AcceptPtr &, std::ostream &) const; + virtual ContentTypeSerializer defaultSerializer(std::ostream &) const; const HttpMethod method; protected: typedef Slicer::StreamSerializerFactory * StreamSerializerFactoryPtr; - typedef std::pair<std::string, std::string> ContentType; - typedef std::map<ContentType, StreamSerializerFactoryPtr> RouteSerializers; + typedef std::map<MimeType, StreamSerializerFactoryPtr> RouteSerializers; RouteSerializers routeSerializers; template <typename T, typename K> @@ -42,8 +41,8 @@ namespace IceSpider { return Interface::ProxyType::uncheckedCast(getProxy(request, typeid(Interface).name())); } - void addRouteSerializer(const ContentType &, StreamSerializerFactoryPtr); - void removeRouteSerializer(const ContentType &); + void addRouteSerializer(const MimeType &, StreamSerializerFactoryPtr); + void removeRouteSerializer(const MimeType &); }; typedef AdHoc::PluginOf<IRouteHandler> RouteHandlers; } diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp index 9b9155f..7df75f2 100644 --- a/icespider/unittests/testApp.cpp +++ b/icespider/unittests/testApp.cpp @@ -9,6 +9,7 @@ #include <Ice/ObjectAdapter.h> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem/operations.hpp> #include <definedDirs.h> #include <slicer/slicer.h> @@ -211,6 +212,7 @@ BOOST_AUTO_TEST_CASE( testCallIndex ) process(&requestGetIndex); auto h = parseHeaders(requestGetIndex.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/json"); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestGetIndex.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } @@ -221,6 +223,7 @@ BOOST_AUTO_TEST_CASE( testCallViewSomething1234 ) process(&requestGetItem); auto h = parseHeaders(requestGetItem.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/json"); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestGetItem.output); BOOST_REQUIRE_EQUAL(v->value, "withParams"); } @@ -231,6 +234,7 @@ BOOST_AUTO_TEST_CASE( testCallViewSomething1234_ ) process(&requestGetItemGiven); auto h = parseHeaders(requestGetItemGiven.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/json"); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestGetItemGiven.output); BOOST_REQUIRE_EQUAL(v->value, "withParams"); } @@ -241,6 +245,7 @@ BOOST_AUTO_TEST_CASE( testCallViewSomething ) process(&requestGetItemDefault); auto h = parseHeaders(requestGetItemDefault.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/json"); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestGetItemDefault.output); BOOST_REQUIRE_EQUAL(v->value, "withParams"); } @@ -274,6 +279,7 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptJson ) process(&requestJson); auto h = parseHeaders(requestJson.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/json"); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestJson.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } @@ -296,6 +302,7 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptApplicationAny ) process(&requestApplicationAny); auto h = parseHeaders(requestApplicationAny.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE(boost::algorithm::starts_with(h["Content-Type"], "application/")); auto v = Slicer::DeserializeAny<Slicer::JsonStreamDeserializer, TestIceSpider::SomeModelPtr>(requestApplicationAny.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } @@ -307,6 +314,7 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptXml ) process(&requestXml); auto h = parseHeaders(requestXml.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/xml"); auto v = Slicer::DeserializeAny<Slicer::XmlStreamDeserializer, TestIceSpider::SomeModelPtr>(requestXml.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } @@ -318,6 +326,7 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptTextHtml ) process(&requestHtml); auto h = parseHeaders(requestHtml.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "text/html"); xmlpp::DomParser d; d.parse_stream(requestHtml.output); BOOST_REQUIRE_EQUAL(d.get_document()->get_root_node()->get_name(), "html"); @@ -352,6 +361,7 @@ BOOST_AUTO_TEST_CASE( testCallIndexComplexAccept ) process(&requestChoice); auto h = parseHeaders(requestChoice.output); BOOST_REQUIRE_EQUAL(h["Status"], "200 OK"); + BOOST_REQUIRE_EQUAL(h["Content-Type"], "application/xml"); auto v = Slicer::DeserializeAny<Slicer::XmlStreamDeserializer, TestIceSpider::SomeModelPtr>(requestChoice.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } |