Move isJSONContentType to content-type parser

Previously this function was based on a basic string comparison.  This
is fine, but found several inconsistencies, like not handling spaces in
the appropriate places.

This commit creates a new function getContentType, using the new parsing
infrastructure.  As doing this, it showed that the existing parser
functions were not handling case insensitive compares for the mime type.
While this is technically not required, it's something we unit test for,
and relatively easy to add.

Note, that because this parser ignores charset, this moves charset=ascii
from something that previously failed, to something that now succeeds.
This is expected.

Tested: Unit tests pass.  Good coverage

Change-Id: I825a72862135b62112ee504ab0d9ead9d6796354
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/http/parsing.hpp b/http/parsing.hpp
index d596211..b39537c 100644
--- a/http/parsing.hpp
+++ b/http/parsing.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "http/http_request.hpp"
+#include "http_utility.hpp"
 #include "logging.hpp"
 #include "str_utility.hpp"
 
@@ -19,10 +20,8 @@
 
 inline bool isJsonContentType(std::string_view contentType)
 {
-    return bmcweb::asciiIEquals(contentType, "application/json") ||
-           bmcweb::asciiIEquals(contentType,
-                                "application/json; charset=utf-8") ||
-           bmcweb::asciiIEquals(contentType, "application/json;charset=utf-8");
+    return http_helpers::getContentType(contentType) ==
+           http_helpers::ContentType::JSON;
 }
 
 inline JsonParseResult parseRequestAsJson(const crow::Request& req,
diff --git a/include/http_utility.hpp b/include/http_utility.hpp
index cb3ebc2..681afa1 100644
--- a/include/http_utility.hpp
+++ b/include/http_utility.hpp
@@ -26,11 +26,50 @@
     EventStream,
 };
 
-inline ContentType getPreferredContentType(
-    std::string_view header, std::span<const ContentType> preferredOrder)
+inline ContentType getContentType(std::string_view contentTypeHeader)
 {
     using boost::spirit::x3::char_;
     using boost::spirit::x3::lit;
+    using boost::spirit::x3::no_case;
+    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<ContentType> knownMimeType{
+        {"application/cbor", ContentType::CBOR},
+        {"application/json", ContentType::JSON},
+        {"application/octet-stream", ContentType::OctetStream},
+        {"text/event-stream", ContentType::EventStream},
+        {"text/html", ContentType::HTML}};
+
+    ContentType ct = ContentType::NoMatch;
+
+    auto typeCharset = +(char_("a-zA-Z0-9.+-"));
+
+    auto parameters =
+        *(lit(';') >> *space >> typeCharset >> lit("=") >> typeCharset);
+    auto parser = no_case[knownMimeType] >> omit[parameters];
+    std::string_view::iterator begin = contentTypeHeader.begin();
+    if (!parse(begin, contentTypeHeader.end(), parser, ct))
+    {
+        return ContentType::NoMatch;
+    }
+    if (begin != contentTypeHeader.end())
+    {
+        return ContentType::NoMatch;
+    }
+
+    return ct;
+}
+
+inline ContentType getPreferredContentType(
+    std::string_view acceptsHeader, std::span<const ContentType> preferredOrder)
+{
+    using boost::spirit::x3::char_;
+    using boost::spirit::x3::lit;
+    using boost::spirit::x3::no_case;
     using boost::spirit::x3::omit;
     using boost::spirit::x3::parse;
     using boost::spirit::x3::space;
@@ -50,10 +89,10 @@
     auto typeCharset = +(char_("a-zA-Z0-9.+-"));
 
     auto parameters = *(lit(';') >> typeCharset >> lit("=") >> typeCharset);
-    auto mimeType = knownMimeType |
+    auto mimeType = no_case[knownMimeType] |
                     omit[+typeCharset >> lit('/') >> +typeCharset];
     auto parser = +(mimeType >> omit[parameters >> -char_(',') >> *space]);
-    if (!parse(header.begin(), header.end(), parser, ct))
+    if (!parse(acceptsHeader.begin(), acceptsHeader.end(), parser, ct))
     {
         return ContentType::NoMatch;
     }
diff --git a/test/http/parsing_test.cpp b/test/http/parsing_test.cpp
index e51e89c..f35f761 100644
--- a/test/http/parsing_test.cpp
+++ b/test/http/parsing_test.cpp
@@ -12,6 +12,7 @@
     // The Redfish specification DSP0266 shows no space between the ; and
     // charset.
     EXPECT_TRUE(isJsonContentType("application/json;charset=utf-8"));
+    EXPECT_TRUE(isJsonContentType("application/json;charset=ascii"));
 
     // Sites like mozilla show the space included [1]
     //  https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
@@ -26,7 +27,6 @@
     EXPECT_FALSE(isJsonContentType(";"));
     EXPECT_FALSE(isJsonContentType("application/json;"));
     EXPECT_FALSE(isJsonContentType("application/json; "));
-    EXPECT_FALSE(isJsonContentType("application/json; charset=ascii"));
     EXPECT_FALSE(isJsonContentType("json"));
 }
 } // namespace