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");  } | 
