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