From 94f655f3563ee428e8282cd7ba4ee7612cdcba8f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 21 Aug 2016 18:18:12 +0100 Subject: Bit of a refactor around Accept / Content-Type and include Content-Type in response headers --- icespider/common/http.ice | 11 +++++++++++ icespider/core/acceptable.cpp | 12 ------------ icespider/core/acceptable.h | 18 ------------------ icespider/core/ihttpRequest.cpp | 40 +++++++++++++++++----------------------- icespider/core/ihttpRequest.h | 9 +++++---- icespider/core/irouteHandler.cpp | 22 ++++++++++++---------- icespider/core/irouteHandler.h | 11 +++++------ icespider/unittests/testApp.cpp | 10 ++++++++++ 8 files changed, 60 insertions(+), 73 deletions(-) delete mode 100644 icespider/core/acceptable.cpp delete mode 100644 icespider/core/acceptable.h 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 -#include - -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 - -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 accepts; + std::vector 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 #include #include -#include "acceptable.h" namespace IceSpider { class Core; @@ -16,6 +15,7 @@ namespace IceSpider { typedef std::vector PathElements; typedef IceUtil::Optional OptionalString; + typedef std::pair 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, s); + Slicer::SerializeAnyWith(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 ContentType; - typedef std::map RouteSerializers; + typedef std::map RouteSerializers; RouteSerializers routeSerializers; template @@ -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 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 #include #include +#include #include #include #include @@ -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(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(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(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(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(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(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(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(requestChoice.output); BOOST_REQUIRE_EQUAL(v->value, "index"); } -- cgit v1.2.3