diff options
| -rw-r--r-- | icespider/common/http.ice | 2 | ||||
| -rw-r--r-- | icespider/core/ihttpRequest.cpp | 53 | ||||
| -rw-r--r-- | icespider/core/ihttpRequest.h | 1 | ||||
| -rw-r--r-- | icespider/unittests/Jamfile.jam | 13 | ||||
| -rw-r--r-- | icespider/unittests/testAccept.cpp | 94 | 
5 files changed, 140 insertions, 23 deletions
| diff --git a/icespider/common/http.ice b/icespider/common/http.ice index 1b51da7..d535ee2 100644 --- a/icespider/common/http.ice +++ b/icespider/common/http.ice @@ -30,6 +30,8 @@ module IceSpider {  		optional(1) string type;  		float q = 1.0;  	}; +	["slicer:ignore"] +	local sequence<Accept> Accepted;  	["slicer:json:object",  		"cpp:type:std::map<std::string, std::string, std::less<>>"] diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp index 7df6084..21506e5 100644 --- a/icespider/core/ihttpRequest.cpp +++ b/icespider/core/ihttpRequest.cpp @@ -33,34 +33,41 @@ namespace IceSpider {  		}  	} +	Accepted +	IHttpRequest::parseAccept(const std::string & acceptHdr) +	{ +		auto accept = acceptHdr.c_str(); +		Accepted accepts; +		accepts.reserve(5); +		char grp[BUFSIZ], type[BUFSIZ]; +		float pri = 0.0f; +		int chars, v; +		while ((v = sscanf(accept, " %[^ /] / %[^ ;,] %n , %n", grp, type, &chars, &chars)) == 2) { +			accept += chars; +			chars = 0; +			auto a = std::make_shared<Accept>(); +			if ((v = sscanf(accept, " ; q = %f %n , %n", &pri, &chars, &chars)) == 1) { +				a->q = pri; +			} +			if (strcmp(grp, "*")) { +				a->group.emplace(grp); +			} +			if (strcmp(type, "*")) { +				a->type.emplace(type); +			} +			accept += chars; +			accepts.push_back(a); +		} +		std::stable_sort(accepts.begin(), accepts.end(), [](const auto & a, const auto & b) { return a->q > b->q; }); +		return accepts; +	} +  	ContentTypeSerializer  	IHttpRequest::getSerializer(const IRouteHandler * handler) const  	{  		auto acceptHdr = getHeaderParam(H::ACCEPT);  		if (acceptHdr) { -			auto accept = acceptHdr->c_str(); -			std::vector<AcceptPtr> accepts; -			accepts.reserve(5); -			char grp[BUFSIZ], type[BUFSIZ]; -			float pri = 0.0f; -			int chars, v; -			while ((v = sscanf(accept, " %[^ /] / %[^ ;,] %n , %n", grp, type, &chars, &chars)) == 2) { -				accept += chars; -				chars = 0; -				auto a = std::make_shared<Accept>(); -				if ((v = sscanf(accept, " ; q = %f %n , %n", &pri, &chars, &chars)) == 1) { -					a->q = pri; -				} -				if (strcmp(grp, "*")) { -					a->group.emplace(grp); -				} -				if (strcmp(type, "*")) { -					a->type.emplace(type); -				} -				accept += chars; -				accepts.push_back(a); -			} -			std::stable_sort(accepts.begin(), accepts.end(), [](const auto & a, const auto & b) { return a->q > b->q; }); +			auto accepts = parseAccept(*acceptHdr);  			auto & strm = getOutputStream();  			for(auto & a : accepts) {  				ContentTypeSerializer serializer = handler->getSerializer(a, strm); diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h index 6e3be55..be3a0e0 100644 --- a/icespider/core/ihttpRequest.h +++ b/icespider/core/ihttpRequest.h @@ -32,6 +32,7 @@ namespace IceSpider {  			virtual OptionalString getHeaderParam(const std::string_view &) const = 0;  			virtual OptionalString getCookieParam(const std::string_view &) const = 0;  			virtual OptionalString getEnv(const std::string_view &) const = 0; +			static Accepted parseAccept(const std::string &);  			virtual Slicer::DeserializerPtr getDeserializer() const;  			virtual ContentTypeSerializer getSerializer(const IRouteHandler *) const;  			virtual std::istream & getInputStream() const = 0; diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam index aa8d879..43e3d82 100644 --- a/icespider/unittests/Jamfile.jam +++ b/icespider/unittests/Jamfile.jam @@ -59,6 +59,19 @@ run  	;  run +	testAccept.cpp +	: : : +	<define>BOOST_TEST_DYN_LINK +	<library>testCommon +	<library>adhocutil +	<library>../common//icespider-common +	<library>../core//icespider-core +	<implicit-dependency>../core//icespider-core +	<library>../testing//icespider-testing +	<implicit-dependency>../common//icespider-common +	; + +run  	testApp.cpp  	base2.cpp  	testRoutes.json diff --git a/icespider/unittests/testAccept.cpp b/icespider/unittests/testAccept.cpp new file mode 100644 index 0000000..fb5705b --- /dev/null +++ b/icespider/unittests/testAccept.cpp @@ -0,0 +1,94 @@ +#define BOOST_TEST_MODULE TestAccept +#include <boost/test/unit_test.hpp> +#include <boost/test/data/test_case.hpp> + +#include <ihttpRequest.h> + +auto parse = IceSpider::IHttpRequest::parseAccept; +using namespace boost::unit_test::data; + +namespace std { +	ostream & operator<<(ostream & s, const IceSpider::Accept & a) +	{ +		s << (a.group ? *a.group : "*") +			<< "/" +			<< (a.type ? *a.type : "*") +			<< ";q=" << a.q; +		return s; +	} +} + +BOOST_DATA_TEST_CASE(empty, make({ +	"", +	"  ", +}), a) +{ +	BOOST_REQUIRE(parse(a).empty()); +} + +BOOST_DATA_TEST_CASE(texthtml, make({ +	"text/html", +	"image/png;q=0, text/html", +	"image/png;q=1.0, text/html;q=1.1", +	"image/png;q=1.0, text/html;q=1.1, something/else;q=1.1", +}), a) +{ +	auto front = parse(a).front(); +	BOOST_REQUIRE(front->group); +	BOOST_REQUIRE_EQUAL(*front->group, "text"); +	BOOST_REQUIRE(front->type); +	BOOST_REQUIRE_EQUAL(*front->type, "html"); +} + +BOOST_DATA_TEST_CASE(textany, make({ +	"text/*", +	"image/png;q=0, text/*", +	"image/png;q=1.0, text/*;q=1.1", +	"image/png;q=1.0, text/*;q=1.1, something/else;q=1.1", +}), a) +{ +	auto front = parse(a).front(); +	BOOST_REQUIRE(front->group); +	BOOST_REQUIRE_EQUAL(*front->group, "text"); +	BOOST_REQUIRE(!front->type); +} + +BOOST_DATA_TEST_CASE(anyhtml, make({ +	"*/html", +	"image/png;q=0, */html", +	"image/png;q=1.0, */html;q=1.1", +	"image/png;q=1.0, */html;q=1.1, something/else;q=1.1", +}), a) +{ +	auto front = parse(a).front(); +	BOOST_REQUIRE(!front->group); +	BOOST_REQUIRE(front->type); +	BOOST_REQUIRE_EQUAL(*front->type, "html"); +} + +BOOST_DATA_TEST_CASE(anyany, make({ +	"*/*", +	"image/png;q=0, */*", +	"image/png;q=1.0, */*;q=1.1", +	"image/png;q=1.0, */*;q=1.1, something/else;q=1.1", +}), a) +{ +	auto front = parse(a).front(); +	BOOST_REQUIRE(!front->group); +	BOOST_REQUIRE(!front->type); +} + +BOOST_DATA_TEST_CASE(q1, make({ +	"*/*", +	"image/png, */*", +	"image/png;q=1.0, */*", +}), a) +{ +	auto all = parse(a); +	for(const auto & accept : all) { +		BOOST_TEST_CONTEXT(*accept) { +			BOOST_CHECK_CLOSE(accept->q, 1.0, 0.1); +		} +	} +} + | 
