summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--icespider/common/http.ice2
-rw-r--r--icespider/core/ihttpRequest.cpp53
-rw-r--r--icespider/core/ihttpRequest.h1
-rw-r--r--icespider/unittests/Jamfile.jam13
-rw-r--r--icespider/unittests/testAccept.cpp94
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);
+ }
+ }
+}
+