summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--icespider/common/http.ice9
-rw-r--r--icespider/core/ihttpRequest.cpp76
-rw-r--r--icespider/core/ihttpRequest.h7
-rw-r--r--icespider/core/irouteHandler.cpp4
-rw-r--r--icespider/core/irouteHandler.h2
-rw-r--r--icespider/unittests/testAccept.cpp28
-rw-r--r--icespider/unittests/testPerf.cpp19
7 files changed, 84 insertions, 61 deletions
diff --git a/icespider/common/http.ice b/icespider/common/http.ice
index 35c518d..4ec6fdd 100644
--- a/icespider/common/http.ice
+++ b/icespider/common/http.ice
@@ -24,15 +24,6 @@ module IceSpider {
string type;
};
- ["slicer:ignore"]
- local class Accept {
- optional(0) string group;
- 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<>>"]
local dictionary<string, string> StringMap;
diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp
index 025509f..9459f60 100644
--- a/icespider/core/ihttpRequest.cpp
+++ b/icespider/core/ihttpRequest.cpp
@@ -35,57 +35,65 @@ namespace IceSpider {
}
Accepted
- IHttpRequest::parseAccept(const std::string_view & acceptHdr)
+ IHttpRequest::parseAccept(std::string_view acceptHdr)
{
- if (acceptHdr.empty()
- || std::find_if_not(acceptHdr.begin(), acceptHdr.end(), std::iswspace) == acceptHdr.end()) {
+ remove_leading(acceptHdr, ' ');
+ if (acceptHdr.empty()) {
throw Http400_BadRequest();
}
- auto accept = std::unique_ptr<FILE, decltype(&fclose)>(
- fmemopen(const_cast<char *>(acceptHdr.data()), acceptHdr.length(), "r"), &fclose);
Accepted accepts;
- accepts.reserve(acceptHdr.length() / 15);
- int grpstart, grpend, typestart, typeend;
- auto reset = [&]() {
- grpstart = grpend = typestart = typeend = 0;
- return ftell(accept.get());
- };
+ accepts.reserve(std::count(acceptHdr.begin(), acceptHdr.end(), ',') + 1);
- for (auto off = reset();
- // NOLINTNEXTLINE(hicpp-vararg)
- fscanf(accept.get(), " %n%*[^ /]%n / %n%*[^ ;,]%n", &grpstart, &grpend, &typestart, &typeend) == 0;
- off = reset()) {
- if (grpend <= grpstart || typestart <= grpend || typeend <= typestart) {
+ auto upto = [](std::string_view & in, const std::string_view term, bool req) {
+ remove_leading(in, ' ');
+ const auto end = in.find_first_of(term);
+ if (req && end == std::string_view::npos) {
+ throw Http400_BadRequest();
+ }
+ auto val = in.substr(0, end);
+ remove_trailing(val, ' ');
+ if (val.empty()) {
throw Http400_BadRequest();
}
- auto a = std::make_shared<Accept>();
- if (auto g = acceptHdr.substr(typestart + off, typeend - typestart); g != "*") {
- a->type.emplace(g);
+ if (end != std::string_view::npos) {
+ const auto tc = in[end];
+ in.remove_prefix(end + 1);
+ return std::make_pair(val, tc);
+ }
+ else {
+ in.remove_prefix(in.length());
+ return std::make_pair(val, '\n');
+ }
+ };
+
+ while (!acceptHdr.empty()) {
+ const auto group = upto(acceptHdr, "/", true).first;
+ const auto [type, tc] = upto(acceptHdr, ";,", false);
+ Accept a;
+ if (type != "*") {
+ a.type.emplace(type);
}
- if (auto t = acceptHdr.substr(grpstart + off, grpend - grpstart); t != "*") {
- a->group.emplace(t);
+ if (group != "*") {
+ a.group.emplace(group);
}
- else if (a->type) {
+ else if (a.type) {
throw Http400_BadRequest();
}
- // NOLINTNEXTLINE(hicpp-vararg)
- if (fscanf(accept.get(), " ; q = %f ", &a->q) == 1) {
- if (a->q <= 0.0F || a->q > 1.0F) {
+ if (tc == ';') {
+ if (upto(acceptHdr, "=", true).first != "q") {
+ throw Http400_BadRequest();
+ }
+ const auto qs = upto(acceptHdr, ",", false).first;
+ a.q = std::atof(std::string(qs).c_str());
+ if (a.q <= 0.0F || a.q > 1.0F) {
throw Http400_BadRequest();
}
}
- accepts.push_back(a);
- // NOLINTNEXTLINE(hicpp-vararg)
- if (fscanf(accept.get(), " ,") != 0) {
- break;
- }
+ accepts.push_back(std::move(a));
}
- if (accepts.empty()) {
- throw Http400_BadRequest();
- }
std::stable_sort(accepts.begin(), accepts.end(), [](const auto & a, const auto & b) {
- return a->q > b->q;
+ return a.q > b.q;
});
return accepts;
}
diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h
index b4bab30..a54eae3 100644
--- a/icespider/core/ihttpRequest.h
+++ b/icespider/core/ihttpRequest.h
@@ -15,6 +15,11 @@ namespace IceSpider {
class Core;
class IRouteHandler;
+ struct Accept {
+ std::optional<std::string_view> group, type;
+ float q {1.0F};
+ };
+ using Accepted = std::vector<Accept>;
using PathElements = std::vector<std::string>;
using OptionalString = std::optional<std::string_view>;
using ContentTypeSerializer = std::pair<MimeType, Slicer::SerializerPtr>;
@@ -34,7 +39,7 @@ namespace IceSpider {
[[nodiscard]] virtual OptionalString getCookieParam(const std::string_view &) const = 0;
[[nodiscard]] virtual OptionalString getEnv(const std::string_view &) const = 0;
[[nodiscard]] virtual bool isSecure() const = 0;
- [[nodiscard]] static Accepted parseAccept(const std::string_view &);
+ [[nodiscard]] static Accepted parseAccept(std::string_view);
[[nodiscard]] virtual Slicer::DeserializerPtr getDeserializer() const;
[[nodiscard]] virtual ContentTypeSerializer getSerializer(const IRouteHandler *) const;
[[nodiscard]] virtual std::istream & getInputStream() const = 0;
diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp
index b968397..fc49aff 100644
--- a/icespider/core/irouteHandler.cpp
+++ b/icespider/core/irouteHandler.cpp
@@ -27,10 +27,10 @@ namespace IceSpider {
}
ContentTypeSerializer
- IRouteHandler::getSerializer(const AcceptPtr & a, std::ostream & strm) const
+ IRouteHandler::getSerializer(const Accept & a, std::ostream & strm) const
{
for (const auto & rs : routeSerializers) {
- if ((!a->group || rs.first.group == a->group) && (!a->type || rs.first.type == a->type)) {
+ if ((!a.group || rs.first.group == a.group) && (!a.type || rs.first.type == a.type)) {
return {rs.first, rs.second->create(strm)};
}
}
diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h
index 7889031..6781151 100644
--- a/icespider/core/irouteHandler.h
+++ b/icespider/core/irouteHandler.h
@@ -23,7 +23,7 @@ namespace IceSpider {
virtual ~IRouteHandler() = default;
virtual void execute(IHttpRequest * request) const = 0;
- virtual ContentTypeSerializer getSerializer(const AcceptPtr &, std::ostream &) const;
+ virtual ContentTypeSerializer getSerializer(const Accept &, std::ostream &) const;
virtual ContentTypeSerializer defaultSerializer(std::ostream &) const;
const HttpMethod method;
diff --git a/icespider/unittests/testAccept.cpp b/icespider/unittests/testAccept.cpp
index 91935fc..eb59bb3 100644
--- a/icespider/unittests/testAccept.cpp
+++ b/icespider/unittests/testAccept.cpp
@@ -46,10 +46,10 @@ BOOST_DATA_TEST_CASE(texthtml,
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_REQUIRE(front.group);
+ BOOST_REQUIRE_EQUAL(*front.group, "text");
+ BOOST_REQUIRE(front.type);
+ BOOST_REQUIRE_EQUAL(*front.type, "html");
}
BOOST_DATA_TEST_CASE(textany,
@@ -62,9 +62,9 @@ BOOST_DATA_TEST_CASE(textany,
a)
{
auto front = parse(a).front();
- BOOST_REQUIRE(front->group);
- BOOST_REQUIRE_EQUAL(*front->group, "text");
- BOOST_REQUIRE(!front->type);
+ BOOST_REQUIRE(front.group);
+ BOOST_REQUIRE_EQUAL(*front.group, "text");
+ BOOST_REQUIRE(!front.type);
}
BOOST_DATA_TEST_CASE(anyhtml,
@@ -78,9 +78,9 @@ BOOST_DATA_TEST_CASE(anyhtml,
a)
{
auto front = parse(a).front();
- BOOST_REQUIRE(front->group);
- BOOST_REQUIRE(front->type);
- BOOST_REQUIRE_EQUAL(*front->type, "html");
+ BOOST_REQUIRE(front.group);
+ BOOST_REQUIRE(front.type);
+ BOOST_REQUIRE_EQUAL(*front.type, "html");
}
BOOST_DATA_TEST_CASE(anyany,
@@ -93,8 +93,8 @@ BOOST_DATA_TEST_CASE(anyany,
a)
{
auto front = parse(a).front();
- BOOST_REQUIRE(!front->group);
- BOOST_REQUIRE(!front->type);
+ BOOST_REQUIRE(!front.group);
+ BOOST_REQUIRE(!front.type);
}
BOOST_DATA_TEST_CASE(q1,
@@ -107,8 +107,8 @@ BOOST_DATA_TEST_CASE(q1,
{
auto all = parse(a);
for (const auto & accept : all) {
- BOOST_TEST_CONTEXT(*accept) {
- BOOST_CHECK_CLOSE(accept->q, 1.0, 0.1);
+ BOOST_TEST_CONTEXT(accept) {
+ BOOST_CHECK_CLOSE(accept.q, 1.0, 0.1);
}
}
}
diff --git a/icespider/unittests/testPerf.cpp b/icespider/unittests/testPerf.cpp
index 1958525..5b82213 100644
--- a/icespider/unittests/testPerf.cpp
+++ b/icespider/unittests/testPerf.cpp
@@ -4,6 +4,8 @@
#include <definedDirs.h>
#include <fstream>
+#define BENCHMARK_CAPTURE_LITERAL(Name, Value) BENCHMARK_CAPTURE(Name, Value, Value);
+
class TestRequest : public IceSpider::CgiRequestBase {
public:
TestRequest(IceSpider::Core * c, char ** env) : IceSpider::CgiRequestBase(c, env)
@@ -109,4 +111,21 @@ BENCHMARK_F(CoreFixture, get_cookie_param)(benchmark::State & state)
}
}
+static void
+AcceptParse(benchmark::State & state, const std::string_view accept)
+{
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(IceSpider::IHttpRequest::parseAccept(accept));
+ }
+}
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "*/*");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "any/html");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "image/png, */*");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "image/png;q=0.1, */*");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "image/png;q=0.1, any/html");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "image/png;q=0.9, any/html;q=1.0, something/else;q=1.0");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "image/png;q=0.9, */*;q=1.0");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "text/*");
+BENCHMARK_CAPTURE_LITERAL(AcceptParse, "text/html");
+
BENCHMARK_MAIN();