Add content-encoding parser
Similar to content-type, add an http content-encoding parser.
Tested: Unit tests pass.
Change-Id: Ic62809934f84804c910458184de19ca9a4207ce5
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/include/http_utility.hpp b/include/http_utility.hpp
index 5b8996c..158228f 100644
--- a/include/http_utility.hpp
+++ b/include/http_utility.hpp
@@ -27,7 +27,7 @@
};
inline ContentType getPreferredContentType(
- std::string_view header, std::span<const ContentType> preferedOrder)
+ std::string_view header, std::span<const ContentType> preferredOrder)
{
using boost::spirit::x3::char_;
using boost::spirit::x3::lit;
@@ -63,8 +63,8 @@
{
return parsedType;
}
- auto it = std::ranges::find(preferedOrder, parsedType);
- if (it != preferedOrder.end())
+ auto it = std::ranges::find(preferredOrder, parsedType);
+ if (it != preferredOrder.end())
{
return *it;
}
@@ -86,4 +86,72 @@
return type == allowed;
}
+enum class Encoding
+{
+ ParseError,
+ NoMatch,
+ UnencodedBytes,
+ GZIP,
+ ZSTD,
+ ANY, // represents *. Never returned. Only used for string matching
+};
+
+inline Encoding
+ getPreferredEncoding(std::string_view acceptEncoding,
+ const std::span<const Encoding> availableEncodings)
+{
+ if (acceptEncoding.empty())
+ {
+ return Encoding::UnencodedBytes;
+ }
+
+ using boost::spirit::x3::char_;
+ using boost::spirit::x3::lit;
+ using boost::spirit::x3::omit;
+ using boost::spirit::x3::parse;
+ using boost::spirit::x3::space;
+ using boost::spirit::x3::symbols;
+ using boost::spirit::x3::uint_;
+
+ const symbols<Encoding> knownAcceptEncoding{{"gzip", Encoding::GZIP},
+ {"zstd", Encoding::ZSTD},
+ {"*", Encoding::ANY}};
+
+ std::vector<Encoding> ct;
+
+ auto parameters = *(lit(';') >> lit("q=") >> uint_ >> -(lit('.') >> uint_));
+ auto typeCharset = char_("a-zA-Z.+-");
+ auto encodeType = knownAcceptEncoding | omit[+typeCharset];
+ auto parser = +(encodeType >> omit[parameters >> -char_(',') >> *space]);
+ if (!parse(acceptEncoding.begin(), acceptEncoding.end(), parser, ct))
+ {
+ return Encoding::ParseError;
+ }
+
+ for (const Encoding parsedType : ct)
+ {
+ if (parsedType == Encoding::ANY)
+ {
+ if (!availableEncodings.empty())
+ {
+ return *availableEncodings.begin();
+ }
+ }
+ auto it = std::ranges::find(availableEncodings, parsedType);
+ if (it != availableEncodings.end())
+ {
+ return *it;
+ }
+ }
+
+ // Fall back to raw bytes if it was allowed
+ auto it = std::ranges::find(availableEncodings, Encoding::UnencodedBytes);
+ if (it != availableEncodings.end())
+ {
+ return *it;
+ }
+
+ return Encoding::NoMatch;
+}
+
} // namespace http_helpers
diff --git a/test/include/http_utility_test.cpp b/test/include/http_utility_test.cpp
index 72c4759..2a5e8a7 100644
--- a/test/include/http_utility_test.cpp
+++ b/test/include/http_utility_test.cpp
@@ -114,5 +114,32 @@
getPreferredContentType("text/html, application/json", contentType),
ContentType::NoMatch);
}
+
+TEST(getPreferredEncoding, PositiveTest)
+{
+ std::array<Encoding, 1> encodingsGzip{Encoding::GZIP};
+ EXPECT_EQ(getPreferredEncoding("gzip", encodingsGzip), Encoding::GZIP);
+
+ std::array<Encoding, 2> encodingsGzipZstd{Encoding::GZIP, Encoding::ZSTD};
+ EXPECT_EQ(getPreferredEncoding("gzip", encodingsGzipZstd), Encoding::GZIP);
+ EXPECT_EQ(getPreferredEncoding("zstd", encodingsGzipZstd), Encoding::ZSTD);
+
+ EXPECT_EQ(getPreferredEncoding("*", encodingsGzipZstd), Encoding::GZIP);
+
+ EXPECT_EQ(getPreferredEncoding("zstd, gzip;q=1.0", encodingsGzipZstd),
+ Encoding::ZSTD);
+}
+
+TEST(getPreferredEncoding, NegativeTest)
+{
+ std::array<Encoding, 2> contentType{Encoding::GZIP,
+ Encoding::UnencodedBytes};
+ EXPECT_EQ(getPreferredEncoding("noexist", contentType),
+ Encoding::UnencodedBytes);
+
+ std::array<Encoding, 1> contentType2{Encoding::GZIP};
+ EXPECT_EQ(getPreferredEncoding("zstd", contentType2), Encoding::NoMatch);
+}
+
} // namespace
} // namespace http_helpers